From 7590fff1ec8c9ef48f9d19eab8c75f65c7842c9c Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 28 Mar 2025 11:17:28 +0530 Subject: [PATCH 01/14] segment overlay change --- .../messages/input_mapper/input_mappings.rs | 2 +- .../tool/common_functionality/shape_editor.rs | 31 ++++++++ .../messages/tool/tool_messages/path_tool.rs | 79 +++++++++++++++---- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index cc83a32ee1..9417ed5861 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -212,7 +212,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDownNoRepeat(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control }), + entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { delete_segment: Control, extend_selection: Shift, lasso_select: Control }), entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick), entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape), entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index f434cdc079..55573e8aca 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -104,6 +104,14 @@ impl ClosestSegment { self.layer } + pub fn segment(&self) -> SegmentId { + self.segment + } + + pub fn points(&self) -> [PointId; 2] { + self.points + } + pub fn closest_point_to_viewport(&self) -> DVec2 { self.bezier_point_to_viewport } @@ -858,6 +866,29 @@ impl ShapeState { .collect::>() } + pub fn dissolve_segment(&self, responses: &mut VecDeque, layer: LayerNodeIdentifier, vector_data: &VectorData, segment: SegmentId, points: [PointId; 2]) { + //take a note that which point was terminal point + let is_point1_terminal = vector_data.connected_count(points[0]) == 1; + let is_point2_terminal = vector_data.connected_count(points[1]) == 1; + + //Delete the segment and terminal points + let modification_type = VectorModificationType::RemoveSegment { id: segment }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + for &handles in vector_data.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) { + let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + + if is_point1_terminal { + let modification_type = VectorModificationType::RemovePoint { id: points[0] }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + if is_point2_terminal { + let modification_type = VectorModificationType::RemovePoint { id: points[1] }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + } + fn dissolve_anchor(anchor: PointId, responses: &mut VecDeque, layer: LayerNodeIdentifier, vector_data: &VectorData) -> Option<[(HandleId, PointId); 2]> { // Delete point let modification_type = VectorModificationType::RemovePoint { id: anchor }; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 4aac4f15c3..c6c7413824 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -62,7 +62,7 @@ pub enum PathToolMessage { ManipulatorMakeHandlesFree, ManipulatorMakeHandlesColinear, MouseDown { - direct_insert_without_sliding: Key, + delete_segment: Key, extend_selection: Key, lasso_select: Key, }, @@ -271,6 +271,7 @@ impl<'a> MessageHandler> for PathToo BreakPath, DeleteAndBreakPath, ClosePath, + PointerMove, ), PathToolFsmState::Dragging(_) => actions!(PathToolMessageDiscriminant; Escape, @@ -487,7 +488,7 @@ impl PathToolData { input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, extend_selection: bool, - direct_insert_without_sliding: bool, + delete_segment: bool, lasso_select: bool, ) -> PathToolFsmState { self.double_click_handled = false; @@ -524,11 +525,40 @@ impl PathToolData { // We didn't find a point nearby, so now we'll try to add a point into the closest path segment else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { responses.add(DocumentMessage::StartTransaction); - if direct_insert_without_sliding { + + if delete_segment { + //delete the segment + // self.start_insertion(responses, closed_segment); + // self.segment = Some(closed_segment); + let segment = closed_segment.segment(); + let layer = closed_segment.layer(); + let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { + return PathToolFsmState::Ready; + }; + let points: [PointId; 2] = closed_segment.points(); + shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); + responses.add(DocumentMessage::EndTransaction); + + return PathToolFsmState::Ready; + } else { + //TODO: Add overlay before the point being added + + //need to calculate the tangential direction to the segment at that point + // if let (Some(handle1), Some(handle2)) = closed_segment.handle_positions(document.metadata()) { + // let tangential_vector = (handle1 - handle2).normalize(); + // let perp = tangential_vector.perp(); + + // } + // else { + // //if there are no handles then it means that segment is a line segment + // // let points = closed_segment.points(); + // // let tangential_vector = (points[0]) + // } + + // let perp = + self.start_insertion(responses, closed_segment); self.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) - } else { - self.start_insertion(responses, closed_segment) } } // We didn't find a segment path, so consider selecting the nearest shape instead @@ -984,12 +1014,20 @@ impl Fsm for PathToolFsmState { let state = tool_data.update_insertion(shape_editor, document, responses, input); if let Some(closest_segment) = &tool_data.segment { - overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + // overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + // if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { + // overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); + // overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None); + // overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); + // overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); + // } + + //instead of this we want to make a perpendicular line if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); - overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None); - overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); - overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); + let tangent = (handle1 - handle2).normalize(); + let perp = tangent.perp(); + let point = closest_segment.closest_point_to_viewport(); + overlay_context.line(point - perp * 10., point + perp * 10., Some(COLOR_OVERLAY_BLUE), None); } } @@ -1021,19 +1059,19 @@ impl Fsm for PathToolFsmState { ( _, PathToolMessage::MouseDown { - direct_insert_without_sliding, + delete_segment, extend_selection, lasso_select, }, ) => { let extend_selection = input.keyboard.get(extend_selection as usize); let lasso_select = input.keyboard.get(lasso_select as usize); - let direct_insert_without_sliding = input.keyboard.get(direct_insert_without_sliding as usize); + let delete_segment = input.keyboard.get(delete_segment as usize); tool_data.selection_mode = None; tool_data.lasso_polygon.clear(); - tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, direct_insert_without_sliding, lasso_select) + tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, delete_segment, lasso_select) } ( PathToolFsmState::Drawing { selection_shape }, @@ -1164,6 +1202,19 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Dragging(tool_data.dragging_state) } + (PathToolFsmState::Ready, PathToolMessage::PointerMove { .. }) => { + //check for nearby point then should not go in insert point mode + log::info!("reached here"); + if let Some(_selected_points) = shape_editor.change_point_selection(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD, false) { + PathToolFsmState::Ready + } + //check for a segment nearby, if present then enter into insert point mode else go for ready + else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { + tool_data.start_insertion(responses, closed_segment) + } else { + PathToolFsmState::Ready + } + } (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning if let Some(offset) = tool_data.auto_panning.shift_viewport(input, responses) { @@ -1377,7 +1428,7 @@ impl Fsm for PathToolFsmState { responses.add(OverlaysMessage::Draw); PathToolFsmState::Ready } - (_, PathToolMessage::PointerMove { .. }) => self, + // (_, PathToolMessage::PointerMove { .. }) => self, (_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => { shape_editor.move_selected_points( tool_data.opposing_handle_lengths.take(), From 722079fd1803908bff4f04bbe8200655599132d5 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Mon, 31 Mar 2025 15:27:11 +0530 Subject: [PATCH 02/14] Segment split and delete --- .../messages/tool/tool_messages/path_tool.rs | 173 ++++++++++-------- 1 file changed, 101 insertions(+), 72 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c6c7413824..46cf457edc 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -366,6 +366,7 @@ struct PathToolData { segment: Option, snap_cache: SnapCache, double_click_handled: bool, + delete_segment_pressed: bool, auto_panning: AutoPanning, saved_points_before_anchor_select_toggle: Vec, select_anchor_toggled: bool, @@ -488,7 +489,6 @@ impl PathToolData { input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, extend_selection: bool, - delete_segment: bool, lasso_select: bool, ) -> PathToolFsmState { self.double_click_handled = false; @@ -523,44 +523,28 @@ impl PathToolData { PathToolFsmState::Dragging(self.dragging_state) } // We didn't find a point nearby, so now we'll try to add a point into the closest path segment - else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { - responses.add(DocumentMessage::StartTransaction); - - if delete_segment { - //delete the segment - // self.start_insertion(responses, closed_segment); - // self.segment = Some(closed_segment); - let segment = closed_segment.segment(); - let layer = closed_segment.layer(); - let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { - return PathToolFsmState::Ready; - }; - let points: [PointId; 2] = closed_segment.points(); - shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); - responses.add(DocumentMessage::EndTransaction); - - return PathToolFsmState::Ready; - } else { - //TODO: Add overlay before the point being added - - //need to calculate the tangential direction to the segment at that point - // if let (Some(handle1), Some(handle2)) = closed_segment.handle_positions(document.metadata()) { - // let tangential_vector = (handle1 - handle2).normalize(); - // let perp = tangential_vector.perp(); - - // } - // else { - // //if there are no handles then it means that segment is a line segment - // // let points = closed_segment.points(); - // // let tangential_vector = (points[0]) - // } - - // let perp = - - self.start_insertion(responses, closed_segment); - self.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) - } - } + // else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { + // responses.add(DocumentMessage::StartTransaction); + + // if delete_segment { + // //delete the segment + // // self.start_insertion(responses, closed_segment); + // // self.segment = Some(closed_segment); + // let segment = closed_segment.segment(); + // let layer = closed_segment.layer(); + // let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { + // return PathToolFsmState::Ready; + // }; + // let points: [PointId; 2] = closed_segment.points(); + // shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); + // responses.add(DocumentMessage::EndTransaction); + + // return PathToolFsmState::Ready; + // } else { + // self.start_insertion(responses, closed_segment); + // self.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) + // } + // } // We didn't find a segment path, so consider selecting the nearest shape instead else if let Some(layer) = document.click(input) { shape_editor.deselect_all_points(); @@ -1014,19 +998,34 @@ impl Fsm for PathToolFsmState { let state = tool_data.update_insertion(shape_editor, document, responses, input); if let Some(closest_segment) = &tool_data.segment { - // overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); - // if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - // overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); - // overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None); - // overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); - // overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); - // } - - //instead of this we want to make a perpendicular line - if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - let tangent = (handle1 - handle2).normalize(); - let perp = tangent.perp(); - let point = closest_segment.closest_point_to_viewport(); + // Making a perpendicular line when in InsertPoint mode + let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { + (handle1 - handle2).normalize() + } else { + let layer = closest_segment.layer(); + let points = closest_segment.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + if let (Some(pos1), Some(pos2)) = ( + ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), + ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), + ) { + (pos1 - pos2).normalize() + } else { + DVec2::ZERO + } + } else { + DVec2::ZERO + } + }; + let perp = tangent.perp(); + let point = closest_segment.closest_point_to_viewport(); + if tool_data.delete_segment_pressed { + let degrees: f64 = 45.0; + let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent); + let tilted_perp = tilted_line.perp(); + overlay_context.line(point - tilted_line * 10., point + tilted_line * 10., Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(point - tilted_perp * 10., point + tilted_perp * 10., Some(COLOR_OVERLAY_BLUE), None); + } else { overlay_context.line(point - perp * 10., point + perp * 10., Some(COLOR_OVERLAY_BLUE), None); } } @@ -1042,36 +1041,59 @@ impl Fsm for PathToolFsmState { } // `Self::InsertPoint` case: - (Self::InsertPoint, PathToolMessage::MouseDown { extend_selection, .. } | PathToolMessage::Enter { extend_selection, .. }) => { + (Self::InsertPoint, PathToolMessage::MouseDown { extend_selection, delete_segment, .. }) => { + //| PathToolMessage::Enter { extend_selection, .. } consider adding support for ctrl key in here tool_data.double_click_handled = true; let extend_selection = input.keyboard.get(extend_selection as usize); - tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) + let delete_segment = input.keyboard.get(delete_segment as usize); + + if delete_segment { + if let Some(closest_segment) = &tool_data.segment { + let segment = closest_segment.segment(); + let layer = closest_segment.layer(); + let points = closest_segment.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); + responses.add(DocumentMessage::EndTransaction); + } + } + return PathToolFsmState::Ready; + } else { + tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) + } } - (Self::InsertPoint, PathToolMessage::PointerMove { .. }) => { + (Self::InsertPoint, PathToolMessage::PointerMove { lock_angle, .. }) => { + let lock_angle_state = input.keyboard.get(lock_angle as usize); + if lock_angle_state { + tool_data.delete_segment_pressed = true; + } else { + tool_data.delete_segment_pressed = false; + } + responses.add(OverlaysMessage::Draw); // `tool_data.update_insertion` would be called on `OverlaysMessage::Draw` // we anyway should to call it on `::Draw` because we can change scale by ctrl+scroll without `::PointerMove` + + // If there is an anchor point very close to the current point then get out the InsertPoint mode + if shape_editor + .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) + .is_some() + { + return PathToolFsmState::Ready; + } self } (Self::InsertPoint, PathToolMessage::Escape | PathToolMessage::Delete | PathToolMessage::RightClick) => tool_data.end_insertion(shape_editor, responses, InsertEndKind::Abort), (Self::InsertPoint, PathToolMessage::GRS { key: _ }) => PathToolFsmState::InsertPoint, // Mouse down - ( - _, - PathToolMessage::MouseDown { - delete_segment, - extend_selection, - lasso_select, - }, - ) => { + (_, PathToolMessage::MouseDown { extend_selection, lasso_select, .. }) => { let extend_selection = input.keyboard.get(extend_selection as usize); let lasso_select = input.keyboard.get(lasso_select as usize); - let delete_segment = input.keyboard.get(delete_segment as usize); tool_data.selection_mode = None; tool_data.lasso_polygon.clear(); - tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, delete_segment, lasso_select) + tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, lasso_select) } ( PathToolFsmState::Drawing { selection_shape }, @@ -1202,17 +1224,24 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Dragging(tool_data.dragging_state) } - (PathToolFsmState::Ready, PathToolMessage::PointerMove { .. }) => { - //check for nearby point then should not go in insert point mode - log::info!("reached here"); - if let Some(_selected_points) = shape_editor.change_point_selection(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD, false) { - PathToolFsmState::Ready + (PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => { + // Check for a point in Selection threshold if it is there then don't change mode + if shape_editor + .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) + .is_some() + { + self } - //check for a segment nearby, if present then enter into insert point mode else go for ready + // Check for a segment nearby, if present then enter into insert point mode else go for ready else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { + let lock_angle_state = input.keyboard.get(lock_angle as usize); + if lock_angle_state { + tool_data.delete_segment_pressed = true; + } + responses.add(DocumentMessage::StartTransaction); tool_data.start_insertion(responses, closed_segment) } else { - PathToolFsmState::Ready + self } } (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { From b530d0bc3e24aef81d8b9f53eee3796808ddeb81 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Mon, 31 Mar 2025 15:35:37 +0530 Subject: [PATCH 03/14] Cleanup --- .../tool/common_functionality/shape_editor.rs | 4 +-- .../messages/tool/tool_messages/path_tool.rs | 28 ++----------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 55573e8aca..7dc1ecc9e6 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -867,11 +867,11 @@ impl ShapeState { } pub fn dissolve_segment(&self, responses: &mut VecDeque, layer: LayerNodeIdentifier, vector_data: &VectorData, segment: SegmentId, points: [PointId; 2]) { - //take a note that which point was terminal point + // Checking which point is terminal point let is_point1_terminal = vector_data.connected_count(points[0]) == 1; let is_point2_terminal = vector_data.connected_count(points[1]) == 1; - //Delete the segment and terminal points + // Delete the segment and terminal points let modification_type = VectorModificationType::RemoveSegment { id: segment }; responses.add(GraphOperationMessage::Vector { layer, modification_type }); for &handles in vector_data.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) { diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 46cf457edc..2d42356180 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -522,30 +522,7 @@ impl PathToolData { } PathToolFsmState::Dragging(self.dragging_state) } - // We didn't find a point nearby, so now we'll try to add a point into the closest path segment - // else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { - // responses.add(DocumentMessage::StartTransaction); - - // if delete_segment { - // //delete the segment - // // self.start_insertion(responses, closed_segment); - // // self.segment = Some(closed_segment); - // let segment = closed_segment.segment(); - // let layer = closed_segment.layer(); - // let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { - // return PathToolFsmState::Ready; - // }; - // let points: [PointId; 2] = closed_segment.points(); - // shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); - // responses.add(DocumentMessage::EndTransaction); - - // return PathToolFsmState::Ready; - // } else { - // self.start_insertion(responses, closed_segment); - // self.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) - // } - // } - // We didn't find a segment path, so consider selecting the nearest shape instead + // We didn't find a point, so consider selecting the nearest shape instead else if let Some(layer) = document.click(input) { shape_editor.deselect_all_points(); if extend_selection { @@ -998,7 +975,7 @@ impl Fsm for PathToolFsmState { let state = tool_data.update_insertion(shape_editor, document, responses, input); if let Some(closest_segment) = &tool_data.segment { - // Making a perpendicular line when in InsertPoint mode + // Perpendicular line when inserting a point, and a cross when deleting a segment let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { (handle1 - handle2).normalize() } else { @@ -1457,7 +1434,6 @@ impl Fsm for PathToolFsmState { responses.add(OverlaysMessage::Draw); PathToolFsmState::Ready } - // (_, PathToolMessage::PointerMove { .. }) => self, (_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => { shape_editor.move_selected_points( tool_data.opposing_handle_lengths.take(), From 0ca26763d3fe6ef1c31ca07353d373c92d524c0f Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Mon, 31 Mar 2025 16:00:17 +0530 Subject: [PATCH 04/14] graceful handling of edge cases --- editor/src/messages/tool/tool_messages/path_tool.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 2d42356180..83192400e0 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -459,6 +459,7 @@ impl PathToolData { fn end_insertion(&mut self, shape_editor: &mut ShapeState, responses: &mut VecDeque, kind: InsertEndKind) -> PathToolFsmState { let mut commit_transaction = false; + self.delete_segment_pressed = false; match self.segment.as_mut() { None => { warn!("Segment was `None` before `end_insertion`") @@ -977,7 +978,7 @@ impl Fsm for PathToolFsmState { if let Some(closest_segment) = &tool_data.segment { // Perpendicular line when inserting a point, and a cross when deleting a segment let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - (handle1 - handle2).normalize() + (handle1 - handle2).try_normalize() } else { let layer = closest_segment.layer(); let points = closest_segment.points(); @@ -986,14 +987,15 @@ impl Fsm for PathToolFsmState { ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), ) { - (pos1 - pos2).normalize() + (pos1 - pos2).try_normalize() } else { - DVec2::ZERO + None } } else { - DVec2::ZERO + None } - }; + } + .unwrap_or(DVec2::ZERO); let perp = tangent.perp(); let point = closest_segment.closest_point_to_viewport(); if tool_data.delete_segment_pressed { From ab841905e29b32db5968c7198818555c4413fa59 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Mon, 14 Apr 2025 01:20:44 +0530 Subject: [PATCH 05/14] Moved constants to conts.rs and tuned the threshold --- editor/src/consts.rs | 2 ++ editor/src/messages/tool/tool_messages/path_tool.rs | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 042c2de2dc..e7d86ac3d7 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -101,6 +101,8 @@ pub const SELECTION_THRESHOLD: f64 = 10.; pub const HIDE_HANDLE_DISTANCE: f64 = 3.; pub const INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE: f64 = 50.; pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.; +pub const SEGMENT_INSERTION_TOLERANCE: f64 = 7.5; +pub const SEGMENT_OVERLAY_SIZE: f64 = 10.; // PEN TOOL pub const CREATE_CURVE_THRESHOLD: f64 = 5.; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 265fff1680..cd5169f2e5 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1,7 +1,8 @@ use super::select_tool::extend_lasso; use super::tool_prelude::*; use crate::consts::{ - COLOR_OVERLAY_BLUE, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, + COLOR_OVERLAY_BLUE, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SEGMENT_INSERTION_TOLERANCE, + SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, }; use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; @@ -1002,10 +1003,10 @@ impl Fsm for PathToolFsmState { let degrees: f64 = 45.0; let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent); let tilted_perp = tilted_line.perp(); - overlay_context.line(point - tilted_line * 10., point + tilted_line * 10., Some(COLOR_OVERLAY_BLUE), None); - overlay_context.line(point - tilted_perp * 10., point + tilted_perp * 10., Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(point - tilted_line * SEGMENT_OVERLAY_SIZE, point + tilted_line * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(point - tilted_perp * SEGMENT_OVERLAY_SIZE, point + tilted_perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); } else { - overlay_context.line(point - perp * 10., point + perp * 10., Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(point - perp * SEGMENT_OVERLAY_SIZE, point + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); } } @@ -1212,7 +1213,7 @@ impl Fsm for PathToolFsmState { self } // Check for a segment nearby, if present then enter into insert point mode else go for ready - else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) { + else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { let lock_angle_state = input.keyboard.get(lock_angle as usize); if lock_angle_state { tool_data.delete_segment_pressed = true; From 177a374b392c2c27942693874c627294549a6775 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 18 Apr 2025 19:16:21 +0530 Subject: [PATCH 06/14] Remove going into another state --- .../messages/tool/tool_messages/path_tool.rs | 129 ++++++++++++++++-- 1 file changed, 114 insertions(+), 15 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 3f572ce742..697e21a9b9 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -564,6 +564,28 @@ impl PathToolData { } PathToolFsmState::Dragging(self.dragging_state) } + //We didn't found a point so we will see if there is a segment and then directly insert the point + else if let Some(closed_segment) = &mut self.segment { + responses.add(DocumentMessage::StartTransaction); + // self.segment = Some(closed_segment.clone()); //do we need this + //here replace start insertion wala logic + + if self.delete_segment_pressed { + let segment = closed_segment.segment(); + let layer = closed_segment.layer(); + let points = closed_segment.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); + responses.add(DocumentMessage::EndTransaction); + } + } else { + closed_segment.adjusted_insert_and_select(shape_editor, responses, extend_selection); + responses.add(DocumentMessage::EndTransaction); + } + self.segment = None; + + PathToolFsmState::Ready + } // We didn't find a point, so consider selecting the nearest shape instead else if let Some(layer) = document.click(input) { shape_editor.deselect_all_points(); @@ -1021,6 +1043,44 @@ impl Fsm for PathToolFsmState { } match self { + Self::Ready => { + //closest segment should be set when pointermoved and tabhi update segment bhi hona chahiye + + //check for a close segment if it is there + if let Some(closest_segment) = &tool_data.segment { + //insert a perpendicular line + let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { + (handle1 - handle2).try_normalize() + } else { + let layer = closest_segment.layer(); + let points = closest_segment.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + if let (Some(pos1), Some(pos2)) = ( + ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), + ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), + ) { + (pos1 - pos2).try_normalize() + } else { + None + } + } else { + None + } + } + .unwrap_or(DVec2::ZERO); + let perp = tangent.perp(); + let point = closest_segment.closest_point_to_viewport(); + if tool_data.delete_segment_pressed { + let degrees: f64 = 45.0; + let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent); + let tilted_perp = tilted_line.perp(); + overlay_context.line(point - tilted_line * SEGMENT_OVERLAY_SIZE, point + tilted_line * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(point - tilted_perp * SEGMENT_OVERLAY_SIZE, point + tilted_perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + } else { + overlay_context.line(point - perp * SEGMENT_OVERLAY_SIZE, point + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); + } + } + } Self::Drawing { selection_shape } => { let mut fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) .unwrap() @@ -1111,8 +1171,7 @@ impl Fsm for PathToolFsmState { responses.add(PathToolMessage::SelectedPointUpdated); return state; - } - _ => {} + } // _ => {} } responses.add(PathToolMessage::SelectedPointUpdated); @@ -1168,10 +1227,10 @@ impl Fsm for PathToolFsmState { ( _, PathToolMessage::MouseDown { - direct_insert_without_sliding, extend_selection, lasso_select, handle_drag_from_anchor, + .. }, ) => { let extend_selection = input.keyboard.get(extend_selection as usize); @@ -1313,24 +1372,63 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Dragging(tool_data.dragging_state) } (PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => { - // Check for a point in Selection threshold if it is there then don't change mode + // if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { + // //there is some closed segment to update it to be the segment in the tool messages + // tool_data.segment = Some(closest_segment); + // let lock_angle_state = input.keyboard.get(lock_angle as usize); + // if lock_angle_state { + // tool_data.delete_segment_pressed = true; + // } + // responses.add(OverlaysMessage::Draw) + // } + + let lock_angle_state = input.keyboard.get(lock_angle as usize); + if lock_angle_state { + tool_data.delete_segment_pressed = true; + } else { + tool_data.delete_segment_pressed = false; + } + + // if selected something + //first if already close to a segment then update its closest point if shape_editor .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) .is_some() { - self - } - // Check for a segment nearby, if present then enter into insert point mode else go for ready - else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { - let lock_angle_state = input.keyboard.get(lock_angle as usize); - if lock_angle_state { - tool_data.delete_segment_pressed = true; + tool_data.segment = None; + responses.add(OverlaysMessage::Draw) + } else if let Some(closest_segment) = &mut tool_data.segment { + closest_segment.update_closest_point(document.metadata(), input.mouse.position); + if closest_segment.too_far(input.mouse.position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, document.metadata()) { + tool_data.segment = None; } - responses.add(DocumentMessage::StartTransaction); - tool_data.start_insertion(responses, closed_segment) - } else { - self + responses.add(OverlaysMessage::Draw) } + // if not check that if there is some closest segment and update it in tool data + else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { + tool_data.segment = Some(closest_segment); + responses.add(OverlaysMessage::Draw) + } + self + + // // Check for a point in Selection threshold if it is there then don't change mode + // if shape_editor + // .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) + // .is_some() + // { + // self + // } + // // Check for a segment nearby, if present then enter into insert point mode else go for ready + // else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { + // let lock_angle_state = input.keyboard.get(lock_angle as usize); + // if lock_angle_state { + // tool_data.delete_segment_pressed = true; + // } + // responses.add(DocumentMessage::StartTransaction); + // tool_data.start_insertion(responses, closed_segment) + // } else { + // self + // } } (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning @@ -1560,6 +1658,7 @@ impl Fsm for PathToolFsmState { responses.add(OverlaysMessage::Draw); PathToolFsmState::Ready } + // (_, PathToolMessage::PointerMove { .. }) => self, (_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => { shape_editor.move_selected_points( tool_data.opposing_handle_lengths.take(), From dc3d41f5e3cb10d0df9c34df2d06468d713d102c Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 18 Apr 2025 19:30:33 +0530 Subject: [PATCH 07/14] Insert point mode cleanup --- .../messages/tool/tool_messages/path_tool.rs | 196 +----------------- 1 file changed, 7 insertions(+), 189 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 697e21a9b9..20937eff5e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -297,15 +297,6 @@ impl<'a> MessageHandler> for PathToo Escape, RightClick, ), - PathToolFsmState::InsertPoint => actions!(PathToolMessageDiscriminant; - Enter, - MouseDown, - PointerMove, - Escape, - Delete, - RightClick, - GRS, - ), } } } @@ -342,12 +333,6 @@ enum PathToolFsmState { Drawing { selection_shape: SelectionShapeType, }, - InsertPoint, -} - -enum InsertEndKind { - Abort, - Add { extend_selection: bool }, } #[derive(Default)] @@ -439,54 +424,6 @@ impl PathToolData { self.selection_status = selection_status; } - fn start_insertion(&mut self, responses: &mut VecDeque, segment: ClosestSegment) -> PathToolFsmState { - if self.segment.is_some() { - warn!("Segment was `Some(..)` before `start_insertion`") - } - self.segment = Some(segment); - responses.add(OverlaysMessage::Draw); - PathToolFsmState::InsertPoint - } - - fn update_insertion(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, responses: &mut VecDeque, input: &InputPreprocessorMessageHandler) -> PathToolFsmState { - if let Some(closed_segment) = &mut self.segment { - closed_segment.update_closest_point(document.metadata(), input.mouse.position); - if closed_segment.too_far(input.mouse.position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, document.metadata()) { - self.end_insertion(shape_editor, responses, InsertEndKind::Abort) - } else { - PathToolFsmState::InsertPoint - } - } else { - warn!("Segment was `None` on `update_insertion`"); - PathToolFsmState::Ready - } - } - - fn end_insertion(&mut self, shape_editor: &mut ShapeState, responses: &mut VecDeque, kind: InsertEndKind) -> PathToolFsmState { - let mut commit_transaction = false; - self.delete_segment_pressed = false; - match self.segment.as_mut() { - None => { - warn!("Segment was `None` before `end_insertion`") - } - Some(closed_segment) => { - if let InsertEndKind::Add { extend_selection } = kind { - closed_segment.adjusted_insert_and_select(shape_editor, responses, extend_selection); - commit_transaction = true; - } - } - } - - self.segment = None; - if commit_transaction { - responses.add(DocumentMessage::EndTransaction); - } else { - responses.add(DocumentMessage::AbortTransaction); - } - responses.add(OverlaysMessage::Draw); - PathToolFsmState::Ready - } - #[allow(clippy::too_many_arguments)] fn mouse_down( &mut self, @@ -564,11 +501,9 @@ impl PathToolData { } PathToolFsmState::Dragging(self.dragging_state) } - //We didn't found a point so we will see if there is a segment and then directly insert the point + //We didn't found a point so we will see if there is a segment to insert the point else if let Some(closed_segment) = &mut self.segment { responses.add(DocumentMessage::StartTransaction); - // self.segment = Some(closed_segment.clone()); //do we need this - //here replace start insertion wala logic if self.delete_segment_pressed { let segment = closed_segment.segment(); @@ -586,7 +521,7 @@ impl PathToolData { PathToolFsmState::Ready } - // We didn't find a point, so consider selecting the nearest shape instead + // We didn't find a segment, so consider selecting the nearest shape instead else if let Some(layer) = document.click(input) { shape_editor.deselect_all_points(); if extend_selection { @@ -1132,97 +1067,12 @@ impl Fsm for PathToolFsmState { } } } - Self::InsertPoint => { - let state = tool_data.update_insertion(shape_editor, document, responses, input); - - if let Some(closest_segment) = &tool_data.segment { - // Perpendicular line when inserting a point, and a cross when deleting a segment - let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - (handle1 - handle2).try_normalize() - } else { - let layer = closest_segment.layer(); - let points = closest_segment.points(); - if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { - if let (Some(pos1), Some(pos2)) = ( - ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), - ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), - ) { - (pos1 - pos2).try_normalize() - } else { - None - } - } else { - None - } - } - .unwrap_or(DVec2::ZERO); - let perp = tangent.perp(); - let point = closest_segment.closest_point_to_viewport(); - if tool_data.delete_segment_pressed { - let degrees: f64 = 45.0; - let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent); - let tilted_perp = tilted_line.perp(); - overlay_context.line(point - tilted_line * SEGMENT_OVERLAY_SIZE, point + tilted_line * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); - overlay_context.line(point - tilted_perp * SEGMENT_OVERLAY_SIZE, point + tilted_perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); - } else { - overlay_context.line(point - perp * SEGMENT_OVERLAY_SIZE, point + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); - } - } - - responses.add(PathToolMessage::SelectedPointUpdated); - return state; - } // _ => {} } responses.add(PathToolMessage::SelectedPointUpdated); self } - // `Self::InsertPoint` case: - (Self::InsertPoint, PathToolMessage::MouseDown { extend_selection, delete_segment, .. }) => { - //| PathToolMessage::Enter { extend_selection, .. } consider adding support for ctrl key in here - tool_data.double_click_handled = true; - let extend_selection = input.keyboard.get(extend_selection as usize); - let delete_segment = input.keyboard.get(delete_segment as usize); - - if delete_segment { - if let Some(closest_segment) = &tool_data.segment { - let segment = closest_segment.segment(); - let layer = closest_segment.layer(); - let points = closest_segment.points(); - if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { - shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); - responses.add(DocumentMessage::EndTransaction); - } - } - return PathToolFsmState::Ready; - } else { - tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection }) - } - } - (Self::InsertPoint, PathToolMessage::PointerMove { lock_angle, .. }) => { - let lock_angle_state = input.keyboard.get(lock_angle as usize); - if lock_angle_state { - tool_data.delete_segment_pressed = true; - } else { - tool_data.delete_segment_pressed = false; - } - - responses.add(OverlaysMessage::Draw); - // `tool_data.update_insertion` would be called on `OverlaysMessage::Draw` - // we anyway should to call it on `::Draw` because we can change scale by ctrl+scroll without `::PointerMove` - - // If there is an anchor point very close to the current point then get out the InsertPoint mode - if shape_editor - .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) - .is_some() - { - return PathToolFsmState::Ready; - } - self - } - (Self::InsertPoint, PathToolMessage::Escape | PathToolMessage::Delete | PathToolMessage::RightClick) => tool_data.end_insertion(shape_editor, responses, InsertEndKind::Abort), - (Self::InsertPoint, PathToolMessage::GRS { key: _ }) => PathToolFsmState::InsertPoint, // Mouse down ( _, @@ -1372,16 +1222,6 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Dragging(tool_data.dragging_state) } (PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => { - // if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { - // //there is some closed segment to update it to be the segment in the tool messages - // tool_data.segment = Some(closest_segment); - // let lock_angle_state = input.keyboard.get(lock_angle as usize); - // if lock_angle_state { - // tool_data.delete_segment_pressed = true; - // } - // responses.add(OverlaysMessage::Draw) - // } - let lock_angle_state = input.keyboard.get(lock_angle as usize); if lock_angle_state { tool_data.delete_segment_pressed = true; @@ -1389,46 +1229,28 @@ impl Fsm for PathToolFsmState { tool_data.delete_segment_pressed = false; } - // if selected something - //first if already close to a segment then update its closest point + // If there is a point nearby then remove the overlay if shape_editor .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) .is_some() { tool_data.segment = None; responses.add(OverlaysMessage::Draw) - } else if let Some(closest_segment) = &mut tool_data.segment { + } + // If already hovering on a segment, then recalculate it's closest point + else if let Some(closest_segment) = &mut tool_data.segment { closest_segment.update_closest_point(document.metadata(), input.mouse.position); if closest_segment.too_far(input.mouse.position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, document.metadata()) { tool_data.segment = None; } responses.add(OverlaysMessage::Draw) } - // if not check that if there is some closest segment and update it in tool data + // If not check that if there is some closest segment or not else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { tool_data.segment = Some(closest_segment); responses.add(OverlaysMessage::Draw) } self - - // // Check for a point in Selection threshold if it is there then don't change mode - // if shape_editor - // .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) - // .is_some() - // { - // self - // } - // // Check for a segment nearby, if present then enter into insert point mode else go for ready - // else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { - // let lock_angle_state = input.keyboard.get(lock_angle as usize); - // if lock_angle_state { - // tool_data.delete_segment_pressed = true; - // } - // responses.add(DocumentMessage::StartTransaction); - // tool_data.start_insertion(responses, closed_segment) - // } else { - // self - // } } (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning @@ -1809,10 +1631,6 @@ impl Fsm for PathToolFsmState { HintInfo::keys([Key::Alt], "Subtract").prepend_plus(), ]), ]), - PathToolFsmState::InsertPoint => HintData(vec![ - HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point")]), - ]), }; responses.add(FrontendMessage::UpdateInputHints { hint_data }); From b09e475e41f0e6d0f19949790b0578dd0a21c24a Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 18 Apr 2025 19:40:42 +0530 Subject: [PATCH 08/14] Linting fix --- editor/src/messages/tool/tool_messages/path_tool.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 616c7a66b4..60bad78c7e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1,8 +1,8 @@ use super::select_tool::extend_lasso; use super::tool_prelude::*; use crate::consts::{ - COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SEGMENT_INSERTION_TOLERANCE, SEGMENT_OVERLAY_SIZE, - SELECTION_THRESHOLD, SELECTION_TOLERANCE, + COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, + SEGMENT_INSERTION_TOLERANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, }; use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; From ddd05da0e35a9b30407a19dfc6db05563d0f81ac Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 19 Apr 2025 00:02:36 +0530 Subject: [PATCH 09/14] Code review --- .../tool/common_functionality/shape_editor.rs | 24 +++++++++++++ .../messages/tool/tool_messages/path_tool.rs | 35 +++---------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 9dd2c17b90..bed09b21da 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -207,6 +207,30 @@ impl ClosestSegment { let id = self.adjusted_insert(responses); shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection) } + + pub fn calculate_perp(&self, document: &DocumentMessageHandler) -> DVec2 { + let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) { + (handle1 - handle2).try_normalize() + } else { + let layer = self.layer(); + let points = self.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + if let (Some(pos1), Some(pos2)) = ( + ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), + ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), + ) { + (pos1 - pos2).try_normalize() + } else { + None + } + } else { + None + } + } + .unwrap_or(DVec2::ZERO); + let perp = tangent.perp(); + return perp; + } } // TODO Consider keeping a list of selected manipulators to minimize traversals of the layers diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 60bad78c7e..14a98a94c0 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -979,35 +979,13 @@ impl Fsm for PathToolFsmState { match self { Self::Ready => { - //closest segment should be set when pointermoved and tabhi update segment bhi hona chahiye - - //check for a close segment if it is there + // Check for a close segment if it is there if let Some(closest_segment) = &tool_data.segment { - //insert a perpendicular line - let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - (handle1 - handle2).try_normalize() - } else { - let layer = closest_segment.layer(); - let points = closest_segment.points(); - if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { - if let (Some(pos1), Some(pos2)) = ( - ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), - ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), - ) { - (pos1 - pos2).try_normalize() - } else { - None - } - } else { - None - } - } - .unwrap_or(DVec2::ZERO); - let perp = tangent.perp(); + let perp = closest_segment.calculate_perp(document); let point = closest_segment.closest_point_to_viewport(); if tool_data.delete_segment_pressed { let degrees: f64 = 45.0; - let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent); + let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(perp); let tilted_perp = tilted_line.perp(); overlay_context.line(point - tilted_line * SEGMENT_OVERLAY_SIZE, point + tilted_line * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); overlay_context.line(point - tilted_perp * SEGMENT_OVERLAY_SIZE, point + tilted_perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); @@ -1221,11 +1199,7 @@ impl Fsm for PathToolFsmState { } (PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => { let lock_angle_state = input.keyboard.get(lock_angle as usize); - if lock_angle_state { - tool_data.delete_segment_pressed = true; - } else { - tool_data.delete_segment_pressed = false; - } + tool_data.delete_segment_pressed = lock_angle_state; // If there is a point nearby then remove the overlay if shape_editor @@ -1478,7 +1452,6 @@ impl Fsm for PathToolFsmState { responses.add(OverlaysMessage::Draw); PathToolFsmState::Ready } - // (_, PathToolMessage::PointerMove { .. }) => self, (_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => { shape_editor.move_selected_points( tool_data.opposing_handle_lengths.take(), From c62edecaec03eb4747a13b7af29954d95514e78c Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 19 Apr 2025 00:28:06 +0530 Subject: [PATCH 10/14] Added hints --- editor/src/messages/tool/tool_messages/path_tool.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 14a98a94c0..5f5e0ab80b 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1519,7 +1519,10 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Ready => HintData(vec![ HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Select Point"), HintInfo::keys([Key::Shift], "Extend").prepend_plus()]), HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"), HintInfo::keys([Key::Control], "Lasso").prepend_plus()]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment")]), + HintGroup(vec![ + HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment"), + HintInfo::keys([Key::Control], "Delete Segment").prepend_plus(), + ]), // TODO: Only show if at least one anchor is selected, and dynamically show either "Smooth" or "Sharp" based on the current state HintGroup(vec![ HintInfo::mouse(MouseMotion::LmbDouble, "Convert Anchor Point"), From eb41b427bd7ff85a4a3917240775d976bb7f0006 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Tue, 22 Apr 2025 00:16:51 +0530 Subject: [PATCH 11/14] Added field for delete segment --- .../src/messages/input_mapper/input_mappings.rs | 2 +- .../src/messages/tool/tool_messages/path_tool.rs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 265ba41db9..d1a9351a0c 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -218,7 +218,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), entry!(KeyDown(KeyR); action_dispatch=PathToolMessage::GRS { key: KeyR }), entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }), - entry!(PointerMove; refresh_keys=[KeyC, Space, Control, Shift, Alt], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space, snap_angle: Shift, lock_angle: Control }), + entry!(PointerMove; refresh_keys=[KeyC, Space, Control, Shift, Alt], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space, snap_angle: Shift, lock_angle: Control, delete_segment: Control }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors), entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints), diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 5f5e0ab80b..3d9cdf1125 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -78,6 +78,7 @@ pub enum PathToolMessage { move_anchor_with_handles: Key, snap_angle: Key, lock_angle: Key, + delete_segment: Key, }, PointerOutsideViewport { equidistant: Key, @@ -85,6 +86,7 @@ pub enum PathToolMessage { move_anchor_with_handles: Key, snap_angle: Key, lock_angle: Key, + delete_segment: Key, }, RightClick, SelectAllAnchors, @@ -1076,6 +1078,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, }, ) => { tool_data.previous_mouse_position = input.mouse.position; @@ -1094,6 +1097,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), PathToolMessage::PointerMove { @@ -1102,6 +1106,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), ]; @@ -1117,6 +1122,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, }, ) => { let mut selected_only_handles = true; @@ -1182,6 +1188,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), PathToolMessage::PointerMove { @@ -1190,6 +1197,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), ]; @@ -1197,9 +1205,8 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Dragging(tool_data.dragging_state) } - (PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => { - let lock_angle_state = input.keyboard.get(lock_angle as usize); - tool_data.delete_segment_pressed = lock_angle_state; + (PathToolFsmState::Ready, PathToolMessage::PointerMove { delete_segment, .. }) => { + tool_data.delete_segment_pressed = input.keyboard.get(delete_segment as usize); // If there is a point nearby then remove the overlay if shape_editor @@ -1248,6 +1255,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, }, ) => { // Auto-panning @@ -1258,6 +1266,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), PathToolMessage::PointerMove { @@ -1266,6 +1275,7 @@ impl Fsm for PathToolFsmState { move_anchor_with_handles, snap_angle, lock_angle, + delete_segment, } .into(), ]; From 8f1930ecddbf2189092434fe249114126c3eab12 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 26 Apr 2025 14:19:43 +0530 Subject: [PATCH 12/14] Change controls and fix too far logic --- editor/src/consts.rs | 2 +- editor/src/messages/input_mapper/input_mappings.rs | 2 +- editor/src/messages/tool/common_functionality/shape_editor.rs | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/editor/src/consts.rs b/editor/src/consts.rs index e7d86ac3d7..2c9787b810 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -99,7 +99,7 @@ pub const MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY: f64 = 48.; pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.; pub const SELECTION_THRESHOLD: f64 = 10.; pub const HIDE_HANDLE_DISTANCE: f64 = 3.; -pub const INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE: f64 = 50.; +pub const INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE: f64 = 40.; pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.; pub const SEGMENT_INSERTION_TOLERANCE: f64 = 7.5; pub const SEGMENT_OVERLAY_SIZE: f64 = 10.; diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index d1a9351a0c..43a0fa5060 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -218,7 +218,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), entry!(KeyDown(KeyR); action_dispatch=PathToolMessage::GRS { key: KeyR }), entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }), - entry!(PointerMove; refresh_keys=[KeyC, Space, Control, Shift, Alt], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space, snap_angle: Shift, lock_angle: Control, delete_segment: Control }), + entry!(PointerMove; refresh_keys=[KeyC, Space, Control, Shift, Alt], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space, snap_angle: Shift, lock_angle: Control, delete_segment: Alt }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors), entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index bed09b21da..6b01764c56 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -136,9 +136,7 @@ impl ClosestSegment { pub fn too_far(&self, mouse_position: DVec2, tolerance: f64, document_metadata: &DocumentMetadata) -> bool { let dist_sq = self.distance_squared(mouse_position); let stroke_width = document_metadata.document_to_viewport.decompose_scale().x.max(1.) * self.stroke_width; - let stroke_width_sq = stroke_width * stroke_width; - let tolerance_sq = tolerance * tolerance; - (stroke_width_sq + tolerance_sq) < dist_sq + (stroke_width + tolerance).powi(2) < dist_sq } pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option, Option) { From eb1269604dff77a102e2154e5d667fe15ee41bbc Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Tue, 29 Apr 2025 03:28:55 -0700 Subject: [PATCH 13/14] Fixes --- editor/src/consts.rs | 3 +-- .../src/messages/tool/tool_messages/path_tool.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 2c9787b810..b4b3d23feb 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -99,9 +99,8 @@ pub const MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY: f64 = 48.; pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.; pub const SELECTION_THRESHOLD: f64 = 10.; pub const HIDE_HANDLE_DISTANCE: f64 = 3.; -pub const INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE: f64 = 40.; pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.; -pub const SEGMENT_INSERTION_TOLERANCE: f64 = 7.5; +pub const SEGMENT_INSERTION_DISTANCE: f64 = 7.5; pub const SEGMENT_OVERLAY_SIZE: f64 = 10.; // PEN TOOL diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 4f51225d1b..91bcd1a1b4 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1,8 +1,8 @@ use super::select_tool::extend_lasso; use super::tool_prelude::*; use crate::consts::{ - COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, - SEGMENT_INSERTION_TOLERANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, + COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, + SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, }; use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; @@ -1249,7 +1249,7 @@ impl Fsm for PathToolFsmState { (PathToolFsmState::Ready, PathToolMessage::PointerMove { delete_segment, .. }) => { tool_data.delete_segment_pressed = input.keyboard.get(delete_segment as usize); - // If there is a point nearby then remove the overlay + // If there is a point nearby, then remove the overlay if shape_editor .find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) .is_some() @@ -1257,16 +1257,16 @@ impl Fsm for PathToolFsmState { tool_data.segment = None; responses.add(OverlaysMessage::Draw) } - // If already hovering on a segment, then recalculate it's closest point + // If already hovering on a segment, then recalculate its closest point else if let Some(closest_segment) = &mut tool_data.segment { closest_segment.update_closest_point(document.metadata(), input.mouse.position); - if closest_segment.too_far(input.mouse.position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, document.metadata()) { + if closest_segment.too_far(input.mouse.position, SEGMENT_INSERTION_DISTANCE, document.metadata()) { tool_data.segment = None; } responses.add(OverlaysMessage::Draw) } - // If not check that if there is some closest segment or not - else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_TOLERANCE) { + // If not, check that if there is some closest segment or not + else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SEGMENT_INSERTION_DISTANCE) { tool_data.segment = Some(closest_segment); responses.add(OverlaysMessage::Draw) } From b582aa034007f2a63d8dd3488de5d19c87f209f5 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Tue, 29 Apr 2025 17:55:56 -0700 Subject: [PATCH 14/14] Code review --- .../messages/input_mapper/input_mappings.rs | 2 +- .../tool/common_functionality/shape_editor.rs | 12 ++++---- .../messages/tool/tool_messages/path_tool.rs | 30 +++++++++---------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index a5c6a9f570..76d2fc7bc6 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -212,7 +212,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDownNoRepeat(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { delete_segment: Control, extend_selection: Shift, lasso_select: Control, handle_drag_from_anchor: Alt }), + entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { extend_selection: Shift, lasso_select: Control, handle_drag_from_anchor: Alt }), entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick), entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape), entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index f7902c98ff..53faeeeb29 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -210,12 +210,11 @@ impl ClosestSegment { let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) { (handle1 - handle2).try_normalize() } else { - let layer = self.layer(); - let points = self.points(); - if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { + let [first_point, last_point] = self.points(); + if let Some(vector_data) = document.network_interface.compute_modified_vector(self.layer()) { if let (Some(pos1), Some(pos2)) = ( - ManipulatorPointId::Anchor(points[0]).get_position(&vector_data), - ManipulatorPointId::Anchor(points[1]).get_position(&vector_data), + ManipulatorPointId::Anchor(first_point).get_position(&vector_data), + ManipulatorPointId::Anchor(last_point).get_position(&vector_data), ) { (pos1 - pos2).try_normalize() } else { @@ -226,8 +225,7 @@ impl ClosestSegment { } } .unwrap_or(DVec2::ZERO); - let perp = tangent.perp(); - return perp; + tangent.perp() } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 91bcd1a1b4..85e28dbdef 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -64,7 +64,6 @@ pub enum PathToolMessage { ManipulatorMakeHandlesFree, ManipulatorMakeHandlesColinear, MouseDown { - delete_segment: Key, extend_selection: Key, lasso_select: Key, handle_drag_from_anchor: Key, @@ -506,22 +505,20 @@ impl PathToolData { } PathToolFsmState::Dragging(self.dragging_state) } - //We didn't found a point so we will see if there is a segment to insert the point + // We didn't find a point nearby, so we will see if there is a segment to insert a point on else if let Some(closed_segment) = &mut self.segment { responses.add(DocumentMessage::StartTransaction); if self.delete_segment_pressed { - let segment = closed_segment.segment(); - let layer = closed_segment.layer(); - let points = closed_segment.points(); - if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) { - shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points); + if let Some(vector_data) = document.network_interface.compute_modified_vector(closed_segment.layer()) { + shape_editor.dissolve_segment(responses, closed_segment.layer(), &vector_data, closed_segment.segment(), closed_segment.points()); responses.add(DocumentMessage::EndTransaction); } } else { closed_segment.adjusted_insert_and_select(shape_editor, responses, extend_selection); responses.add(DocumentMessage::EndTransaction); } + self.segment = None; PathToolFsmState::Ready @@ -1018,17 +1015,21 @@ impl Fsm for PathToolFsmState { match self { Self::Ready => { - // Check for a close segment if it is there if let Some(closest_segment) = &tool_data.segment { let perp = closest_segment.calculate_perp(document); let point = closest_segment.closest_point_to_viewport(); + + // Draw an X on the segment if tool_data.delete_segment_pressed { - let degrees: f64 = 45.0; - let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(perp); + let angle = 45_f64.to_radians(); + let tilted_line = DVec2::from_angle(angle).rotate(perp); let tilted_perp = tilted_line.perp(); + overlay_context.line(point - tilted_line * SEGMENT_OVERLAY_SIZE, point + tilted_line * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); overlay_context.line(point - tilted_perp * SEGMENT_OVERLAY_SIZE, point + tilted_perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); - } else { + } + // Draw a line on the segment + else { overlay_context.line(point - perp * SEGMENT_OVERLAY_SIZE, point + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); } } @@ -1270,6 +1271,7 @@ impl Fsm for PathToolFsmState { tool_data.segment = Some(closest_segment); responses.add(OverlaysMessage::Draw) } + self } (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { @@ -1575,10 +1577,8 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Ready => HintData(vec![ HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Select Point"), HintInfo::keys([Key::Shift], "Extend").prepend_plus()]), HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"), HintInfo::keys([Key::Control], "Lasso").prepend_plus()]), - HintGroup(vec![ - HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment"), - HintInfo::keys([Key::Control], "Delete Segment").prepend_plus(), - ]), + HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment")]), + HintGroup(vec![HintInfo::keys_and_mouse([Key::Alt], MouseMotion::Lmb, "Delete Segment")]), // TODO: Only show if at least one anchor is selected, and dynamically show either "Smooth" or "Sharp" based on the current state HintGroup(vec![ HintInfo::mouse(MouseMotion::LmbDouble, "Convert Anchor Point"),