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

Added ellipse selection tool

Icons most likely temporary. A bit problematic when the selection is in negative coords and the preview does not work well with mirroring and mirror view. Same is true for the shape tools.
This commit is contained in:
Manolis Papadeas 2021-05-14 03:48:33 +03:00
parent 6b9f860618
commit d13c5c1063
15 changed files with 448 additions and 29 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/ellipseselect.png-3aa794487637f452345ceefe7b89a706.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/blue_themes/tools/ellipseselect.png"
dest_files=[ "res://.import/ellipseselect.png-3aa794487637f452345ceefe7b89a706.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: 157 B

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/ellipseselect.png-f9ec2fe700b321112a77dbc69f7dd507.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/dark_themes/tools/ellipseselect.png"
dest_files=[ "res://.import/ellipseselect.png-f9ec2fe700b321112a77dbc69f7dd507.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: 157 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/ellipseselect.png-a30bc8f57f2d1e9fb209dd271ccfcf01.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/light_themes/tools/ellipseselect.png"
dest_files=[ "res://.import/ellipseselect.png-a30bc8f57f2d1e9fb209dd271ccfcf01.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

@ -566,6 +566,16 @@ right_linetool_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":76,"unicode":0,"echo":false,"script":null)
]
}
left_ellipse_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":89,"unicode":0,"echo":false,"script":null)
]
}
right_ellipse_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":89,"unicode":0,"echo":false,"script":null)
]
}
[locale]

View file

