From 7b189cc45e51728b3c8e3c8035ca04fee226d4ba Mon Sep 17 00:00:00 2001 From: Variable <77773850+Variable-ind@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:22:32 +0500 Subject: [PATCH] TileMode for selection, (plus some other fixes) (#834) * Tilemode selections * Selection TileMode * Fixed some bugs with the ORIGINAL tile mode Image brushes needed to be adjusted * remove behaviour checkbutton * fixed remaining things * formating * formatting * formatting * formatting * fix crash on rotating image * Fix Color getting transparent on double chicking same swatch twice * typo * typo * Update RotateImage.gd * Use sheckbox instead of option button * make checkbox directly under select menu --- src/Classes/SelectionMap.gd | 55 ++++++++++++++++ src/Classes/Tiles.gd | 9 +++ src/Tools/Draw.gd | 71 +++++++++++++-------- src/UI/Canvas/Selection.gd | 5 +- src/UI/TopMenuContainer/TopMenuContainer.gd | 51 ++++++++------- 5 files changed, 141 insertions(+), 50 deletions(-) diff --git a/src/Classes/SelectionMap.gd b/src/Classes/SelectionMap.gd index fc4807fb5..117d1e5aa 100644 --- a/src/Classes/SelectionMap.gd +++ b/src/Classes/SelectionMap.gd @@ -13,6 +13,61 @@ func is_pixel_selected(pixel: Vector2) -> bool: return selected +func get_nearest_position(pixel: Vector2) -> Vector2: + if Global.canvas.selection.flag_tilemode: + # functions more or less the same way as the tilemode + var size = Global.current_project.size + var selection_rect = get_used_rect() + var start_x = selection_rect.position.x - selection_rect.size.x + var end_x = selection_rect.position.x + 2 * selection_rect.size.x + var start_y = selection_rect.position.y - selection_rect.size.y + var end_y = selection_rect.position.y + 2 * selection_rect.size.y + for x in range(start_x, end_x, selection_rect.size.x): + for y in range(start_y, end_y, selection_rect.size.y): + var test_image = Image.new() + test_image.create(size.x, size.y, false, Image.FORMAT_LA8) + test_image.blit_rect(self, selection_rect, Vector2(x, y)) + test_image.lock() + if ( + pixel.x < 0 + or pixel.y < 0 + or pixel.x >= test_image.get_width() + or pixel.y >= test_image.get_height() + ): + continue + var selected: bool = test_image.get_pixelv(pixel).a > 0 + test_image.unlock() + if selected: + var offset = Vector2(x, y) - selection_rect.position + return offset + return Vector2.ZERO + else: + return Vector2.ZERO + + +func get_point_in_tile_mode(pixel: Vector2) -> Array: + var result = [] + if Global.canvas.selection.flag_tilemode: + var selection_rect = get_used_rect() + var start_x = selection_rect.position.x - selection_rect.size.x + var end_x = selection_rect.position.x + 2 * selection_rect.size.x + var start_y = selection_rect.position.y - selection_rect.size.y + var end_y = selection_rect.position.y + 2 * selection_rect.size.y + for x in range(start_x, end_x, selection_rect.size.x): + for y in range(start_y, end_y, selection_rect.size.y): + result.append(Vector2(x, y) + pixel - selection_rect.position) + else: + result.append(pixel) + return result + + +func get_canon_position(position) -> Vector2: + if Global.canvas.selection.flag_tilemode: + return position - get_nearest_position(position) + else: + return position + + func select_pixel(pixel: Vector2, select := true) -> void: lock() if select: diff --git a/src/Classes/Tiles.gd b/src/Classes/Tiles.gd index 61779be80..3bc214237 100644 --- a/src/Classes/Tiles.gd +++ b/src/Classes/Tiles.gd @@ -86,6 +86,15 @@ func get_canon_position(position: Vector2) -> Vector2: return position +func get_point_in_tiles(pixel: Vector2) -> Array: + var positions = Global.canvas.tile_mode.get_tile_positions() + positions.append(Vector2.ZERO) + var result = [] + for pos in positions: + result.append(pos + pixel) + return result + + func has_point(point: Vector2) -> bool: var positions = Global.canvas.tile_mode.get_tile_positions() positions.append(Vector2.ZERO) # The central tile is included manually diff --git a/src/Tools/Draw.gd b/src/Tools/Draw.gd index 884638f70..edee4df53 100644 --- a/src/Tools/Draw.gd +++ b/src/Tools/Draw.gd @@ -374,38 +374,44 @@ func _draw_tool_circle_from_map(position: Vector2) -> PoolVector2Array: return result -func draw_tool_brush(position: Vector2) -> void: +func draw_tool_brush(brush_position: Vector2) -> void: var project: Project = Global.current_project - position = project.tiles.get_canon_position(position) + # image brushes work differently, (we have to consider all 8 surrounding points) + var central_point = project.tiles.get_canon_position(brush_position) + var positions = project.tiles.get_point_in_tiles(central_point) + if Global.current_project.has_selection and project.tiles.mode == Tiles.MODE.NONE: + positions = Global.current_project.selection_map.get_point_in_tile_mode(central_point) var size := _brush_image.get_size() - var dst := position - (size / 2).floor() - var dst_rect := Rect2(dst, size) - var draw_rect := _get_draw_rect() - dst_rect = dst_rect.clip(draw_rect) - if dst_rect.size == Vector2.ZERO: - return - var src_rect := Rect2(dst_rect.position - dst, dst_rect.size) - var brush_image: Image = remove_unselected_parts_of_brush(_brush_image, dst) - dst = dst_rect.position - _draw_brush_image(brush_image, src_rect, dst) + for i in positions.size(): + var position: Vector2 = positions[i] + var dst: Vector2 = position - (size / 2).floor() + var dst_rect := Rect2(dst, size) + var draw_rect := _get_draw_rect() + dst_rect = dst_rect.clip(draw_rect) + if dst_rect.size == Vector2.ZERO: + continue + var src_rect := Rect2(dst_rect.position - dst, dst_rect.size) + var brush_image: Image = remove_unselected_parts_of_brush(_brush_image, dst) + dst = dst_rect.position + _draw_brush_image(brush_image, src_rect, dst) - # Handle Mirroring - var mirror_x = (project.x_symmetry_point + 1) - dst.x - src_rect.size.x - var mirror_y = (project.y_symmetry_point + 1) - dst.y - src_rect.size.y + # Handle Mirroring + var mirror_x = (project.x_symmetry_point + 1) - dst.x - src_rect.size.x + var mirror_y = (project.y_symmetry_point + 1) - dst.y - src_rect.size.y - if Tools.horizontal_mirror: - var x_dst := Vector2(mirror_x, dst.y) - var mirror_brush_x: Image = remove_unselected_parts_of_brush(_mirror_brushes.x, x_dst) - _draw_brush_image(mirror_brush_x, _flip_rect(src_rect, size, true, false), x_dst) + if Tools.horizontal_mirror: + var x_dst := Vector2(mirror_x, dst.y) + var mirror_brush_x: Image = remove_unselected_parts_of_brush(_mirror_brushes.x, x_dst) + _draw_brush_image(mirror_brush_x, _flip_rect(src_rect, size, true, false), x_dst) + if Tools.vertical_mirror: + var xy_dst := Vector2(mirror_x, mirror_y) + var mirror_brush_xy := remove_unselected_parts_of_brush(_mirror_brushes.xy, xy_dst) + _draw_brush_image(mirror_brush_xy, _flip_rect(src_rect, size, true, true), xy_dst) if Tools.vertical_mirror: - var xy_dst := Vector2(mirror_x, mirror_y) - var mirror_brush_xy := remove_unselected_parts_of_brush(_mirror_brushes.xy, xy_dst) - _draw_brush_image(mirror_brush_xy, _flip_rect(src_rect, size, true, true), xy_dst) - if Tools.vertical_mirror: - var y_dst := Vector2(dst.x, mirror_y) - var mirror_brush_y: Image = remove_unselected_parts_of_brush(_mirror_brushes.y, y_dst) - _draw_brush_image(mirror_brush_y, _flip_rect(src_rect, size, false, true), y_dst) + var y_dst := Vector2(dst.x, mirror_y) + var mirror_brush_y: Image = remove_unselected_parts_of_brush(_mirror_brushes.y, y_dst) + _draw_brush_image(mirror_brush_y, _flip_rect(src_rect, size, false, true), y_dst) func remove_unselected_parts_of_brush(brush: Image, dst: Vector2) -> Image: @@ -429,6 +435,17 @@ func remove_unselected_parts_of_brush(brush: Image, dst: Vector2) -> Image: func draw_indicator(left: bool) -> void: var color := Global.left_tool_color if left else Global.right_tool_color draw_indicator_at(snap_position(_cursor), Vector2.ZERO, color) + if ( + Global.current_project.has_selection + and Global.current_project.tiles.mode == Tiles.MODE.NONE + ): + var position := _line_start if _draw_line else _cursor + var nearest_pos := Global.current_project.selection_map.get_nearest_position(position) + if nearest_pos != Vector2.ZERO: + var offset := nearest_pos + draw_indicator_at(snap_position(_cursor), offset, Color.green) + return + if Global.current_project.tiles.mode and Global.current_project.tiles.has_point(_cursor): var position := _line_start if _draw_line else _cursor var nearest_tile := Global.current_project.tiles.get_nearest_tile(position) @@ -470,6 +487,8 @@ func _set_pixel(position: Vector2, ignore_mirroring := false) -> void: func _set_pixel_no_cache(position: Vector2, ignore_mirroring := false) -> void: position = _stroke_project.tiles.get_canon_position(position) + if Global.current_project.has_selection: + position = Global.current_project.selection_map.get_canon_position(position) if !_stroke_project.can_pixel_get_drawn(position): return diff --git a/src/UI/Canvas/Selection.gd b/src/UI/Canvas/Selection.gd index 234ae0bdf..2d716c944 100644 --- a/src/UI/Canvas/Selection.gd +++ b/src/UI/Canvas/Selection.gd @@ -1,10 +1,13 @@ extends Node2D enum SelectionOperation { ADD, SUBTRACT, INTERSECT } - +enum SelectionFlags { TILE_MODE } const KEY_MOVE_ACTION_NAMES := ["ui_up", "ui_down", "ui_left", "ui_right"] const CLIPBOARD_FILE_PATH := "user://clipboard.txt" +# flags (additional properties of selection that can be toggled) +var flag_tilemode = false + var is_moving_content := false var arrow_key_move := false var is_pasting := false diff --git a/src/UI/TopMenuContainer/TopMenuContainer.gd b/src/UI/TopMenuContainer/TopMenuContainer.gd index 816d24085..8ce0530cc 100644 --- a/src/UI/TopMenuContainer/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer/TopMenuContainer.gd @@ -299,13 +299,14 @@ func _setup_image_menu() -> void: func _setup_select_menu() -> void: # Order as in Global.SelectMenu enum - var select_menu_items := ["All", "Clear", "Invert"] + var select_menu_items := ["All", "Clear", "Invert", "Tile Mode"] var select_menu: PopupMenu = select_menu_button.get_popup() - var i := 0 - for item in select_menu_items: - select_menu.add_item(item, i) - i += 1 - + for i in select_menu_items.size(): + var item: String = select_menu_items[i] + if item == "Tile Mode": + select_menu.add_check_item(item, i) + else: + select_menu.add_item(item, i) select_menu.connect("id_pressed", self, "select_menu_id_pressed") @@ -474,6 +475,23 @@ func view_menu_id_pressed(id: int) -> void: Global.canvas.update() +func window_menu_id_pressed(id: int) -> void: + if not Global.can_draw: + return + match id: + Global.WindowMenu.WINDOW_OPACITY: + _popup_dialog(window_opacity_dialog) + Global.WindowMenu.MOVABLE_PANELS: + ui.tabs_visible = !ui.tabs_visible + window_menu.set_item_checked(id, ui.tabs_visible) + Global.WindowMenu.ZEN_MODE: + _toggle_zen_mode() + Global.WindowMenu.FULLSCREEN_MODE: + _toggle_fullscreen() + _: + _handle_metadata(id, window_menu_button) + + func _tile_mode_submenu_id_pressed(id: int) -> void: Global.current_project.tiles.mode = id Global.transparent_checker.fit_rect(Global.current_project.tiles.get_bounding_rect()) @@ -497,23 +515,6 @@ func _snap_to_submenu_id_pressed(id: int) -> void: snap_to_submenu.set_item_checked(id, Global.snap_to_perspective_guides) -func window_menu_id_pressed(id: int) -> void: - if not Global.can_draw: - return - match id: - Global.WindowMenu.WINDOW_OPACITY: - _popup_dialog(window_opacity_dialog) - Global.WindowMenu.MOVABLE_PANELS: - ui.tabs_visible = !ui.tabs_visible - window_menu.set_item_checked(id, ui.tabs_visible) - Global.WindowMenu.ZEN_MODE: - _toggle_zen_mode() - Global.WindowMenu.FULLSCREEN_MODE: - _toggle_fullscreen() - _: - _handle_metadata(id, window_menu_button) - - func _panels_submenu_id_pressed(id: int) -> void: if zen_mode: return @@ -699,6 +700,10 @@ func select_menu_id_pressed(id: int) -> void: Global.canvas.selection.clear_selection(true) Global.SelectMenu.INVERT: Global.canvas.selection.invert() + Global.SelectMenu.TILE_MODE: + var state = select_menu_button.get_popup().is_item_checked(id) + Global.canvas.selection.flag_tilemode = !state + select_menu_button.get_popup().set_item_checked(id, !state) _: _handle_metadata(id, select_menu_button)