diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index 238248e7c..12f37953c 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -143,6 +143,7 @@ var show_mouse_guides := false var snapping_distance := 10.0 var snap_to_rectangular_grid := false var snap_to_guides := false +var snap_to_perspective_guides := false # Onion skinning options var onion_skinning := false diff --git a/src/Tools/BaseTool.gd b/src/Tools/BaseTool.gd index 1b8384f16..663d43d20 100644 --- a/src/Tools/BaseTool.gd +++ b/src/Tools/BaseTool.gd @@ -132,24 +132,32 @@ func snap_position(position: Vector2) -> Vector2: if closest_point_grid != Vector2.INF: position = closest_point_grid.floor() + var snap_to := Vector2.INF if Global.snap_to_guides: - var snap_to := Vector2.INF for guide in Global.current_project.guides: if guide is SymmetryGuide: continue - var closest_point := _get_closest_point_to_segment( - position, snap_distance, guide.points[0], guide.points[1] - ) - if closest_point == Vector2.INF: # Is not close to a guide + var s1: Vector2 = guide.points[0] + var s2: Vector2 = guide.points[1] + var snap := _snap_to_guide(snap_to, position, snap_distance, s1, s2) + if snap == Vector2.INF: continue - # Snap to the closest guide - if ( - snap_to == Vector2.INF - or (snap_to - position).length() > (closest_point - position).length() - ): - snap_to = closest_point - if snap_to != Vector2.INF: - position = snap_to.floor() + snap_to = snap + + if Global.snap_to_perspective_guides: + for point in Global.current_project.vanishing_points: + for i in point.angles.size(): + var angle: float = -deg2rad(point.angles[i]) + var length: float = point.lengths[i] + var s1 := Vector2(point.position_x, point.position_y) + var s2 := s1 + Vector2(length * cos(angle), length * sin(angle)) + 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: + position = snap_to.floor() + return position @@ -198,6 +206,22 @@ func _get_closest_point_to_segment( return closest_point +func _snap_to_guide( + snap_to: Vector2, position: Vector2, distance: Vector2, s1: Vector2, s2: Vector2 +) -> Vector2: + var closest_point := _get_closest_point_to_segment(position, distance, s1, s2) + if closest_point == Vector2.INF: # Is not close to a guide + return Vector2.INF + # Snap to the closest guide + if ( + snap_to == Vector2.INF + or (snap_to - position).length() > (closest_point - position).length() + ): + snap_to = closest_point + + return snap_to + + func _get_draw_rect() -> Rect2: if Global.current_project.has_selection: return Global.current_project.selection_map.get_used_rect() diff --git a/src/UI/TopMenuContainer/TopMenuContainer.gd b/src/UI/TopMenuContainer/TopMenuContainer.gd index e90aa7e22..cf91005fe 100644 --- a/src/UI/TopMenuContainer/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer/TopMenuContainer.gd @@ -188,6 +188,7 @@ func _setup_snap_to_submenu(item: String) -> void: snap_to_submenu.set_name("snap_to_submenu") snap_to_submenu.add_check_item("Snap to Rectangular Grid") snap_to_submenu.add_check_item("Snap to Guides") + snap_to_submenu.add_check_item("Snap to Perspective Guides") snap_to_submenu.connect("id_pressed", self, "_snap_to_submenu_id_pressed") view_menu.add_child(snap_to_submenu) view_menu.add_submenu_item(item, snap_to_submenu.get_name()) @@ -491,6 +492,9 @@ func _snap_to_submenu_id_pressed(id: int) -> void: elif id == 1: Global.snap_to_guides = !Global.snap_to_guides snap_to_submenu.set_item_checked(id, Global.snap_to_guides) + elif id == 2: + Global.snap_to_perspective_guides = !Global.snap_to_perspective_guides + snap_to_submenu.set_item_checked(id, Global.snap_to_perspective_guides) func window_menu_id_pressed(id: int) -> void: