1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-31 07:29:49 +00:00

Compare commits

..

90 commits

Author SHA1 Message Date
Emmanouil Papadeas 4e51d3daf7
Merge e0e328f967 into b48bb4a094 2024-12-04 04:41:10 +02:00
Emmanouil Papadeas e0e328f967 Support draw tiles mode in the magic wand tool
Now all selection tools are supported. They can all move, but not yet resize.
2024-12-04 04:40:54 +02:00
Emmanouil Papadeas 0c3facf376 Support draw tiles mode in the paint select tool 2024-12-04 04:29:24 +02:00
Emmanouil Papadeas 58ab5b7083 Support draw tiles mode in lasso and polygon select tools 2024-12-04 04:26:09 +02:00
Emmanouil Papadeas 4365ed8a3a Support draw tiles mode in elliptical and color select tools 2024-12-04 03:07:35 +02:00
Emmanouil Papadeas d579baf830 Some refactoring and initial support for draw tile mode for selection tools
Only rectangle selection for now, and resizing doesn't yet work
2024-12-04 01:38:56 +02:00
Emmanouil Papadeas ad2fcf4891 Add a grid_offset parameter for the rectangular grid snap methods 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 7fb65c3136 Add a _snap_to_rectangular_grid_boundary() method to BaseTool
And rename _snap_to_grid_center() to _snap_to_rectangular_grid_center()
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 4d5eebc670 The bucket tool now works with draw tiles mode 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 8efbf0bf83 Add more keyboard shortcuts 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 61a3488ead Use the AutoInvertColors shader for when showing the tile mode indices 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 23304079b6 Support mirroring when using draw tiles mode 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 889e93e548 Add support for the draw tile mode for the rest of the draw tools, except bucket
Also fixes issues with the draw tile mode with the pencil and eraser tools, such as leaving gaps if the mouse is moving fast, and support for spacing and fill inside tool options.
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 228bc65a38 Change tileset in a layer from the project properties 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas e20585fde3 Update the default layouts to include the Tiles panel 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ef1b77662c Duplicate tilesets from the project properties 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 137ac25c00 Add tilesets in the project properties and a button to delete them
Can only delete tilesets that are not currently used by a tilemap layer
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas a9946099ef Add get_text_info() in TileSetCustom 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 8346b465b6 Disable draw tiles mode when pressing one of the tile edit mode buttons 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas cfa067ebb0 Add rotate left and right buttons in the tiles panel instead of transpose 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 800c8a6c19 Update Translations.pot 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 423c77235d Add documentation for CelTileMap and rename update_tileset to update_tilemap 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 31c5bf7cac Add documentation for Project and TileSetCustom 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ca2e67612f Include all cels that share the same tileset in undo/redo if manual mode is enabled. 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas a1f785f67e Make drawing on multiple selected tilemap cels that share the same tileset and applied a layer effect to all cels of the layer work
This now creates an issue when manual mode is used and we undo. Other cels don't get updated, even though they were changed by manual mode.
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 59a2ec4db1 Fix layer effect applying not updating the tilesets properly 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 381eed84d5 Only resize cells on undo/redo when needed 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 5e34faf793 Resizing should now work
Also fixes cel replacing
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas a3324591be Revert 3f39dbf3 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas d5dac1c527 Almost make cel replacing work
Needs to fix image resizing in order for this to work properly with undo/redo.
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 3cd4bd92ec Merge layers into tilemap layers 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas c14ac5d579 Layer/frame cloning works. 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas f197c6c55b Linked tilemap cels should now work 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 0b2fa7ab6e Draw tiles on all selected draw cels
Not working properly yet
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas d341d73fcf Don't execute update_tileset is we are on draw tiles mode 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas f9b2ff2d6a Continue with the undo/redo rewrite
Works everywhere except image resizing, replacing cels and merging layers
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 533b28452b Fixed bugs when placing a transformed tile over a non-transformed tile 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 58d4e7efab Fix placing tiles not working when switching to indexed mode 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 45ae8b7dee Format TileModeIndices 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ae52151021 Fix issues with transposed tiles 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 5e3cc0a9a1 Some improvements to TileModeIndices 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 50da6c0ce9 Fix variable name shadowing in TopMenuContainer 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ad83823f58 Remove transformations from cells when using auto or stack mode 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 442285d15f Make undo/redo store tilemap cell indices and tileset tiles
Fixes issues with cases 0.5 and 5 of auto mode, and should be a better system overall. Only works with BaseDraw tools, needs to be applied everywhere as well.
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas b059ae4c8b Write some documentation in CelTIleMap
WIP
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 5e4eebe139 Rename some variables and methods in CelTileMap 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 2c6dcdcf78 Add documentation for LayerTileMap and TileSetCustom, along with a class description for CelTileMap 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ac75c8197c Experimental undo redo for tileset tiles 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 5f10a913d4 Fix manual mode when the tilemap is empty. 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 3bf556345d Support tile transformation, no undo/redo yet 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 4e62d00296 Add logic for checking if two tiles are equal with transformations applied to them
There is currently no exposed way to apply transformations to tiles.
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas a696db3fc0 Refactor CelTileMap to eventually support alternative tiles
Such as rotated and flipped tiles
2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 610d2deb27 Add smart tileset importing 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 9f3564fe71 Load images as tilesets 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 177428cc1b Prevent users from editing tileset name and size if they choose to not create a new tileset 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas afe51262c9 Automatically hide and show the tiles panel when the current cel is a tilemap cel 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 81d4812b92 Preview tiles when using tools and draw tiles mode is enabled. 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas ad1252c142 Manual mode should update other cels that have the same tileset 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 2301ba9fcc Make manual tile editing mode automatically update all other image portions that have the same tile index 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas c1fd209588 Fix tileset panel updating when undoing and the wrong tilemap cel is selected 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 20c7a9fdfc Resize tileset buttons 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 14d0c76310 Enable tile drawing mode when clicking on a tile button 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 6c200d3afe When a tilemap cel is selected, force the first grid to have the same size as the tile size 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 04ab9faa87 Fix out of bounds issues when undoing/redoing when the place tiles mode is enabled 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 9c5b0f0f76 Add a dialog when creating a tilemap layer to allow users to set the tileset's settings 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 419154bafe Resize indices on project resize 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas f077b147e9 Save and load to/from pxo files 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas c24a2240fe Prevent from setting tile indices out of bounds of the canvas 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas d8c27a7966 Place tiles mode works with eraser and color picker tools 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 5b50270ee2 Undo/redo now removes tiles and re-indexes tilemap tiles 2024-12-04 01:21:54 +02:00
Emmanouil Papadeas 664197c9f3 Implement placing tiles for pencil tool
Still needs undo support
2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 0678fd8719 Tileset panel UI improvements 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 12eda32176 Properly update the tileset when using any tool 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas b87bfdf7e8 Better tile buttons 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 6b77e30e08 Improve tileset panel UI updating logic 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 6c79136f09 Prevent from drawing on empty image portions on manual mode. 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas cd6212d892 Make the manual mode work, kind of 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas d95c3f7555 Properly implement the auto tile editing mode
Should work well now.
2024-12-04 01:21:53 +02:00
Emmanouil Papadeas bd90f28de8 Show tile indices when pressing Control 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas f69e4bdc9e Add a way to show the indices of each tile, WIP 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 9187d8a9be Don't delete tiles that have been added using the stack mode 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas dd4f6b7b6c Change tile editing mode from the UI 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 27c0787f26 Make manual mode work when the tileset is empty 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas f42454ef03 Improve tileset editing logic 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas a8755bd92f Support ImageExtended 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 4f1ee0e828 Add a tileset panel
Code is a bit meh, needs to be written better.
2024-12-04 01:21:53 +02:00
Emmanouil Papadeas e119a91f5b Add tilemap layers 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 062889b4bb Implement all draw modes (untested) 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas 174f7d4b9f Initial work for tilemap layers 2024-12-04 01:21:53 +02:00
Emmanouil Papadeas b48bb4a094 Fix moving selections with arrow keys moving two pixels instead of one 2024-12-04 01:19:45 +02:00
20 changed files with 417 additions and 164 deletions

