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

Added Polygon selection tool

Cursor icon is temporary. It works by clicking multiple times on the places you want to create edges, and to complete the polygon you either have to click at the first edge (if you hover the mouse, a circle appears in that position), or you can auto-fill by double clicking.
This commit is contained in:
Manolis Papadeas 2021-05-18 01:56:55 +03:00
parent de649c2057
commit 5b6f4ebe3e
16 changed files with 418 additions and 30 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/polygonselect.png-448fe1e0b611f853c789ce4def03e3d8.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/blue_themes/tools/polygonselect.png"
dest_files=[ "res://.import/polygonselect.png-448fe1e0b611f853c789ce4def03e3d8.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

View file

@ -0,0 +1,13 @@
[remap]
importer="image"
type="Image"
path="res://.import/polygonselect_cursor.png-75834308db46d270a4fc7c38d5d978ac.image"
[deps]
source_file="res://assets/graphics/cursor_icons/polygonselect_cursor.png"
dest_files=[ "res://.import/polygonselect_cursor.png-75834308db46d270a4fc7c38d5d978ac.image" ]
[params]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/polygonselect.png-0c3075019f6acbc7e1ab495dbfbd82b3.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/dark_themes/tools/polygonselect.png"
dest_files=[ "res://.import/polygonselect.png-0c3075019f6acbc7e1ab495dbfbd82b3.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/polygonselect.png-53b426a7a45b4c3dd874336a05d66eda.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/light_themes/tools/polygonselect.png"
dest_files=[ "res://.import/polygonselect.png-53b426a7a45b4c3dd874336a05d66eda.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View file

@ -586,6 +586,16 @@ right_lasso_tool={
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":70,"unicode":0,"echo":false,"script":null)
]
}
left_polygon_select_tool={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":75,"unicode":0,"echo":false,"script":null)
]
}
right_polygon_select_tool={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":75,"unicode":0,"echo":false,"script":null)
]
}
[locale]

View file

@ -484,6 +484,13 @@ func update_hint_tooltips() -> void:
%s for right mouse button""") % [InputMap.get_action_list("left_ellipse_select_tool")[0].as_text(), InputMap.get_action_list("right_ellipse_select_tool")[0].as_text()]
var polygon_select : BaseButton = tool_buttons.find_node("PolygonSelect")
polygon_select.hint_tooltip = tr("""Polygonal Selection
%s for left mouse button
%s for right mouse button""") % [InputMap.get_action_list("left_polygon_select_tool")[0].as_text(), InputMap.get_action_list("right_polygon_select_tool")[0].as_text()]
var color_select : BaseButton = tool_buttons.find_node("ColorSelect")
color_select.hint_tooltip = tr("""Select By Color

View file

