From 4c45adc04eb715805d5979e3d4dbea37f6f6dbb4 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 6 Jun 2025 01:25:37 +0530 Subject: [PATCH 1/6] Start on segment --- .../messages/input_mapper/input_mappings.rs | 2 +- .../graph_modification_utils.rs | 6 ++- .../tool/common_functionality/shape_editor.rs | 10 +++-- .../messages/tool/tool_messages/pen_tool.rs | 41 +++++++++++++++++-- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index bd3c6285e3..20e537520c 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -255,7 +255,7 @@ pub fn input_mappings() -> Mapping { // PenToolMessage entry!(PointerMove; refresh_keys=[Control, Alt, Shift, KeyC], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control, colinear: KeyC, move_anchor_with_handles: Space }), entry!(KeyDownNoRepeat(Tab); action_dispatch=PenToolMessage::SwapHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PenToolMessage::DragStart { append_to_selected: Shift }), + entry!(KeyDown(MouseLeft); action_dispatch=PenToolMessage::DragStart { append_to_selected: Shift, start_on_segment: Alt }), entry!(KeyUp(MouseLeft); action_dispatch=PenToolMessage::DragStop), entry!(KeyDown(MouseRight); action_dispatch=PenToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=PenToolMessage::Abort), diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index ea9ab3bb60..b2fc699de4 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -57,8 +57,10 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde } // Move the `second_layer` below the `first_layer` for positioning purposes - let first_layer_parent = first_layer.parent(document.metadata()).unwrap(); - let first_layer_index = first_layer_parent.children(document.metadata()).position(|child| child == first_layer).unwrap(); + let Some(first_layer_parent) = first_layer.parent(document.metadata()) else { return }; + let Some(first_layer_index) = first_layer_parent.children(document.metadata()).position(|child| child == first_layer) else { + return; + }; responses.add(NodeGraphMessage::MoveLayerToStack { layer: second_layer, parent: first_layer_parent, diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 7228732682..9769a4974b 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -161,6 +161,10 @@ impl ClosestSegment { self.points } + pub fn closest_point(&self) -> DVec2 { + self.bezier.evaluate(TValue::Parametric(self.t)) + } + pub fn closest_point_to_viewport(&self) -> DVec2 { self.bezier_point_to_viewport } @@ -202,7 +206,7 @@ impl ClosestSegment { (first_handle, second_handle) } - pub fn adjusted_insert(&self, responses: &mut VecDeque) -> PointId { + pub fn adjusted_insert(&self, responses: &mut VecDeque) -> (PointId, [SegmentId; 2]) { let layer = self.layer; let [first, second] = self.bezier.split(TValue::Parametric(self.t)); @@ -247,11 +251,11 @@ impl ClosestSegment { responses.add(GraphOperationMessage::Vector { layer, modification_type }); } - midpoint + (midpoint, segment_ids) } pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque, extend_selection: bool) { - let id = self.adjusted_insert(responses); + let (id, _) = self.adjusted_insert(responses); shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection) } diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index f0ceb00b9c..4ea22b356a 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -61,6 +61,7 @@ pub enum PenToolMessage { Confirm, DragStart { append_to_selected: Key, + start_on_segment: Key, }, DragStop, PointerMove { @@ -361,6 +362,9 @@ struct PenToolData { current_layer: Option, prior_segment_endpoint: Option, prior_segment: Option, + + /// For vector meshes, storing all the previous segments the last anchor point was connected to + prior_segments: Option>, handle_type: TargetHandle, handle_start_offset: Option, handle_end_offset: Option, @@ -1129,7 +1133,9 @@ impl PenToolData { responses: &mut VecDeque, tool_options: &PenOptions, append: bool, + start_on_segment: bool, preferences: &PreferencesMessageHandler, + shape_editor: &mut ShapeState, ) { let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); @@ -1145,6 +1151,22 @@ impl PenToolData { self.current_layer = Some(layer); self.extend_existing_path(document, layer, point, position); return; + } else if preferences.vector_meshes && start_on_segment { + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { + let (point, segments) = closest_segment.adjusted_insert(responses); + let layer = closest_segment.layer(); + let position = closest_segment.closest_point(); + + // Setting any one of the new segments created as the previous segment + self.prior_segment_endpoint = Some(point); + self.prior_segment_layer = Some(layer); + + // This does not work as vector data is not updated yet + self.prior_segments = Some(segments.to_vec()); + + self.extend_existing_path(document, layer, point, position); + return; + } } if append { @@ -1186,6 +1208,7 @@ impl PenToolData { tool_options.fill.apply_fill(layer, responses); tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses); self.prior_segment = None; + self.prior_segments = None; responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }); // This causes the following message to be run only after the next graph evaluation runs and the transforms are updated @@ -1266,6 +1289,7 @@ impl PenToolData { self.prior_segment = None; self.prior_segment_endpoint = None; self.prior_segment_layer = None; + self.prior_segments = None; if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) { self.prior_segment_endpoint = Some(point); @@ -1537,6 +1561,12 @@ impl Fsm for PenToolFsmState { PenOverlayMode::FrontierHandles => { if let Some(latest_segment) = tool_data.prior_segment { path_overlays(document, DrawHandles::SelectedAnchors(vec![latest_segment]), shape_editor, &mut overlay_context); + } + // If a vector mesh then there can be more than one prior segments + else if preferences.vector_meshes { + if let Some(segments) = tool_data.prior_segments.clone() { + path_overlays(document, DrawHandles::SelectedAnchors(segments), shape_editor, &mut overlay_context); + } } else { path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context); }; @@ -1658,13 +1688,16 @@ impl Fsm for PenToolFsmState { ))); self } - (PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected }) => { + (PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected, start_on_segment }) => { responses.add(DocumentMessage::StartTransaction); tool_data.handle_mode = HandleMode::Free; // Get the closest point and the segment it is on + let append = input.keyboard.key(append_to_selected); + let start_on_segment = input.keyboard.key(start_on_segment); + tool_data.store_clicked_endpoint(document, &transform, input, preferences); - tool_data.create_initial_point(document, input, responses, tool_options, input.keyboard.key(append_to_selected), preferences); + tool_data.create_initial_point(document, input, responses, tool_options, append, start_on_segment, preferences, shape_editor); // Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle PenToolFsmState::DraggingHandle(tool_data.handle_mode) @@ -1678,7 +1711,7 @@ impl Fsm for PenToolFsmState { tool_data.recalculate_latest_points_position(document); state } - (PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => { + (PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected, start_on_segment }) => { let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); @@ -1717,7 +1750,7 @@ impl Fsm for PenToolFsmState { // Even if no buffer was started, the message still has to be run again in order to call bend_from_previous_point tool_data.buffering_merged_vector = true; - responses.add(PenToolMessage::DragStart { append_to_selected }); + responses.add(PenToolMessage::DragStart { append_to_selected, start_on_segment }); PenToolFsmState::PlacingAnchor } } From 273462336ead754959a5bd6f903bab07373ebdcf Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 7 Jun 2025 12:35:40 +0530 Subject: [PATCH 2/6] Path tool ending on segment --- .../messages/input_mapper/input_mappings.rs | 2 +- .../messages/tool/tool_messages/pen_tool.rs | 66 +++++++++++++++---- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 20e537520c..bd3c6285e3 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -255,7 +255,7 @@ pub fn input_mappings() -> Mapping { // PenToolMessage entry!(PointerMove; refresh_keys=[Control, Alt, Shift, KeyC], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control, colinear: KeyC, move_anchor_with_handles: Space }), entry!(KeyDownNoRepeat(Tab); action_dispatch=PenToolMessage::SwapHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PenToolMessage::DragStart { append_to_selected: Shift, start_on_segment: Alt }), + entry!(KeyDown(MouseLeft); action_dispatch=PenToolMessage::DragStart { append_to_selected: Shift }), entry!(KeyUp(MouseLeft); action_dispatch=PenToolMessage::DragStop), entry!(KeyDown(MouseRight); action_dispatch=PenToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=PenToolMessage::Abort), diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 4ea22b356a..9710c29e60 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -61,7 +61,6 @@ pub enum PenToolMessage { Confirm, DragStart { append_to_selected: Key, - start_on_segment: Key, }, DragStop, PointerMove { @@ -537,7 +536,15 @@ impl PenToolData { } /// If the user places the anchor on top of the previous anchor, it becomes sharp and the outgoing handle may be dragged. - fn bend_from_previous_point(&mut self, snap_data: SnapData, transform: DAffine2, layer: LayerNodeIdentifier, preferences: &PreferencesMessageHandler) { + fn bend_from_previous_point( + &mut self, + snap_data: SnapData, + transform: DAffine2, + layer: LayerNodeIdentifier, + preferences: &PreferencesMessageHandler, + shape_editor: &mut ShapeState, + responses: &mut VecDeque, + ) { self.g1_continuous = true; let document = snap_data.document; self.next_handle_start = self.next_point; @@ -571,6 +578,43 @@ impl PenToolData { } // Closing path + let closing_path_on_point = self.close_path_on_point(snap_data, &vector_data, document, preferences, id, &transform); + if !closing_path_on_point && preferences.vector_meshes { + // Attempt to find nearest segment and close path on segment by creating an anchor point on it + let tolerance = crate::consts::SNAP_POINT_TOLERANCE; + log::info!("reached here only"); + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, transform.transform_point2(self.next_point), tolerance) { + let (point, _) = closest_segment.adjusted_insert(responses); + + log::info!("reached here for sure"); + self.update_handle_type(TargetHandle::PreviewInHandle); + self.handle_end_offset = None; + self.path_closed = true; + self.next_handle_start = self.next_point; + + self.prior_segment_endpoint = Some(point); + self.prior_segment_layer = Some(closest_segment.layer()); + + // Should also update the SnapCache here + + self.handle_mode = HandleMode::Free; + if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) { + self.set_lock_angle(&vector_data, prior_endpoint, self.prior_segment); + self.switch_to_free_on_ctrl_release = true; + } + } + } + } + + fn close_path_on_point( + &mut self, + snap_data: SnapData, + vector_data: &VectorData, + document: &DocumentMessageHandler, + preferences: &PreferencesMessageHandler, + id: PointId, + transform: &DAffine2, + ) -> bool { for id in vector_data.extendable_points(preferences.vector_meshes).filter(|&point| point != id) { let Some(pos) = vector_data.point_domain.position_from_id(id) else { continue }; let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(self.next_point)); @@ -587,8 +631,10 @@ impl PenToolData { self.set_lock_angle(&vector_data, prior_endpoint, self.prior_segment); self.switch_to_free_on_ctrl_release = true; } + return true; } } + false } fn finish_placing_handle(&mut self, snap_data: SnapData, transform: DAffine2, preferences: &PreferencesMessageHandler, responses: &mut VecDeque) -> Option { @@ -1133,7 +1179,6 @@ impl PenToolData { responses: &mut VecDeque, tool_options: &PenOptions, append: bool, - start_on_segment: bool, preferences: &PreferencesMessageHandler, shape_editor: &mut ShapeState, ) { @@ -1145,13 +1190,13 @@ impl PenToolData { let selected_nodes = document.network_interface.selected_nodes(); self.handle_end = None; - let tolerance = crate::consts::SNAP_POINT_TOLERANCE; + let tolerance: f64 = crate::consts::SNAP_POINT_TOLERANCE; let extension_choice = should_extend(document, viewport, tolerance, selected_nodes.selected_layers(document.metadata()), preferences); if let Some((layer, point, position)) = extension_choice { self.current_layer = Some(layer); self.extend_existing_path(document, layer, point, position); return; - } else if preferences.vector_meshes && start_on_segment { + } else if preferences.vector_meshes { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let (point, segments) = closest_segment.adjusted_insert(responses); let layer = closest_segment.layer(); @@ -1688,16 +1733,15 @@ impl Fsm for PenToolFsmState { ))); self } - (PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected, start_on_segment }) => { + (PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected }) => { responses.add(DocumentMessage::StartTransaction); tool_data.handle_mode = HandleMode::Free; // Get the closest point and the segment it is on let append = input.keyboard.key(append_to_selected); - let start_on_segment = input.keyboard.key(start_on_segment); tool_data.store_clicked_endpoint(document, &transform, input, preferences); - tool_data.create_initial_point(document, input, responses, tool_options, append, start_on_segment, preferences, shape_editor); + tool_data.create_initial_point(document, input, responses, tool_options, append, preferences, shape_editor); // Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle PenToolFsmState::DraggingHandle(tool_data.handle_mode) @@ -1711,7 +1755,7 @@ impl Fsm for PenToolFsmState { tool_data.recalculate_latest_points_position(document); state } - (PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected, start_on_segment }) => { + (PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => { let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); @@ -1721,7 +1765,7 @@ impl Fsm for PenToolFsmState { if let Some(layer) = layer { tool_data.buffering_merged_vector = false; tool_data.handle_mode = HandleMode::ColinearLocked; - tool_data.bend_from_previous_point(SnapData::new(document, input), transform, layer, preferences); + tool_data.bend_from_previous_point(SnapData::new(document, input), transform, layer, preferences, shape_editor, responses); tool_data.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses); } tool_data.buffering_merged_vector = false; @@ -1750,7 +1794,7 @@ impl Fsm for PenToolFsmState { // Even if no buffer was started, the message still has to be run again in order to call bend_from_previous_point tool_data.buffering_merged_vector = true; - responses.add(PenToolMessage::DragStart { append_to_selected, start_on_segment }); + responses.add(PenToolMessage::DragStart { append_to_selected }); PenToolFsmState::PlacingAnchor } } From 0c22c23e2b2e89c66e2e815183304086ede25396 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 7 Jun 2025 17:27:50 +0530 Subject: [PATCH 3/6] Overlays for feature --- .../messages/tool/tool_messages/pen_tool.rs | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 9710c29e60..6eeb78b2bd 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1,5 +1,5 @@ use super::tool_prelude::*; -use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE}; +use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE}; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; @@ -582,11 +582,9 @@ impl PenToolData { if !closing_path_on_point && preferences.vector_meshes { // Attempt to find nearest segment and close path on segment by creating an anchor point on it let tolerance = crate::consts::SNAP_POINT_TOLERANCE; - log::info!("reached here only"); if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, transform.transform_point2(self.next_point), tolerance) { let (point, _) = closest_segment.adjusted_insert(responses); - log::info!("reached here for sure"); self.update_handle_type(TargetHandle::PreviewInHandle); self.handle_end_offset = None; self.path_closed = true; @@ -594,8 +592,10 @@ impl PenToolData { self.prior_segment_endpoint = Some(point); self.prior_segment_layer = Some(closest_segment.layer()); + self.prior_segments = None; + self.prior_segment = None; - // Should also update the SnapCache here + // Should also update the SnapCache here? self.handle_mode = HandleMode::Free; if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) { @@ -1205,8 +1205,6 @@ impl PenToolData { // Setting any one of the new segments created as the previous segment self.prior_segment_endpoint = Some(point); self.prior_segment_layer = Some(layer); - - // This does not work as vector data is not updated yet self.prior_segments = Some(segments.to_vec()); self.extend_existing_path(document, layer, point, position); @@ -1562,6 +1560,18 @@ impl Fsm for PenToolFsmState { path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context); } } + // Check if there is an anchor within threshold + // If not check if there is a closest segment within threshold, if yes then draw overlay + let tolerance = crate::consts::SNAP_POINT_TOLERANCE; + let close_to_point = closest_point(document, input.mouse.position, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); + if preferences.vector_meshes && !close_to_point { + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, tolerance) { + let pos = closest_segment.closest_point_to_viewport(); + let perp = closest_segment.calculate_perp(&document); + overlay_context.manipulator_anchor(pos, true, None); + overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + } + } tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); self } @@ -1607,9 +1617,9 @@ impl Fsm for PenToolFsmState { if let Some(latest_segment) = tool_data.prior_segment { path_overlays(document, DrawHandles::SelectedAnchors(vec![latest_segment]), shape_editor, &mut overlay_context); } - // If a vector mesh then there can be more than one prior segments - else if preferences.vector_meshes { - if let Some(segments) = tool_data.prior_segments.clone() { + // // If a vector mesh then there can be more than one prior segments + else if let Some(segments) = tool_data.prior_segments.clone() { + if preferences.vector_meshes { path_overlays(document, DrawHandles::SelectedAnchors(segments), shape_editor, &mut overlay_context); } } else { @@ -1673,6 +1683,16 @@ impl Fsm for PenToolFsmState { overlay_context.manipulator_anchor(next_anchor, false, None); } + if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes { + let tolerance = crate::consts::SNAP_POINT_TOLERANCE; + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, tolerance) { + let pos = closest_segment.closest_point_to_viewport(); + let perp = closest_segment.calculate_perp(&document); + overlay_context.manipulator_anchor(pos, true, None); + overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + } + } + // Display a filled overlay of the shape if the new point closes the path if let Some(latest_point) = tool_data.latest_point() { let handle_start = latest_point.handle_start; From 92b6a483a2f4774d461f1c10bb88a904c979e07a Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Mon, 9 Jun 2025 15:29:00 +0530 Subject: [PATCH 4/6] Fixed merge build --- editor/src/messages/tool/common_functionality/shape_editor.rs | 2 +- editor/src/messages/tool/tool_messages/pen_tool.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 143ade4a8b..b0fe92c130 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -163,7 +163,7 @@ impl ClosestSegment { self.points } - pub fn closest_point(&self) -> DVec2 { + pub fn closest_point_document(&self) -> DVec2 { self.bezier.evaluate(TValue::Parametric(self.t)) } diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 6eeb78b2bd..a83832eff2 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1200,7 +1200,7 @@ impl PenToolData { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let (point, segments) = closest_segment.adjusted_insert(responses); let layer = closest_segment.layer(); - let position = closest_segment.closest_point(); + let position = closest_segment.closest_point_document(); // Setting any one of the new segments created as the previous segment self.prior_segment_endpoint = Some(point); From 9098528cab170316ab57459525d270be0076f5bc Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Tue, 10 Jun 2025 16:54:22 +0530 Subject: [PATCH 5/6] Fix overlays --- .../messages/tool/tool_messages/pen_tool.rs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index a83832eff2..54299b244d 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1563,9 +1563,13 @@ impl Fsm for PenToolFsmState { // Check if there is an anchor within threshold // If not check if there is a closest segment within threshold, if yes then draw overlay let tolerance = crate::consts::SNAP_POINT_TOLERANCE; - let close_to_point = closest_point(document, input.mouse.position, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); + let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); + let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); + let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); + + let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); if preferences.vector_meshes && !close_to_point { - if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, tolerance) { + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let pos = closest_segment.closest_point_to_viewport(); let perp = closest_segment.calculate_perp(&document); overlay_context.manipulator_anchor(pos, true, None); @@ -1685,11 +1689,17 @@ impl Fsm for PenToolFsmState { if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes { let tolerance = crate::consts::SNAP_POINT_TOLERANCE; - if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, tolerance) { - let pos = closest_segment.closest_point_to_viewport(); - let perp = closest_segment.calculate_perp(&document); - overlay_context.manipulator_anchor(pos, true, None); - overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); + let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); + let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); + let close_to_point = closest_point(&document, viewport, tolerance, document.metadata().all_layers(), |_| false, &preferences).is_some(); + if !close_to_point { + if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { + let pos = closest_segment.closest_point_to_viewport(); + let perp = closest_segment.calculate_perp(&document); + overlay_context.manipulator_anchor(pos, true, None); + overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + } } } From 0a5bec1b50342b118ad42b6e93ed0372f04de970 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 14 Jun 2025 15:31:28 -0700 Subject: [PATCH 6/6] Code review --- .../src/messages/tool/tool_messages/pen_tool.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 54299b244d..ccc1243924 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -625,10 +625,10 @@ impl PenToolData { self.handle_end_offset = None; self.path_closed = true; self.next_handle_start = self.next_point; - self.store_clicked_endpoint(document, &transform, snap_data.input, preferences); + self.store_clicked_endpoint(document, transform, snap_data.input, preferences); self.handle_mode = HandleMode::Free; if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) { - self.set_lock_angle(&vector_data, prior_endpoint, self.prior_segment); + self.set_lock_angle(vector_data, prior_endpoint, self.prior_segment); self.switch_to_free_on_ctrl_release = true; } return true; @@ -1172,6 +1172,7 @@ impl PenToolData { transform.inverse().transform_point2(document_pos) } + #[allow(clippy::too_many_arguments)] fn create_initial_point( &mut self, document: &DocumentMessageHandler, @@ -1190,7 +1191,7 @@ impl PenToolData { let selected_nodes = document.network_interface.selected_nodes(); self.handle_end = None; - let tolerance: f64 = crate::consts::SNAP_POINT_TOLERANCE; + let tolerance = crate::consts::SNAP_POINT_TOLERANCE; let extension_choice = should_extend(document, viewport, tolerance, selected_nodes.selected_layers(document.metadata()), preferences); if let Some((layer, point, position)) = extension_choice { self.current_layer = Some(layer); @@ -1571,7 +1572,7 @@ impl Fsm for PenToolFsmState { if preferences.vector_meshes && !close_to_point { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let pos = closest_segment.closest_point_to_viewport(); - let perp = closest_segment.calculate_perp(&document); + let perp = closest_segment.calculate_perp(document); overlay_context.manipulator_anchor(pos, true, None); overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); } @@ -1613,6 +1614,7 @@ impl Fsm for PenToolFsmState { // Draw the line between the currently-being-placed anchor and its currently-being-dragged-out outgoing handle (opposite the one currently being dragged out) overlay_context.line(next_anchor, next_handle_start, None, None); } + match tool_options.pen_overlay_mode { PenOverlayMode::AllHandles => { path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context); @@ -1621,7 +1623,7 @@ impl Fsm for PenToolFsmState { if let Some(latest_segment) = tool_data.prior_segment { path_overlays(document, DrawHandles::SelectedAnchors(vec![latest_segment]), shape_editor, &mut overlay_context); } - // // If a vector mesh then there can be more than one prior segments + // If a vector mesh then there can be more than one prior segments else if let Some(segments) = tool_data.prior_segments.clone() { if preferences.vector_meshes { path_overlays(document, DrawHandles::SelectedAnchors(segments), shape_editor, &mut overlay_context); @@ -1692,11 +1694,11 @@ impl Fsm for PenToolFsmState { let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); - let close_to_point = closest_point(&document, viewport, tolerance, document.metadata().all_layers(), |_| false, &preferences).is_some(); + let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); if !close_to_point { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let pos = closest_segment.closest_point_to_viewport(); - let perp = closest_segment.calculate_perp(&document); + let perp = closest_segment.calculate_perp(document); overlay_context.manipulator_anchor(pos, true, None); overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); }