View file

@ -583,6 +583,87 @@ func calculate_mirror_x_minus_y(pos: Vector2i, project: Project) -> Vector2i:
)
func is_placing_tiles() -> bool:
if Global.current_project.frames.size() == 0 or Global.current_project.layers.size() == 0:
return false
return Global.current_project.get_current_cel() is CelTileMap and TileSetPanel.placing_tiles
func _get_closest_point_to_grid(pos: Vector2, distance: float, grid_pos: Vector2) -> Vector2:
# If the cursor is close to the start/origin of a grid cell, snap to that
var snap_distance := distance * Vector2.ONE
var closest_point := Vector2.INF
var rect := Rect2()
rect.position = pos - (snap_distance / 4.0)
rect.end = pos + (snap_distance / 4.0)
if rect.has_point(grid_pos):
closest_point = grid_pos
return closest_point
# If the cursor is far from the grid cell origin but still close to a grid line
# Look for a point close to a horizontal grid line
var grid_start_hor := Vector2(0, grid_pos.y)
var grid_end_hor := Vector2(Global.current_project.size.x, grid_pos.y)
var closest_point_hor := get_closest_point_to_segment(
pos, distance, grid_start_hor, grid_end_hor
)
# Look for a point close to a vertical grid line
var grid_start_ver := Vector2(grid_pos.x, 0)
var grid_end_ver := Vector2(grid_pos.x, Global.current_project.size.y)
var closest_point_ver := get_closest_point_to_segment(
pos, distance, grid_start_ver, grid_end_ver
)
# Snap to the closest point to the closest grid line
var horizontal_distance := (closest_point_hor - pos).length()
var vertical_distance := (closest_point_ver - pos).length()
if horizontal_distance < vertical_distance:
closest_point = closest_point_hor
elif horizontal_distance > vertical_distance:
closest_point = closest_point_ver
elif horizontal_distance == vertical_distance and closest_point_hor != Vector2.INF:
closest_point = grid_pos
return closest_point
func get_closest_point_to_segment(
pos: Vector2, distance: float, s1: Vector2, s2: Vector2
) -> Vector2:
var test_line := (s2 - s1).rotated(deg_to_rad(90)).normalized()
var from_a := pos - test_line * distance
var from_b := pos + test_line * distance
var closest_point := Vector2.INF
if Geometry2D.segment_intersects_segment(from_a, from_b, s1, s2):
closest_point = Geometry2D.get_closest_point_to_segment(pos, s1, s2)
return closest_point
func snap_to_rectangular_grid_boundary(
pos: Vector2, grid_size: Vector2i, grid_offset := Vector2i.ZERO, snapping_distance := 9999.0
) -> Vector2:
var grid_pos := pos.snapped(grid_size)
grid_pos += Vector2(grid_offset)
# keeping grid_pos as is would have been fine but this adds extra accuracy as to
# which snap point (from the list below) is closest to mouse and occupy THAT point
# t_l is for "top left" and so on
var t_l := grid_pos + Vector2(-grid_size.x, -grid_size.y)
var t_c := grid_pos + Vector2(0, -grid_size.y)
var t_r := grid_pos + Vector2(grid_size.x, -grid_size.y)
var m_l := grid_pos + Vector2(-grid_size.x, 0)
var m_c := grid_pos
var m_r := grid_pos + Vector2(grid_size.x, 0)
var b_l := grid_pos + Vector2(-grid_size.x, grid_size.y)
var b_c := grid_pos + Vector2(0, grid_size.y)
var b_r := grid_pos + Vector2(grid_size)
var vec_arr: PackedVector2Array = [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
for vec in vec_arr:
if vec.distance_to(pos) < grid_pos.distance_to(pos):
grid_pos = vec
var grid_point := _get_closest_point_to_grid(pos, snapping_distance, grid_pos)
if grid_point != Vector2.INF:
pos = grid_point.floor()
return pos
func set_button_size(button_size: int) -> void:
var size := Vector2(24, 24) if button_size == Global.ButtonSize.SMALL else Vector2(32, 32)
if not is_instance_valid(_tool_buttons):

View file

@ -77,6 +77,13 @@ func select_pixel(pixel: Vector2i, select := true) -> void:
set_pixelv(pixel, Color(0))
func select_rect(rect: Rect2i, select := true) -> void:
if select:
fill_rect(rect, Color(1, 1, 1, 1))
else:
fill_rect(rect, Color(0))
func select_all() -> void:
fill(Color(1, 1, 1, 1))

View file

@ -5,6 +5,7 @@ const SPLASH_DIALOG_SCENE_PATH := "res://src/UI/Dialogs/SplashDialog.tscn"
var opensprite_file_selected := false
var redone := false
var is_quitting_on_save := false
var is_writing_text := false
var changed_projects_on_quit: Array[Project]
var cursor_image := preload("res://assets/graphics/cursor.png")
## Used to download an image when dragged and dropped directly from a browser into Pixelorama
@ -202,7 +203,7 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
if event is InputEventKey and is_instance_valid(Global.main_viewport):
if is_writing_text and event is InputEventKey and is_instance_valid(Global.main_viewport):
Global.main_viewport.get_child(0).push_input(event)
left_cursor.position = get_global_mouse_position() + Vector2(-32, 32)
right_cursor.position = get_global_mouse_position() + Vector2(32, 32)

View file

@ -163,7 +163,7 @@ func update_config() -> void:
func update_brush() -> void:
$Brush/BrushSize.suffix = "px" # Assume we are using default brushes
if is_placing_tiles():
if Tools.is_placing_tiles():
var tilemap_cel := Global.current_project.get_current_cel() as CelTileMap
var tileset := tilemap_cel.tileset
var tile_index := clampi(TileSetPanel.selected_tile_index, 0, tileset.tiles.size() - 1)
@ -517,7 +517,7 @@ func remove_unselected_parts_of_brush(brush: Image, dst: Vector2i) -> Image:
func draw_indicator(left: bool) -> void:
var color := Global.left_tool_color if left else Global.right_tool_color
var snapped_position := snap_position(_cursor)
if is_placing_tiles():
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
snapped_position = _snap_to_rectangular_grid_center(
@ -545,7 +545,7 @@ func draw_indicator(left: bool) -> void:
func draw_indicator_at(pos: Vector2i, offset: Vector2i, color: Color) -> void:
var canvas: Node2D = Global.canvas.indicators
if _brush.type in IMAGE_BRUSHES and not _draw_line or is_placing_tiles():
if _brush.type in IMAGE_BRUSHES and not _draw_line or Tools.is_placing_tiles():
pos -= _brush_image.get_size() / 2
pos -= offset
canvas.draw_texture(_brush_texture, pos)
@ -580,7 +580,7 @@ func _set_pixel_no_cache(pos: Vector2i, ignore_mirroring := false) -> void:
pos = _stroke_project.tiles.get_canon_position(pos)
if Global.current_project.has_selection:
pos = Global.current_project.selection_map.get_canon_position(pos)
if is_placing_tiles():
if Tools.is_placing_tiles():
draw_tile(pos)
return
if !_stroke_project.can_pixel_get_drawn(pos):

View file

@ -152,6 +152,10 @@ func draw_move(pos: Vector2i) -> void:
if not _move:
return
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size)
if Input.is_action_pressed("transform_snap_axis"): # Snap to axis
var angle := Vector2(pos).angle_to_point(_start_pos)
if absf(angle) <= PI / 4 or absf(angle) >= 3 * PI / 4:
@ -211,6 +215,13 @@ func apply_selection(_position: Vector2i) -> void:
_intersect = true
func select_tilemap_cell(
cel: CelTileMap, cell_position: int, selection: SelectionMap, select: bool
) -> void:
var rect := Rect2i(cel.get_cell_coords_in_image(cell_position), cel.tileset.tile_size)
selection.select_rect(rect, select)
func _on_confirm_button_pressed() -> void:
if selection_node.is_moving_content:
selection_node.transform_content_confirm()

View file

@ -189,7 +189,7 @@ func _draw_shape(origin: Vector2i, dest: Vector2i) -> void:
_drawer.reset()
# Draw each point offsetted based on the shape's thickness
var draw_pos := point + thickness_vector
if is_placing_tiles():
if Tools.is_placing_tiles():
draw_tile(draw_pos)
else:
if Global.current_project.can_pixel_get_drawn(draw_pos):

View file

@ -79,12 +79,6 @@ func draw_end(_pos: Vector2i) -> void:
project.can_undo = true
func is_placing_tiles() -> bool:
if Global.current_project.frames.size() == 0 or Global.current_project.layers.size() == 0:
return false
return Global.current_project.get_current_cel() is CelTileMap and TileSetPanel.placing_tiles
func get_cell_position(pos: Vector2i) -> int:
var tile_pos := 0
if Global.current_project.get_current_cel() is not CelTileMap:
@ -145,7 +139,7 @@ func draw_preview() -> void:
func snap_position(pos: Vector2) -> Vector2:
var snapping_distance := Global.snapping_distance / Global.camera.zoom.x
if Global.snap_to_rectangular_grid_boundary:
pos = _snap_to_rectangular_grid_boundary(
pos = Tools.snap_to_rectangular_grid_boundary(
pos, Global.grids[0].grid_size, Global.grids[0].grid_offset, snapping_distance
)
@ -218,81 +212,6 @@ func mirror_array(array: Array[Vector2i], callable := func(_array): pass) -> Arr
return new_array
func _get_closest_point_to_grid(pos: Vector2, distance: float, grid_pos: Vector2) -> Vector2:
# If the cursor is close to the start/origin of a grid cell, snap to that
var snap_distance := distance * Vector2.ONE
var closest_point := Vector2.INF
var rect := Rect2()
rect.position = pos - (snap_distance / 4.0)
rect.end = pos + (snap_distance / 4.0)
if rect.has_point(grid_pos):
closest_point = grid_pos
return closest_point
# If the cursor is far from the grid cell origin but still close to a grid line
# Look for a point close to a horizontal grid line
var grid_start_hor := Vector2(0, grid_pos.y)
var grid_end_hor := Vector2(Global.current_project.size.x, grid_pos.y)
var closest_point_hor := _get_closest_point_to_segment(
pos, distance, grid_start_hor, grid_end_hor
)
# Look for a point close to a vertical grid line
var grid_start_ver := Vector2(grid_pos.x, 0)
var grid_end_ver := Vector2(grid_pos.x, Global.current_project.size.y)
var closest_point_ver := _get_closest_point_to_segment(
pos, distance, grid_start_ver, grid_end_ver
)
# Snap to the closest point to the closest grid line
var horizontal_distance := (closest_point_hor - pos).length()
var vertical_distance := (closest_point_ver - pos).length()
if horizontal_distance < vertical_distance:
closest_point = closest_point_hor
elif horizontal_distance > vertical_distance:
closest_point = closest_point_ver
elif horizontal_distance == vertical_distance and closest_point_hor != Vector2.INF:
closest_point = grid_pos
return closest_point
func _get_closest_point_to_segment(
pos: Vector2, distance: float, s1: Vector2, s2: Vector2
) -> Vector2:
var test_line := (s2 - s1).rotated(deg_to_rad(90)).normalized()
var from_a := pos - test_line * distance
var from_b := pos + test_line * distance
var closest_point := Vector2.INF
if Geometry2D.segment_intersects_segment(from_a, from_b, s1, s2):
closest_point = Geometry2D.get_closest_point_to_segment(pos, s1, s2)
return closest_point
func _snap_to_rectangular_grid_boundary(
pos: Vector2, grid_size: Vector2i, grid_offset: Vector2i, snapping_distance: float
) -> Vector2:
var grid_pos := pos.snapped(grid_size)
grid_pos += Vector2(grid_offset)
# keeping grid_pos as is would have been fine but this adds extra accuracy as to
# which snap point (from the list below) is closest to mouse and occupy THAT point
# t_l is for "top left" and so on
var t_l := grid_pos + Vector2(-grid_size.x, -grid_size.y)
var t_c := grid_pos + Vector2(0, -grid_size.y)
var t_r := grid_pos + Vector2(grid_size.x, -grid_size.y)
var m_l := grid_pos + Vector2(-grid_size.x, 0)
var m_c := grid_pos
var m_r := grid_pos + Vector2(grid_size.x, 0)
var b_l := grid_pos + Vector2(-grid_size.x, grid_size.y)
var b_c := grid_pos + Vector2(0, grid_size.y)
var b_r := grid_pos + Vector2(grid_size)
var vec_arr: PackedVector2Array = [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
for vec in vec_arr:
if vec.distance_to(pos) < grid_pos.distance_to(pos):
grid_pos = vec
var grid_point := _get_closest_point_to_grid(pos, snapping_distance, grid_pos)
if grid_point != Vector2.INF:
pos = grid_point.floor()
return pos
func _snap_to_rectangular_grid_center(
pos: Vector2, grid_size: Vector2i, grid_offset: Vector2i, snapping_distance: float
) -> Vector2:
@ -325,7 +244,7 @@ func _snap_to_rectangular_grid_center(
func _snap_to_guide(
snap_to: Vector2, pos: Vector2, distance: float, s1: Vector2, s2: Vector2
) -> Vector2:
var closest_point := _get_closest_point_to_segment(pos, distance, s1, s2)
var closest_point := Tools.get_closest_point_to_segment(pos, distance, s1, s2)
if closest_point == Vector2.INF: # Is not close to a guide
return Vector2.INF
# Snap to the closest guide
@ -386,7 +305,7 @@ func _pick_color(pos: Vector2i) -> void:
if pos.x < 0 or pos.y < 0:
return
if is_placing_tiles():
if Tools.is_placing_tiles():
var cel := Global.current_project.get_current_cel() as CelTileMap
Tools.selected_tile_index_changed.emit(cel.get_cell_index_at_coords(pos))
return

View file

@ -204,7 +204,7 @@ func fill(pos: Vector2i) -> void:
func fill_in_color(pos: Vector2i) -> void:
var project := Global.current_project
if is_placing_tiles():
if Tools.is_placing_tiles():
for cel in _get_selected_draw_cels():
if cel is not CelTileMap:
continue
@ -331,7 +331,7 @@ func _flood_fill(pos: Vector2i) -> void:
# implements the floodfill routine by Shawn Hargreaves
# from https://www1.udel.edu/CIS/software/dist/allegro-4.2.1/src/flood.c
var project := Global.current_project
if is_placing_tiles():
if Tools.is_placing_tiles():
for cel in _get_selected_draw_cels():
if cel is not CelTileMap:
continue

View file

@ -195,7 +195,7 @@ func _draw_shape() -> void:
func _draw_pixel(point: Vector2i, images: Array[ImageExtended]) -> void:
if is_placing_tiles():
if Tools.is_placing_tiles():
draw_tile(point)
else:
if Global.current_project.can_pixel_get_drawn(point):

View file

@ -174,7 +174,7 @@ func _draw_shape() -> void:
for point in points:
# Reset drawer every time because pixel perfect sometimes breaks the tool
_drawer.reset()
if is_placing_tiles():
if Tools.is_placing_tiles():
draw_tile(point)
else:
# Draw each point offsetted based on the shape's thickness

View file

@ -32,23 +32,46 @@ func apply_selection(pos: Vector2i) -> void:
if pos.x > project.size.x - 1 or pos.y > project.size.y - 1:
return
var cel_image := Image.new()
cel_image.copy_from(_get_draw_image())
var color := cel_image.get_pixelv(pos)
var operation := 0
if _subtract:
operation = 1
elif _intersect:
operation = 2
var params := {"color": color, "tolerance": _tolerance, "operation": operation}
if _add or _subtract or _intersect:
var selection_tex := ImageTexture.create_from_image(project.selection_map)
params["selection"] = selection_tex
var gen := ShaderImageEffect.new()
gen.generate_image(cel_image, shader, params, project.size)
cel_image.convert(Image.FORMAT_LA8)
if Tools.is_placing_tiles():
var prev_selection_map := SelectionMap.new() # Used for intersect
prev_selection_map.copy_from(project.selection_map)
if !_add and !_subtract and !_intersect:
Global.canvas.selection.clear_selection()
if _intersect:
project.selection_map.clear()
for cel in _get_selected_draw_cels():
if cel is not CelTileMap:
continue
var tilemap_cel := cel as CelTileMap
var tile_index := tilemap_cel.get_cell_index_at_coords(pos)
for i in tilemap_cel.cells.size():
var cell := tilemap_cel.cells[i]
if cell.index == tile_index:
if _intersect:
var p := (cel as CelTileMap).get_cell_coords_in_image(i)
select_tilemap_cell(
cel, i, project.selection_map, prev_selection_map.is_pixel_selected(p)
)
else:
select_tilemap_cell(cel, i, project.selection_map, !_subtract)
else:
var cel_image := Image.new()
cel_image.copy_from(_get_draw_image())
var color := cel_image.get_pixelv(pos)
var params := {"color": color, "tolerance": _tolerance, "operation": operation}
if _add or _subtract or _intersect:
var selection_tex := ImageTexture.create_from_image(project.selection_map)
params["selection"] = selection_tex
var gen := ShaderImageEffect.new()
gen.generate_image(cel_image, shader, params, project.size)
cel_image.convert(Image.FORMAT_LA8)
project.selection_map.copy_from(cel_image)
project.selection_map.copy_from(cel_image)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
Global.canvas.selection.commit_undo("Select", undo_data)

View file

@ -81,18 +81,36 @@ func apply_selection(_position: Vector2i) -> void:
Global.canvas.selection.commit_undo("Select", undo_data)
if _rect.size == Vector2i.ZERO:
return
set_ellipse(project.selection_map, _rect.position)
# Handle mirroring
var mirror_positions := Tools.get_mirrored_positions(_rect.position, project, 1)
var mirror_ends := Tools.get_mirrored_positions(_rect.end, project, 1)
for i in mirror_positions.size():
var mirror_rect := Rect2i()
mirror_rect.position = mirror_positions[i]
mirror_rect.end = mirror_ends[i]
set_ellipse(project.selection_map, mirror_rect.abs().position)
if Tools.is_placing_tiles():
var operation := 0
if _subtract:
operation = 1
elif _intersect:
operation = 2
Global.canvas.selection.select_rect(_rect, operation)
# Handle mirroring
var mirror_positions := Tools.get_mirrored_positions(_rect.position, project, 1)
var mirror_ends := Tools.get_mirrored_positions(_rect.end, project, 1)
for i in mirror_positions.size():
var mirror_rect := Rect2i()
mirror_rect.position = mirror_positions[i]
mirror_rect.end = mirror_ends[i]
Global.canvas.selection.select_rect(mirror_rect.abs(), operation)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
Global.canvas.selection.commit_undo("Select", undo_data)
Global.canvas.selection.commit_undo("Select", undo_data)
else:
set_ellipse(project.selection_map, _rect.position)
# Handle mirroring
var mirror_positions := Tools.get_mirrored_positions(_rect.position, project, 1)
var mirror_ends := Tools.get_mirrored_positions(_rect.end, project, 1)
for i in mirror_positions.size():
var mirror_rect := Rect2i()
mirror_rect.position = mirror_positions[i]
mirror_rect.end = mirror_ends[i]
set_ellipse(project.selection_map, mirror_rect.abs().position)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
Global.canvas.selection.commit_undo("Select", undo_data)
func set_ellipse(selection_map: SelectionMap, pos: Vector2i) -> void:
@ -116,8 +134,12 @@ func set_ellipse(selection_map: SelectionMap, pos: Vector2i) -> void:
# 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:
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size)
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size)
var rect := Rect2i()
# Center the rect on the mouse
if _expand_from_center:
var new_size := dest - origin
@ -140,6 +162,7 @@ func _get_result_rect(origin: Vector2i, dest: Vector2i) -> Rect2i:
rect.position = Vector2i(mini(origin.x, dest.x), mini(origin.y, dest.y))
rect.size = (origin - dest).abs()
rect.size += Vector2i.ONE
if not Tools.is_placing_tiles():
rect.size += Vector2i.ONE
return rect

View file

@ -70,9 +70,9 @@ func apply_selection(_position) -> void:
if _draw_points.size() > 3:
if _intersect:
project.selection_map.clear()
lasso_selection(_draw_points, project.selection_map, previous_selection_map)
lasso_selection(_draw_points, project, previous_selection_map)
# Handle mirroring
var callable := lasso_selection.bind(project.selection_map, previous_selection_map)
var callable := lasso_selection.bind(project, previous_selection_map)
mirror_array(_draw_points, callable)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
else:
@ -85,8 +85,9 @@ func apply_selection(_position) -> void:
func lasso_selection(
points: Array[Vector2i], selection_map: SelectionMap, previous_selection_map: SelectionMap
points: Array[Vector2i], project: Project, previous_selection_map: SelectionMap
) -> void:
var selection_map := project.selection_map
var selection_size := selection_map.get_size()
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
for point in points:
@ -95,9 +96,9 @@ func lasso_selection(
bounding_rect = bounding_rect.expand(point)
if _intersect:
if previous_selection_map.is_pixel_selected(point):
selection_map.select_pixel(point, true)
select_pixel(point, project, true)
else:
selection_map.select_pixel(point, !_subtract)
select_pixel(point, project, !_subtract)
var v := Vector2i()
for x in bounding_rect.size.x:
@ -107,9 +108,17 @@ func lasso_selection(
if Geometry2D.is_point_in_polygon(v, points):
if _intersect:
if previous_selection_map.is_pixel_selected(v):
selection_map.select_pixel(v, true)
select_pixel(v, project, true)
else:
selection_map.select_pixel(v, !_subtract)
select_pixel(v, project, !_subtract)
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
if Tools.is_placing_tiles():
var tilemap := project.get_current_cel() as CelTileMap
var cell_position := tilemap.get_cell_position(point)
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
project.selection_map.select_pixel(point, select)
# Bresenham's Algorithm

View file

@ -34,10 +34,10 @@ func apply_selection(pos: Vector2i) -> void:
var cel_image := Image.new()
cel_image.copy_from(_get_draw_image())
_flood_fill(pos, cel_image, project.selection_map, previous_selection_map)
_flood_fill(pos, cel_image, project, previous_selection_map)
# Handle mirroring
for mirror_pos in Tools.get_mirrored_positions(pos):
_flood_fill(mirror_pos, cel_image, project.selection_map, previous_selection_map)
_flood_fill(mirror_pos, cel_image, project, previous_selection_map)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
Global.canvas.selection.commit_undo("Select", undo_data)
@ -59,6 +59,39 @@ func update_config() -> void:
$ToleranceSlider.value = _tolerance * 255.0
func _on_tolerance_slider_value_changed(value: float) -> void:
_tolerance = value / 255.0
update_config()
save_config()
func _flood_fill(
pos: Vector2i, image: Image, project: Project, previous_selection_map: SelectionMap
) -> void:
# implements the floodfill routine by Shawn Hargreaves
# from https://www1.udel.edu/CIS/software/dist/allegro-4.2.1/src/flood.c
var selection_map := project.selection_map
if Tools.is_placing_tiles():
for cel in _get_selected_draw_cels():
if cel is not CelTileMap:
continue
var tile_index := (cel as CelTileMap).get_cell_index_at_coords(pos)
# init flood data structures
_allegro_flood_segments = []
_allegro_image_segments = []
_compute_segments_for_tilemap(pos, cel, tile_index)
_select_segments_tilemap(project, previous_selection_map)
return
var color := image.get_pixelv(pos)
# init flood data structures
_allegro_flood_segments = []
_allegro_image_segments = []
_compute_segments_for_image(pos, project, image, color)
# now actually color the image: since we have already checked a few things for the points
# we'll process here, we're going to skip a bunch of safety checks to speed things up.
_select_segments(selection_map, previous_selection_map)
# Add a new segment to the array
func _add_new_segment(y := 0) -> void:
_allegro_flood_segments.append(Segment.new(y))
@ -140,22 +173,6 @@ func _check_flooded_segment(
return ret
func _flood_fill(
pos: Vector2i, image: Image, selection_map: SelectionMap, previous_selection_map: SelectionMap
) -> void:
# implements the floodfill routine by Shawn Hargreaves
# from https://www1.udel.edu/CIS/software/dist/allegro-4.2.1/src/flood.c
var project := Global.current_project
var color := image.get_pixelv(pos)
# init flood data structures
_allegro_flood_segments = []
_allegro_image_segments = []
_compute_segments_for_image(pos, project, image, color)
# now actually color the image: since we have already checked a few things for the points
# we'll process here, we're going to skip a bunch of safety checks to speed things up.
_select_segments(selection_map, previous_selection_map)
func _compute_segments_for_image(
pos: Vector2i, project: Project, image: Image, src_color: Color
) -> void:
@ -201,7 +218,128 @@ func _set_bit(p: Vector2i, selection_map: SelectionMap, prev_selection_map: Sele
selection_map.select_pixel(p, !_subtract)
func _on_tolerance_slider_value_changed(value: float) -> void:
_tolerance = value / 255.0
update_config()
save_config()
func _compute_segments_for_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> void:
# initially allocate at least 1 segment per line of the tilemap
for j in cel.vertical_cells:
_add_new_segment(j)
pos /= cel.tileset.tile_size
# start flood algorithm
_flood_line_around_point_tilemap(pos, cel, src_index)
# test all segments while also discovering more
var done := false
while not done:
done = true
var max_index := _allegro_flood_segments.size()
for c in max_index:
var p := _allegro_flood_segments[c]
if p.todo_below: # check below the segment?
p.todo_below = false
if _check_flooded_segment_tilemap(
p.y + 1, p.left_position, p.right_position, cel, src_index
):
done = false
if p.todo_above: # check above the segment?
p.todo_above = false
if _check_flooded_segment_tilemap(
p.y - 1, p.left_position, p.right_position, cel, src_index
):
done = false
## Fill an horizontal segment around the specified position, and adds it to the
## list of segments filled. Returns the first x coordinate after the part of the
## line that has been filled.
## Τhis method is called by [method _flood_fill] after the required data structures
## have been initialized.
func _flood_line_around_point_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> int:
if cel.get_cell_index_at_coords_in_tilemap_space(pos) != src_index:
return pos.x + 1
var west := pos
var east := pos
while west.x >= 0 && cel.get_cell_index_at_coords_in_tilemap_space(west) == src_index:
west += Vector2i.LEFT
while (
east.x < cel.horizontal_cells
&& cel.get_cell_index_at_coords_in_tilemap_space(east) == src_index
):
east += Vector2i.RIGHT
# Make a note of the stuff we processed
var c := pos.y
var segment := _allegro_flood_segments[c]
# we may have already processed some segments on this y coordinate
if segment.flooding:
while segment.next > 0:
c = segment.next # index of next segment in this line of image
segment = _allegro_flood_segments[c]
# found last current segment on this line
c = _allegro_flood_segments.size()
segment.next = c
_add_new_segment(pos.y)
segment = _allegro_flood_segments[c]
# set the values for the current segment
segment.flooding = true
segment.left_position = west.x + 1
segment.right_position = east.x - 1
segment.y = pos.y
segment.next = 0
# Should we process segments above or below this one?
# when there is a selected area, the pixels above and below the one we started creating this
# segment from may be outside it. It's easier to assume we should be checking for segments
# above and below this one than to specifically check every single pixel in it, because that
# test will be performed later anyway.
# On the other hand, this test we described is the same `project.can_pixel_get_drawn` does if
# there is no selection, so we don't need branching here.
segment.todo_above = pos.y > 0
segment.todo_below = pos.y < cel.vertical_cells - 1
# this is an actual segment we should be coloring, so we add it to the results for the
# current image
if segment.right_position >= segment.left_position:
_allegro_image_segments.append(segment)
# we know the point just east of the segment is not part of a segment that should be
# processed, else it would be part of this segment
return east.x + 1
func _check_flooded_segment_tilemap(
y: int, left: int, right: int, cel: CelTileMap, src_index: int
) -> bool:
var ret := false
var c := 0
while left <= right:
c = y
while true:
var segment := _allegro_flood_segments[c]
if left >= segment.left_position and left <= segment.right_position:
left = segment.right_position + 2
break
c = segment.next
if c == 0: # couldn't find a valid segment, so we draw a new one
left = _flood_line_around_point_tilemap(Vector2i(left, y), cel, src_index)
ret = true
break
return ret
func _select_segments_tilemap(project: Project, previous_selection_map: SelectionMap) -> void:
# short circuit for flat colors
for c in _allegro_image_segments.size():
var p := _allegro_image_segments[c]
for px in range(p.left_position, p.right_position + 1):
# We don't have to check again whether the point being processed is within the bounds
_set_bit_rect(Vector2i(px, p.y), project, previous_selection_map)
func _set_bit_rect(p: Vector2i, project: Project, prev_selection_map: SelectionMap) -> void:
var selection_map := project.selection_map
var tilemap := project.get_current_cel() as CelTileMap
var cell_position := tilemap.get_cell_position_in_tilemap_space(p)
if _intersect:
var image_coords := tilemap.get_cell_coords_in_image(cell_position)
select_tilemap_cell(
tilemap,
cell_position,
project.selection_map,
prev_selection_map.is_pixel_selected(image_coords)
)
else:
select_tilemap_cell(tilemap, cell_position, project.selection_map, !_subtract)

View file

@ -99,10 +99,10 @@ func apply_selection(pos: Vector2i) -> void:
if _draw_points.size() >= 1:
if _intersect:
project.selection_map.clear()
paint_selection(project.selection_map, previous_selection_map, _draw_points)
paint_selection(project, previous_selection_map, _draw_points)
# Handle mirroring
var mirror := mirror_array(_draw_points)
paint_selection(project.selection_map, previous_selection_map, mirror)
paint_selection(project, previous_selection_map, mirror)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
else:
if !cleared:
@ -114,17 +114,26 @@ func apply_selection(pos: Vector2i) -> void:
func paint_selection(
selection_map: SelectionMap, previous_selection_map: SelectionMap, points: Array[Vector2i]
project: Project, previous_selection_map: SelectionMap, points: Array[Vector2i]
) -> void:
var selection_map := project.selection_map
var selection_size := selection_map.get_size()
for point in points:
if point.x < 0 or point.y < 0 or point.x >= selection_size.x or point.y >= selection_size.y:
continue
if _intersect:
if previous_selection_map.is_pixel_selected(point):
selection_map.select_pixel(point, true)
select_pixel(point, project, true)
else:
selection_map.select_pixel(point, !_subtract)
select_pixel(point, project, !_subtract)
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
if Tools.is_placing_tiles():
var tilemap := project.get_current_cel() as CelTileMap
var cell_position := tilemap.get_cell_position(point)
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
project.selection_map.select_pixel(point, select)
# Bresenham's Algorithm

View file

@ -107,9 +107,9 @@ func apply_selection(pos: Vector2i) -> void:
if _draw_points.size() > 3:
if _intersect:
project.selection_map.clear()
lasso_selection(_draw_points, project.selection_map, previous_selection_map)
lasso_selection(_draw_points, project, previous_selection_map)
# Handle mirroring
var callable := lasso_selection.bind(project.selection_map, previous_selection_map)
var callable := lasso_selection.bind(project, previous_selection_map)
mirror_array(_draw_points, callable)
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
else:
@ -128,8 +128,9 @@ func _clear() -> void:
func lasso_selection(
points: Array[Vector2i], selection_map: SelectionMap, previous_selection_map: SelectionMap
points: Array[Vector2i], project: Project, previous_selection_map: SelectionMap
) -> void:
var selection_map := project.selection_map
var selection_size := selection_map.get_size()
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
for point in points:
@ -138,9 +139,9 @@ func lasso_selection(
bounding_rect = bounding_rect.expand(point)
if _intersect:
if previous_selection_map.is_pixel_selected(point):
selection_map.select_pixel(point, true)
select_pixel(point, project, true)
else:
selection_map.select_pixel(point, !_subtract)
select_pixel(point, project, !_subtract)
var v := Vector2i()
for x in bounding_rect.size.x:
@ -150,9 +151,17 @@ func lasso_selection(
if Geometry2D.is_point_in_polygon(v, points):
if _intersect:
if previous_selection_map.is_pixel_selected(v):
selection_map.select_pixel(v, true)
select_pixel(v, project, true)
else:
selection_map.select_pixel(v, !_subtract)
select_pixel(v, project, !_subtract)
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
if Tools.is_placing_tiles():
var tilemap := project.get_current_cel() as CelTileMap
var cell_position := tilemap.get_cell_position(point)
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
project.selection_map.select_pixel(point, select)
# Bresenham's Algorithm

View file

@ -101,6 +101,11 @@ func apply_selection(pos: Vector2i) -> void:
## 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:
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size)
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size)
var rect := Rect2i()
# Center the rect on the mouse
@ -125,6 +130,7 @@ func _get_result_rect(origin: Vector2i, dest: Vector2i) -> Rect2i:
rect.position = Vector2i(mini(origin.x, dest.x), mini(origin.y, dest.y))
rect.size = (origin - dest).abs()
rect.size += Vector2i.ONE
if not Tools.is_placing_tiles():
rect.size += Vector2i.ONE
return rect

View file

@ -65,7 +65,7 @@ func _pick_color(pos: Vector2i) -> void:
pos = project.tiles.get_canon_position(pos)
if pos.x < 0 or pos.y < 0:
return
if is_placing_tiles():
if Tools.is_placing_tiles():
var cel := Global.current_project.get_current_cel() as CelTileMap
Tools.selected_tile_index_changed.emit(cel.get_cell_index_at_coords(pos))
return

View file

@ -10,6 +10,7 @@ var text_edit: TextToolEdit:
set(value):
text_edit = value
confirm_buttons.visible = is_instance_valid(text_edit)
get_tree().current_scene.is_writing_text = is_instance_valid(text_edit)
var text_size := 16
var font := FontVariation.new()
var font_name := "":

View file

@ -224,6 +224,10 @@ func _move_with_arrow_keys(event: InputEvent) -> void:
if is_zero_approx(absf(move.y)):
move.y = 0
var final_direction := (move * step).round()
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
final_direction *= Vector2(grid_size)
move_content(final_direction)
@ -313,6 +317,8 @@ func _update_on_zoom() -> void:
func _gizmo_resize() -> void:
if Tools.is_placing_tiles():
return
var dir := dragged_gizmo.direction
if Input.is_action_pressed("shape_center"):
# Code inspired from https://github.com/GDQuest/godot-open-rpg
@ -379,10 +385,11 @@ func resize_selection() -> void:
else:
Global.current_project.selection_map.copy_from(original_bitmap)
if is_moving_content:
content_pivot = original_big_bounding_rectangle.size / 2.0
preview_image.copy_from(original_preview_image)
DrawingAlgos.nn_rotate(preview_image, angle, content_pivot)
preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
if not Tools.is_placing_tiles():
content_pivot = original_big_bounding_rectangle.size / 2.0
DrawingAlgos.nn_rotate(preview_image, angle, content_pivot)
preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
if temp_rect.size.x < 0:
preview_image.flip_x()
if temp_rect.size.y < 0:
@ -456,6 +463,15 @@ func move_borders(move: Vector2i) -> void:
return
marching_ants_outline.offset += Vector2(move)
big_bounding_rectangle.position += move
if Tools.is_placing_tiles():
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
var grid_size := tileset.tile_size
marching_ants_outline.offset = Tools.snap_to_rectangular_grid_boundary(
marching_ants_outline.offset, grid_size
)
big_bounding_rectangle.position = Vector2i(
Tools.snap_to_rectangular_grid_boundary(big_bounding_rectangle.position, grid_size)
)
queue_redraw()