From 62b9278537dbec95b6b653c5d502d3a6557158aa Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Thu, 19 Sep 2019 00:10:23 +0300 Subject: [PATCH] Selection can be moved outside the canvas - Rectangle selection can be created and moved outside the canvas - Added new View menu for Tile Mode and Show Grid. - When creating a new canvas, you can now choose a color to fill your new image with - FPS accepts values with up to 2 decimal points. SpinBox's step is changed from 1 to 0.01 - Fixed errors that occured when the user drew outside the canvas, caused by get_pixel() - Fixed error that occured when the user was pasting an empty image - Removed point_in_rectangle_equal() and new_canvas() --- Main.tscn | 35 ++++++++++++++--- Scripts/Canvas.gd | 33 ++++++++-------- Scripts/Global.gd | 2 + Scripts/Main.gd | 72 ++++++++++++++++++++++------------- Scripts/SelectionRectangle.gd | 38 ++++++++++-------- 5 files changed, 115 insertions(+), 65 deletions(-) diff --git a/Main.tscn b/Main.tscn index b928d122f..d7fbdb12d 100644 --- a/Main.tscn +++ b/Main.tscn @@ -36,7 +36,6 @@ margin_bottom = 294.0 size_flags_vertical = 3 [node name="MenuItems" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] -editor/display_folded = true margin_right = 320.0 margin_bottom = 20.0 @@ -55,6 +54,14 @@ mouse_default_cursor_shape = 2 theme = ExtResource( 2 ) text = "Edit" +[node name="ViewMenu" type="MenuButton" parent="UI/ToolPanel/Tools/MenusAndTools/MenuItems"] +margin_left = 79.0 +margin_right = 121.0 +margin_bottom = 20.0 +mouse_default_cursor_shape = 2 +theme = ExtResource( 2 ) +text = "View" + [node name="ToolsContainer" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] margin_top = 24.0 margin_right = 320.0 @@ -120,7 +127,6 @@ margin_right = 320.0 margin_bottom = 302.0 [node name="ToolOptions" type="HBoxContainer" parent="UI/ToolPanel/Tools"] -editor/display_folded = true margin_top = 306.0 margin_right = 320.0 margin_bottom = 600.0 @@ -272,7 +278,6 @@ anchor_bottom = 1.0 size_flags_horizontal = 3 [node name="ButtonContainer" type="CenterContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] -editor/display_folded = true margin_right = 536.0 margin_bottom = 24.0 @@ -320,7 +325,8 @@ margin_right = 356.0 margin_bottom = 24.0 hint_tooltip = "How many frames per second should the animation preview be? The more FPS, the faster the animation plays." mouse_default_cursor_shape = 2 -min_value = 1.0 +min_value = 0.01 +step = 0.01 value = 1.0 suffix = "FPS" @@ -567,7 +573,7 @@ margin_bottom = 70.0 margin_left = 8.0 margin_top = 8.0 margin_right = 192.0 -margin_bottom = 78.0 +margin_bottom = 102.0 [node name="ImageSize" type="Label" parent="CreateNewImage/VBoxContainer"] margin_right = 184.0 @@ -575,6 +581,7 @@ margin_bottom = 14.0 text = "Image Size" [node name="WidthCont" type="HBoxContainer" parent="CreateNewImage/VBoxContainer"] +editor/display_folded = true margin_top = 18.0 margin_right = 184.0 margin_bottom = 42.0 @@ -614,6 +621,24 @@ max_value = 16384.0 value = 64.0 suffix = "px" +[node name="FillColor" type="HBoxContainer" parent="CreateNewImage/VBoxContainer"] +margin_top = 74.0 +margin_right = 184.0 +margin_bottom = 94.0 + +[node name="FillColorLabel" type="Label" parent="CreateNewImage/VBoxContainer/FillColor"] +margin_top = 3.0 +margin_right = 94.0 +margin_bottom = 17.0 +text = "Fill with color: " + +[node name="FillColor" type="ColorPickerButton" parent="CreateNewImage/VBoxContainer/FillColor"] +margin_left = 98.0 +margin_right = 162.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 64, 20 ) +color = Color( 0, 0, 0, 0 ) + [node name="OpenSprite" type="FileDialog" parent="."] margin_right = 515.0 margin_bottom = 348.0 diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index b47ba1b55..e91c31e88 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -94,9 +94,10 @@ func _process(delta) -> void: current_color = Global.right_color_picker.color flood_fill(mouse_pos, layers[current_layer_index][0].get_pixelv(mouse_pos), current_color) "RectSelect": - if point_in_rectangle(mouse_pos_floored, location - Vector2.ONE, location + size) && Global.can_draw && Global.has_focus && Global.current_frame == frame: + #Check SelectionRectangle.gd for more code on Rectangle Selection + if Global.can_draw && Global.has_focus && Global.current_frame == frame: #If we're creating a new selection - if Global.selected_pixels.size() == 0 || !point_in_rectangle_equal(mouse_pos_floored, Global.selection_rectangle.polygon[0], Global.selection_rectangle.polygon[2]): + if Global.selected_pixels.size() == 0 || !point_in_rectangle(mouse_pos_floored, Global.selection_rectangle.polygon[0] - Vector2.ONE, Global.selection_rectangle.polygon[2]): if Input.is_action_just_pressed(current_mouse_button): Global.selection_rectangle.polygon[0] = mouse_pos_floored Global.selection_rectangle.polygon[1] = mouse_pos_floored @@ -146,7 +147,8 @@ func _process(delta) -> void: for xx in range(start_pos.x, end_pos.x): for yy in range(start_pos.y, end_pos.y): - Global.selected_pixels.append(Vector2(xx, yy)) + if xx >= location.x && xx < size.x && yy >= location.y && yy < size.y: + Global.selected_pixels.append(Vector2(xx, yy)) is_making_selection = "None" if sprite_changed_this_frame: @@ -315,27 +317,24 @@ func draw_pixel(pos : Vector2, color : Color, brush_size : int) -> void: var north_limit := location.y var south_limit := location.y + size.y if Global.selected_pixels.size() != 0: - west_limit = Global.selection_rectangle.polygon[0].x - east_limit = Global.selection_rectangle.polygon[2].x - north_limit = Global.selection_rectangle.polygon[0].y - south_limit = Global.selection_rectangle.polygon[2].y + west_limit = max(west_limit, Global.selection_rectangle.polygon[0].x) + east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x) + north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y) + south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y) var start_pos_x = pos.x - (brush_size >> 1) var start_pos_y = pos.y - (brush_size >> 1) for cur_pos_x in range(start_pos_x, start_pos_x + brush_size): #layers[current_layer_index][0].set_pixel(cur_pos_x, pos.y, color) for cur_pos_y in range(start_pos_y, start_pos_y + brush_size): - if layers[current_layer_index][0].get_pixel(cur_pos_x, cur_pos_y) != color: #don't draw the same pixel over and over - if point_in_rectangle_equal(Vector2(cur_pos_x, cur_pos_y), Vector2(west_limit, north_limit), Vector2(east_limit - 1, south_limit - 1)): + if point_in_rectangle(Vector2(cur_pos_x, cur_pos_y), Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)): + if layers[current_layer_index][0].get_pixel(cur_pos_x, cur_pos_y) != color: #don't draw the same pixel over and over layers[current_layer_index][0].set_pixel(cur_pos_x, cur_pos_y, color) #layers[current_layer_index][0].set_pixelv(pos, color) sprite_changed_this_frame = true func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y - -func point_in_rectangle_equal(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: - return p.x >= coord1.x && p.y >= coord1.y && p.x <= coord2.x && p.y <= coord2.y #Bresenham's Algorithm #Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency @@ -376,12 +375,12 @@ func flood_fill(pos : Vector2, target_color : Color, replace_color : Color) -> v var north_limit := location.y var south_limit := location.y + size.y if Global.selected_pixels.size() != 0: - west_limit = Global.selection_rectangle.polygon[0].x - east_limit = Global.selection_rectangle.polygon[2].x - north_limit = Global.selection_rectangle.polygon[0].y - south_limit = Global.selection_rectangle.polygon[2].y + west_limit = max(west_limit, Global.selection_rectangle.polygon[0].x) + east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x) + north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y) + south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y) - if !point_in_rectangle_equal(pos, Vector2(west_limit, north_limit), Vector2(east_limit - 1, south_limit - 1)): + if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)): return var q = [pos] diff --git a/Scripts/Global.gd b/Scripts/Global.gd index 34dd155d6..7c15d154c 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -33,6 +33,7 @@ var image_clipboard : Image var file_menu : MenuButton var edit_menu : MenuButton +var view_menu : MenuButton var left_indicator : Sprite var right_indicator : Sprite var left_color_picker : ColorPickerButton @@ -71,6 +72,7 @@ func _ready() -> void: file_menu = find_node_by_name(root, "FileMenu") edit_menu = find_node_by_name(root, "EditMenu") + view_menu = find_node_by_name(root, "ViewMenu") left_indicator = find_node_by_name(root, "LeftIndicator") right_indicator = find_node_by_name(root, "RightIndicator") left_color_picker = find_node_by_name(root, "LeftColorPickerButton") diff --git a/Scripts/Main.gd b/Scripts/Main.gd index b007710ff..0d6515f90 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -3,6 +3,7 @@ extends Control var current_save_path := "" var current_export_path := "" var opensprite_file_selected := false +var view_menu : PopupMenu var pencil_tool var eraser_tool var fill_tool @@ -33,14 +34,19 @@ func _ready() -> void: } var edit_menu_items := { "Scale Image" : 0, - "Tile Mode" : KEY_MASK_CTRL + KEY_T, - "Show Grid" : KEY_MASK_CTRL + KEY_G, "Clear Selection" : 0 #"Undo" : KEY_MASK_CTRL + KEY_Z, #"Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z, } + var view_menu_items := { + "Tile Mode" : KEY_MASK_CTRL + KEY_T, + "Show Grid" : KEY_MASK_CTRL + KEY_G + #"Undo" : KEY_MASK_CTRL + KEY_Z, + #"Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z, + } var file_menu : PopupMenu = Global.file_menu.get_popup() var edit_menu : PopupMenu = Global.edit_menu.get_popup() + view_menu = Global.view_menu.get_popup() var i = 0 for item in file_menu_items.keys(): file_menu.add_item(item, i, file_menu_items[item]) @@ -49,13 +55,19 @@ func _ready() -> void: for item in edit_menu_items.keys(): edit_menu.add_item(item, i, edit_menu_items[item]) i += 1 + i = 0 + for item in view_menu_items.keys(): + view_menu.add_check_item(item, i, view_menu_items[item]) + i += 1 file_menu.connect("id_pressed", self, "file_menu_id_pressed") edit_menu.connect("id_pressed", self, "edit_menu_id_pressed") + view_menu.connect("id_pressed", self, "view_menu_id_pressed") - pencil_tool = $UI/ToolPanel/Tools/MenusAndTools/ToolsContainer/Pencil - eraser_tool = $UI/ToolPanel/Tools/MenusAndTools/ToolsContainer/Eraser - fill_tool = $UI/ToolPanel/Tools/MenusAndTools/ToolsContainer/Fill - rectselect_tool = $UI/ToolPanel/Tools/MenusAndTools/ToolsContainer/RectSelect + var root = get_tree().get_root() + pencil_tool = Global.find_node_by_name(root, "Pencil") + eraser_tool = Global.find_node_by_name(root, "Eraser") + fill_tool = Global.find_node_by_name(root, "Fill") + rectselect_tool = Global.find_node_by_name(root, "RectSelect") pencil_tool.connect("pressed", self, "_on_Tool_pressed", [pencil_tool]) eraser_tool.connect("pressed", self, "_on_Tool_pressed", [eraser_tool]) @@ -78,7 +90,7 @@ func _ready() -> void: $ExportSprites.get_vbox().add_child(export_as_single_file) $ExportSprites.get_vbox().add_child(export_vertical_spritesheet) - + func _input(event): #Handle tool shortcuts if event.is_action_pressed("right_pencil_tool"): @@ -98,7 +110,6 @@ func _input(event): elif event.is_action_pressed("left_rectangle_select_tool"): _on_Tool_pressed(rectselect_tool, false, true) - func file_menu_id_pressed(id : int) -> void: match id: 0: #New @@ -138,21 +149,39 @@ func edit_menu_id_pressed(id : int) -> void: 0: #Scale Image $ScaleImage.popup_centered() Global.can_draw = false - 1: #Tile mode - Global.tile_mode = !Global.tile_mode - 2: #Show grid - Global.draw_grid = !Global.draw_grid - 3: #Clear selection + 1: #Clear selection Global.selection_rectangle.polygon[0] = Vector2.ZERO Global.selection_rectangle.polygon[1] = Vector2.ZERO Global.selection_rectangle.polygon[2] = Vector2.ZERO Global.selection_rectangle.polygon[3] = Vector2.ZERO Global.selected_pixels.clear() +func view_menu_id_pressed(id : int) -> void: + match id: + 0: #Tile mode + Global.tile_mode = !Global.tile_mode + view_menu.set_item_checked(0, Global.tile_mode) + 1: #Show grid + Global.draw_grid = !Global.draw_grid + view_menu.set_item_checked(1, Global.draw_grid) + func _on_CreateNewImage_confirmed() -> void: - var width = float($CreateNewImage/VBoxContainer/WidthCont/WidthValue.value) - var height = float($CreateNewImage/VBoxContainer/HeightCont/HeightValue.value) - new_canvas(Vector2(width, height).floor()) + var width := float($CreateNewImage/VBoxContainer/WidthCont/WidthValue.value) + var height := float($CreateNewImage/VBoxContainer/HeightCont/HeightValue.value) + var fill_color : Color = $CreateNewImage/VBoxContainer/FillColor/FillColor.color + clear_canvases() + Global.canvas = load("res://Canvas.tscn").instance() + Global.canvas.size = Vector2(width, height).floor() + + Global.canvas_parent.add_child(Global.canvas) + Global.canvases.append(Global.canvas) + Global.current_frame = 0 + if fill_color.a > 0: + Global.canvas.layers[0][0].fill(fill_color) + Global.canvas.layers[0][0].lock() + Global.canvas.update_texture(0) + Global.remove_frame_button.disabled = true + Global.remove_frame_button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN func _on_OpenSprite_file_selected(path) -> void: var file := File.new() @@ -296,17 +325,6 @@ func clear_canvases() -> void: child.queue_free() Global.canvases.clear() -func new_canvas(size : Vector2) -> void: - clear_canvases() - Global.canvas = load("res://Canvas.tscn").instance() - Global.canvas.size = size - - Global.canvas_parent.add_child(Global.canvas) - Global.canvases.append(Global.canvas) - Global.current_frame = 0 - Global.remove_frame_button.disabled = true - Global.remove_frame_button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN - func _on_ExportSprites_file_selected(path : String) -> void: current_export_path = path export_project() diff --git a/Scripts/SelectionRectangle.gd b/Scripts/SelectionRectangle.gd index ac5fa6936..1297b506e 100644 --- a/Scripts/SelectionRectangle.gd +++ b/Scripts/SelectionRectangle.gd @@ -44,11 +44,14 @@ func _process(delta) -> void: img.resize(polygon[2].x - polygon[0].x, polygon[2].y - polygon[0].y, 0) img.lock() for i in range(Global.selected_pixels.size()): - orig_colors.append(layer.get_pixelv(Global.selected_pixels[i])) - var px = Global.selected_pixels[i] - Global.selected_pixels[0] - img.set_pixelv(px, orig_colors[i]) - layer.set_pixelv(Global.selected_pixels[i], Color(0, 0, 0, 0)) - #print(layer.get_pixelv(Global.selected_pixels[i])) + var curr_px = Global.selected_pixels[i] + if point_in_rectangle(curr_px, Global.canvas.location - Vector2.ONE, Global.canvas.size): + orig_colors.append(layer.get_pixelv(curr_px)) + var px = curr_px - Global.selected_pixels[0] + img.set_pixelv(px, orig_colors[i]) + layer.set_pixelv(curr_px, Color(0, 0, 0, 0)) + else: + orig_colors.append(Color(0, 0, 0, 0)) Global.canvas.update_texture(Global.canvas.current_layer_index) tex.create_from_image(img, 0) update() @@ -58,13 +61,13 @@ func _process(delta) -> void: if is_dragging: if (Global.current_left_tool == "RectSelect" && Input.is_action_pressed("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_pressed("right_mouse")): #Drag - if orig_x + mouse_pos_floored.x >= Global.canvas.location.x && diff_x + mouse_pos_floored.x <= Global.canvas.size.x: - start_pos.x = orig_x + mouse_pos_floored.x - end_pos.x = diff_x + mouse_pos_floored.x + #if orig_x + mouse_pos_floored.x >= Global.canvas.location.x && diff_x + mouse_pos_floored.x <= Global.canvas.size.x: + start_pos.x = orig_x + mouse_pos_floored.x + end_pos.x = diff_x + mouse_pos_floored.x - if orig_y + mouse_pos_floored.y >= Global.canvas.location.y && diff_y + mouse_pos_floored.y <= Global.canvas.size.y: - start_pos.y = orig_y + mouse_pos_floored.y - end_pos.y = diff_y + mouse_pos_floored.y + #if orig_y + mouse_pos_floored.y >= Global.canvas.location.y && diff_y + mouse_pos_floored.y <= Global.canvas.size.y: + start_pos.y = orig_y + mouse_pos_floored.y + end_pos.y = diff_y + mouse_pos_floored.y polygon[0] = start_pos polygon[1] = Vector2(end_pos.x, start_pos.y) polygon[2] = end_pos @@ -74,11 +77,15 @@ func _process(delta) -> void: #Release Drag is_dragging = false if move_pixels: - for i in range(Global.selected_pixels.size()): + for i in range(orig_colors.size()): if orig_colors[i].a > 0: var px = polygon[0] + Global.selected_pixels[i] - Global.selected_pixels[0] - layer.set_pixelv(px, orig_colors[i]) + if point_in_rectangle(px, Global.canvas.location - Vector2.ONE, Global.canvas.size): + layer.set_pixelv(px, orig_colors[i]) Global.canvas.update_texture(Global.canvas.current_layer_index) + img.fill(Color(0, 0, 0, 0)) + tex.create_from_image(img, 0) + update() orig_colors.clear() Global.selected_pixels.clear() @@ -89,11 +96,10 @@ func _process(delta) -> void: #Handle copy if Input.is_action_just_pressed("copy") && Global.selected_pixels.size() > 0: Global.image_clipboard = layer.get_rect(Rect2(polygon[0], polygon[2])) - print(Rect2(polygon[0], polygon[2]), Global.image_clipboard.get_size()) - print(Global.image_clipboard.get_data()[0]) #Handle paste - if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && !is_dragging: + #if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && !is_dragging: + if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && Global.image_clipboard.get_size() > Vector2.ZERO: layer.blend_rect(Global.image_clipboard, Rect2(Vector2.ZERO, polygon[2]-polygon[0]), polygon[0]) layer.lock() Global.canvas.update_texture(Global.canvas.current_layer_index)