1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00

Compare commits

...

2 commits

Author SHA1 Message Date
Emmanouil Papadeas 2e589d6dcc Fix lint warning 2024-12-05 02:18:24 +02:00
Emmanouil Papadeas adf250583c Resizing selections now work when draw tiles mode is enabled
Working with 2D arrays is a bit of a pain, but I managed. Needs testing.
2024-12-05 02:13:26 +02:00
2 changed files with 200 additions and 36 deletions

View file

@ -1,3 +1,4 @@
# gdlint: ignore=max-public-methods
class_name CelTileMap
extends PixelCel
@ -150,7 +151,7 @@ func get_cell_index_at_coords_in_tilemap_space(coords: Vector2i) -> int:
## Returns [code]true[/code] if the tile at cell position [param cell_position]
## with image [param image_portion] is equal to [param tile_image].
func tiles_equal(cell_position: int, image_portion: Image, tile_image: Image) -> bool:
func _tiles_equal(cell_position: int, image_portion: Image, tile_image: Image) -> bool:
var cell_data := cells[cell_position]
var final_image_portion := transform_tile(
tile_image, cell_data.flip_h, cell_data.flip_v, cell_data.transpose
@ -187,6 +188,125 @@ func transform_tile(
return transformed_tile
## Given a [param selection_map] and a [param selection_rect],
## the method finds the cells that are currently selected and returns them
## in the form of a 2D array that contains the serialiazed data
##of the selected cells in the form of [Dictionary].
func get_selected_cells(selection_map: SelectionMap, selection_rect: Rect2i) -> Array[Array]:
var selected_cells: Array[Array] = []
for x in range(0, selection_rect.size.x, tileset.tile_size.x):
selected_cells.append([])
for y in range(0, selection_rect.size.y, tileset.tile_size.y):
var pos := Vector2i(x, y) + selection_rect.position
var x_index := x / tileset.tile_size.x
if selection_map.is_pixel_selected(pos):
var cell_pos := get_cell_position(pos)
selected_cells[x_index].append(cells[cell_pos].serialize())
else:
# If it's not selected, append the transparent tile 0.
selected_cells[x_index].append(
{"index": 0, "flip_h": false, "flip_v": false, "transpose": false}
)
return selected_cells
## Resizes [param selected_indices], which is an array of arrays of [Dictionary],
## to [param horizontal_size] and [param vertical_size].
## This method is used when resizing a selection and draw tiles mode is enabled.
func resize_selection(
selected_cells: Array[Array], horizontal_size: int, vertical_size: int
) -> Array[Array]:
var resized_cells: Array[Array] = []
var current_columns := selected_cells.size()
if current_columns == 0:
return resized_cells
var current_rows := selected_cells[0].size()
if current_rows == 0:
return resized_cells
resized_cells.resize(horizontal_size)
for x in horizontal_size:
resized_cells[x] = []
resized_cells[x].resize(vertical_size)
var column_middles := current_columns - 2
if current_columns == 1:
for x in horizontal_size:
_resize_rows(selected_cells[0], resized_cells[x], current_rows, vertical_size)
else:
for x in horizontal_size:
if x == 0:
_resize_rows(selected_cells[0], resized_cells[x], current_rows, vertical_size)
elif x == horizontal_size - 1:
_resize_rows(selected_cells[-1], resized_cells[x], current_rows, vertical_size)
else:
if x < current_columns - 1:
_resize_rows(selected_cells[x], resized_cells[x], current_rows, vertical_size)
else:
if column_middles == 0:
_resize_rows(
selected_cells[-1], resized_cells[x], current_rows, vertical_size
)
else:
var x_index := x - (column_middles * ((x - 1) / column_middles))
_resize_rows(
selected_cells[x_index], resized_cells[x], current_rows, vertical_size
)
return resized_cells
## Helper method of [method resize_selection].
func _resize_rows(
selected_cells: Array, resized_cells: Array, current_rows: int, vertical_size: int
) -> void:
var row_middles := current_rows - 2
if current_rows == 1:
for y in vertical_size:
resized_cells[y] = selected_cells[0]
else:
for y in vertical_size:
if y == 0:
resized_cells[y] = selected_cells[0]
elif y == vertical_size - 1:
resized_cells[y] = selected_cells[-1]
else:
if y < current_rows - 1:
resized_cells[y] = selected_cells[y]
else:
if row_middles == 0:
resized_cells[y] = selected_cells[-1]
else:
var y_index := y - (row_middles * ((y - 1) / row_middles))
resized_cells[y] = selected_cells[y_index]
## Applies the [param selected_cells] data to [param target_image] data,
## offset by [param selection_rect]. The target image needs to be resized first.
## This method is used when resizing a selection and draw tiles mode is enabled.
func apply_resizing_to_image(
target_image: Image, selected_cells: Array[Array], selection_rect: Rect2i
) -> void:
for x in selected_cells.size():
for y in selected_cells[x].size():
var pos := Vector2i(x, y) * tileset.tile_size + selection_rect.position
var cell_pos := get_cell_position(pos)
var coords := get_cell_coords_in_image(cell_pos) - selection_rect.position
var rect := Rect2i(coords, tileset.tile_size)
var image_portion := target_image.get_region(rect)
var cell_data := Cell.new()
cell_data.deserialize(selected_cells[x][y])
var index := cell_data.index
if index >= tileset.tiles.size():
index = 0
var current_tile := tileset.tiles[index].image
var transformed_tile := transform_tile(
current_tile, cell_data.flip_h, cell_data.flip_v, cell_data.transpose
)
if image_portion.get_data() != transformed_tile.get_data():
var tile_size := transformed_tile.get_size()
target_image.blit_rect(transformed_tile, Rect2i(Vector2i.ZERO, tile_size), coords)
if target_image is ImageExtended:
target_image.convert_rgb_to_indexed()
## Appends data to a [Dictionary] to be used for undo/redo.
func serialize_undo_data() -> Dictionary:
var dict := {}
@ -261,7 +381,7 @@ func update_tilemap(
tileset.add_tile(image_portion, self)
cells[i].index = tileset.tiles.size() - 1
continue
if not tiles_equal(i, image_portion, current_tile.image):
if not _tiles_equal(i, image_portion, current_tile.image):
tileset.replace_tile_at(image_portion, index, self)
elif tile_editing_mode == TileSetPanel.TileEditingMode.AUTO:
_handle_auto_editing_mode(i, image_portion, tileset_size_before_update)
@ -271,7 +391,7 @@ func update_tilemap(
var found_tile := false
for j in range(1, tileset.tiles.size()):
var tile := tileset.tiles[j]
if tiles_equal(i, image_portion, tile.image):
if _tiles_equal(i, image_portion, tile.image):
if cells[i].index != j:
cells[i].index = j
cells[i].remove_transformations()
@ -296,7 +416,7 @@ func update_tilemap(
if index >= tileset.tiles.size():
index = 0
var current_tile := tileset.tiles[index]
if not tiles_equal(i, image_portion, current_tile.image):
if not _tiles_equal(i, image_portion, current_tile.image):
set_index(i, cells[i].index)
@ -371,7 +491,7 @@ func _handle_auto_editing_mode(
tileset.add_tile(image_portion, self)
cells[i].index = tileset.tiles.size() - 1
else: # If the cell is already mapped.
if tiles_equal(i, image_portion, current_tile.image):
if _tiles_equal(i, image_portion, current_tile.image):
# Case 3: The cell is mapped and it did not change.
# Do nothing and move on to the next cell.
return
@ -452,12 +572,12 @@ func _re_index_all_cells() -> void:
var index := cells[i].index
if index > 0 and index < tileset.tiles.size():
var current_tile := tileset.tiles[index]
if not tiles_equal(i, image_portion, current_tile.image):
if not _tiles_equal(i, image_portion, current_tile.image):
set_index(i, cells[i].index)
continue
for j in range(1, tileset.tiles.size()):
var tile := tileset.tiles[j]
if tiles_equal(i, image_portion, tile.image):
if _tiles_equal(i, image_portion, tile.image):
cells[i].index = j
break
@ -551,7 +671,7 @@ func update_texture(undo := false) -> void:
var tile_size := image_portion.get_size()
image.blit_rect(transformed_editing_image, Rect2i(Vector2i.ZERO, tile_size), coords)
else:
if not tiles_equal(i, image_portion, current_tile.image):
if not _tiles_equal(i, image_portion, current_tile.image):
var transformed_image := transform_tile(
image_portion, cell_data.flip_h, cell_data.flip_v, cell_data.transpose, true
)

View file

@ -28,7 +28,7 @@ var big_bounding_rectangle := Rect2i():
if slot.tool_node is BaseSelectionTool:
slot.tool_node.set_spinbox_values()
_update_gizmos()
var image_current_pixel := Vector2.ZERO ## The ACTUAL pixel coordinate of image
var image_current_pixel := Vector2.ZERO ## The pixel coordinates of the cursor
var temp_rect := Rect2()
var rect_aspect_ratio := 0.0
@ -38,6 +38,7 @@ var original_big_bounding_rectangle := Rect2i()
var original_preview_image := Image.new()
var original_bitmap := SelectionMap.new()
var original_offset := Vector2.ZERO
var original_selected_tilemap_cells: Array[Array]
var preview_image := Image.new()
var preview_image_texture := ImageTexture.new()
@ -317,20 +318,22 @@ func _update_on_zoom() -> void:
func _gizmo_resize() -> void:
if Tools.is_placing_tiles():
return
var dir := dragged_gizmo.direction
var mouse_pos := image_current_pixel
if Tools.is_placing_tiles():
var tilemap := Global.current_project.get_current_cel() as CelTileMap
mouse_pos = mouse_pos.snapped(tilemap.tileset.tile_size)
if Input.is_action_pressed("shape_center"):
# Code inspired from https://github.com/GDQuest/godot-open-rpg
if dir.x != 0 and dir.y != 0: # Border gizmos
temp_rect.size = ((image_current_pixel - temp_rect_pivot) * 2.0 * Vector2(dir))
temp_rect.size = ((mouse_pos - temp_rect_pivot) * 2.0 * Vector2(dir))
elif dir.y == 0: # Center left and right gizmos
temp_rect.size.x = (image_current_pixel.x - temp_rect_pivot.x) * 2.0 * dir.x
temp_rect.size.x = (mouse_pos.x - temp_rect_pivot.x) * 2.0 * dir.x
elif dir.x == 0: # Center top and bottom gizmos
temp_rect.size.y = (image_current_pixel.y - temp_rect_pivot.y) * 2.0 * dir.y
temp_rect.size.y = (mouse_pos.y - temp_rect_pivot.y) * 2.0 * dir.y
temp_rect = Rect2(-1.0 * temp_rect.size / 2 + temp_rect_pivot, temp_rect.size)
else:
_resize_rect(image_current_pixel, dir)
_resize_rect(mouse_pos, dir)
if Input.is_action_pressed("shape_perfect") or resize_keep_ratio: # Maintain aspect ratio
var end_y := temp_rect.end.y
@ -386,14 +389,28 @@ func resize_selection() -> void:
Global.current_project.selection_map.copy_from(original_bitmap)
if is_moving_content:
preview_image.copy_from(original_preview_image)
if not Tools.is_placing_tiles():
if Tools.is_placing_tiles():
for cel in _get_selected_draw_cels():
if cel is not CelTileMap:
continue
var tilemap := cel as CelTileMap
var horizontal_size := size.x / tilemap.tileset.tile_size.x
var vertical_size := size.y / tilemap.tileset.tile_size.y
var selected_cells := tilemap.resize_selection(
original_selected_tilemap_cells, horizontal_size, vertical_size
)
preview_image.crop(size.x, size.y)
tilemap.apply_resizing_to_image(
preview_image, selected_cells, big_bounding_rectangle
)
else:
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:
preview_image.flip_y()
if temp_rect.size.x < 0:
preview_image.flip_x()
if temp_rect.size.y < 0:
preview_image.flip_y()
preview_image_texture = ImageTexture.create_from_image(preview_image)
Global.current_project.selection_map.copy_from(original_bitmap)
@ -495,9 +512,15 @@ func transform_content_start() -> void:
undo_data = get_undo_data(false)
return
is_moving_content = true
original_bitmap.copy_from(Global.current_project.selection_map)
var project := Global.current_project
original_bitmap.copy_from(project.selection_map)
original_big_bounding_rectangle = big_bounding_rectangle
original_offset = Global.current_project.selection_offset
original_offset = project.selection_offset
var current_cel := project.get_current_cel()
if current_cel is CelTileMap:
original_selected_tilemap_cells = (current_cel as CelTileMap).get_selected_cells(
project.selection_map, big_bounding_rectangle
)
queue_redraw()
canvas.queue_redraw()
@ -517,21 +540,40 @@ func transform_content_confirm() -> void:
if not is_pasting:
src.copy_from(cel.transformed_content)
cel.transformed_content = null
DrawingAlgos.nn_rotate(src, angle, content_pivot)
src.resize(
preview_image.get_width(), preview_image.get_height(), Image.INTERPOLATE_NEAREST
)
if temp_rect.size.x < 0:
src.flip_x()
if temp_rect.size.y < 0:
src.flip_y()
if Tools.is_placing_tiles():
if cel is not CelTileMap:
continue
var tilemap := cel as CelTileMap
var horizontal_size := preview_image.get_width() / tilemap.tileset.tile_size.x
var vertical_size := preview_image.get_height() / tilemap.tileset.tile_size.y
var selected_cells := tilemap.resize_selection(
original_selected_tilemap_cells, horizontal_size, vertical_size
)
src.crop(preview_image.get_width(), preview_image.get_height())
tilemap.apply_resizing_to_image(src, selected_cells, big_bounding_rectangle)
else:
DrawingAlgos.nn_rotate(src, angle, content_pivot)
src.resize(
preview_image.get_width(), preview_image.get_height(), Image.INTERPOLATE_NEAREST
)
if temp_rect.size.x < 0:
src.flip_x()
if temp_rect.size.y < 0:
src.flip_y()
cel_image.blit_rect_mask(
src,
src,
Rect2i(Vector2i.ZERO, project.selection_map.get_size()),
big_bounding_rectangle.position
)
if Tools.is_placing_tiles():
cel_image.blit_rect(
src,
Rect2i(Vector2i.ZERO, project.selection_map.get_size()),
big_bounding_rectangle.position
)
else:
cel_image.blit_rect_mask(
src,
src,
Rect2i(Vector2i.ZERO, project.selection_map.get_size()),
big_bounding_rectangle.position
)
cel_image.convert_rgb_to_indexed()
project.selection_map.move_bitmap_values(project)
commit_undo("Move Selection", undo_data)
@ -539,6 +581,7 @@ func transform_content_confirm() -> void:
original_preview_image = Image.new()
preview_image = Image.new()
original_bitmap = SelectionMap.new()
original_selected_tilemap_cells.clear()
is_moving_content = false
is_pasting = false
angle = 0.0
@ -573,6 +616,7 @@ func transform_content_cancel() -> void:
original_preview_image = Image.new()
preview_image = Image.new()
original_bitmap = SelectionMap.new()
original_selected_tilemap_cells.clear()
is_pasting = false
angle = 0.0
content_pivot = Vector2.ZERO