202 lines
8.2 KiB
GDScript
202 lines
8.2 KiB
GDScript
extends RefCounted
|
|
class_name VizBend
|
|
|
|
var _error : String
|
|
|
|
var _bend_normal : Vector3
|
|
var _bend_plane : Plane
|
|
var _begin_on_bend_plane : Vector3
|
|
var _end_on_bend_plane : Vector3
|
|
var _intersect_data : OptVector3
|
|
|
|
var _plane_join_point : Vector3
|
|
|
|
var _bend_side : float
|
|
var _curve_side : float
|
|
|
|
var _bend_begin_triangle : VizTriangle
|
|
var _bend_end_triangle : VizTriangle
|
|
|
|
var _arc_begin_mid : Vector3
|
|
var _arc_end_mid : Vector3
|
|
|
|
var _begin : Vector3
|
|
var _begin_dir : Vector3
|
|
var _end : Vector3
|
|
var _end_dir : Vector3
|
|
|
|
class OptVector3:
|
|
var vector : Vector3
|
|
func _init(p_data : Vector3):
|
|
vector = p_data
|
|
|
|
func _init(p_begin : VisualizationSpot, p_end : VisualizationSpot, p_width : float, p_bend_lip : float):
|
|
if not _is_bendable(p_begin, p_end):
|
|
return
|
|
|
|
_calc_bend_normal(p_begin, p_end)
|
|
if not _calc_intersection_point(p_begin, p_end):
|
|
return
|
|
_calc_join_point(p_begin, p_end)
|
|
_calc_bend_side(p_begin, p_end)
|
|
_calc_triangles(p_begin, p_end, p_width, p_bend_lip)
|
|
_calc_arc_plane()
|
|
_begin = _bend_begin_triangle.get_leg_mid()
|
|
if not _is_in_order(p_begin.point, _begin, _plane_join_point):
|
|
_error = "bend point is too close to beginning for %s to %s with lip %s" % [p_begin.point, p_end.point, p_bend_lip]
|
|
return
|
|
_end = _bend_end_triangle.get_leg_mid()
|
|
if not _is_in_order(_plane_join_point, _end, p_end.point):
|
|
_error = "bend point is too close to end for %s to %s with lip %s" % [p_begin.point, p_end.point, p_bend_lip]
|
|
return
|
|
|
|
func _to_string():
|
|
return "%s->%s->%s" % [_begin, _plane_join_point, _end]
|
|
|
|
# ---------------------------------------------
|
|
# Public Methoeds
|
|
|
|
func get_error() -> String:
|
|
return _error
|
|
|
|
func is_invalid() -> bool:
|
|
if _error != "":
|
|
return true
|
|
return false
|
|
|
|
func get_begin() -> Vector3:
|
|
return _begin
|
|
|
|
func get_end() -> Vector3:
|
|
return _end
|
|
|
|
func is_angled() -> bool:
|
|
return not is_zero_approx(_bend_side)
|
|
|
|
func is_angled_against_curve() -> bool:
|
|
if _bend_side * _curve_side < 0:
|
|
return true
|
|
return false
|
|
|
|
func get_half_leg_width() -> Vector3:
|
|
return (_bend_begin_triangle.leg_left - _bend_begin_triangle.leg_right) / 2.0
|
|
|
|
func get_half_bend_width() -> Vector3:
|
|
return (_bend_begin_triangle.bend_left - _bend_begin_triangle.bend_right) / 2.0
|
|
|
|
func get_begin_triangle() -> VizTriangle:
|
|
return _bend_begin_triangle
|
|
|
|
func get_end_triangle() -> VizTriangle:
|
|
return _bend_end_triangle
|
|
|
|
func get_shift_width() -> Vector3:
|
|
return _arc_begin_mid - _bend_begin_triangle.get_bend_mid() + _bend_end_triangle.get_bend_mid() - _arc_end_mid
|
|
|
|
func get_shift_offset() -> Vector3:
|
|
return _arc_begin_mid - _bend_begin_triangle.get_bend_mid()
|
|
|
|
func get_arc_begin_point() -> Vector3:
|
|
return _arc_begin_mid
|
|
|
|
func get_arc_end_point() -> Vector3:
|
|
return _arc_end_mid
|
|
|
|
func get_arc_point(weight : float, sharpness : float) -> Vector3:
|
|
return _curve_point(_arc_begin_mid, _plane_join_point, _arc_end_mid, weight, sharpness)
|
|
|
|
# ---------------------------------------------
|
|
# Private Methods
|
|
|
|
func _curve_point(p0 : Vector3, p1 : Vector3, p2 : Vector3, t : float, s : float) -> Vector3:
|
|
var p := p1 + pow(1-t, 2.0) * s * (p0-p1) + pow(t, 2.0) * s * (p2-p1)
|
|
return p
|
|
|
|
func _is_in_order(b : Vector3, m : Vector3, e : Vector3) -> bool:
|
|
return (e-m).normalized().dot((m-b).normalized()) > 0
|
|
|
|
func _is_bendable(p_begin : VisualizationSpot, p_end : VisualizationSpot) -> bool:
|
|
var dir := (p_end.point - p_begin.point).normalized()
|
|
var begin_norm_sign := signf(dir.dot(p_begin.normal))
|
|
var end_norm_sign := signf(dir.dot(p_end.normal))
|
|
if begin_norm_sign == end_norm_sign:
|
|
_error = "%s cannot be twisted into %s" % [ p_begin.normal, p_end.normal ]
|
|
return false
|
|
_curve_side = end_norm_sign
|
|
return true
|
|
|
|
func _calc_bend_normal(p_begin : VisualizationSpot, p_end : VisualizationSpot):
|
|
var middle = p_begin.point + (p_end.point - p_begin.point) / 2.0
|
|
_bend_normal = p_begin.normal.cross(p_end.normal).normalized()
|
|
_bend_plane = Plane(_bend_normal, middle)
|
|
|
|
func _calc_intersection_point(p_begin : VisualizationSpot, p_end : VisualizationSpot) -> bool:
|
|
_begin_on_bend_plane = _bend_plane.project(p_begin.point)
|
|
_end_on_bend_plane = _bend_plane.project(p_end.point)
|
|
var dir := _end_on_bend_plane - _begin_on_bend_plane
|
|
var begin_normal_on_bend_plane := _bend_plane.project(p_begin.point + p_begin.normal) - _begin_on_bend_plane
|
|
var end_normal_on_bend_plane := _bend_plane.project(p_end.point + p_end.normal) - _end_on_bend_plane
|
|
var begin_bend_binormal := begin_normal_on_bend_plane.cross(dir)
|
|
var begin_bend_tangent := begin_bend_binormal.cross(begin_normal_on_bend_plane).normalized()
|
|
var end_bend_binormal := end_normal_on_bend_plane.cross(dir)
|
|
var end_bend_tangent := end_bend_binormal.cross(end_normal_on_bend_plane).normalized()
|
|
_intersect_data = _intersect(_begin_on_bend_plane, begin_bend_tangent, _end_on_bend_plane, end_bend_tangent, _bend_normal)
|
|
if _intersect_data == null:
|
|
_error = "intersection failed unexpectedly: normal %s at %s is parallel with normal %s at %s" % [ begin_normal_on_bend_plane, _begin_on_bend_plane, end_normal_on_bend_plane, _end_on_bend_plane ]
|
|
return false
|
|
return true
|
|
|
|
func _calc_join_point(p_begin : VisualizationSpot, p_end : VisualizationSpot):
|
|
var begin_bend_from_intersect := p_begin.point + _intersect_data.vector - _begin_on_bend_plane
|
|
var end_bend_from_intersect := p_end.point + _intersect_data.vector - _end_on_bend_plane
|
|
var begin_intersect_distance := begin_bend_from_intersect.distance_to(p_begin.point)
|
|
var end_intersect_distance := end_bend_from_intersect.distance_to(p_end.point)
|
|
var begin_to_end_on_bend_distance := end_bend_from_intersect.distance_to(begin_bend_from_intersect)
|
|
var begin_to_end_intersect_join_numerator := begin_to_end_on_bend_distance * begin_intersect_distance / end_intersect_distance
|
|
var begin_to_end_intersect_join_denominator := 1.0 + begin_intersect_distance / end_intersect_distance
|
|
var begin_on_end_intersect_join_distance := begin_to_end_intersect_join_numerator / begin_to_end_intersect_join_denominator
|
|
var begin_bend_dir := begin_bend_from_intersect - p_begin.point
|
|
var end_bend_dir := end_bend_from_intersect - p_begin.point
|
|
var begin_to_end_normal := begin_bend_dir.cross(end_bend_dir)
|
|
var begin_to_end_binormal := begin_to_end_normal.cross(_bend_normal)
|
|
var begin_on_end_intersect_side = signf(end_bend_dir.dot(begin_to_end_binormal))
|
|
_plane_join_point = begin_bend_from_intersect - begin_on_end_intersect_side * begin_on_end_intersect_join_distance * _bend_normal
|
|
|
|
func _calc_bend_side(p_begin : VisualizationSpot, p_end : VisualizationSpot):
|
|
_begin_dir = Vector3(_plane_join_point - p_begin.point).normalized()
|
|
_end_dir = Vector3(p_end.point - _plane_join_point).normalized()
|
|
_bend_side = signf(_bend_normal.dot(_end_dir))
|
|
|
|
func _calc_triangles(p_begin : VisualizationSpot, p_end : VisualizationSpot, p_width : float, p_bend_lip : float):
|
|
var begin_binormal := _begin_dir.cross(p_begin.normal).normalized()
|
|
var end_binormal := _end_dir.cross(p_end.normal).normalized()
|
|
_bend_begin_triangle = VizTriangle.new()
|
|
_bend_begin_triangle.update(_begin_dir, _plane_join_point, begin_binormal, p_width, p_bend_lip, _bend_normal, _bend_side, _curve_side)
|
|
_bend_end_triangle = VizTriangle.new()
|
|
_bend_end_triangle.update(-_end_dir, _plane_join_point, end_binormal, p_width, p_bend_lip, _bend_normal, -_bend_side, _curve_side)
|
|
|
|
func _calc_arc_plane():
|
|
var begin_mid := _bend_begin_triangle.get_leg_mid()
|
|
var end_mid := _bend_end_triangle.get_leg_mid()
|
|
var arc_center := _plane_join_point - (_plane_join_point - begin_mid) - (_plane_join_point - end_mid)
|
|
var arc_plane := Plane(_bend_normal, arc_center)
|
|
_arc_begin_mid = arc_plane.project(_bend_begin_triangle.get_bend_mid())
|
|
_arc_end_mid = arc_plane.project(_bend_end_triangle.get_bend_mid())
|
|
|
|
func _intersect(a : Vector3, a_norm : Vector3, b : Vector3, b_norm : Vector3, plane_norm : Vector3) -> OptVector3:
|
|
var seg := b - a
|
|
var seg_len := seg.length()
|
|
var seg_norm := seg.normalized()
|
|
var alpha_sign := signf(b_norm.cross(a_norm).dot(plane_norm))
|
|
var cos_alpha := b_norm.dot(a_norm)
|
|
var beta_sign := signf(seg_norm.cross(b_norm).dot(plane_norm))
|
|
var cos_beta := seg_norm.dot(b_norm)
|
|
var intersect_side := alpha_sign * beta_sign
|
|
var sin_alpha := sqrt(1 - cos_alpha*cos_alpha)
|
|
var sin_beta := sqrt(1 - cos_beta*cos_beta)
|
|
if is_equal_approx(sin_alpha, 0.0):
|
|
return null
|
|
var q := seg_len * sin_beta / sin_alpha
|
|
var p := a - intersect_side * q * a_norm
|
|
return OptVector3.new(p)
|
|
|