From aa5af815697b5cd1060740dd924048803b482c8d Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 1 Aug 2025 13:28:39 +0530 Subject: [PATCH] Add Merge Point message --- .../messages/tool/tool_messages/path_tool.rs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index e04b75d3f4..9ef50791fc 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -119,6 +119,7 @@ pub enum PathToolMessage { UpdateSelectedPointsStatus { overlay_context: OverlayContext, }, + MergeSelectedPoints, } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default, serde::Serialize, serde::Deserialize, specta::Type)] @@ -274,6 +275,11 @@ impl LayoutHolder for PathTool { .disabled(!self.tool_data.single_path_node_compatible_layer_selected) .widget_holder(); + let merge_button = IconButton::new("Folder", 24) + .tooltip("Merge selected points") + .on_update(|_| PathToolMessage::MergeSelectedPoints.into()) + .widget_holder(); + let [_checkbox, _dropdown] = { let pivot_gizmo_type_widget = pivot_gizmo_type_widget(self.tool_data.pivot_gizmo.state, PivotToolSource::Path); [pivot_gizmo_type_widget[0].clone(), pivot_gizmo_type_widget[2].clone()] @@ -305,6 +311,9 @@ impl LayoutHolder for PathTool { path_overlay_mode_widget, unrelated_seperator.clone(), path_node_button, + unrelated_seperator.clone(), + merge_button, + unrelated_seperator.clone(), // checkbox.clone(), // related_seperator.clone(), // dropdown.clone(), @@ -399,6 +408,7 @@ impl<'a> MessageHandler> for Path DeleteAndBreakPath, ClosePath, PointerMove, + MergeSelectedPoints, ), PathToolFsmState::Dragging(_) => actions!(PathToolMessageDiscriminant; Escape, @@ -2454,6 +2464,77 @@ impl Fsm for PathToolFsmState { self } + (_, PathToolMessage::MergeSelectedPoints) => { + // Get all the selected points and merge the selected points + // Assuming that all these points are on the same layer + + //TODO: Check here that there are more than two points selected and all are within certain threshold + + if let Some(layer) = shape_editor.selected_layers().next() { + responses.add(DocumentMessage::AddTransaction); + let state = shape_editor.selected_shape_state.get(layer).expect("No state for selected layer"); + let points = state + .selected_points() + .filter_map(|point| if let ManipulatorPointId::Anchor(anchor) = point { Some(anchor) } else { None }); + + // Calculate the centroid + + if let Some(vector_data) = document.network_interface.compute_modified_vector(*layer) { + let positions = points.filter_map(|point| ManipulatorPointId::Anchor(point).get_position(&vector_data)); + + let mut sum = DVec2::default(); + let mut count = 0 as f64; + + for position in positions { + sum += position; + count += 1.; + } + + let centroid = sum / count; + + // Add a new point with the new coordinates + let new_id = PointId::generate(); + let modification_type = VectorModificationType::InsertPoint { id: new_id, position: centroid }; + responses.add(GraphOperationMessage::Vector { layer: *layer, modification_type }); + + // Remove old points + for point in state + .selected_points() + .filter_map(|point| if let ManipulatorPointId::Anchor(anchor) = point { Some(anchor) } else { None }) + { + let modification_type = VectorModificationType::RemovePoint { id: point }; + responses.add(GraphOperationMessage::Vector { layer: *layer, modification_type }); + } + + // Find those segments which were connected to just one of the selected points + for (_, bezier, start, end) in vector_data.segment_bezier_iter() { + if state.is_point_selected(ManipulatorPointId::Anchor(start)) { + let id = SegmentId::generate(); + let points = [new_id, end]; + let handles = match bezier.handles { + BezierHandles::Linear => [None, None], + BezierHandles::Quadratic { handle } => [Some(handle - bezier.start), None], + BezierHandles::Cubic { handle_start, handle_end } => [Some(handle_start - bezier.start), Some(handle_end - bezier.end)], + }; + let modification_type = VectorModificationType::InsertSegment { id, points, handles }; + responses.add(GraphOperationMessage::Vector { layer: *layer, modification_type }); + } else if state.is_point_selected(ManipulatorPointId::Anchor(end)) { + let id = SegmentId::generate(); + let points = [start, new_id]; + let handles = match bezier.handles { + BezierHandles::Linear => [None, None], + BezierHandles::Quadratic { handle } => [Some(handle - bezier.start), None], + BezierHandles::Cubic { handle_start, handle_end } => [Some(handle_start - bezier.start), Some(handle_end - bezier.end)], + }; + let modification_type = VectorModificationType::InsertSegment { id, points, handles }; + responses.add(GraphOperationMessage::Vector { layer: *layer, modification_type }); + } + } + } + } + + PathToolFsmState::Ready + } (_, _) => PathToolFsmState::Ready, } }