1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
tool
extends Node2D
const DEBUG_DRAW_LINE_LENGTH = 128.0
class_name SS2D_Shape_Anchor, "../assets/Anchor.svg"
export (NodePath) var shape_path: NodePath setget set_shape_path
export (int) var shape_point_index: int = 0 setget set_shape_point_index
export (float, 0.0, 1.0) var shape_point_offset: float = 0.0 setget set_shape_point_offset
export (float, 0, 3.14) var child_rotation: float = 3.14 setget set_child_rotation
export (bool) var use_shape_scale: bool = false setget set_use_shape_scale
export (bool) var debug_draw: bool = false setget set_debug_draw
var cached_shape_transform:Transform2D = Transform2D.IDENTITY
var shape = null
###########
# SETTERS #
###########
func set_shape_path(value: NodePath):
# Assign path value
shape_path = value
set_shape()
property_list_changed_notify()
refresh()
func set_shape():
# Disconnect old shape
if shape != null:
disconnect_shape(shape)
# Set shape if path is valid and connect
shape = null
if has_node(shape_path):
var new_node = get_node(shape_path)
if not new_node is SS2D_Shape_Base:
push_error("Shape Path isn't a valid subtype of SS2D_Shape_Base! Aborting...")
return
shape = new_node
connect_shape(shape)
shape_point_index = get_shape_index_range(shape, shape_point_index)
func get_shape_index_range(s:SS2D_Shape_Base, idx:int)->int:
var point_count = s.get_point_count()
# Subtract 2;
# 'point_count' is out of bounds; subtract 1
# cannot use final idx as starting point_index; subtract another 1
var final_idx = point_count - 2
if idx < 0:
idx = final_idx
idx = idx % (final_idx + 1)
return idx
func set_shape_point_index(value: int):
if value == shape_point_index:
return
if shape == null:
shape_point_index = value
return
shape_point_index = get_shape_index_range(shape, value)
#property_list_changed_notify()
refresh()
func set_shape_point_offset(value: float):
shape_point_offset = value
#property_list_changed_notify()
refresh()
func set_use_shape_scale(value: bool):
use_shape_scale = value
#property_list_changed_notify()
refresh()
func set_child_rotation(value: float):
child_rotation = value
#property_list_changed_notify()
refresh()
func set_debug_draw(v: bool):
debug_draw = v
#property_list_changed_notify()
refresh()
##########
# EVENTS #
##########
func _process(delta):
if shape == null:
set_shape()
return
if shape.is_queued_for_deletion():
return
if shape.get_global_transform() != cached_shape_transform:
cached_shape_transform = shape.get_global_transform()
refresh()
func _monitored_node_leaving():
set_shape_path("")
func _handle_point_change():
refresh()
#########
# LOGIC #
#########
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float) -> Vector2:
var q0 = p0.linear_interpolate(p1, t)
var q1 = p1.linear_interpolate(p2, t)
var q2 = p2.linear_interpolate(p3, t)
var r0 = q0.linear_interpolate(q1, t)
var r1 = q1.linear_interpolate(q2, t)
var s = r0.linear_interpolate(r1, t)
return s
func disconnect_shape(s: SS2D_Shape_Base):
s.disconnect("points_modified", self, "_handle_point_change")
s.disconnect("tree_exiting", self, "_monitored_node_leaving")
func connect_shape(s: SS2D_Shape_Base):
s.connect("points_modified", self, "_handle_point_change")
s.connect("tree_exiting", self, "_monitored_node_leaving")
func refresh():
if shape == null:
return
if not is_instance_valid(shape):
return
if shape.is_queued_for_deletion():
disconnect_shape(shape)
shape = null
return
# Subtract one, cannot use final point as starting index
var point_count = shape.get_point_count() - 1
var pt_a_index = shape_point_index
var pt_b_index = shape_point_index + 1
var pt_a_key = shape.get_point_key_at_index(pt_a_index)
var pt_b_key = shape.get_point_key_at_index(pt_b_index)
var pt_a: Vector2 = shape.global_transform.xform(shape.get_point_position(pt_a_key))
var pt_b: Vector2 = shape.global_transform.xform(shape.get_point_position(pt_b_key))
var pt_a_handle: Vector2
var pt_b_handle: Vector2
var n_pt: Vector2
var n_pt_a: Vector2
var n_pt_b: Vector2
var angle = 0.0
pt_a_handle = shape.global_transform.xform(
shape.get_point_position(pt_a_key) + shape.get_point_out(pt_a_key)
)
pt_b_handle = shape.global_transform.xform(
shape.get_point_position(pt_b_key) + shape.get_point_in(pt_b_key)
)
n_pt = _cubic_bezier(pt_a, pt_a_handle, pt_b_handle, pt_b, shape_point_offset)
n_pt_a = _cubic_bezier(
pt_a, pt_a_handle, pt_b_handle, pt_b, clamp(shape_point_offset - 0.1, 0.0, 1.0)
)
n_pt_b = _cubic_bezier(
pt_a, pt_a_handle, pt_b_handle, pt_b, clamp(shape_point_offset + 0.1, 0.0, 1.0)
)
angle = atan2(n_pt_a.y - n_pt_b.y, n_pt_a.x - n_pt_b.x)
self.global_transform = Transform2D(angle + child_rotation, n_pt)
if use_shape_scale:
self.scale = shape.scale
update()
func _draw():
if Engine.editor_hint and debug_draw:
draw_line(Vector2.ZERO, Vector2(0, -DEBUG_DRAW_LINE_LENGTH), self.modulate)
|