Skip to content

Commit d25f5b5

Browse files
authored
Various fixes related to grid snapping and tilemap layers (#1340)
* Fix snapping on paste * clear selection when using move tool and tool is placing tiles * fix some issues * Fix more stuff + as i am an idiot, i had to remove some useless code * typo: fixed snapping to offset * Fix tiles not updating when pasting, also fix errors popping up when selection bounds is zero
1 parent 9250aff commit d25f5b5

File tree

8 files changed

+92
-76
lines changed

8 files changed

+92
-76
lines changed

src/Autoload/DrawingAlgos.gd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ func transform_image_with_viewport(
343343

344344
# Estimate new bounding box
345345
var bounds := get_transformed_bounds(original_image.get_size(), full_transform)
346+
if bounds.size.x == 0 or bounds.size.y == 0:
347+
return
346348
var viewport_size := bounds.size.ceil() as Vector2i
347349
if viewport_size.x == 1:
348350
viewport_size.x = 2

src/Autoload/Tools.gd

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -689,33 +689,44 @@ func get_closest_point_to_segment(
689689

690690

691691
func snap_to_rectangular_grid_boundary(
692-
pos: Vector2, grid_size: Vector2i, grid_offset := Vector2i.ZERO, snapping_distance := 9999.0
692+
pos: Vector2, grid_size: Vector2i, grid_offset: Vector2, snapping_distance := 9999.0
693693
) -> Vector2:
694-
var grid_pos := pos.snapped(grid_size)
695-
grid_pos += Vector2(grid_offset)
696-
# keeping grid_pos as is would have been fine but this adds extra accuracy as to
697-
# which snap point (from the list below) is closest to mouse and occupy THAT point
698-
# t_l is for "top left" and so on
699-
var t_l := grid_pos + Vector2(-grid_size.x, -grid_size.y)
700-
var t_c := grid_pos + Vector2(0, -grid_size.y)
701-
var t_r := grid_pos + Vector2(grid_size.x, -grid_size.y)
702-
var m_l := grid_pos + Vector2(-grid_size.x, 0)
703-
var m_c := grid_pos
704-
var m_r := grid_pos + Vector2(grid_size.x, 0)
705-
var b_l := grid_pos + Vector2(-grid_size.x, grid_size.y)
706-
var b_c := grid_pos + Vector2(0, grid_size.y)
707-
var b_r := grid_pos + Vector2(grid_size)
708-
var vec_arr: PackedVector2Array = [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
709-
for vec in vec_arr:
710-
if vec.distance_to(pos) < grid_pos.distance_to(pos):
711-
grid_pos = vec
712-
694+
## Get the closest grid intersection
695+
var grid_pos := (pos - grid_offset).snapped(grid_size) # Get closest box without offset
696+
grid_pos += Vector2(grid_offset) # apply offset
697+
## Get the point on boundary of grid box (that contains the intersection)
713698
var grid_point := _get_closest_point_to_grid(pos, snapping_distance, grid_pos)
714699
if grid_point != Vector2.INF:
715700
pos = grid_point.floor()
716701
return pos
717702

718703

704+
func snap_to_rectangular_grid_center(
705+
pos: Vector2, grid_size: Vector2i, grid_offset: Vector2i, snapping_distance := 9999.0
706+
) -> Vector2:
707+
var grid_center := pos.snapped(grid_size) + Vector2(grid_size / 2)
708+
grid_center += Vector2(grid_offset)
709+
if snapping_distance < 0:
710+
pos = grid_center.floor()
711+
else:
712+
if grid_center.distance_to(pos) <= snapping_distance:
713+
pos = grid_center.floor()
714+
return pos
715+
716+
717+
func snap_to_guide(
718+
snap_to: Vector2, pos: Vector2, distance: float, s1: Vector2, s2: Vector2
719+
) -> Vector2:
720+
var closest_point := Tools.get_closest_point_to_segment(pos, distance, s1, s2)
721+
if closest_point == Vector2.INF: # Is not close to a guide
722+
return Vector2.INF
723+
# Snap to the closest guide
724+
if snap_to == Vector2.INF or (snap_to - pos).length() > (closest_point - pos).length():
725+
snap_to = closest_point
726+
727+
return snap_to
728+
729+
719730
func set_button_size(button_size: int) -> void:
720731
var size := Vector2(24, 24) if button_size == Global.ButtonSize.SMALL else Vector2(32, 32)
721732
if not is_instance_valid(_tool_buttons):

src/Tools/BaseSelectionTool.gd

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ func set_spinbox_values() -> void:
9494
_skip_slider_logic = false
9595

9696

97-
func draw_start(pos: Vector2i) -> void:
98-
pos = snap_position(pos)
97+
func draw_start(mouse_pos: Vector2i) -> void:
98+
var pos: Vector2i = snap_position(mouse_pos)
9999
super(pos)
100100
_transformation_status_changed = false
101101
if transformation_handles.arrow_key_move:
@@ -108,8 +108,9 @@ func draw_start(pos: Vector2i) -> void:
108108
_offset = pos
109109

110110
var quick_copy := Input.is_action_pressed("transform_copy_selection_content", true)
111+
# we check if the un-snapped point was hovering a selection
111112
if (
112-
selection_node.preview_selection_map.is_pixel_selected(pos)
113+
selection_node.preview_selection_map.is_pixel_selected(mouse_pos)
113114
and (!_add and !_subtract and !_intersect or quick_copy)
114115
and !_ongoing_selection
115116
):
@@ -155,6 +156,7 @@ func draw_move(pos: Vector2i) -> void:
155156
var cel := project.get_current_cel() as CelTileMap
156157
var grid_size := cel.get_tile_size()
157158
var offset := cel.offset % grid_size
159+
_offset = Tools.snap_to_rectangular_grid_boundary(_offset, grid_size, offset)
158160
pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size, offset)
159161
if Input.is_action_pressed("transform_snap_axis"): # Snap to axis
160162
var angle := Vector2(pos).angle_to_point(_start_pos)

src/Tools/BaseTool.gd

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func snap_position(pos: Vector2) -> Vector2:
144144
)
145145

146146
if Global.snap_to_rectangular_grid_center:
147-
pos = _snap_to_rectangular_grid_center(
147+
pos = Tools.snap_to_rectangular_grid_center(
148148
pos, Global.grids[0].grid_size, Global.grids[0].grid_offset, snapping_distance
149149
)
150150

@@ -155,7 +155,7 @@ func snap_position(pos: Vector2) -> Vector2:
155155
continue
156156
var s1: Vector2 = guide.points[0]
157157
var s2: Vector2 = guide.points[1]
158-
var snap := _snap_to_guide(snap_to, pos, snapping_distance, s1, s2)
158+
var snap := Tools.snap_to_guide(snap_to, pos, snapping_distance, s1, s2)
159159
if snap == Vector2.INF:
160160
continue
161161
snap_to = snap
@@ -171,7 +171,7 @@ func snap_position(pos: Vector2) -> Vector2:
171171
var start := Vector2(point.pos_x, point.pos_y)
172172
var s1 := start
173173
var s2 := s1 + Vector2(length * cos(angle), length * sin(angle))
174-
var snap := _snap_to_guide(snap_to, pos, snapping_distance, s1, s2)
174+
var snap := Tools.snap_to_guide(snap_to, pos, snapping_distance, s1, s2)
175175
if snap == Vector2.INF:
176176
continue
177177
snap_to = snap
@@ -212,48 +212,6 @@ func mirror_array(array: Array[Vector2i], callable := func(_array): pass) -> Arr
212212
return new_array
213213

214214

215-
func _snap_to_rectangular_grid_center(
216-
pos: Vector2, grid_size: Vector2i, grid_offset: Vector2i, snapping_distance: float
217-
) -> Vector2:
218-
var grid_center := pos.snapped(grid_size) + Vector2(grid_size / 2)
219-
grid_center += Vector2(grid_offset)
220-
# keeping grid_center as is would have been fine but this adds extra accuracy as to
221-
# which snap point (from the list below) is closest to mouse and occupy THAT point
222-
# t_l is for "top left" and so on
223-
var t_l := grid_center + Vector2(-grid_size.x, -grid_size.y)
224-
var t_c := grid_center + Vector2(0, -grid_size.y)
225-
var t_r := grid_center + Vector2(grid_size.x, -grid_size.y)
226-
var m_l := grid_center + Vector2(-grid_size.x, 0)
227-
var m_c := grid_center
228-
var m_r := grid_center + Vector2(grid_size.x, 0)
229-
var b_l := grid_center + Vector2(-grid_size.x, grid_size.y)
230-
var b_c := grid_center + Vector2(0, grid_size.y)
231-
var b_r := grid_center + Vector2(grid_size)
232-
var vec_arr := [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
233-
for vec in vec_arr:
234-
if vec.distance_to(pos) < grid_center.distance_to(pos):
235-
grid_center = vec
236-
if snapping_distance < 0:
237-
pos = grid_center.floor()
238-
else:
239-
if grid_center.distance_to(pos) <= snapping_distance:
240-
pos = grid_center.floor()
241-
return pos
242-
243-
244-
func _snap_to_guide(
245-
snap_to: Vector2, pos: Vector2, distance: float, s1: Vector2, s2: Vector2
246-
) -> Vector2:
247-
var closest_point := Tools.get_closest_point_to_segment(pos, distance, s1, s2)
248-
if closest_point == Vector2.INF: # Is not close to a guide
249-
return Vector2.INF
250-
# Snap to the closest guide
251-
if snap_to == Vector2.INF or (snap_to - pos).length() > (closest_point - pos).length():
252-
snap_to = closest_point
253-
254-
return snap_to
255-
256-
257215
func _get_stabilized_position(normal_pos: Vector2) -> Vector2:
258216
if not Tools.stabilizer_enabled:
259217
return normal_pos

src/Tools/UtilityTools/Move.gd

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ func draw_start(pos: Vector2i) -> void:
2727
_offset = pos
2828
_undo_data = _get_undo_data()
2929
if Tools.is_placing_tiles():
30+
# Clear selection if it it present (i tried moving the selection proview only but the)
31+
# code for it gets too complex so i chose to clear it instead
32+
if project.has_selection:
33+
Global.canvas.selection.clear_selection(true)
34+
project.selection_map_changed()
3035
for cel in _get_selected_draw_cels():
3136
if cel is not CelTileMap:
3237
continue

src/UI/Canvas/Measurements.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func _prepare_cel_rect() -> void:
6464

6565
func _prepare_movement_rect() -> void:
6666
var project := Global.current_project
67-
if project.has_selection:
67+
if project.has_selection and not Tools.is_placing_tiles():
6868
rect_bounds = canvas.selection.preview_selection_map.get_used_rect()
6969
rect_bounds.position = Vector2i(
7070
canvas.selection.transformation_handles.preview_transform.origin

src/UI/Canvas/Selection.gd

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func transform_content_confirm() -> void:
110110
src.copy_from(cel.transformed_content)
111111
cel.transformed_content = null
112112
var transformation_origin := transformation_handles.get_transform_top_left(src.get_size())
113-
if Tools.is_placing_tiles():
113+
if Tools.is_placing_tiles() and not is_pasting:
114114
if cel is not CelTileMap:
115115
continue
116116
var tilemap := cel as CelTileMap
@@ -149,7 +149,7 @@ func transform_content_cancel() -> void:
149149
project.selection_map_changed()
150150
for cel in get_selected_draw_cels():
151151
var cel_image := cel.get_image()
152-
if !is_pasting:
152+
if !is_pasting and cel.transformed_content:
153153
cel_image.blit_rect_mask(
154154
cel.transformed_content,
155155
cel.transformed_content,
@@ -170,7 +170,7 @@ func commit_undo(action: String, undo_data_tmp: Dictionary) -> void:
170170
print("No undo data found!")
171171
return
172172
var project := Global.current_project
173-
if Tools.is_placing_tiles():
173+
if Tools.is_placing_tiles() and not is_pasting:
174174
for cel in undo_data_tmp:
175175
if cel is CelTileMap:
176176
(cel as CelTileMap).re_index_all_cells(true)
@@ -373,19 +373,51 @@ func paste(in_place := false) -> void:
373373
if Tools.is_placing_tiles():
374374
var tilemap_cel := Global.current_project.get_current_cel() as CelTileMap
375375
var grid_size := tilemap_cel.get_tile_size()
376+
var offset := tilemap_cel.offset % grid_size
377+
transform_origin = Vector2i(
378+
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size, offset)
379+
)
380+
elif Global.snap_to_rectangular_grid_center:
381+
var grid_size := Global.grids[0].grid_size
382+
var grid_offset := Global.grids[0].grid_offset
376383
transform_origin = Vector2i(
377-
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size)
384+
Tools.snap_to_rectangular_grid_center(transform_origin, grid_size, grid_offset)
385+
)
386+
elif Global.snap_to_rectangular_grid_boundary:
387+
var grid_size := Global.grids[0].grid_size
388+
var grid_offset := Global.grids[0].grid_offset
389+
transform_origin = Vector2i(
390+
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size, grid_offset)
378391
)
379392
project.selection_map.move_bitmap_values(Global.current_project, false)
380393
else:
381394
if Tools.is_placing_tiles():
382395
var tilemap_cel := Global.current_project.get_current_cel() as CelTileMap
383396
var grid_size := tilemap_cel.get_tile_size()
397+
var offset := tilemap_cel.offset % grid_size
398+
project.selection_offset = Tools.snap_to_rectangular_grid_boundary(
399+
project.selection_offset, grid_size, offset
400+
)
401+
transform_origin = Vector2i(
402+
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size, offset)
403+
)
404+
elif Global.snap_to_rectangular_grid_center:
405+
var grid_size := Global.grids[0].grid_size
406+
var grid_offset := Global.grids[0].grid_offset
407+
project.selection_offset = Tools.snap_to_rectangular_grid_center(
408+
project.selection_offset, grid_size, grid_offset
409+
)
410+
transform_origin = Vector2i(
411+
Tools.snap_to_rectangular_grid_center(transform_origin, grid_size, grid_offset)
412+
)
413+
elif Global.snap_to_rectangular_grid_boundary:
414+
var grid_size := Global.grids[0].grid_size
415+
var grid_offset := Global.grids[0].grid_offset
384416
project.selection_offset = Tools.snap_to_rectangular_grid_boundary(
385-
project.selection_offset, grid_size
417+
project.selection_offset, grid_size, grid_offset
386418
)
387419
transform_origin = Vector2i(
388-
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size)
420+
Tools.snap_to_rectangular_grid_boundary(transform_origin, grid_size, grid_offset)
389421
)
390422

391423
is_pasting = true

src/UI/Canvas/TransformationHandles.gd

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ func _on_preview_transform_changed() -> void:
308308
var bounds := DrawingAlgos.get_transformed_bounds(
309309
transformed_selection_map.get_size(), preview_transform
310310
)
311+
if bounds.size.x == 0 or bounds.size.y == 0:
312+
return
311313
if Tools.is_placing_tiles():
312314
for cel in selection_node.get_selected_draw_cels():
313315
if cel is not CelTileMap:
@@ -384,8 +386,10 @@ func begin_drag(mouse_pos: Vector2) -> void:
384386
func move_transform(pos: Vector2) -> void:
385387
var final_pos := pos
386388
if Tools.is_placing_tiles():
389+
# NOTE: we don't use offset here because the [param pos] is expected to already have
390+
# offseted coordinates.
387391
var grid_size := (Global.current_project.get_current_cel() as CelTileMap).get_tile_size()
388-
final_pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size)
392+
final_pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size, Vector2.ZERO)
389393
preview_transform = preview_transform.translated(final_pos)
390394
queue_redraw()
391395

@@ -684,6 +688,8 @@ func bake_transform_to_selection(map: SelectionMap, is_confirmed := false) -> vo
684688
var bounds := DrawingAlgos.get_transformed_bounds(
685689
transformed_selection_map.get_size(), preview_transform
686690
)
691+
if bounds.size.x == 0 or bounds.size.y == 0:
692+
return
687693
var transformation_origin := get_transform_top_left().max(Vector2.ZERO)
688694
if is_confirmed:
689695
var position_top_left := position + get_transform_top_left()

0 commit comments

Comments
 (0)