-
-
Notifications
You must be signed in to change notification settings - Fork 473
Expand file tree
/
Copy pathBaseShapeDrawer.gd
More file actions
264 lines (205 loc) · 7.4 KB
/
BaseShapeDrawer.gd
File metadata and controls
264 lines (205 loc) · 7.4 KB
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
extends BaseDrawTool
var _start := Vector2i.ZERO
var _offset := Vector2i.ZERO
var _dest := Vector2i.ZERO
var _fill_inside := false
var _drawing := false
var _displace_origin := false
var _thickness := 1
func _init() -> void:
_drawer.color_op = Drawer.ColorOp.new()
update_indicator()
func _ready() -> void:
super()
if tool_slot.button == MOUSE_BUTTON_RIGHT:
$ThicknessSlider.allow_global_input_events = not Global.share_options_between_tools
Global.share_options_between_tools_changed.connect(
func(enabled): $ThicknessSlider.allow_global_input_events = not enabled
)
func update_brush() -> void:
pass
func _on_Thickness_value_changed(value: int) -> void:
_thickness = value
update_indicator()
update_config()
save_config()
func update_indicator() -> void:
var indicator := BitMap.new()
var rect := _get_result_rect(_start, _dest)
var points := _get_points(rect.size)
var t_offset := _thickness - 1
var t_offsetv := Vector2i(t_offset, t_offset)
indicator.create(rect.size + t_offsetv)
for point in points:
indicator.set_bitv(point, 1)
_indicator = indicator
_polylines = _create_polylines(_indicator)
func _on_FillCheckbox_toggled(button_pressed: bool) -> void:
_fill_inside = button_pressed
update_config()
save_config()
func get_config() -> Dictionary:
var config := super.get_config()
config["fill_inside"] = _fill_inside
config["thickness"] = _thickness
return config
func set_config(config: Dictionary) -> void:
super.set_config(config)
_fill_inside = config.get("fill_inside", _fill_inside)
_thickness = config.get("thickness", _thickness)
func update_config() -> void:
super.update_config()
$FillCheckbox.button_pressed = _fill_inside
$ThicknessSlider.value = _thickness
## This tool has no brush, so just return the indicator as it is.
func _create_brush_indicator() -> BitMap:
return _indicator
func _get_shape_points(_size: Vector2i) -> Array[Vector2i]:
return []
func _get_shape_points_filled(_size: Vector2i) -> Array[Vector2i]:
return []
func _input(event: InputEvent) -> void:
if _drawing:
if event.is_action_pressed("shape_displace"):
_displace_origin = true
elif event.is_action_released("shape_displace"):
_displace_origin = false
else:
# If options are being shared, no need to change the brush size on the right tool slots,
# otherwise it will be changed twice on both left and right tools.
if tool_slot.button == MOUSE_BUTTON_RIGHT and Global.share_options_between_tools:
return
var brush_size_value := _mm_action.get_action_distance_int(event)
$ThicknessSlider.value += brush_size_value
func draw_start(pos: Vector2i) -> void:
pos = snap_position(pos)
super.draw_start(pos)
if Input.is_action_pressed(&"draw_color_picker", true):
_picking_color = true
_pick_color(pos)
return
_picking_color = false
Global.canvas.selection.transform_content_confirm()
update_mask()
if Global.mirror_view:
# mirroring position is ONLY required by "Preview"
pos.x = Global.current_project.size.x - pos.x - 1
_start = pos
_offset = pos
_dest = pos
_drawing = true
func draw_move(pos: Vector2i) -> void:
pos = snap_position(pos)
super.draw_move(pos)
if _picking_color: # Still return even if we released draw_color_picker (Alt)
if Input.is_action_pressed(&"draw_color_picker", true):
_pick_color(pos)
return
if _drawing:
if Global.mirror_view:
# mirroring position is ONLY required by "Preview"
pos.x = Global.current_project.size.x - pos.x - 1
if _displace_origin:
_start += pos - _offset
_dest = pos
_offset = pos
_set_cursor_text(_get_result_rect(_start, pos))
func draw_end(pos: Vector2i) -> void:
pos = snap_position(pos)
if _picking_color:
super.draw_end(pos)
return
if _drawing:
if Global.mirror_view:
# now we revert back the coordinates from their mirror form so that shape can be drawn
_start.x = (Global.current_project.size.x - 1) - _start.x
_offset.x = (Global.current_project.size.x - 1) - _offset.x
_dest.x = (Global.current_project.size.x - 1) - _dest.x
if _thickness % 2 == 0:
_start.x += 1
_offset.x += 1
_dest.x += 1
pos.x += 1
_draw_shape(_start, pos)
_reset_tool()
super.draw_end(pos)
func cancel_tool() -> void:
super()
_reset_tool()
func _reset_tool() -> void:
_start = Vector2i.ZERO
_dest = Vector2i.ZERO
_drawing = false
Global.canvas.previews_sprite.texture = null
_displace_origin = false
cursor_text = ""
func draw_preview() -> void:
var canvas := Global.canvas.previews_sprite
if _drawing:
var rect := _get_result_rect(_start, _dest)
var points := _get_points(rect.size)
var image := Image.create(
Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_LA8
)
var thickness_vector := (
rect.position - Vector2i((Vector2(0.5, 0.5) * (_thickness - 1)).ceil())
)
for i in points.size():
points[i] += thickness_vector
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(points[i]):
image.set_pixelv(points[i], Color.WHITE)
# Handle mirroring
for point in mirror_array(points):
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
image.set_pixelv(point, Color.WHITE)
var texture := ImageTexture.create_from_image(image)
canvas.texture = texture
func _draw_shape(origin: Vector2i, dest: Vector2i) -> void:
var rect := _get_result_rect(origin, dest)
var points := _get_points(rect.size)
prepare_undo()
var images := _get_selected_draw_images()
var thickness_vector := rect.position - Vector2i((Vector2(0.5, 0.5) * (_thickness - 1)).ceil())
for point in points:
# Reset drawer every time because pixel perfect sometimes breaks the tool
_drawer.reset()
# Draw each point offsetted based on the shape's thickness
var draw_pos := point + thickness_vector
if Tools.is_placing_tiles():
draw_tile(draw_pos)
else:
if Global.current_project.can_pixel_get_drawn(draw_pos):
for image in images:
_drawer.set_pixel(image, draw_pos, tool_slot.color)
commit_undo("Draw Shape")
## Given an origin point and destination point, returns a rect representing
## where the shape will be drawn and what is its size
func _get_result_rect(origin: Vector2i, dest: Vector2i) -> Rect2i:
var rect := Rect2i()
# Center the rect on the mouse
if Input.is_action_pressed("shape_center"):
var new_size := dest - origin
# Make rect 1:1 while centering it on the mouse
if Input.is_action_pressed("shape_perfect"):
var square_size := maxi(absi(new_size.x), absi(new_size.y))
new_size = Vector2i(square_size, square_size)
origin -= new_size
dest = origin + 2 * new_size
# Make rect 1:1 while not trying to center it
if Input.is_action_pressed("shape_perfect"):
var square_size := mini(absi(origin.x - dest.x), absi(origin.y - dest.y))
rect.position.x = origin.x if origin.x < dest.x else origin.x - square_size
rect.position.y = origin.y if origin.y < dest.y else origin.y - square_size
rect.size = Vector2i(square_size, square_size)
# Get the rect without any modifications
else:
rect.position = Vector2i(mini(origin.x, dest.x), mini(origin.y, dest.y))
rect.size = (origin - dest).abs()
rect.size += Vector2i.ONE
return rect
func _get_points(shape_size: Vector2i) -> Array[Vector2i]:
return _get_shape_points_filled(shape_size) if _fill_inside else _get_shape_points(shape_size)
func _set_cursor_text(rect: Rect2i) -> void:
cursor_text = "%s, %s" % [rect.position.x, rect.position.y]
cursor_text += " -> %s, %s" % [rect.end.x - 1, rect.end.y - 1]
cursor_text += " (%s, %s)" % [rect.size.x, rect.size.y]