@ -475,9 +475,14 @@ func update_hint_tooltips() -> void:
rect_select.hint_tooltip = tr("""Rectangular Selection
%s for left mouse button
%s for right mouse button
%s for right mouse button""") % [InputMap.get_action_list("left_rectangle_select_tool")[0].as_text(), InputMap.get_action_list("right_rectangle_select_tool")[0].as_text()]
var ellipse_select : BaseButton = tool_buttons.find_node("EllipseSelect")
ellipse_select.hint_tooltip = tr("""Elliptical Selection
%s for left mouse button
%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()]
Press %s to move the content""") % [InputMap.get_action_list("left_rectangle_select_tool")[0].as_text(), InputMap.get_action_list("right_rectangle_select_tool")[0].as_text(), "Shift"]
var color_select : BaseButton = tool_buttons.find_node("ColorSelect")
color_select.hint_tooltip = tr("""Select By Color

View file

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

View file

@ -0,0 +1,254 @@
extends SelectionTool
var _rect := Rect2(0, 0, 0, 0)
var _square := false # Mouse Click + Shift
var _expand_from_center := false # Mouse Click + Ctrl
var _displace_origin = false # Mouse Click + Alt
func _input(event : InputEvent) -> void:
._input(event)
if !_move and !_rect.has_no_area():
if event.is_action_pressed("shift"):
_square = true
elif event.is_action_released("shift"):
_square = false
if event.is_action_pressed("ctrl"):
_expand_from_center = true
elif event.is_action_released("ctrl"):
_expand_from_center = false
if event.is_action_pressed("alt"):
_displace_origin = true
elif event.is_action_released("alt"):
_displace_origin = false
func draw_move(position : Vector2) -> void:
if selection_node.arrow_key_move:
return
.draw_move(position)
if !_move:
if _displace_origin:
_start_pos += position - _offset
_rect = _get_result_rect(_start_pos, position)
_set_cursor_text(_rect)
_offset = position
func draw_end(position : Vector2) -> void:
if selection_node.arrow_key_move:
return
.draw_end(position)
_rect = Rect2(0, 0, 0, 0)
_square = false
_expand_from_center = false
_displace_origin = false
func draw_preview() -> void:
if !_move && !_rect.has_no_area():
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 border := _get_shape_points_filled(_rect.size)
var indicator := _fill_bitmap_with_points(border, _rect.size)
canvas.draw_set_transform(_rect.position, canvas.rotation, _scale)
for line in _create_polylines(indicator):
canvas.draw_polyline(PoolVector2Array(line), Color.black)
canvas.draw_set_transform(canvas.position, canvas.rotation, canvas.scale)
func apply_selection(_position : Vector2) -> void:
var project : Project = Global.current_project
if !_add and !_subtract and !_intersect:
Global.canvas.selection.clear_selection()
if _rect.size == Vector2.ZERO and Global.current_project.has_selection:
Global.canvas.selection.commit_undo("Rectangle Select", undo_data)
if _rect.size != Vector2.ZERO:
var selection_bitmap_copy : BitMap = project.selection_bitmap.duplicate()
set_ellipse(selection_bitmap_copy, _rect.position)
# Handle mirroring
if tool_slot.horizontal_mirror:
var mirror_x_rect := _rect
mirror_x_rect.position.x = Global.current_project.x_symmetry_point - _rect.position.x
mirror_x_rect.end.x = Global.current_project.x_symmetry_point - _rect.end.x
set_ellipse(selection_bitmap_copy, mirror_x_rect.abs().position)
if tool_slot.vertical_mirror:
var mirror_xy_rect := mirror_x_rect
mirror_xy_rect.position.y = Global.current_project.y_symmetry_point - _rect.position.y
mirror_xy_rect.end.y = Global.current_project.y_symmetry_point - _rect.end.y
set_ellipse(selection_bitmap_copy, mirror_xy_rect.abs().position)
if tool_slot.vertical_mirror:
var mirror_y_rect := _rect
mirror_y_rect.position.y = Global.current_project.y_symmetry_point - _rect.position.y
mirror_y_rect.end.y = Global.current_project.y_symmetry_point - _rect.end.y
set_ellipse(selection_bitmap_copy, mirror_y_rect.abs().position)
project.selection_bitmap = selection_bitmap_copy
Global.canvas.selection.big_bounding_rectangle = project.get_selection_rectangle(project.selection_bitmap)
Global.canvas.selection.commit_undo("Rectangle Select", undo_data)
func set_ellipse(bitmap : BitMap, position : Vector2) -> void:
var project : Project = Global.current_project
var bitmap_size : Vector2 = bitmap.get_size()
if _intersect:
bitmap.set_bit_rect(Rect2(Vector2.ZERO, bitmap_size), false)
var points := _get_shape_points_filled(_rect.size)
for p in points:
var pos : Vector2 = position + p
if pos.x < 0 or pos.y < 0 or pos.x >= bitmap_size.x or pos.y >= bitmap_size.y:
continue
if _intersect:
if project.selection_bitmap.get_bit(pos):
bitmap.set_bit(pos, true)
else:
bitmap.set_bit(pos, !_subtract)
# Given an origin point and destination point, returns a rect representing where the shape will be drawn and what it's size
func _get_result_rect(origin: Vector2, dest: Vector2) -> Rect2:
var rect := Rect2(Vector2.ZERO, Vector2.ZERO)
# Center the rect on the mouse
if _expand_from_center:
var new_size := (dest - origin).floor()
# Make rect 1:1 while centering it on the mouse
if _square:
var _square_size := max(abs(new_size.x), abs(new_size.y))
new_size = Vector2(_square_size, _square_size)
origin -= new_size
dest = origin + 2 * new_size
# Make rect 1:1 while not trying to center it
if _square:
var square_size := min(abs(origin.x - dest.x), abs(origin.y - dest.y))
rect.position.x = origin.x if origin.x < dest.x else origin.x - square_size
rect.position.y = origin.y if origin.y < dest.y else origin.y - square_size
rect.size = Vector2(square_size, square_size)
# Get the rect without any modifications
else:
rect.position = Vector2(min(origin.x, dest.x), min(origin.y, dest.y))
rect.size = (origin - dest).abs()
rect.size += Vector2.ONE
return rect
func _get_shape_points_filled(size: Vector2) -> PoolVector2Array:
var border := _get_ellipse_points(Vector2.ZERO, size)
var filling := []
var bitmap := _fill_bitmap_with_points(border, size)
for x in range(1, ceil(size.x / 2)):
var fill := false
var prev_is_true := false
for y in range(0, ceil(size.y / 2)):
var top_l_p := Vector2(x, y)
var bit := bitmap.get_bit(top_l_p)
if bit and not fill:
prev_is_true = true
continue
if not bit and (fill or prev_is_true):
filling.append(top_l_p)
filling.append(Vector2(x, size.y - y - 1))
filling.append(Vector2(size.x - x - 1, y))
filling.append(Vector2(size.x - x - 1, size.y - y - 1))
if prev_is_true:
fill = true
prev_is_true = false
elif bit and fill:
break
return PoolVector2Array(border + filling)
# Algorithm based on http://members.chello.at/easyfilter/bresenham.html
func _get_ellipse_points (pos: Vector2, size: Vector2) -> Array:
var array := []
var x0 := int(pos.x)
var x1 := pos.x + int(size.x - 1)
var y0 := int(pos.y)
var y1 := int(pos.y) + int(size.y - 1)
var a := int(abs(x1 - x0))
var b := int(abs(y1 - x0))
var b1 := b & 1
var dx := 4*(1-a)*b*b
var dy := 4*(b1+1)*a*a
var err := dx+dy+b1*a*a
var e2 := 0
if x0 > x1:
x0 = x1
x1 += a
if y0 > y1:
y0 = y1
# warning-ignore:integer_division
y0 += (b+1) / 2
y1 = y0-b1
a *= 8*a
b1 = 8*b*b
while x0 <= x1:
var v1 := Vector2(x1, y0)
var v2 := Vector2(x0, y0)
var v3 := Vector2(x0, y1)
var v4 := Vector2(x1, y1)
array.append(v1)
array.append(v2)
array.append(v3)
array.append(v4)
e2 = 2*err;
if e2 <= dy:
y0 += 1
y1 -= 1
dy += a
err += dy
if e2 >= dx || 2*err > dy:
x0+=1
x1-=1
dx += b1
err += dx
while y0-y1 < b:
var v1 := Vector2(x0-1, y0)
var v2 := Vector2(x1+1, y0)
var v3 := Vector2(x0-1, y1)
var v4 := Vector2(x1+1, y1)
array.append(v1)
array.append(v2)
array.append(v3)
array.append(v4)
y0+=1
y1-=1
return array
func _fill_bitmap_with_points(points: Array, size: Vector2) -> BitMap:
var bitmap := BitMap.new()
bitmap.create(size)
for point in points:
bitmap.set_bit(point, 1)
return bitmap

View file

@ -0,0 +1,7 @@
[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/EllipseSelect.gd" type="Script" id=2]
[node name="ToolOptions" instance=ExtResource( 1 )]
script = ExtResource( 2 )

View file

@ -4,6 +4,7 @@ extends GridContainer
# Node, shortcut
onready var tools := [
[$RectSelect, "rectangle_select"],
[$EllipseSelect, "ellipse_select"],
[$ColorSelect, "color_select"],
[$MagicWand, "magic_wand"],
[$Move, "move"],

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=34 format=2]
[gd_scene load_steps=35 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]
@ -30,6 +30,7 @@
[ext_resource path="res://assets/graphics/dark_themes/tools/colorselect.png" type="Texture" id=28]
[ext_resource path="res://assets/graphics/dark_themes/tools/magicwand.png" type="Texture" id=29]
[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]
[sub_resource type="ShaderMaterial" id=1]
shader = ExtResource( 9 )
@ -94,7 +95,7 @@ __meta__ = {
margin_left = 7.0
margin_top = 7.0
margin_right = 39.0
margin_bottom = 507.0
margin_bottom = 543.0
size_flags_horizontal = 4
size_flags_vertical = 0
script = ExtResource( 1 )
@ -123,7 +124,7 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="ColorSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
[node name="EllipseSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 36.0
@ -133,6 +134,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/EllipseSelect"]
margin_right = 32.0
margin_bottom = 32.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ToolIcon" type="TextureRect" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons/EllipseSelect"]
margin_right = 32.0
margin_bottom = 32.0
texture = ExtResource( 31 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ColorSelect" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 72.0
margin_right = 32.0
margin_bottom = 104.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
@ -151,9 +177,9 @@ __meta__ = {
[node name="MagicWand" type="Button" parent="ToolsAndCanvas/ToolPanel/PanelContainer/ToolButtons" groups=[
"UIButtons",
]]
margin_top = 72.0
margin_top = 108.0
margin_right = 32.0
margin_bottom = 104.0
margin_bottom = 140.0
rect_min_size = Vector2( 32, 32 )
mouse_default_cursor_shape = 2
button_mask = 3
@ -176,9 +202,9 @@ __meta__ = {
[node name="Move" 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
@ -201,9 +227,9 @@ __meta__ = {
[node name="Zoom" 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
@ -226,9 +252,9 @@ __meta__ = {
[node name="Pan" 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
@ -251,9 +277,9 @@ __meta__ = {
[node name="ColorPicker" 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
@ -276,9 +302,9 @@ __meta__ = {
[node name="Pencil" 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
@ -302,9 +328,9 @@ __meta__ = {
[node name="Eraser" 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="Bucket" 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
@ -353,9 +379,9 @@ __meta__ = {
[node name="LightenDarken" 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
@ -378,9 +404,9 @@ __meta__ = {
[node name="LineTool" 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
@ -403,9 +429,9 @@ __meta__ = {
[node name="RectangleTool" 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
@ -428,9 +454,9 @@ __meta__ = {
[node name="EllipseTool" 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