projektluzid/addons/vizpath/utilities/viz_mid.gd
2025-06-07 19:10:56 +02:00

102 lines
3.4 KiB
GDScript

extends RefCounted
class_name VizMid
var _seg1 : VizSegment
var _seg2 : VizSegment
var _width : float
var _inner_radius : float
var _rotation_point : VisualizationSpot
var _angle : float
var _error : String
func _init(p_seg1 : VizSegment, p_seg2 : VizSegment, p_stroke_width : float, p_inner_radius : float):
_seg1 = p_seg1
_seg2 = p_seg2
_width = p_stroke_width
_inner_radius = p_inner_radius
assert(p_seg1.get_end().is_equal(p_seg2.get_begin()))
_calc_rotation_point()
var center_distance := calc_center_distance(p_stroke_width, p_inner_radius)
var turn_distance := calc_turn_distance(center_distance, _angle)
p_seg1.adjust_end(turn_distance)
if p_seg1.is_invalid():
return
p_seg2.adjust_begin(turn_distance)
if p_seg2.is_invalid():
return
# ---------------------------------------------
# Public Methoeds
func is_invalid() -> bool:
return _error != ""
func get_error() -> String:
return _error
func update_mesh(_mesh_instance : MeshInstance3D, u : float, num_segs : int, mat : Material):
return _create_fan(_mesh_instance, u, num_segs, mat)
# ---------------------------------------------
# Private Methods
func _calc_rotation_point():
var seg1_ray := _seg1.get_end_ray()
var seg2_ray := _seg2.get_begin_ray()
_angle = seg1_ray.angle_to(seg2_ray)
var join_point := _seg1.get_end()
var plane_normal := seg1_ray.cross(seg2_ray).normalized()
if plane_normal == Vector3.ZERO:
plane_normal = join_point.normal
var binormal = plane_normal.cross(seg1_ray).normalized()
var d := calc_center_distance(_width, _inner_radius)
var e := calc_turn_distance(d, _angle)
_rotation_point = VisualizationSpot.new()
_rotation_point.point = join_point.point - e * seg1_ray + d * binormal
_rotation_point.normal = plane_normal
func _create_fan(mesh_node : Node3D, u : float, num_segs : int, mat : Material) -> float:
var radius := _width/2.0 + _inner_radius
var arc_len := absf(_angle) * radius
var begin_half_width := _seg1.get_end_binormal() * _width/2.0
var end_half_width := _seg2.get_begin_binormal() * _width/2.0
var st := SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
st.set_color(Color(1, 0, 0))
st.set_normal(_seg1.get_end().normal)
st.set_uv(Vector2(u, 1))
st.add_vertex(_seg1.get_end().point + begin_half_width)
st.set_uv(Vector2(u, 0))
st.add_vertex(_seg1.get_end().point - begin_half_width)
var outer_base := _seg1.get_end().point + begin_half_width - _rotation_point.point
var inner_base := _seg1.get_end().point - begin_half_width - _rotation_point.point
for i in range(1, num_segs-1):
var cur_angle : float = _angle * float(i) / float(num_segs-1)
var cur_arc_len = absf(cur_angle) * radius
var inner := _rotation_point.point + inner_base.rotated(_rotation_point.normal, cur_angle)
var outer := _rotation_point.point + outer_base.rotated(_rotation_point.normal, cur_angle)
st.set_uv(Vector2(u+cur_arc_len, 1))
st.add_vertex(outer)
st.set_uv(Vector2(u+cur_arc_len, 0))
st.add_vertex(inner)
st.set_uv(Vector2(u+arc_len, 1))
st.add_vertex(_seg2.get_begin().point + end_half_width)
st.set_uv(Vector2(u+arc_len, 0))
st.add_vertex(_seg2.get_begin().point - end_half_width)
st.set_material(mat)
mesh_node.mesh = st.commit(mesh_node.mesh)
return u + arc_len
static func calc_center_distance(stroke_width : float, inner_radius : float) -> float:
return stroke_width / 2.0 + inner_radius
static func calc_turn_distance(center_distance : float, angle : float) -> float:
return center_distance * tan(abs(angle)/2.0)