@ -41,6 +41,7 @@ signal color_changed(color, button)
var _tools = {
"RectSelect" : "res://src/Tools/SelectionTools/RectSelect.tscn",
"EllipseSelect" : "res://src/Tools/SelectionTools/EllipseSelect.tscn",
"PolygonSelect" : "res://src/Tools/SelectionTools/PolygonSelect.tscn",
"ColorSelect" : "res://src/Tools/SelectionTools/ColorSelect.tscn",
"MagicWand" : "res://src/Tools/SelectionTools/MagicWand.tscn",
"Lasso" : "res://src/Tools/SelectionTools/Lasso.tscn",

View file

@ -0,0 +1,214 @@
extends SelectionTool
var _last_position := Vector2.INF
var _draw_points := []
var ready_to_apply := false
func _input(event : InputEvent) -> void:
._input(event)
if _move:
return
if event is InputEventMouseMotion:
_last_position = Global.canvas.current_pixel.floor()
elif event is InputEventMouseButton:
if event.doubleclick and event.button_index == tool_slot.button and _draw_points:
$DoubleClickTimer.start()
append_gap(_draw_points[-1], _draw_points[0], _draw_points)
ready_to_apply = true
apply_selection(Vector2.ZERO) # Argument doesn't matter
func draw_start(position : Vector2) -> void:
if !$DoubleClickTimer.is_stopped():
return
.draw_start(position)
if !_move and !_draw_points:
_ongoing_selection = true
_draw_points.append(position)
_last_position = position
func draw_move(position : Vector2) -> void:
if selection_node.arrow_key_move:
return
.draw_move(position)
func draw_end(position : Vector2) -> void:
if selection_node.arrow_key_move:
return
if !_move and _draw_points:
append_gap(_draw_points[-1], position, _draw_points)
if position == _draw_points[0] and _draw_points.size() > 1:
ready_to_apply = true
.draw_end(position)
func draw_preview() -> void:
if _ongoing_selection and !_move:
var canvas : Node2D = Global.canvas.previews
var _position := canvas.position
var _scale := canvas.scale
if Global.mirror_view:
_position.x = _position.x + Global.current_project.size.x
_scale.x = -1
var preview_draw_points := _draw_points.duplicate()
append_gap(_draw_points[-1], _last_position, preview_draw_points)
canvas.draw_set_transform(_position, canvas.rotation, _scale)
var indicator := _fill_bitmap_with_points(preview_draw_points, Global.current_project.size)
for line in _create_polylines(indicator):
canvas.draw_polyline(PoolVector2Array(line), Color.black)
var circle_radius := Global.camera.zoom * 10
circle_radius.x = clamp(circle_radius.x, 2, circle_radius.x)
circle_radius.y = clamp(circle_radius.y, 2, circle_radius.y)
if _last_position == _draw_points[0] and _draw_points.size() > 1:
draw_empty_circle(canvas, _draw_points[0] + Vector2.ONE * 0.5, circle_radius, Color.black)
# Handle mirroring
if tool_slot.horizontal_mirror:
for line in _create_polylines(_fill_bitmap_with_points(mirror_array(preview_draw_points, true, false), Global.current_project.size)):
canvas.draw_polyline(PoolVector2Array(line), Color.black)
if tool_slot.vertical_mirror:
for line in _create_polylines(_fill_bitmap_with_points(mirror_array(preview_draw_points, true, true), Global.current_project.size)):
canvas.draw_polyline(PoolVector2Array(line), Color.black)
if tool_slot.vertical_mirror:
for line in _create_polylines(_fill_bitmap_with_points(mirror_array(preview_draw_points, false, true), Global.current_project.size)):
canvas.draw_polyline(PoolVector2Array(line), Color.black)
canvas.draw_set_transform(canvas.position, canvas.rotation, canvas.scale)
func apply_selection(_position) -> void:
if !ready_to_apply:
return
var project : Project = Global.current_project
var cleared := false
if !_add and !_subtract and !_intersect:
cleared = true
Global.canvas.selection.clear_selection()
if _draw_points.size() > 3:
var selection_bitmap_copy : BitMap = project.selection_bitmap.duplicate()
var bitmap_size : Vector2 = selection_bitmap_copy.get_size()
if _intersect:
selection_bitmap_copy.set_bit_rect(Rect2(Vector2.ZERO, bitmap_size), false)
lasso_selection(selection_bitmap_copy, _draw_points)
# Handle mirroring
if tool_slot.horizontal_mirror:
lasso_selection(selection_bitmap_copy, mirror_array(_draw_points, true, false))
if tool_slot.vertical_mirror:
lasso_selection(selection_bitmap_copy, mirror_array(_draw_points, true, true))
if tool_slot.vertical_mirror:
lasso_selection(selection_bitmap_copy, mirror_array(_draw_points, false, true))
project.selection_bitmap = selection_bitmap_copy
Global.canvas.selection.big_bounding_rectangle = project.get_selection_rectangle(project.selection_bitmap)
else:
if !cleared:
Global.canvas.selection.clear_selection()
Global.canvas.selection.commit_undo("Rectangle Select", undo_data)
_ongoing_selection = false
_draw_points.clear()
ready_to_apply = false
Global.canvas.previews.update()
func lasso_selection(bitmap : BitMap, points : PoolVector2Array) -> void:
var project : Project = Global.current_project
var size := bitmap.get_size()
for point in points:
if point.x < 0 or point.y < 0 or point.x >= size.x or point.y >= size.y:
continue
if _intersect:
if project.selection_bitmap.get_bit(point):
bitmap.set_bit(point, true)
else:
bitmap.set_bit(point, !_subtract)
var image = _get_draw_image()
var v := Vector2()
var image_size = image.get_size()
for x in image_size.x:
v.x = x
for y in image_size.y:
v.y = y
if Geometry.is_point_in_polygon(v, points):
if _intersect:
if project.selection_bitmap.get_bit(v):
bitmap.set_bit(v, true)
else:
bitmap.set_bit(v, !_subtract)
# Bresenham's Algorithm
# Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency
func append_gap(start : Vector2, end : Vector2, array : Array) -> void:
var dx := int(abs(end.x - start.x))
var dy := int(-abs(end.y - start.y))
var err := dx + dy
var e2 := err << 1
var sx = 1 if start.x < end.x else -1
var sy = 1 if start.y < end.y else -1
var x = start.x
var y = start.y
while !(x == end.x && y == end.y):
e2 = err << 1
if e2 >= dy:
err += dy
x += sx
if e2 <= dx:
err += dx
y += sy
array.append(Vector2(x, y))
func _fill_bitmap_with_points(points: Array, size: Vector2) -> BitMap:
var bitmap := BitMap.new()
bitmap.create(size)
for point in points:
if point.x < 0 or point.y < 0 or point.x >= size.x or point.y >= size.y:
continue
bitmap.set_bit(point, 1)
return bitmap
func mirror_array(array : Array, h : bool, v : bool) -> Array:
var new_array := []
var project := Global.current_project
for point in array:
if h and v:
new_array.append(Vector2(project.x_symmetry_point - point.x, project.y_symmetry_point - point.y))
elif h:
new_array.append(Vector2(project.x_symmetry_point - point.x, point.y))
elif v:
new_array.append(Vector2(point.x, project.y_symmetry_point - point.y))
return new_array
# Thanks to https://www.reddit.com/r/godot/comments/3ktq39/drawing_empty_circles_and_curves/cv0f4eo/?utm_source=reddit&utm_medium=web2x&context=3
func draw_empty_circle(canvas : CanvasItem, circle_center : Vector2, circle_radius : Vector2, color : Color) -> void:
var draw_counter := 1
var line_origin := Vector2()
var line_end := Vector2()
line_origin = circle_radius + circle_center
while draw_counter <= 360:
line_end = circle_radius.rotated(deg2rad(draw_counter)) + circle_center
canvas.draw_line(line_origin, line_end, color)
draw_counter += 1
line_origin = line_end
line_end = circle_radius.rotated(deg2rad(360)) + circle_center
canvas.draw_line(line_origin, line_end, color)

View file

@ -0,0 +1,11 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/Tools/SelectionTools/SelectionTool.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/Tools/SelectionTools/PolygonSelect.gd" type="Script" id=2]
[node name="ToolOptions" instance=ExtResource( 1 )]
script = ExtResource( 2 )
[node name="DoubleClickTimer" type="Timer" parent="." index="11"]
wait_time = 0.1
one_shot = true

View file

@ -5,6 +5,9 @@ var _move := false
var _move_content := true
var _start_pos := Vector2.ZERO
var _offset := Vector2.ZERO
# For tools such as the Polygon selection tool where you have to
# click multiple times to create a selection
var _ongoing_selection := false
var _add := false # Shift + Mouse Click
var _subtract := false # Ctrl + Mouse Click
@ -71,7 +74,7 @@ func draw_start(position : Vector2) -> void:
offsetted_pos.x -= selection_position.x
if selection_position.y < 0:
offsetted_pos.y -= selection_position.y
if offsetted_pos.x >= 0 and offsetted_pos.y >= 0 and project.selection_bitmap.get_bit(offsetted_pos) and (!Tools.control or Tools.alt) and !Tools.shift:
if offsetted_pos.x >= 0 and offsetted_pos.y >= 0 and project.selection_bitmap.get_bit(offsetted_pos) and (!Tools.control or Tools.alt) and !Tools.shift and !_ongoing_selection:
# Move current selection
_move = true
if Tools.control and Tools.alt: # Move selection without content

View file

@ -5,6 +5,7 @@ extends GridContainer
onready var tools := [
[$RectSelect, "rectangle_select"],
[$EllipseSelect, "ellipse_select"],
[$PolygonSelect, "polygon_select"],
[$ColorSelect, "color_select"],
[$MagicWand, "magic_wand"],
[$Lasso, "lasso"],

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=36 format=2]
[gd_scene load_steps=37 format=2]
[ext_resource path="res://src/UI/ToolButtons.gd" type="Script" id=1]
[ext_resource path="res://src/UI/Canvas/CanvasPreview.tscn" type="PackedScene" id=2]
@ -32,6 +32,7 @@
[ext_resource path="res://assets/graphics/dark_themes/tools/linetool.png" type="Texture" id=30]
[ext_resource path="res://assets/graphics/dark_themes/tools/ellipseselect.png" type="Texture" id=31]
[ext_resource path="res://assets/graphics/dark_themes/tools/lasso.png" type="Texture" id=32]
[ext_resource path="res://assets/graphics/dark_themes/tools/polygonselect.png" type="Texture" id=33]
[sub_resource type="ShaderMaterial" id=1]
shader = ExtResource( 9 )
@ -96,7 +97,7 @@ __meta__ = {
margin_left = 7.0
margin_top = 7.0
margin_right = 39.0
margin_bottom = 579.0
margin_bottom = 615.0
size_flags_horizontal = 4
size_flags_vertical = 0
script = ExtResource( 1 )
@ -150,7 +151,7 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="ColorSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
[node name="PolygonSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 72.0
@ -160,6 +161,31 @@ rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
[node name="Background" type="TextureRect" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons/PolygonSelect"]
margin_right = 32.0
margin_bottom = 32.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ToolIcon" type="TextureRect" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons/PolygonSelect"]
margin_right = 32.0
margin_bottom = 32.0
texture = ExtResource( 33 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ColorSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 108.0
margin_right = 32.0
margin_bottom = 140.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
[node name="Background" type="TextureRect" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons/ColorSelect"]
margin_right = 32.0
margin_bottom = 32.0
@ -178,9 +204,9 @@ __meta__ = {
[node name="MagicWand" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 108.0
margin_top = 144.0
margin_right = 32.0
margin_bottom = 140.0
margin_bottom = 176.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -203,9 +229,9 @@ __meta__ = {
[node name="Lasso" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 144.0
margin_top = 180.0
margin_right = 32.0
margin_bottom = 176.0
margin_bottom = 212.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -228,9 +254,9 @@ __meta__ = {
[node name="Move" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 180.0
margin_top = 216.0
margin_right = 32.0
margin_bottom = 212.0
margin_bottom = 248.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -253,9 +279,9 @@ __meta__ = {
[node name="Zoom" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 216.0
margin_top = 252.0
margin_right = 32.0
margin_bottom = 248.0
margin_bottom = 284.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -278,9 +304,9 @@ __meta__ = {
[node name="Pan" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 252.0
margin_top = 288.0
margin_right = 32.0
margin_bottom = 284.0
margin_bottom = 320.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -303,9 +329,9 @@ __meta__ = {
[node name="ColorPicker" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 288.0
margin_top = 324.0
margin_right = 32.0
margin_bottom = 320.0
margin_bottom = 356.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -328,9 +354,9 @@ __meta__ = {
[node name="Pencil" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 324.0
margin_top = 360.0
margin_right = 32.0
margin_bottom = 356.0
margin_bottom = 392.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -354,9 +380,9 @@ __meta__ = {
[node name="Eraser" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 360.0
margin_top = 396.0
margin_right = 32.0
margin_bottom = 392.0
margin_bottom = 428.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -380,9 +406,9 @@ __meta__ = {
[node name="Bucket" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 396.0
margin_top = 432.0
margin_right = 32.0
margin_bottom = 428.0
margin_bottom = 464.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -405,9 +431,9 @@ __meta__ = {
[node name="LightenDarken" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 432.0
margin_top = 468.0
margin_right = 32.0
margin_bottom = 464.0
margin_bottom = 500.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -430,9 +456,9 @@ __meta__ = {
[node name="LineTool" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 468.0
margin_top = 504.0
margin_right = 32.0
margin_bottom = 500.0
margin_bottom = 536.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -455,9 +481,9 @@ __meta__ = {
[node name="RectangleTool" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 504.0
margin_top = 540.0
margin_right = 32.0
margin_bottom = 536.0
margin_bottom = 572.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -480,9 +506,9 @@ __meta__ = {
[node name="EllipseTool" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 540.0
margin_top = 576.0
margin_right = 32.0
margin_bottom = 572.0
margin_bottom = 608.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3