1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-30 23:19:49 +00:00

Added the Perspective editor (#806)

* Added Perspective Editor

* removed unintended changes

* Removed unintended changes

* removed un-intended changes

* Delete Axes.tscn

* Delete Axis.gd

* Removed MouseGuides from editor

I will add them separately

* Added MouseGuides

* Some changes

* Fixed some things

* added mouse guide

* make MouseGuides remember last setting

* don't move if dialog is open

* Dont move tracker guides if dialog is open

* UI improvement to editor

* Update VanishingPoint.gd

* UI Improvement

* Minor Color Improvements

* fixed a bug

This was causing a crash

* Some UI Changes

* Improve UI some more

* fix typo

* Added Undo/Redo and improved UI

* Formatting

* formatting

* formatting

* formatting

* Fix Definition out of order

* Fix Definition out of order

* formatting

* formatting

* fix Duplicate error

* Fix some things

* Some UI Changes

* Some code refinement

* Removed un-needed lines

* Some code refinement

* Some more UI Changes

* Changes, Changes and Changes

* Delete LineButton.gd

* Delete LineButton.tscn

* Delete PerspectiveEditor.gd

* Delete PerspectiveEditor.tscn

* Delete PointCollapseContainer.gd

* Delete PerspectiveLine.tscn

* Delete PerspectiveLine.gd

* Changed boundary separators to ColorRects

* make the guide update more frequently

* make default color have full alpha

* Dim the boundaries based on luminance

* typo

* Formatting

* Formatting

* formatting i forgot to do

* Delete VanishingPoint.gd

* Delete VanishingPoint.tscn

* Fixed rouge collapsible container

---------

Co-authored-by: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com>
This commit is contained in:
Variable 2023-02-12 18:20:53 +05:00 committed by GitHub
parent c6d6c6184c
commit 7307743f83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 917 additions and 5 deletions

View file

@ -159,6 +159,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/UI/Buttons/PatternsPopup.gd"
}, {
"base": "Line2D",
"class": "PerspectiveLine",
"language": "GDScript",
"path": "res://src/UI/PerspectiveEditor/PerspectiveLine.gd"
}, {
"base": "BaseCel",
"class": "PixelCel",
"language": "GDScript",
@ -250,6 +255,7 @@ _global_script_class_icons={
"PalettePanel": "",
"PaletteSwatch": "",
"Patterns": "",
"PerspectiveLine": "",
"PixelCel": "",
"PixelLayer": "",
"Project": "",

View file

@ -20,6 +20,7 @@ enum ViewMenu {
SHOW_PIXEL_GRID,
SHOW_RULERS,
SHOW_GUIDES,
SHOW_MOUSE_GUIDES,
SNAP_TO,
}
enum WindowMenu { WINDOW_OPACITY, PANELS, LAYOUTS, MOVABLE_PANELS, ZEN_MODE, FULLSCREEN_MODE }
@ -138,6 +139,7 @@ var draw_grid := false
var draw_pixel_grid := false
var show_rulers := true
var show_guides := true
var show_mouse_guides := false
var snapping_distance := 10.0
var snap_to_rectangular_grid := false
var snap_to_guides := false
@ -181,6 +183,7 @@ onready var patterns_popup: Popup = control.find_node("PatternsPopup")
onready var palette_panel: PalettePanel = control.find_node("Palettes")
onready var references_panel: ReferencesPanel = control.find_node("Reference Images")
onready var perspective_editor := control.find_node("Perspective Editor")
onready var top_menu_container: Panel = control.find_node("TopMenuContainer")
onready var rotation_level_button: Button = control.find_node("RotationLevel")

View file

@ -24,6 +24,7 @@ var animation_tags := [] setget _animation_tags_changed # Array of AnimationTag
var guides := [] # Array of Guides
var brushes := [] # Array of Images
var reference_images := [] # Array of ReferenceImages
var vanishing_points := [] # Array of Vanishing Points
var fps := 6.0
var x_symmetry_point
@ -91,6 +92,12 @@ func remove() -> void:
undo_redo.free()
for ri in reference_images:
ri.queue_free()
if self == Global.current_project:
# If the project is not current_project then the points need not be removed
for point_idx in vanishing_points.size():
var editor = Global.perspective_editor
for c in editor.vanishing_point_container.get_children():
c.queue_free()
for guide in guides:
guide.queue_free()
# Prevents memory leak (due to the layers' project reference stopping ref counting from freeing)
@ -186,6 +193,7 @@ func change_project() -> void:
Global.horizontal_ruler.update()
Global.vertical_ruler.update()
Global.references_panel.project_changed()
Global.perspective_editor.update()
Global.cursor_position_label.text = "[%s×%s]" % [size.x, size.y]
Global.window_title = "%s - Pixelorama %s" % [name, Global.current_version]
@ -325,6 +333,7 @@ func serialize() -> Dictionary:
"frames": frame_data,
"brushes": brush_data,
"reference_images": reference_image_data,
"vanishing_points": vanishing_points,
"export_directory_path": directory_path,
"export_file_name": file_name,
"export_file_format": file_format,
@ -412,6 +421,9 @@ func deserialize(dict: Dictionary) -> void:
ri.project = self
ri.deserialize(g)
Global.canvas.add_child(ri)
if dict.has("vanishing_points"):
vanishing_points = dict.vanishing_points
Global.perspective_editor.update()
if dict.has("symmetry_points"):
x_symmetry_point = dict.symmetry_points[0]
y_symmetry_point = dict.symmetry_points[1]

View file

@ -416,6 +416,7 @@ func _exit_tree() -> void:
Global.config_cache.set_value("view_menu", "draw_pixel_grid", Global.draw_pixel_grid)
Global.config_cache.set_value("view_menu", "show_rulers", Global.show_rulers)
Global.config_cache.set_value("view_menu", "show_guides", Global.show_guides)
Global.config_cache.set_value("view_menu", "show_mouse_guides", Global.show_mouse_guides)
Global.config_cache.save("user://cache.ini")
var i := 0

View file

@ -16,6 +16,7 @@ onready var grid = $Grid
onready var selection = $Selection
onready var indicators = $Indicators
onready var previews = $Previews
onready var mouse_guide_container = $MouseGuideContainer
func _ready() -> void:

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=15 format=2]
[gd_scene load_steps=16 format=2]
[ext_resource path="res://src/UI/Canvas/Canvas.gd" type="Script" id=1]
[ext_resource path="res://src/UI/Canvas/Grid.gd" type="Script" id=2]
@ -10,6 +10,7 @@
[ext_resource path="res://src/UI/Canvas/Selection.gd" type="Script" id=8]
[ext_resource path="res://src/Shaders/MarchingAntsOutline.shader" type="Shader" id=9]
[ext_resource path="res://src/Shaders/AutoInvertColors.shader" type="Shader" id=10]
[ext_resource path="res://src/UI/Canvas/MouseGuideContainer.tscn" type="PackedScene" id=11]
[ext_resource path="res://src/UI/Canvas/OnionSkinning.gd" type="Script" id=12]
[sub_resource type="CanvasItemMaterial" id=1]
@ -72,3 +73,5 @@ script = ExtResource( 12 )
[node name="OnionFuture" type="Node2D" parent="."]
script = ExtResource( 12 )
[node name="MouseGuideContainer" parent="." instance=ExtResource( 11 )]

View file

@ -0,0 +1,77 @@
extends Line2D
enum Types { VERTICAL, HORIZONTAL }
const INPUT_WIDTH := 4
export var type := 0
var track_mouse := true
func _ready() -> void:
# Add a subtle difference to the normal guide color by mixing in some green
default_color = Global.guide_color.linear_interpolate(Color(0.2, 0.92, 0.2), .6)
width = Global.camera.zoom.x * 2
draw_guide_line()
func draw_guide_line():
if type == Types.HORIZONTAL:
points[0] = Vector2(-19999, 0)
points[1] = Vector2(19999, 0)
else:
points[0] = Vector2(0, 19999)
points[1] = Vector2(0, -19999)
func _input(event: InputEvent) -> void:
if !Global.show_mouse_guides or !Global.can_draw or !Global.has_focus:
visible = false
return
visible = true
if event is InputEventMouseMotion:
var tmp_transform = get_canvas_transform().affine_inverse()
var tmp_position = Global.main_viewport.get_local_mouse_position()
var mouse_point = (tmp_transform.basis_xform(tmp_position) + tmp_transform.origin).snapped(
Vector2(0.5, 0.5)
)
var project_size = Global.current_project.size
if Rect2(Vector2.ZERO, project_size).has_point(mouse_point):
visible = true
else:
visible = false
return
if type == Types.HORIZONTAL:
points[0].y = mouse_point.y
points[1].y = mouse_point.y
else:
points[0].x = mouse_point.x
points[1].x = mouse_point.x
update()
func _draw() -> void:
width = Global.camera.zoom.x * 2
var viewport_size: Vector2 = Global.main_viewport.rect_size
var zoom: Vector2 = Global.camera.zoom
# viewport_poly is an array of the points that make up the corners of the viewport
var viewport_poly := [
Vector2.ZERO, Vector2(viewport_size.x, 0), viewport_size, Vector2(0, viewport_size.y)
]
# Adjusting viewport_poly to take into account the camera offset, zoom, and rotation
for p in range(viewport_poly.size()):
viewport_poly[p] = (
viewport_poly[p].rotated(Global.camera.rotation) * zoom
+ Vector2(
(
Global.camera.offset.x
- (viewport_size.rotated(Global.camera.rotation).x / 2) * zoom.x
),
(
Global.camera.offset.y
- (viewport_size.rotated(Global.camera.rotation).y / 2) * zoom.y
)
)
)
draw_set_transform(viewport_poly[0], Global.camera.rotation, zoom * 2)

View file

@ -0,0 +1,31 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://assets/graphics/dotted_line.png" type="Texture" id=1]
[ext_resource path="res://src/UI/Canvas/MouseGuide.gd" type="Script" id=2]
[sub_resource type="AtlasTexture" id=1]
flags = 2
atlas = ExtResource( 1 )
region = Rect2( 0, 0, 8, 1 )
[node name="MouseGuideContainer" type="Node2D"]
[node name="Vertical" type="Line2D" parent="."]
visible = false
points = PoolVector2Array( 0, 19999, 0, -19999 )
default_color = Color( 1, 1, 1, 1 )
texture = SubResource( 1 )
texture_mode = 1
script = ExtResource( 2 )
[node name="Horizontal" type="Line2D" parent="."]
visible = false
points = PoolVector2Array( -19999, 0, 19999, 0 )
default_color = Color( 1, 1, 1, 1 )
texture = SubResource( 1 )
texture_mode = 1
script = ExtResource( 2 )
__meta__ = {
"_editor_description_": ""
}
type = 1

View file

@ -0,0 +1,9 @@
extends Button
onready var length_slider = $"%LengthSlider"
func _ready():
var p_size = Global.current_project.size
var suitable_length = sqrt(pow(p_size.x, 2) + pow(p_size.y, 2))
length_slider.max_value = suitable_length

View file

@ -0,0 +1,61 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Nodes/CollapsibleContainer.gd" type="Script" id=3]
[node name="LineButton" type="VBoxContainer"]
margin_right = 159.0
margin_bottom = 20.0
theme_type_variation = "CollapsibleContainer"
script = ExtResource( 3 )
[node name="HBoxContainer" type="HBoxContainer" parent="."]
visible = false
margin_top = 24.0
margin_right = 159.0
margin_bottom = 76.0
[node name="Spacer" type="Control" parent="HBoxContainer"]
margin_right = 20.0
margin_bottom = 52.0
rect_min_size = Vector2( 20, 0 )
[node name="VSeparator" type="VSeparator" parent="HBoxContainer"]
margin_left = 24.0
margin_right = 28.0
margin_bottom = 52.0
[node name="Properties" type="VBoxContainer" parent="HBoxContainer"]
margin_left = 32.0
margin_right = 83.0
margin_bottom = 52.0
size_flags_horizontal = 3
[node name="AngleSlider" parent="HBoxContainer/Properties" instance=ExtResource( 1 )]
margin_right = 51.0
max_value = 359.999
step = 0.001
prefix = "Angle:"
suffix = "°"
[node name="LengthSlider" parent="HBoxContainer/Properties" instance=ExtResource( 1 )]
unique_name_in_owner = true
margin_top = 28.0
margin_right = 51.0
margin_bottom = 52.0
max_value = 19999.0
value = 19999.0
allow_greater = true
prefix = "Length:"
suffix = "px"
[node name="Delete" type="Button" parent="HBoxContainer"]
margin_left = 87.0
margin_right = 151.0
margin_bottom = 52.0
text = "Remove"
[node name="VSeparator2" type="VSeparator" parent="HBoxContainer"]
margin_left = 155.0
margin_right = 159.0
margin_bottom = 52.0

View file

@ -0,0 +1,67 @@
extends Control
var axes: Node2D
var do_pool = [] # A pool that stores data of points removed by undo
var delete_pool = [] # A pool that containg deleted data and their index
var vanishing_point_res := preload("res://src/UI/PerspectiveEditor/VanishingPoint.tscn")
onready var vanishing_point_container = $"%VanishingPointContainer"
func _on_AddPoint_pressed() -> void:
do_pool.clear() # Reset
var project = Global.current_project
project.undos += 1
project.undo_redo.create_action("Add Vanishing Point")
project.undo_redo.add_do_method(self, "add_vanishing_point", true)
project.undo_redo.add_undo_method(self, "undo_add_vanishing_point")
project.undo_redo.commit_action()
func update():
for c in vanishing_point_container.get_children():
c.queue_free()
for idx in Global.current_project.vanishing_points.size():
var point_data = Global.current_project.vanishing_points[idx]
var vanishing_point := vanishing_point_res.instance()
vanishing_point_container.add_child(vanishing_point)
vanishing_point.initiate(point_data, idx)
func add_vanishing_point(is_redo := false):
var vanishing_point := vanishing_point_res.instance()
vanishing_point_container.add_child(vanishing_point)
if is_redo and !do_pool.empty():
vanishing_point.initiate(do_pool.pop_back())
vanishing_point.update_data_to_project()
else:
vanishing_point.initiate()
func undo_add_vanishing_point():
var point = vanishing_point_container.get_child(vanishing_point_container.get_child_count() - 1)
point.queue_free()
do_pool.append(point.data.duplicate())
point.update_data_to_project(true)
func delete_point(idx):
var project = Global.current_project
project.undos += 1
project.undo_redo.create_action("Delete Vanishing Point")
project.undo_redo.add_do_method(self, "do_delete_point", idx)
project.undo_redo.add_undo_method(self, "undo_delete_point", idx)
project.undo_redo.commit_action()
func do_delete_point(idx):
var point = vanishing_point_container.get_child(idx)
delete_pool.append(point.data)
point.queue_free()
point.update_data_to_project(true)
func undo_delete_point(idx):
var point = delete_pool.pop_back()
Global.current_project.vanishing_points.insert(idx, point)
update()

View file

@ -0,0 +1,72 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://src/UI/PerspectiveEditor/PerspectiveEditor.gd" type="Script" id=1]
[node name="PerspectiveEditor" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = -1013.0
margin_bottom = -510.0
rect_min_size = Vector2( 270, 10 )
script = ExtResource( 1 )
[node name="VBoxContainer" type="VBoxContainer" parent="."]
self_modulate = Color( 0.698039, 0.698039, 0.698039, 0.698039 )
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 4.0
margin_right = -4.0
rect_clip_content = true
size_flags_vertical = 3
__meta__ = {
"_editor_description_": ""
}
[node name="Header" type="HBoxContainer" parent="VBoxContainer"]
margin_right = 262.0
margin_bottom = 30.0
custom_constants/separation = 0
[node name="Label" type="Label" parent="VBoxContainer/Header"]
margin_top = 8.0
margin_right = 232.0
margin_bottom = 22.0
size_flags_horizontal = 3
theme_type_variation = "Header"
text = "Perspective Editor"
align = 1
valign = 1
[node name="AddPoint" type="Button" parent="VBoxContainer/Header"]
margin_left = 232.0
margin_right = 262.0
margin_bottom = 30.0
rect_min_size = Vector2( 30, 30 )
text = "+"
clip_text = true
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
margin_top = 34.0
margin_right = 262.0
margin_bottom = 38.0
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
margin_top = 42.0
margin_right = 262.0
margin_bottom = 46.0
[node name="Content" type="ScrollContainer" parent="VBoxContainer"]
margin_top = 50.0
margin_right = 262.0
margin_bottom = 210.0
size_flags_vertical = 3
[node name="VanishingPointContainer" type="VBoxContainer" parent="VBoxContainer/Content"]
unique_name_in_owner = true
margin_right = 262.0
margin_bottom = 160.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_constants/separation = 5
[connection signal="pressed" from="VBoxContainer/Header/AddPoint" to="." method="_on_AddPoint_pressed"]

View file

@ -0,0 +1,101 @@
class_name PerspectiveLine
extends Line2D
const INPUT_WIDTH := 4
var hidden = false
var track_mouse := false
var _data = {"start": Vector2.ZERO, "angle": 0, "length": 19999, "color": Color.black}
func initiate(data: Dictionary):
width = Global.camera.zoom.x * 2
Global.canvas.add_child(self)
refresh(data)
func refresh(data: Dictionary):
_data = data
default_color = data.color
draw_perspective_line()
func draw_perspective_line():
var angle = -_data.angle
points[0] = _data.start
if hidden:
points[1] = _data.start
else:
points[1] = (
_data.start
+ Vector2(_data.length * cos(deg2rad(angle)), _data.length * sin(deg2rad(angle)))
)
func hide_perspective_line():
points[1] = _data.start
hidden = true
func _input(event: InputEvent) -> void:
if event is InputEventMouse:
if track_mouse:
if !Global.can_draw or !Global.has_focus:
hide_perspective_line()
return
default_color.a = 0.5
var tmp_transform = get_canvas_transform().affine_inverse()
var tmp_position = Global.main_viewport.get_local_mouse_position()
var mouse_point = tmp_transform.basis_xform(tmp_position) + tmp_transform.origin
var project_size = Global.current_project.size
if Rect2(Vector2.ZERO, project_size).has_point(mouse_point):
hidden = false
draw_perspective_line()
var rel_vector = mouse_point - _data.start
var test_vector = Vector2(_data.start.x, 0)
if sign(test_vector.x) == 0:
test_vector.x += 0.5
_data.angle = rad2deg(test_vector.angle_to(rel_vector))
if sign(test_vector.x) == -1:
_data.angle += 180
points[1] = (
_data.start
+ Vector2(
_data.length * cos(deg2rad(_data.angle)),
_data.length * sin(deg2rad(_data.angle))
)
)
else:
hide_perspective_line()
update()
func _draw() -> void:
draw_circle(_data.start, Global.camera.zoom.x * 5, default_color)
width = Global.camera.zoom.x * 2
if hidden: # Hidden line
return
var viewport_size: Vector2 = Global.main_viewport.rect_size
var zoom: Vector2 = Global.camera.zoom
# viewport_poly is an array of the points that make up the corners of the viewport
var viewport_poly := [
Vector2.ZERO, Vector2(viewport_size.x, 0), viewport_size, Vector2(0, viewport_size.y)
]
# Adjusting viewport_poly to take into account the camera offset, zoom, and rotation
for p in range(viewport_poly.size()):
viewport_poly[p] = (
viewport_poly[p].rotated(Global.camera.rotation) * zoom
+ Vector2(
(
Global.camera.offset.x
- (viewport_size.rotated(Global.camera.rotation).x / 2) * zoom.x
),
(
Global.camera.offset.y
- (viewport_size.rotated(Global.camera.rotation).y / 2) * zoom.y
)
)
)
# If there's no intersection with a viewport edge, show string in top left corner
draw_set_transform(viewport_poly[0], Global.camera.rotation, zoom * 2)

View file

@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://src/UI/PerspectiveEditor/PerspectiveLine.gd" type="Script" id=1]
[node name="PerspectiveLine" type="Line2D"]
points = PoolVector2Array( 0, 0, 64, 0 )
script = ExtResource( 1 )

View file

@ -0,0 +1,41 @@
extends Button
# This is NOT related to the CollapsibleContainer class (though it behaves similarly)
# i did it like this because the "Content" is part of a different node
export var point_text := "" setget _set_text
export var visible_content := false setget _set_visible_content
onready var content = $"%Content"
func _ready() -> void:
_set_visible(pressed)
content.connect("visibility_changed", self, "_child_visibility_changed")
func _set_text(value: String) -> void:
$Label.text = value
rect_min_size = $Label.rect_size
func _set_visible_content(value: bool) -> void:
visible_content = value
pressed = value
func _on_Button_toggled(button_pressed: bool) -> void:
_set_visible(button_pressed)
func _set_visible(pressed: bool) -> void:
if pressed:
$TextureRect.rect_rotation = 0
else:
$TextureRect.rect_rotation = -90
content.visible = pressed
# Checks if a child becomes visible from another source and ensures
# it remains invisible if the button is not pressed
func _child_visibility_changed() -> void:
if not pressed:
content.visible = false

View file

@ -0,0 +1,228 @@
extends VBoxContainer
var perspective_lines = []
var tracker_line: PerspectiveLine
var data = {
"position_x": 0,
"position_y": 0,
"angles": [],
"lengths": [],
"color": Color(randf(), randf(), randf(), 1).to_html(),
}
onready var color_picker_button = $"%ColorPickerButton"
onready var title = $"%PointCollapseContainer"
onready var pos_x = $"%X"
onready var pos_y = $"%Y"
onready var line_buttons_container = $"%LinesContainer"
onready var boundary_l = $Content/BoundaryL
onready var boundary_r = $Content/BoundaryR
onready var boundary_b = $Content/VBoxContainer/BoundaryB
func initiate(start_data = null, idx = -1) -> void:
if start_data:
data = start_data.duplicate()
add_line(null, true)
for i in data.angles.size():
var loaded_line_data = {
"start": Vector2(data.position_x, data.position_y),
"angle": data.angles[i],
"length": data.lengths[i],
"color": Color(data.color)
}
add_line(loaded_line_data)
if idx != -1:
title.point_text = str("Point: ", idx + 1)
else:
title.point_text = str("Point: ", get_parent().get_child_count())
pos_x.value = data.position_x
pos_y.value = data.position_y
color_picker_button.color = Color(data.color)
update_boundary_color(color_picker_button.color)
color_picker_button.connect("color_changed", self, "_on_color_changed")
pos_x.connect("value_changed", self, "_on_X_value_changed")
pos_y.connect("value_changed", self, "_on_Y_value_changed")
if !start_data:
update_data_to_project()
func update_boundary_color(color: Color):
var luminance = (0.2126 * color.r) + (0.7152 * color.g) + (0.0722 * color.b)
color.a = 0.9 - luminance * 0.4 # Interpolates between 0.5 to 0.9
boundary_l.color = color
boundary_r.color = color
boundary_b.color = color
# Signals
func _on_AddLine_pressed() -> void:
add_line()
update_data_to_project()
func _on_Delete_pressed() -> void:
Global.perspective_editor.delete_point(get_index())
func _on_color_changed(_color: Color):
update_boundary_color(_color)
data.color = _color.to_html()
refresh(-1)
update_data_to_project()
func _on_X_value_changed(value: float) -> void:
data.position_x = value
refresh(-1)
update_data_to_project()
func _on_Y_value_changed(value):
data.position_y = value
refresh(-1)
update_data_to_project()
func _angle_changed(value: float, line_button):
var line_index = line_button.get_index()
data.angles[line_index] = value
refresh(line_index)
update_data_to_project()
func _length_changed(value: float, line_button):
var line_index = line_button.get_index()
data.lengths[line_index] = value
refresh(line_index)
update_data_to_project()
func _remove_line_pressed(line_button):
var index = line_button.get_index()
remove_line(index)
line_button.queue_free()
update_data_to_project()
# Methods
func add_line(loaded_line_data = null, is_tracker := false):
var p_size = Global.current_project.size
var default_line_data = {
"start": Vector2(data.position_x, data.position_y),
"angle": 0,
"length": 19999,
"color": Color(data.color)
}
if default_line_data.start.x > p_size.x / 2:
# If new line is created ahed of half project distance then
# reverse it's angle (for beautification)
default_line_data.angle = 180
# Check if we have loading data instead of creating line
# Then THAT should be our data
if loaded_line_data:
default_line_data = loaded_line_data
if is_tracker: # if we are creating tracker line then length adjustment is not required
if tracker_line != null: # Also if the tracker line already exists then cancel creation
return
else: # If we are not creating a perspective line then adjust it's length
var suitable_length = sqrt(pow(p_size.x, 2) + pow(p_size.y, 2))
default_line_data.length = suitable_length
# Create the visual line
var line = preload("res://src/UI/PerspectiveEditor/PerspectiveLine.tscn").instance()
line.initiate(default_line_data)
# Set it's mode accordingly
if is_tracker: # Settings for Tracker mode
line.track_mouse = true
tracker_line = line
tracker_line.hide_perspective_line()
else: # Settings for Normal mode
var line_button = preload("res://src/UI/PerspectiveEditor/LineButton.tscn").instance()
line_buttons_container.add_child(line_button)
var index = line_button.get_parent().get_child_count() - 2
line_button.get_parent().move_child(line_button, index)
var line_name = str(
"Line", line_button.get_index() + 1, " (", int(default_line_data.angle), "°)"
)
line_button.text = line_name
var remove_button = line_button.find_node("Delete")
var angle_slider = line_button.find_node("AngleSlider")
var length_slider = line_button.find_node("LengthSlider")
angle_slider.value = default_line_data.angle
length_slider.value = default_line_data.length
if !loaded_line_data:
data.angles.append(angle_slider.value)
data.lengths.append(length_slider.value)
angle_slider.connect("value_changed", self, "_angle_changed", [line_button])
length_slider.connect("value_changed", self, "_length_changed", [line_button])
remove_button.connect("pressed", self, "_remove_line_pressed", [line_button])
perspective_lines.append(line)
func remove_line(line_index):
var line_to_remove = perspective_lines[line_index]
perspective_lines.remove(line_index)
data.angles.remove(line_index)
data.lengths.remove(line_index)
line_to_remove.queue_free()
func update_data_to_project(removal := false):
var project = Global.current_project
var idx = get_index()
if removal:
project.vanishing_points.remove(idx)
return
if idx < project.vanishing_points.size():
project.vanishing_points[idx] = data
else:
project.vanishing_points.append(data)
Global.current_project.has_changed = true
func refresh(index: int):
if index == -1: # means all lines should be refreshed
refresh_tracker()
for i in data.angles.size():
refresh_line(i)
else:
refresh_line(index)
func refresh_line(index: int):
var line_data = {
"start": Vector2(data.position_x, data.position_y),
"angle": data.angles[index],
"length": data.lengths[index],
"color": Color(data.color)
}
var line_button = line_buttons_container.get_child(index)
var line_name = str("Line", line_button.get_index() + 1, " (", int(line_data.angle), "°)")
line_button.text = line_name
perspective_lines[index].refresh(line_data)
func refresh_tracker():
var line_data = {
"start": Vector2(data.position_x, data.position_y),
"angle": tracker_line._data.angle,
"length": tracker_line._data.length,
"color": Color(data.color)
}
tracker_line.refresh(line_data)
func _exit_tree() -> void:
if tracker_line:
tracker_line.queue_free()
tracker_line = null
for idx in perspective_lines.size():
perspective_lines[idx].queue_free()

View file

@ -0,0 +1,171 @@
[gd_scene load_steps=6 format=2]
[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/PerspectiveEditor/VanishingPoint.gd" type="Script" id=2]
[ext_resource path="res://src/UI/Nodes/CollapsibleContainer.tscn" type="PackedScene" id=3]
[ext_resource path="res://assets/graphics/misc/value_arrow.svg" type="Texture" id=4]
[ext_resource path="res://src/UI/PerspectiveEditor/PointCollapseContainer.gd" type="Script" id=5]
[node name="VanishingPoint" type="VBoxContainer" groups=["Entry"]]
margin_right = 261.0
margin_bottom = 145.0
script = ExtResource( 2 )
[node name="TitleContainer" type="HBoxContainer" parent="."]
margin_right = 261.0
margin_bottom = 20.0
[node name="PointCollapseContainer" type="Button" parent="TitleContainer"]
unique_name_in_owner = true
margin_right = 207.0
margin_bottom = 20.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "CollapsibleCheckBox"
toggle_mode = true
script = ExtResource( 5 )
[node name="TextureRect" type="TextureRect" parent="TitleContainer/PointCollapseContainer" groups=["UIButtons"]]
anchor_top = 0.5
anchor_bottom = 0.5
margin_left = 2.0
margin_top = -6.0
margin_right = 14.0
margin_bottom = 6.0
rect_rotation = -90.0
rect_pivot_offset = Vector2( 6, 6 )
texture = ExtResource( 4 )
[node name="Label" type="Label" parent="TitleContainer/PointCollapseContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 14.0
theme_type_variation = "Header"
valign = 1
[node name="ColorPickerButton" type="ColorPickerButton" parent="TitleContainer"]
unique_name_in_owner = true
margin_left = 211.0
margin_right = 261.0
margin_bottom = 20.0
rect_min_size = Vector2( 50, 0 )
[node name="Content" type="HBoxContainer" parent="."]
unique_name_in_owner = true
margin_top = 24.0
margin_right = 261.0
margin_bottom = 130.0
[node name="Spacer" type="Control" parent="Content"]
margin_right = 5.0
margin_bottom = 106.0
rect_min_size = Vector2( 5, 0 )
[node name="BoundaryL" type="ColorRect" parent="Content"]
margin_left = 9.0
margin_right = 11.0
margin_bottom = 106.0
rect_min_size = Vector2( 2, 0 )
[node name="VBoxContainer" type="VBoxContainer" parent="Content"]
margin_left = 15.0
margin_right = 255.0
margin_bottom = 106.0
size_flags_horizontal = 3
[node name="PointInfo" type="HBoxContainer" parent="Content/VBoxContainer"]
margin_right = 240.0
margin_bottom = 52.0
[node name="Label" type="Label" parent="Content/VBoxContainer/PointInfo"]
margin_right = 56.0
margin_bottom = 52.0
size_flags_vertical = 5
text = "Position:"
[node name="Position" type="VBoxContainer" parent="Content/VBoxContainer/PointInfo"]
margin_left = 60.0
margin_right = 240.0
margin_bottom = 52.0
size_flags_horizontal = 3
[node name="X" parent="Content/VBoxContainer/PointInfo/Position" instance=ExtResource( 1 )]
unique_name_in_owner = true
margin_right = 180.0
step = 0.5
allow_greater = true
allow_lesser = true
prefix = "X :"
[node name="Y" parent="Content/VBoxContainer/PointInfo/Position" instance=ExtResource( 1 )]
unique_name_in_owner = true
margin_top = 28.0
margin_right = 180.0
margin_bottom = 52.0
step = 0.5
allow_greater = true
allow_lesser = true
prefix = "Y :"
[node name="CollapsibleContainer" parent="Content/VBoxContainer" instance=ExtResource( 3 )]
margin_top = 56.0
margin_right = 240.0
margin_bottom = 76.0
text = "Lines"
[node name="HBoxContainer" type="HBoxContainer" parent="Content/VBoxContainer/CollapsibleContainer"]
visible = false
margin_top = 24.0
margin_right = 261.0
margin_bottom = 44.0
[node name="Spacer" type="Control" parent="Content/VBoxContainer/CollapsibleContainer/HBoxContainer"]
margin_right = 20.0
margin_bottom = 20.0
rect_min_size = Vector2( 20, 0 )
[node name="VSeparator" type="VSeparator" parent="Content/VBoxContainer/CollapsibleContainer/HBoxContainer"]
margin_right = 4.0
margin_bottom = 40.0
[node name="LinesContainer" type="VBoxContainer" parent="Content/VBoxContainer/CollapsibleContainer/HBoxContainer"]
unique_name_in_owner = true
margin_left = 24.0
margin_right = 261.0
margin_bottom = 20.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="AddLine" type="Button" parent="Content/VBoxContainer/CollapsibleContainer/HBoxContainer/LinesContainer"]
margin_right = 237.0
margin_bottom = 20.0
text = "Add Line"
[node name="Delete" type="Button" parent="Content/VBoxContainer"]
margin_top = 80.0
margin_right = 240.0
margin_bottom = 100.0
rect_min_size = Vector2( 40, 0 )
text = "Delete Point"
[node name="BoundaryB" type="ColorRect" parent="Content/VBoxContainer"]
margin_top = 104.0
margin_right = 240.0
margin_bottom = 106.0
rect_min_size = Vector2( 0, 2 )
[node name="BoundaryR" type="ColorRect" parent="Content"]
margin_left = 259.0
margin_right = 261.0
margin_bottom = 106.0
rect_min_size = Vector2( 2, 0 )
[node name="HSeparator" type="HSeparator" parent="."]
margin_top = 134.0
margin_right = 261.0
margin_bottom = 144.0
custom_constants/separation = 10
[connection signal="toggled" from="TitleContainer/PointCollapseContainer" to="TitleContainer/PointCollapseContainer" method="_on_Button_toggled"]
[connection signal="pressed" from="Content/VBoxContainer/CollapsibleContainer/HBoxContainer/LinesContainer/AddLine" to="." method="_on_AddLine_pressed"]
[connection signal="pressed" from="Content/VBoxContainer/Delete" to="." method="_on_Delete_pressed"]

View file

@ -123,6 +123,7 @@ func _setup_view_menu() -> void:
"Show Pixel Grid",
"Show Rulers",
"Show Guides",
"Show Mouse Guides",
"Snap To",
]
view_menu = view_menu_button.get_popup()
@ -160,8 +161,13 @@ func _setup_view_menu() -> void:
var show_guides: bool = Global.config_cache.get_value(
"view_menu", "show_guides", Global.show_guides
)
var show_mouse_guides: bool = Global.config_cache.get_value(
"view_menu", "show_mouse_guides", Global.show_mouse_guides
)
if show_guides != Global.show_guides:
_toggle_show_guides()
if show_mouse_guides != Global.show_mouse_guides:
_toggle_show_mouse_guides()
func _setup_tile_mode_submenu(item: String) -> void:
@ -460,6 +466,8 @@ func view_menu_id_pressed(id: int) -> void:
_toggle_show_rulers()
Global.ViewMenu.SHOW_GUIDES:
_toggle_show_guides()
Global.ViewMenu.SHOW_MOUSE_GUIDES:
_toggle_show_mouse_guides()
_:
_handle_metadata(id, view_menu_button)
@ -574,8 +582,6 @@ func _toggle_show_grid() -> void:
func _toggle_show_pixel_grid() -> void:
Global.draw_pixel_grid = !Global.draw_pixel_grid
view_menu.set_item_checked(Global.ViewMenu.SHOW_PIXEL_GRID, Global.draw_pixel_grid)
if Global.canvas.pixel_grid:
Global.canvas.pixel_grid.update()
func _toggle_show_rulers() -> void:
@ -598,6 +604,15 @@ func _toggle_show_guides() -> void:
guide.visible = Global.show_y_symmetry_axis and Global.show_guides
func _toggle_show_mouse_guides() -> void:
Global.show_mouse_guides = !Global.show_mouse_guides
view_menu.set_item_checked(Global.ViewMenu.SHOW_MOUSE_GUIDES, Global.show_mouse_guides)
if Global.show_mouse_guides:
if Global.canvas.mouse_guide_container:
Global.canvas.mouse_guide_container.get_child(0).update()
Global.canvas.mouse_guide_container.get_child(1).update()
func _toggle_zen_mode() -> void:
for i in ui_elements.size():
if ui_elements[i].name == "Main Canvas":

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=48 format=2]
[gd_scene load_steps=49 format=2]
[ext_resource path="res://src/UI/Tools/Tools.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Canvas/CanvasPreview.tscn" type="PackedScene" id=2]
@ -11,6 +11,7 @@
[ext_resource path="res://src/Shaders/TransparentChecker.shader" type="Shader" id=9]
[ext_resource path="res://src/UI/GlobalToolOptions/GlobalToolOptions.tscn" type="PackedScene" id=10]
[ext_resource path="res://src/UI/ReferenceImages/ReferencesPanel.tscn" type="PackedScene" id=11]
[ext_resource path="res://src/UI/PerspectiveEditor/PerspectiveEditor.tscn" type="PackedScene" id=12]
[ext_resource path="res://addons/dockable_container/layout.gd" type="Script" id=14]
[ext_resource path="res://src/UI/CanvasPreviewContainer/CanvasPreviewContainer.tscn" type="PackedScene" id=16]
[ext_resource path="res://src/UI/ColorPickers/ColorPickers.tscn" type="PackedScene" id=17]
@ -34,7 +35,7 @@ shader_param/size = Vector2( 100, 100 )
[sub_resource type="Resource" id=1]
resource_name = "Tabs"
script = ExtResource( 36 )
names = PoolStringArray( "Tools" )
names = PoolStringArray( "Tools", "Perspective Editor" )
current_tab = 0
[sub_resource type="Resource" id=8]
@ -404,6 +405,11 @@ margin_left = 1079.0
margin_right = 1080.0
margin_bottom = 1004.0
[node name="Perspective Editor" parent="DockableContainer" instance=ExtResource( 12 )]
visible = false
margin_right = -1010.0
margin_bottom = -710.0
[connection signal="item_rect_changed" from="DockableContainer/Main Canvas" to="." method="_on_main_canvas_item_rect_changed"]
[connection signal="visibility_changed" from="DockableContainer/Main Canvas" to="." method="_on_main_canvas_visibility_changed"]
[connection signal="reposition_active_tab_request" from="DockableContainer/Main Canvas/TabsContainer/Tabs" to="DockableContainer/Main Canvas/TabsContainer/Tabs" method="_on_Tabs_reposition_active_tab_request"]