mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-21 13:03:13 +00:00
Move perspective lines with mouse (plus code improvements) (#821)
* Code Improvements to Perspective editor * formatting * fixed a bug * Some drastic changes * Delete src/UI/Tools directory * Delete src/UI/UI/PerspectiveEditor directory * Some drastic changes * i messed up so here's the fix * Added easy movement and rotation * formatting * useless script * fix some things and add tracker disabler * formatting * fix incorrect length on redo * remove useless code
This commit is contained in:
parent
c60675b4e7
commit
0da30c015c
8 changed files with 304 additions and 198 deletions
|
@ -193,7 +193,7 @@ func change_project() -> void:
|
||||||
Global.horizontal_ruler.update()
|
Global.horizontal_ruler.update()
|
||||||
Global.vertical_ruler.update()
|
Global.vertical_ruler.update()
|
||||||
Global.references_panel.project_changed()
|
Global.references_panel.project_changed()
|
||||||
Global.perspective_editor.update()
|
Global.perspective_editor.update_points()
|
||||||
Global.cursor_position_label.text = "[%s×%s]" % [size.x, size.y]
|
Global.cursor_position_label.text = "[%s×%s]" % [size.x, size.y]
|
||||||
|
|
||||||
Global.window_title = "%s - Pixelorama %s" % [name, Global.current_version]
|
Global.window_title = "%s - Pixelorama %s" % [name, Global.current_version]
|
||||||
|
|
|
@ -162,15 +162,18 @@ func snap_position(position: Vector2) -> Vector2:
|
||||||
|
|
||||||
if Global.snap_to_perspective_guides:
|
if Global.snap_to_perspective_guides:
|
||||||
for point in Global.current_project.vanishing_points:
|
for point in Global.current_project.vanishing_points:
|
||||||
for i in point.angles.size():
|
if point.has("pos_x") and point.has("pos_y"): # Sanity check
|
||||||
var angle: float = -deg2rad(point.angles[i])
|
for i in point.lines.size():
|
||||||
var length: float = point.lengths[i]
|
if point.lines[i].has("angle") and point.lines[i].has("length"): # Sanity check
|
||||||
var s1 := Vector2(point.position_x, point.position_y)
|
var angle: float = deg2rad(point.lines[i].angle)
|
||||||
var s2 := s1 + Vector2(length * cos(angle), length * sin(angle))
|
var length: float = point.lines[i].length
|
||||||
var snap := _snap_to_guide(snap_to, position, snap_distance, s1, s2)
|
var start = Vector2(point.pos_x, point.pos_y)
|
||||||
if snap == Vector2.INF:
|
var s1: Vector2 = start
|
||||||
continue
|
var s2 := s1 + Vector2(length * cos(angle), length * sin(angle))
|
||||||
snap_to = snap
|
var snap := _snap_to_guide(snap_to, position, snap_distance, s1, s2)
|
||||||
|
if snap == Vector2.INF:
|
||||||
|
continue
|
||||||
|
snap_to = snap
|
||||||
if snap_to != Vector2.INF:
|
if snap_to != Vector2.INF:
|
||||||
position = snap_to.floor()
|
position = snap_to.floor()
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
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
|
|
|
@ -35,6 +35,7 @@ size_flags_horizontal = 3
|
||||||
margin_right = 51.0
|
margin_right = 51.0
|
||||||
max_value = 359.999
|
max_value = 359.999
|
||||||
step = 0.001
|
step = 0.001
|
||||||
|
allow_greater = true
|
||||||
prefix = "Angle:"
|
prefix = "Angle:"
|
||||||
suffix = "°"
|
suffix = "°"
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ unique_name_in_owner = true
|
||||||
margin_top = 28.0
|
margin_top = 28.0
|
||||||
margin_right = 51.0
|
margin_right = 51.0
|
||||||
margin_bottom = 52.0
|
margin_bottom = 52.0
|
||||||
|
min_value = 1.0
|
||||||
max_value = 19999.0
|
max_value = 19999.0
|
||||||
value = 19999.0
|
value = 19999.0
|
||||||
allow_greater = true
|
allow_greater = true
|
||||||
|
|
|
@ -4,12 +4,12 @@ var axes: Node2D
|
||||||
var do_pool = [] # A pool that stores data of points removed by undo
|
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 delete_pool = [] # A pool that containg deleted data and their index
|
||||||
var vanishing_point_res := preload("res://src/UI/PerspectiveEditor/VanishingPoint.tscn")
|
var vanishing_point_res := preload("res://src/UI/PerspectiveEditor/VanishingPoint.tscn")
|
||||||
|
var tracker_disabled := false
|
||||||
onready var vanishing_point_container = $"%VanishingPointContainer"
|
onready var vanishing_point_container = $"%VanishingPointContainer"
|
||||||
|
|
||||||
|
|
||||||
func _on_AddPoint_pressed() -> void:
|
func _on_AddPoint_pressed() -> void:
|
||||||
do_pool.clear() # Reset
|
do_pool.clear() # Reset (clears Redo history of vanishing points)
|
||||||
var project = Global.current_project
|
var project = Global.current_project
|
||||||
project.undos += 1
|
project.undos += 1
|
||||||
project.undo_redo.create_action("Add Vanishing Point")
|
project.undo_redo.create_action("Add Vanishing Point")
|
||||||
|
@ -18,20 +18,16 @@ func _on_AddPoint_pressed() -> void:
|
||||||
project.undo_redo.commit_action()
|
project.undo_redo.commit_action()
|
||||||
|
|
||||||
|
|
||||||
func update():
|
func _on_TrackerLines_toggled(button_pressed):
|
||||||
for c in vanishing_point_container.get_children():
|
for point in vanishing_point_container.get_children():
|
||||||
c.queue_free()
|
tracker_disabled = !button_pressed
|
||||||
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):
|
func add_vanishing_point(is_redo := false):
|
||||||
var vanishing_point := vanishing_point_res.instance()
|
var vanishing_point := vanishing_point_res.instance()
|
||||||
vanishing_point_container.add_child(vanishing_point)
|
vanishing_point_container.add_child(vanishing_point)
|
||||||
if is_redo and !do_pool.empty():
|
if is_redo and !do_pool.empty():
|
||||||
|
# if it's a redo then initialize it with the redo data
|
||||||
vanishing_point.initiate(do_pool.pop_back())
|
vanishing_point.initiate(do_pool.pop_back())
|
||||||
vanishing_point.update_data_to_project()
|
vanishing_point.update_data_to_project()
|
||||||
else:
|
else:
|
||||||
|
@ -41,7 +37,7 @@ func add_vanishing_point(is_redo := false):
|
||||||
func undo_add_vanishing_point():
|
func undo_add_vanishing_point():
|
||||||
var point = vanishing_point_container.get_child(vanishing_point_container.get_child_count() - 1)
|
var point = vanishing_point_container.get_child(vanishing_point_container.get_child_count() - 1)
|
||||||
point.queue_free()
|
point.queue_free()
|
||||||
do_pool.append(point.data.duplicate())
|
do_pool.append(point.serialize())
|
||||||
point.update_data_to_project(true)
|
point.update_data_to_project(true)
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +52,7 @@ func delete_point(idx):
|
||||||
|
|
||||||
func do_delete_point(idx):
|
func do_delete_point(idx):
|
||||||
var point = vanishing_point_container.get_child(idx)
|
var point = vanishing_point_container.get_child(idx)
|
||||||
delete_pool.append(point.data)
|
delete_pool.append(point.serialize())
|
||||||
point.queue_free()
|
point.queue_free()
|
||||||
point.update_data_to_project(true)
|
point.update_data_to_project(true)
|
||||||
|
|
||||||
|
@ -64,4 +60,18 @@ func do_delete_point(idx):
|
||||||
func undo_delete_point(idx):
|
func undo_delete_point(idx):
|
||||||
var point = delete_pool.pop_back()
|
var point = delete_pool.pop_back()
|
||||||
Global.current_project.vanishing_points.insert(idx, point)
|
Global.current_project.vanishing_points.insert(idx, point)
|
||||||
update()
|
update_points()
|
||||||
|
|
||||||
|
|
||||||
|
func update_points():
|
||||||
|
# Delete old vanishing points
|
||||||
|
for c in vanishing_point_container.get_children():
|
||||||
|
c.queue_free()
|
||||||
|
# Add the "updated" vanising points from the current_project
|
||||||
|
for idx in Global.current_project.vanishing_points.size():
|
||||||
|
# Create the point
|
||||||
|
var vanishing_point := vanishing_point_res.instance()
|
||||||
|
vanishing_point_container.add_child(vanishing_point)
|
||||||
|
# Initialize it
|
||||||
|
var point_data = Global.current_project.vanishing_points[idx]
|
||||||
|
vanishing_point.initiate(point_data, idx)
|
||||||
|
|
|
@ -45,18 +45,25 @@ rect_min_size = Vector2( 30, 30 )
|
||||||
text = "+"
|
text = "+"
|
||||||
clip_text = true
|
clip_text = true
|
||||||
|
|
||||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
[node name="TrackerLines" type="CheckButton" parent="VBoxContainer"]
|
||||||
margin_top = 34.0
|
margin_top = 34.0
|
||||||
margin_right = 262.0
|
margin_right = 262.0
|
||||||
margin_bottom = 38.0
|
margin_bottom = 74.0
|
||||||
|
pressed = true
|
||||||
|
text = "Tracker Lines"
|
||||||
|
|
||||||
|
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
||||||
|
margin_top = 78.0
|
||||||
|
margin_right = 262.0
|
||||||
|
margin_bottom = 82.0
|
||||||
|
|
||||||
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
|
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
|
||||||
margin_top = 42.0
|
margin_top = 86.0
|
||||||
margin_right = 262.0
|
margin_right = 262.0
|
||||||
margin_bottom = 46.0
|
margin_bottom = 90.0
|
||||||
|
|
||||||
[node name="Content" type="ScrollContainer" parent="VBoxContainer"]
|
[node name="Content" type="ScrollContainer" parent="VBoxContainer"]
|
||||||
margin_top = 50.0
|
margin_top = 94.0
|
||||||
margin_right = 262.0
|
margin_right = 262.0
|
||||||
margin_bottom = 210.0
|
margin_bottom = 210.0
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
@ -64,9 +71,10 @@ size_flags_vertical = 3
|
||||||
[node name="VanishingPointContainer" type="VBoxContainer" parent="VBoxContainer/Content"]
|
[node name="VanishingPointContainer" type="VBoxContainer" parent="VBoxContainer/Content"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
margin_right = 262.0
|
margin_right = 262.0
|
||||||
margin_bottom = 160.0
|
margin_bottom = 116.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
custom_constants/separation = 5
|
custom_constants/separation = 5
|
||||||
|
|
||||||
[connection signal="pressed" from="VBoxContainer/Header/AddPoint" to="." method="_on_AddPoint_pressed"]
|
[connection signal="pressed" from="VBoxContainer/Header/AddPoint" to="." method="_on_AddPoint_pressed"]
|
||||||
|
[connection signal="toggled" from="VBoxContainer/TrackerLines" to="." method="_on_TrackerLines_toggled"]
|
||||||
|
|
|
@ -1,101 +1,156 @@
|
||||||
class_name PerspectiveLine
|
class_name PerspectiveLine
|
||||||
extends Line2D
|
extends Line2D
|
||||||
|
|
||||||
const INPUT_WIDTH := 4
|
const LINE_WIDTH := 2
|
||||||
|
const CIRCLE_RAD := 4
|
||||||
|
|
||||||
|
var angle := 0
|
||||||
|
var length := 19999
|
||||||
|
|
||||||
var hidden = false
|
var hidden = false
|
||||||
|
var has_focus := false
|
||||||
var track_mouse := false
|
var track_mouse := false
|
||||||
var _data = {"start": Vector2.ZERO, "angle": 0, "length": 19999, "color": Color.black}
|
var change_length = false
|
||||||
|
|
||||||
|
var line_button: Node
|
||||||
|
var _vanishing_point: Node
|
||||||
|
|
||||||
|
|
||||||
func initiate(data: Dictionary):
|
func serialize() -> Dictionary:
|
||||||
width = Global.camera.zoom.x * 2
|
var data = {"angle": angle, "length": length}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
func deserialize(data: Dictionary):
|
||||||
|
if data.has("angle"):
|
||||||
|
angle = data.angle
|
||||||
|
if data.has("length"):
|
||||||
|
length = data.length
|
||||||
|
|
||||||
|
|
||||||
|
func initiate(data: Dictionary, vanishing_point: Node):
|
||||||
|
_vanishing_point = vanishing_point
|
||||||
|
width = Global.camera.zoom.x * LINE_WIDTH
|
||||||
Global.canvas.add_child(self)
|
Global.canvas.add_child(self)
|
||||||
refresh(data)
|
deserialize(data)
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
|
||||||
func refresh(data: Dictionary):
|
func refresh():
|
||||||
_data = data
|
default_color = _vanishing_point.color
|
||||||
default_color = data.color
|
|
||||||
draw_perspective_line()
|
draw_perspective_line()
|
||||||
|
|
||||||
|
|
||||||
func draw_perspective_line():
|
func draw_perspective_line():
|
||||||
var angle = -_data.angle
|
var start = Vector2(_vanishing_point.pos_x.value, _vanishing_point.pos_y.value)
|
||||||
points[0] = _data.start
|
points[0] = start
|
||||||
if hidden:
|
if hidden:
|
||||||
points[1] = _data.start
|
points[1] = start
|
||||||
else:
|
else:
|
||||||
points[1] = (
|
points[1] = (start + Vector2(length * cos(deg2rad(angle)), length * sin(deg2rad(angle))))
|
||||||
_data.start
|
|
||||||
+ Vector2(_data.length * cos(deg2rad(angle)), _data.length * sin(deg2rad(angle)))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func hide_perspective_line():
|
func hide_perspective_line():
|
||||||
points[1] = _data.start
|
var start = Vector2(_vanishing_point.pos_x.value, _vanishing_point.pos_y.value)
|
||||||
|
points[1] = start
|
||||||
hidden = true
|
hidden = true
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if event is InputEventMouse:
|
if event is InputEventMouse:
|
||||||
|
var mouse_point = Global.canvas.current_pixel
|
||||||
|
var project_size = Global.current_project.size
|
||||||
|
|
||||||
if track_mouse:
|
if track_mouse:
|
||||||
if !Global.can_draw or !Global.has_focus:
|
if !Global.can_draw or !Global.has_focus or Global.perspective_editor.tracker_disabled:
|
||||||
hide_perspective_line()
|
hide_perspective_line()
|
||||||
return
|
return
|
||||||
default_color.a = 0.5
|
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):
|
if Rect2(Vector2.ZERO, project_size).has_point(mouse_point):
|
||||||
|
var start = Vector2(_vanishing_point.pos_x.value, _vanishing_point.pos_y.value)
|
||||||
hidden = false
|
hidden = false
|
||||||
draw_perspective_line()
|
draw_perspective_line()
|
||||||
var rel_vector = mouse_point - _data.start
|
angle = rad2deg(mouse_point.angle_to_point(points[0]))
|
||||||
var test_vector = Vector2(_data.start.x, 0)
|
if angle < 0:
|
||||||
if sign(test_vector.x) == 0:
|
angle += 360
|
||||||
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] = (
|
points[1] = (
|
||||||
_data.start
|
start
|
||||||
+ Vector2(
|
+ Vector2(length * cos(deg2rad(angle)), length * sin(deg2rad(angle)))
|
||||||
_data.length * cos(deg2rad(_data.angle)),
|
|
||||||
_data.length * sin(deg2rad(_data.angle))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
hide_perspective_line()
|
hide_perspective_line()
|
||||||
|
else:
|
||||||
|
try_rotate_scale()
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
|
||||||
|
func try_rotate_scale():
|
||||||
|
var mouse_point = Global.canvas.current_pixel
|
||||||
|
var project_size = Global.current_project.size
|
||||||
|
var test_line := (points[1] - points[0]).rotated(deg2rad(90)).normalized()
|
||||||
|
var from_a = mouse_point - test_line * Global.camera.zoom.x * LINE_WIDTH * 2
|
||||||
|
var from_b = mouse_point + test_line * Global.camera.zoom.x * LINE_WIDTH * 2
|
||||||
|
if Input.is_action_just_pressed("left_mouse") and Global.can_draw and Global.has_focus:
|
||||||
|
if (
|
||||||
|
Geometry.segment_intersects_segment_2d(from_a, from_b, points[0], points[1])
|
||||||
|
or mouse_point.distance_to(points[1]) < Global.camera.zoom.x * CIRCLE_RAD * 2
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
!Rect2(Vector2.ZERO, project_size).has_point(mouse_point)
|
||||||
|
or Global.move_guides_on_canvas
|
||||||
|
):
|
||||||
|
if mouse_point.distance_to(points[1]) < Global.camera.zoom.x * CIRCLE_RAD * 2:
|
||||||
|
change_length = true
|
||||||
|
has_focus = true
|
||||||
|
Global.has_focus = false
|
||||||
|
update()
|
||||||
|
if has_focus:
|
||||||
|
if Input.is_action_pressed("left_mouse"):
|
||||||
|
# rotation code here
|
||||||
|
if line_button:
|
||||||
|
var new_angle = rad2deg(mouse_point.angle_to_point(points[0]))
|
||||||
|
if new_angle < 0:
|
||||||
|
new_angle += 360
|
||||||
|
_vanishing_point.angle_changed(new_angle, line_button)
|
||||||
|
if change_length:
|
||||||
|
var new_length = mouse_point.distance_to(points[0])
|
||||||
|
_vanishing_point.length_changed(new_length, line_button)
|
||||||
|
|
||||||
|
elif Input.is_action_just_released("left_mouse"):
|
||||||
|
Global.has_focus = true
|
||||||
|
has_focus = false
|
||||||
|
change_length = false
|
||||||
|
update()
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
draw_circle(_data.start, Global.camera.zoom.x * 5, default_color)
|
var mouse_point = Global.canvas.current_pixel
|
||||||
width = Global.camera.zoom.x * 2
|
var arc_points = []
|
||||||
|
draw_circle(points[0], Global.camera.zoom.x * CIRCLE_RAD, default_color) # Starting circle
|
||||||
|
if !track_mouse and mouse_point.distance_to(points[0]) < Global.camera.zoom.x * CIRCLE_RAD * 2:
|
||||||
|
if (
|
||||||
|
!Rect2(Vector2.ZERO, Global.current_project.size).has_point(mouse_point)
|
||||||
|
or Global.move_guides_on_canvas
|
||||||
|
or has_focus
|
||||||
|
):
|
||||||
|
arc_points.append(points[0])
|
||||||
|
if (
|
||||||
|
mouse_point.distance_to(points[1]) < Global.camera.zoom.x * CIRCLE_RAD * 2
|
||||||
|
or (has_focus and Input.is_action_pressed("left_mouse"))
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
!Rect2(Vector2.ZERO, Global.current_project.size).has_point(mouse_point)
|
||||||
|
or Global.move_guides_on_canvas
|
||||||
|
or has_focus
|
||||||
|
):
|
||||||
|
if !arc_points.has(points[0]):
|
||||||
|
arc_points.append(points[0])
|
||||||
|
arc_points.append(points[1])
|
||||||
|
|
||||||
|
for point in arc_points:
|
||||||
|
draw_arc(point, Global.camera.zoom.x * CIRCLE_RAD * 2, 0, 360, 360, default_color, 0.5)
|
||||||
|
|
||||||
|
width = Global.camera.zoom.x * LINE_WIDTH
|
||||||
if hidden: # Hidden line
|
if hidden: # Hidden line
|
||||||
return
|
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)
|
|
||||||
|
|
|
@ -1,53 +1,66 @@
|
||||||
extends VBoxContainer
|
extends VBoxContainer
|
||||||
|
|
||||||
|
var has_focus := false
|
||||||
var perspective_lines = []
|
var perspective_lines = []
|
||||||
|
var color := Color(randf(), randf(), randf(), 1)
|
||||||
|
|
||||||
var tracker_line: PerspectiveLine
|
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 color_picker_button = $"%ColorPickerButton"
|
||||||
onready var title = $"%PointCollapseContainer"
|
onready var title := $"%PointCollapseContainer"
|
||||||
onready var pos_x = $"%X"
|
onready var pos_x := $"%X"
|
||||||
onready var pos_y = $"%Y"
|
onready var pos_y := $"%Y"
|
||||||
onready var line_buttons_container = $"%LinesContainer"
|
onready var line_buttons_container := $"%LinesContainer"
|
||||||
onready var boundary_l = $Content/BoundaryL
|
onready var boundary_l := $Content/BoundaryL
|
||||||
onready var boundary_r = $Content/BoundaryR
|
onready var boundary_r := $Content/BoundaryR
|
||||||
onready var boundary_b = $Content/VBoxContainer/BoundaryB
|
onready var boundary_b := $Content/VBoxContainer/BoundaryB
|
||||||
|
|
||||||
|
|
||||||
func initiate(start_data = null, idx = -1) -> void:
|
func serialize() -> Dictionary:
|
||||||
if start_data:
|
var lines_data := []
|
||||||
data = start_data.duplicate()
|
for line in perspective_lines:
|
||||||
add_line(null, true)
|
lines_data.append(line.serialize())
|
||||||
for i in data.angles.size():
|
var data = {
|
||||||
var loaded_line_data = {
|
"pos_x": pos_x.value,
|
||||||
"start": Vector2(data.position_x, data.position_y),
|
"pos_y": pos_y.value,
|
||||||
"angle": data.angles[i],
|
"lines": lines_data,
|
||||||
"length": data.lengths[i],
|
"color": color.to_html(),
|
||||||
"color": Color(data.color)
|
}
|
||||||
}
|
return data
|
||||||
add_line(loaded_line_data)
|
|
||||||
if idx != -1:
|
|
||||||
|
func deserialize(start_data: Dictionary):
|
||||||
|
if start_data: # Data is not {} means the project knows about this point
|
||||||
|
if start_data.has("pos_x") and start_data.has("pos_y"):
|
||||||
|
pos_x.value = start_data.pos_x
|
||||||
|
pos_y.value = start_data.pos_y
|
||||||
|
if start_data.has("color"):
|
||||||
|
color = Color(start_data.color)
|
||||||
|
# Add lines if their data is provided
|
||||||
|
if start_data.has("lines"):
|
||||||
|
for line_data in start_data["lines"]:
|
||||||
|
add_line(line_data)
|
||||||
|
else: # If the project doesn't know about this point
|
||||||
|
update_data_to_project()
|
||||||
|
|
||||||
|
add_line({}, true) # This is a tracker line (Always follows mouse)
|
||||||
|
color_picker_button.color = color
|
||||||
|
update_boundary_color()
|
||||||
|
|
||||||
|
|
||||||
|
func initiate(start_data: Dictionary = {}, idx = -1) -> void:
|
||||||
|
deserialize(start_data)
|
||||||
|
# Title of Vanishing point button
|
||||||
|
if idx != -1: # If the initialization is part of a Redo
|
||||||
title.point_text = str("Point: ", idx + 1)
|
title.point_text = str("Point: ", idx + 1)
|
||||||
else:
|
else:
|
||||||
title.point_text = str("Point: ", get_parent().get_child_count())
|
title.point_text = str("Point: ", get_parent().get_child_count())
|
||||||
pos_x.value = data.position_x
|
# connect signals
|
||||||
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")
|
color_picker_button.connect("color_changed", self, "_on_color_changed")
|
||||||
pos_x.connect("value_changed", self, "_on_X_value_changed")
|
pos_x.connect("value_changed", self, "_on_pos_value_changed")
|
||||||
pos_y.connect("value_changed", self, "_on_Y_value_changed")
|
pos_y.connect("value_changed", self, "_on_pos_value_changed")
|
||||||
if !start_data:
|
|
||||||
update_data_to_project()
|
|
||||||
|
|
||||||
|
|
||||||
func update_boundary_color(color: Color):
|
func update_boundary_color():
|
||||||
var luminance = (0.2126 * color.r) + (0.7152 * color.g) + (0.0722 * color.b)
|
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
|
color.a = 0.9 - luminance * 0.4 # Interpolates between 0.5 to 0.9
|
||||||
boundary_l.color = color
|
boundary_l.color = color
|
||||||
|
@ -55,6 +68,33 @@ func update_boundary_color(color: Color):
|
||||||
boundary_b.color = color
|
boundary_b.color = color
|
||||||
|
|
||||||
|
|
||||||
|
func _input(_event):
|
||||||
|
var mouse_point = Global.canvas.current_pixel
|
||||||
|
var project_size = Global.current_project.size
|
||||||
|
var start = Vector2(pos_x.value, pos_y.value)
|
||||||
|
if (
|
||||||
|
Input.is_action_just_pressed("left_mouse")
|
||||||
|
and Global.can_draw
|
||||||
|
and Global.has_focus
|
||||||
|
and mouse_point.distance_to(start) < Global.camera.zoom.x * 8
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
!Rect2(Vector2.ZERO, project_size).has_point(Global.canvas.current_pixel)
|
||||||
|
or Global.move_guides_on_canvas
|
||||||
|
):
|
||||||
|
has_focus = true
|
||||||
|
Global.has_focus = false
|
||||||
|
if has_focus:
|
||||||
|
if Input.is_action_pressed("left_mouse"):
|
||||||
|
# rotation code here
|
||||||
|
pos_x.value = mouse_point.x
|
||||||
|
pos_y.value = mouse_point.y
|
||||||
|
|
||||||
|
elif Input.is_action_just_released("left_mouse"):
|
||||||
|
Global.has_focus = true
|
||||||
|
has_focus = false
|
||||||
|
|
||||||
|
|
||||||
# Signals
|
# Signals
|
||||||
func _on_AddLine_pressed() -> void:
|
func _on_AddLine_pressed() -> void:
|
||||||
add_line()
|
add_line()
|
||||||
|
@ -66,35 +106,38 @@ func _on_Delete_pressed() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_color_changed(_color: Color):
|
func _on_color_changed(_color: Color):
|
||||||
update_boundary_color(_color)
|
update_boundary_color()
|
||||||
data.color = _color.to_html()
|
color = _color
|
||||||
refresh(-1)
|
refresh(-1)
|
||||||
update_data_to_project()
|
update_data_to_project()
|
||||||
|
|
||||||
|
|
||||||
func _on_X_value_changed(value: float) -> void:
|
func _on_pos_value_changed(_value: float) -> void:
|
||||||
data.position_x = value
|
|
||||||
refresh(-1)
|
refresh(-1)
|
||||||
update_data_to_project()
|
update_data_to_project()
|
||||||
|
|
||||||
|
|
||||||
func _on_Y_value_changed(value):
|
func angle_changed(value: float, line_button):
|
||||||
data.position_y = value
|
# check if the properties are changing the line or is the line changing properties
|
||||||
refresh(-1)
|
var angle_slider = line_button.find_node("AngleSlider")
|
||||||
|
if angle_slider.value != value: # the line is changing the properties
|
||||||
|
angle_slider.value = value
|
||||||
|
else:
|
||||||
|
var line_index = line_button.get_index()
|
||||||
|
perspective_lines[line_index].angle = value
|
||||||
|
refresh(line_index)
|
||||||
update_data_to_project()
|
update_data_to_project()
|
||||||
|
|
||||||
|
|
||||||
func _angle_changed(value: float, line_button):
|
func length_changed(value: float, line_button):
|
||||||
var line_index = line_button.get_index()
|
# check if the properties are changing the line or is the line changing properties
|
||||||
data.angles[line_index] = value
|
var length_slider = line_button.find_node("LengthSlider")
|
||||||
refresh(line_index)
|
if length_slider.value != value: # the line is changing the properties
|
||||||
update_data_to_project()
|
length_slider.value = value
|
||||||
|
else:
|
||||||
|
var line_index = line_button.get_index()
|
||||||
func _length_changed(value: float, line_button):
|
perspective_lines[line_index].length = value
|
||||||
var line_index = line_button.get_index()
|
refresh(line_index)
|
||||||
data.lengths[line_index] = value
|
|
||||||
refresh(line_index)
|
|
||||||
update_data_to_project()
|
update_data_to_project()
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,34 +149,39 @@ func _remove_line_pressed(line_button):
|
||||||
|
|
||||||
|
|
||||||
# Methods
|
# Methods
|
||||||
func add_line(loaded_line_data = null, is_tracker := false):
|
func generate_line_data(initial_data: Dictionary = {}) -> Dictionary:
|
||||||
var p_size = Global.current_project.size
|
# The default data
|
||||||
var default_line_data = {
|
var line_data = {"angle": 0, "length": 19999}
|
||||||
"start": Vector2(data.position_x, data.position_y),
|
# If any data needs to be changed by initial_data from project (or possibly by redo data)
|
||||||
"angle": 0,
|
if initial_data.has("angle"):
|
||||||
"length": 19999,
|
line_data.angle = initial_data["angle"]
|
||||||
"color": Color(data.color)
|
if initial_data.has("length"):
|
||||||
}
|
line_data.length = initial_data["length"]
|
||||||
if default_line_data.start.x > p_size.x / 2:
|
return line_data
|
||||||
# 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
|
func add_line(loaded_line_data := {}, is_tracker := false):
|
||||||
if loaded_line_data:
|
var p_size = Global.current_project.size # for use later in function
|
||||||
default_line_data = loaded_line_data
|
|
||||||
|
# Note: line_data will automatically get default values if loaded_line_data = {}
|
||||||
|
var line_data = generate_line_data(loaded_line_data)
|
||||||
|
|
||||||
|
# This code in if block is purely for beautification
|
||||||
|
if pos_x.value > p_size.x / 2 and !loaded_line_data:
|
||||||
|
# If new line is created ahed of half project distance then
|
||||||
|
# reverse it's angle
|
||||||
|
line_data.angle = 180
|
||||||
|
|
||||||
if is_tracker: # if we are creating tracker line then length adjustment is not required
|
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
|
if tracker_line != null: # Also if the tracker line already exists then cancel creation
|
||||||
return
|
return
|
||||||
else: # If we are not creating a perspective line then adjust it's length
|
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))
|
if !loaded_line_data:
|
||||||
default_line_data.length = suitable_length
|
line_data.length = p_size.x
|
||||||
|
|
||||||
# Create the visual line
|
# Create the visual line
|
||||||
var line = preload("res://src/UI/PerspectiveEditor/PerspectiveLine.tscn").instance()
|
var line = preload("res://src/UI/PerspectiveEditor/PerspectiveLine.tscn").instance()
|
||||||
line.initiate(default_line_data)
|
line.initiate(line_data, self)
|
||||||
|
|
||||||
# Set it's mode accordingly
|
# Set it's mode accordingly
|
||||||
if is_tracker: # Settings for Tracker mode
|
if is_tracker: # Settings for Tracker mode
|
||||||
|
@ -147,7 +195,7 @@ func add_line(loaded_line_data = null, is_tracker := false):
|
||||||
line_button.get_parent().move_child(line_button, index)
|
line_button.get_parent().move_child(line_button, index)
|
||||||
|
|
||||||
var line_name = str(
|
var line_name = str(
|
||||||
"Line", line_button.get_index() + 1, " (", int(default_line_data.angle), "°)"
|
"Line", line_button.get_index() + 1, " (", int(abs(line_data.angle)), "°)"
|
||||||
)
|
)
|
||||||
line_button.text = line_name
|
line_button.text = line_name
|
||||||
|
|
||||||
|
@ -155,14 +203,12 @@ func add_line(loaded_line_data = null, is_tracker := false):
|
||||||
var angle_slider = line_button.find_node("AngleSlider")
|
var angle_slider = line_button.find_node("AngleSlider")
|
||||||
var length_slider = line_button.find_node("LengthSlider")
|
var length_slider = line_button.find_node("LengthSlider")
|
||||||
|
|
||||||
angle_slider.value = default_line_data.angle
|
angle_slider.value = abs(line_data.angle)
|
||||||
length_slider.value = default_line_data.length
|
length_slider.value = 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])
|
line.line_button = line_button # In case we need to change properties from line
|
||||||
length_slider.connect("value_changed", self, "_length_changed", [line_button])
|
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])
|
remove_button.connect("pressed", self, "_remove_line_pressed", [line_button])
|
||||||
perspective_lines.append(line)
|
perspective_lines.append(line)
|
||||||
|
|
||||||
|
@ -170,54 +216,45 @@ func add_line(loaded_line_data = null, is_tracker := false):
|
||||||
func remove_line(line_index):
|
func remove_line(line_index):
|
||||||
var line_to_remove = perspective_lines[line_index]
|
var line_to_remove = perspective_lines[line_index]
|
||||||
perspective_lines.remove(line_index)
|
perspective_lines.remove(line_index)
|
||||||
data.angles.remove(line_index)
|
|
||||||
data.lengths.remove(line_index)
|
|
||||||
line_to_remove.queue_free()
|
line_to_remove.queue_free()
|
||||||
|
|
||||||
|
|
||||||
func update_data_to_project(removal := false):
|
func update_data_to_project(removal := false):
|
||||||
var project = Global.current_project
|
var project = Global.current_project
|
||||||
var idx = get_index()
|
var idx = get_index()
|
||||||
|
# If deletion is requested
|
||||||
if removal:
|
if removal:
|
||||||
project.vanishing_points.remove(idx)
|
project.vanishing_points.remove(idx)
|
||||||
return
|
return
|
||||||
|
# If project knows about this vanishing point then update it
|
||||||
|
var data = serialize()
|
||||||
if idx < project.vanishing_points.size():
|
if idx < project.vanishing_points.size():
|
||||||
project.vanishing_points[idx] = data
|
project.vanishing_points[idx] = data
|
||||||
|
# If project doesn't know about this vanishing point then NOW it knows
|
||||||
else:
|
else:
|
||||||
project.vanishing_points.append(data)
|
project.vanishing_points.append(data)
|
||||||
Global.current_project.has_changed = true
|
Global.current_project.has_changed = true
|
||||||
|
|
||||||
|
|
||||||
func refresh(index: int):
|
func refresh(index: int):
|
||||||
if index == -1: # means all lines should be refreshed
|
if index == -1: # means all lines should be refreshed (including the tracker line)
|
||||||
refresh_tracker()
|
refresh_tracker()
|
||||||
for i in data.angles.size():
|
for i in perspective_lines.size():
|
||||||
refresh_line(i)
|
refresh_line(i)
|
||||||
else:
|
else:
|
||||||
refresh_line(index)
|
refresh_line(index)
|
||||||
|
|
||||||
|
|
||||||
func refresh_line(index: int):
|
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_button = line_buttons_container.get_child(index)
|
||||||
var line_name = str("Line", line_button.get_index() + 1, " (", int(line_data.angle), "°)")
|
var line_data = perspective_lines[index].serialize()
|
||||||
|
var line_name = str("Line", line_button.get_index() + 1, " (", int(abs(line_data.angle)), "°)")
|
||||||
line_button.text = line_name
|
line_button.text = line_name
|
||||||
perspective_lines[index].refresh(line_data)
|
perspective_lines[index].refresh()
|
||||||
|
|
||||||
|
|
||||||
func refresh_tracker():
|
func refresh_tracker():
|
||||||
var line_data = {
|
tracker_line.refresh()
|
||||||
"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:
|
func _exit_tree() -> void:
|
||||||
|
|
Loading…
Add table
Reference in a new issue