From 5f22260ef6aee31bd433141f2dc938625c0a7c47 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Thu, 1 Feb 2024 16:20:32 +0800 Subject: [PATCH 1/9] feat: break closed curve --- .../messages/input_mapper/default_mapping.rs | 2 + .../node_graph/graph_operation_message.rs | 1 + .../transform_utils.rs | 10 +++++ .../tool/common_functionality/shape_editor.rs | 38 +++++++++++++++++++ .../messages/tool/tool_messages/path_tool.rs | 8 ++++ .../bezier-rs/src/subpath/manipulators.rs | 4 ++ 6 files changed, 63 insertions(+) diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index 3c6f47f612a..35b61cf205d 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -177,6 +177,8 @@ pub fn default_mapping() -> Mapping { entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=LineToolMessage::PointerMove { center: Alt, lock_angle: Control, snap_angle: Shift }), // // PathToolMessage + entry!(KeyDown(Delete); modifiers=[Control], action_dispatch=PathToolMessage::BreakPath), + entry!(KeyDown(Backspace); modifiers=[Control], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Lmb); action_dispatch=PathToolMessage::DragStart { add_to_selection: Shift }), entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs index d98bf98ed56..9ad80c7147d 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs @@ -133,6 +133,7 @@ pub enum VectorDataModification { SetClosed { index: usize, closed: bool }, SetManipulatorHandleMirroring { id: ManipulatorGroupId, mirror_angle: bool }, SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 }, + ShiftManipulatorGroup { id: ManipulatorGroupId }, ToggleManipulatorHandleMirroring { id: ManipulatorGroupId }, UpdateSubpaths { subpaths: Vec> }, } diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs index 33bdbb0ccb4..9ccd324707f 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs @@ -229,6 +229,15 @@ impl<'a> VectorModificationState<'a> { } } + fn shift_group_to_head(&mut self, id: ManipulatorGroupId) { + for subpath in self.subpaths.iter_mut() { + if let Some(index) = subpath.manipulator_index_from_id(id) { + subpath.shift_manipulator_group_to_head(index); + break; + } + } + } + fn remove_point(&mut self, point: ManipulatorPointId) { for subpath in self.subpaths.iter_mut() { if point.manipulator_type == SelectedType::Anchor { @@ -300,6 +309,7 @@ impl<'a> VectorModificationState<'a> { VectorDataModification::SetClosed { index, closed } => self.subpaths[index].set_closed(closed), VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle } => self.set_mirror(id, mirror_angle), VectorDataModification::SetManipulatorPosition { point, position } => self.set_position(point, position), + VectorDataModification::ShiftManipulatorGroup { id } => self.shift_group_to_head(id), VectorDataModification::ToggleManipulatorHandleMirroring { id } => self.toggle_mirror(id), VectorDataModification::UpdateSubpaths { subpaths } => *self.subpaths = subpaths, } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index cd123ef57ec..ddd3ed942b9 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -675,6 +675,44 @@ impl ShapeState { } } + /// Break closed curve. + pub fn break_closed_curve(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { + for (&layer, state) in &self.selected_shape_state { + if state.selected_points.len() > 1 { + return; + } + for &point in &state.selected_points { + let Some(subpaths) = get_subpaths(layer, document_network) else { + continue; + }; + if !subpaths[0].closed { + continue; + } + let Some(&group) = graph_modification_utils::get_manipulator_from_id(subpaths, point.group) else { + continue; + }; + + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::ShiftManipulatorGroup { id: group.id }, + }); + + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::AddEndManipulatorGroup { + subpath_index: 0, + manipulator_group: ManipulatorGroup::new(group.anchor, group.in_handle, group.out_handle), + }, + }); + + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::SetClosed { index: 0, closed: false }, + }); + } + } + } + /// Toggle if the handles should mirror angle across the anchor position. pub fn toggle_handle_mirroring_on_selected(&self, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 220c97cf9d9..73e1c492c7b 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -32,6 +32,7 @@ pub enum PathToolMessage { SelectionChanged, // Tool-specific messages + BreakPath, Delete, DragStart { add_to_selection: Key, @@ -159,6 +160,7 @@ impl<'a> MessageHandler> for PathToo NudgeSelectedPoints, Enter, SelectAllPoints, + BreakPath, ), Dragging => actions!(PathToolMessageDiscriminant; InsertPoint, @@ -166,6 +168,7 @@ impl<'a> MessageHandler> for PathToo PointerMove, Delete, SelectAllPoints, + BreakPath, ), DrawingBox => actions!(PathToolMessageDiscriminant; InsertPoint, @@ -174,6 +177,7 @@ impl<'a> MessageHandler> for PathToo Delete, Enter, SelectAllPoints, + BreakPath, ), } } @@ -418,6 +422,10 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Ready } + (_, PathToolMessage::BreakPath) => { + shape_editor.break_closed_curve(&document.network, responses); + PathToolFsmState::Ready + } (_, PathToolMessage::InsertPoint) => { // First we try and flip the sharpness (if they have clicked on an anchor) if !shape_editor.flip_sharp(&document.network, &document.metadata, input.mouse.position, SELECTION_TOLERANCE, responses) { diff --git a/libraries/bezier-rs/src/subpath/manipulators.rs b/libraries/bezier-rs/src/subpath/manipulators.rs index 336b81c8f90..ceaf33c5499 100644 --- a/libraries/bezier-rs/src/subpath/manipulators.rs +++ b/libraries/bezier-rs/src/subpath/manipulators.rs @@ -51,6 +51,10 @@ impl Subpath { self.manipulator_groups.remove(index) } + pub fn shift_manipulator_group_to_head(&mut self, index: usize) { + self.manipulator_groups.rotate_left(index); + } + /// Inserts a `ManipulatorGroup` at a certain point along the subpath based on the parametric `t`-value provided. /// Expects `t` to be within the inclusive range `[0, 1]`. pub fn insert(&mut self, t: SubpathTValue) { From 1f21e335113eba6efc6690b64636a97109c5d000 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Fri, 2 Feb 2024 11:21:02 +0800 Subject: [PATCH 2/9] feat: update hotkeys and handles --- editor/src/messages/input_mapper/default_mapping.rs | 4 ++-- .../messages/tool/common_functionality/shape_editor.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index 35b61cf205d..ce901c551c3 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -177,8 +177,8 @@ pub fn default_mapping() -> Mapping { entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=LineToolMessage::PointerMove { center: Alt, lock_angle: Control, snap_angle: Shift }), // // PathToolMessage - entry!(KeyDown(Delete); modifiers=[Control], action_dispatch=PathToolMessage::BreakPath), - entry!(KeyDown(Backspace); modifiers=[Control], action_dispatch=PathToolMessage::BreakPath), + entry!(KeyDown(Delete); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), + entry!(KeyDown(Backspace); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Lmb); action_dispatch=PathToolMessage::DragStart { add_to_selection: Shift }), entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index ddd3ed942b9..f42e8badb2e 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -697,11 +697,18 @@ impl ShapeState { modification: VectorDataModification::ShiftManipulatorGroup { id: group.id }, }); + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::RemoveManipulatorPoint { + point: ManipulatorPointId::new(group.id, SelectedType::InHandle), + }, + }); + responses.add(GraphOperationMessage::Vector { layer, modification: VectorDataModification::AddEndManipulatorGroup { subpath_index: 0, - manipulator_group: ManipulatorGroup::new(group.anchor, group.in_handle, group.out_handle), + manipulator_group: ManipulatorGroup::new(group.anchor, group.in_handle, None), }, }); From e83c94db33fdae76f5c3ca35abd58db53a4fe111 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Sun, 4 Feb 2024 19:18:34 +0800 Subject: [PATCH 3/9] feat: break an open path --- .../tool/common_functionality/shape_editor.rs | 85 +++++++++++-------- .../messages/tool/tool_messages/path_tool.rs | 2 +- .../bezier-rs/src/subpath/manipulators.rs | 1 + 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index f42e8badb2e..e8977568867 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -675,48 +675,61 @@ impl ShapeState { } } - /// Break closed curve. - pub fn break_closed_curve(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { + /// Break path at selected points. + pub fn break_path_at_selected_point(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { - if state.selected_points.len() > 1 { - return; - } - for &point in &state.selected_points { - let Some(subpaths) = get_subpaths(layer, document_network) else { - continue; - }; - if !subpaths[0].closed { - continue; - } - let Some(&group) = graph_modification_utils::get_manipulator_from_id(subpaths, point.group) else { - continue; - }; + let Some(subpaths) = get_subpaths(layer, document_network) else { + continue; + }; - responses.add(GraphOperationMessage::Vector { - layer, - modification: VectorDataModification::ShiftManipulatorGroup { id: group.id }, - }); + let mut new_subpaths = Vec::>::new(); - responses.add(GraphOperationMessage::Vector { - layer, - modification: VectorDataModification::RemoveManipulatorPoint { - point: ManipulatorPointId::new(group.id, SelectedType::InHandle), - }, - }); + for subpath in subpaths { + let mut updated = false; + for &point in &state.selected_points { + let Some(&group) = subpath.manipulator_from_id(point.group) else { + continue; + }; - responses.add(GraphOperationMessage::Vector { - layer, - modification: VectorDataModification::AddEndManipulatorGroup { - subpath_index: 0, - manipulator_group: ManipulatorGroup::new(group.anchor, group.in_handle, None), - }, - }); + let Some(manipulator_index) = subpath.manipulator_index_from_id(point.group) else { + continue; + }; + updated = true; + if subpath.closed { + let mut first_half = subpath.manipulator_groups()[..manipulator_index].to_vec(); + first_half.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); - responses.add(GraphOperationMessage::Vector { - layer, - modification: VectorDataModification::SetClosed { index: 0, closed: false }, - }); + let mut second_half = subpath.manipulator_groups()[manipulator_index + 1..].to_vec(); + second_half.insert(0, ManipulatorGroup::new(group.anchor, None, group.in_handle)); + + second_half.extend(first_half); + + new_subpaths.push(bezier_rs::Subpath::new(second_half, false)); + } else { + // Do not need to handle first and last manipulator. + if manipulator_index == 0 || manipulator_index == subpath.len() - 1 { + continue; + } + + let mut first_half = subpath.manipulator_groups()[..manipulator_index].to_vec(); + first_half.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); + + let mut second_half = subpath.manipulator_groups()[manipulator_index + 1..].to_vec(); + second_half.insert(0, ManipulatorGroup::new(group.anchor, None, group.in_handle)); + + new_subpaths.push(bezier_rs::Subpath::new(first_half, false)); + new_subpaths.push(bezier_rs::Subpath::new(second_half, false)); + } + } + if !updated { + new_subpaths.push(subpath.clone()); + } } + + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::UpdateSubpaths { subpaths: new_subpaths }, + }); } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 73e1c492c7b..a75b45e033e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -423,7 +423,7 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Ready } (_, PathToolMessage::BreakPath) => { - shape_editor.break_closed_curve(&document.network, responses); + shape_editor.break_path_at_selected_point(&document.network, responses); PathToolFsmState::Ready } (_, PathToolMessage::InsertPoint) => { diff --git a/libraries/bezier-rs/src/subpath/manipulators.rs b/libraries/bezier-rs/src/subpath/manipulators.rs index ceaf33c5499..ebaead2dcdf 100644 --- a/libraries/bezier-rs/src/subpath/manipulators.rs +++ b/libraries/bezier-rs/src/subpath/manipulators.rs @@ -51,6 +51,7 @@ impl Subpath { self.manipulator_groups.remove(index) } + /// shift the vec to let the element at index to be the first element. pub fn shift_manipulator_group_to_head(&mut self, index: usize) { self.manipulator_groups.rotate_left(index); } From dcdd504bbc2cef3f73790a2428fb1f79b7edc771 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Sun, 4 Feb 2024 23:48:05 +0800 Subject: [PATCH 4/9] feat: elegantly handle breaking at multi points in a subpath --- .../messages/input_mapper/default_mapping.rs | 2 + .../tool/common_functionality/shape_editor.rs | 80 ++++++++++++------- .../messages/tool/tool_messages/path_tool.rs | 8 ++ 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index ce901c551c3..ccf495654ef 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -179,6 +179,8 @@ pub fn default_mapping() -> Mapping { // PathToolMessage entry!(KeyDown(Delete); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Backspace); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), + entry!(KeyDown(Delete); modifiers=[Control], action_dispatch=PathToolMessage::DeleteAndBreakPath), + entry!(KeyDown(Backspace); modifiers=[Control], action_dispatch=PathToolMessage::DeleteAndBreakPath), entry!(KeyDown(Lmb); action_dispatch=PathToolMessage::DragStart { add_to_selection: Shift }), entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index e8977568867..0a73c99c0e5 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -685,45 +685,64 @@ impl ShapeState { let mut new_subpaths = Vec::>::new(); for subpath in subpaths { - let mut updated = false; - for &point in &state.selected_points { - let Some(&group) = subpath.manipulator_from_id(point.group) else { - continue; - }; + let mut points: Vec<_> = state + .selected_points + .iter() + .filter_map(|&point| { + let Some(manipulator_index) = subpath.manipulator_index_from_id(point.group) else { + return None; + }; + let Some(manipulator) = subpath.manipulator_from_id(point.group) else { + return None; + }; + Some((manipulator_index, manipulator)) + }) + .collect(); - let Some(manipulator_index) = subpath.manipulator_index_from_id(point.group) else { - continue; + points.sort_by(|&a, &b| { + return match a.0 > b.0 { + true => std::cmp::Ordering::Greater, + false => std::cmp::Ordering::Less, }; - updated = true; - if subpath.closed { - let mut first_half = subpath.manipulator_groups()[..manipulator_index].to_vec(); - first_half.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); + }); - let mut second_half = subpath.manipulator_groups()[manipulator_index + 1..].to_vec(); - second_half.insert(0, ManipulatorGroup::new(group.anchor, None, group.in_handle)); + match points.len() { + 0 => new_subpaths.push(subpath.clone()), + _ => { + let mut last_manipulator_index = 0; + let mut to_extend_with_last_group: Option>> = None; + let mut last_manipulator_group: Option<&ManipulatorGroup> = None; + for (i, &(manipulator_index, group)) in points.iter().enumerate() { + debug!("index: {}", manipulator_index); + let mut segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); + if i != 0 { + segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); + } - second_half.extend(first_half); + segment.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); - new_subpaths.push(bezier_rs::Subpath::new(second_half, false)); - } else { - // Do not need to handle first and last manipulator. - if manipulator_index == 0 || manipulator_index == subpath.len() - 1 { - continue; + if subpath.closed && i == 0 { + to_extend_with_last_group = Some(segment); + } else { + new_subpaths.push(bezier_rs::Subpath::new(segment, false)); + } + + last_manipulator_index = manipulator_index + 1; + last_manipulator_group = Some(group); } - let mut first_half = subpath.manipulator_groups()[..manipulator_index].to_vec(); - first_half.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); + let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); + final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); - let mut second_half = subpath.manipulator_groups()[manipulator_index + 1..].to_vec(); - second_half.insert(0, ManipulatorGroup::new(group.anchor, None, group.in_handle)); + if let Some(group) = to_extend_with_last_group { + final_segment.extend(group); + } - new_subpaths.push(bezier_rs::Subpath::new(first_half, false)); - new_subpaths.push(bezier_rs::Subpath::new(second_half, false)); + new_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); + + debug!("123"); } } - if !updated { - new_subpaths.push(subpath.clone()); - } } responses.add(GraphOperationMessage::Vector { @@ -733,6 +752,11 @@ impl ShapeState { } } + /// Delete point and break path. + pub fn delete_point_and_break_path(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { + debug!("delete point and break path"); + } + /// Toggle if the handles should mirror angle across the anchor position. pub fn toggle_handle_mirroring_on_selected(&self, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index a75b45e033e..069cc27e862 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -34,6 +34,7 @@ pub enum PathToolMessage { // Tool-specific messages BreakPath, Delete, + DeleteAndBreakPath, DragStart { add_to_selection: Key, }, @@ -161,6 +162,7 @@ impl<'a> MessageHandler> for PathToo Enter, SelectAllPoints, BreakPath, + DeleteAndBreakPath, ), Dragging => actions!(PathToolMessageDiscriminant; InsertPoint, @@ -169,6 +171,7 @@ impl<'a> MessageHandler> for PathToo Delete, SelectAllPoints, BreakPath, + DeleteAndBreakPath, ), DrawingBox => actions!(PathToolMessageDiscriminant; InsertPoint, @@ -178,6 +181,7 @@ impl<'a> MessageHandler> for PathToo Enter, SelectAllPoints, BreakPath, + DeleteAndBreakPath, ), } } @@ -426,6 +430,10 @@ impl Fsm for PathToolFsmState { shape_editor.break_path_at_selected_point(&document.network, responses); PathToolFsmState::Ready } + (_, PathToolMessage::DeleteAndBreakPath) => { + shape_editor.delete_point_and_break_path(&document.network, responses); + PathToolFsmState::Ready + } (_, PathToolMessage::InsertPoint) => { // First we try and flip the sharpness (if they have clicked on an anchor) if !shape_editor.flip_sharp(&document.network, &document.metadata, input.mouse.position, SELECTION_TOLERANCE, responses) { From 420b24703fd171ccca9406a506360e5c7c508e8a Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Mon, 5 Feb 2024 00:07:30 +0800 Subject: [PATCH 5/9] feat: handle break at end points --- .../tool/common_functionality/shape_editor.rs | 13 +++++++++++-- 1 file changed, 11 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 0a73c99c0e5..8702938e496 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -713,7 +713,12 @@ impl ShapeState { let mut to_extend_with_last_group: Option>> = None; let mut last_manipulator_group: Option<&ManipulatorGroup> = None; for (i, &(manipulator_index, group)) in points.iter().enumerate() { - debug!("index: {}", manipulator_index); + if manipulator_index == 0 { + last_manipulator_index = manipulator_index + 1; + last_manipulator_group = Some(group); + continue; + } + let mut segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); if i != 0 { segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); @@ -731,6 +736,10 @@ impl ShapeState { last_manipulator_group = Some(group); } + if last_manipulator_index == subpath.len() { + return; + } + let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); @@ -752,7 +761,7 @@ impl ShapeState { } } - /// Delete point and break path. + /// Delete point and its adjacent segments, then break path. pub fn delete_point_and_break_path(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { debug!("delete point and break path"); } From 26d73eafd7b0d7fc4600a6a122c3046467516a84 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Mon, 5 Feb 2024 00:49:36 +0800 Subject: [PATCH 6/9] feat: ctrl+delete to remove segments and break path --- .../tool/common_functionality/shape_editor.rs | 90 ++++++++++++++++--- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 8702938e496..3ea698e9669 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -682,7 +682,7 @@ impl ShapeState { continue; }; - let mut new_subpaths = Vec::>::new(); + let mut broken_subpaths = Vec::>::new(); for subpath in subpaths { let mut points: Vec<_> = state @@ -707,13 +707,13 @@ impl ShapeState { }); match points.len() { - 0 => new_subpaths.push(subpath.clone()), + 0 => broken_subpaths.push(subpath.clone()), _ => { let mut last_manipulator_index = 0; let mut to_extend_with_last_group: Option>> = None; let mut last_manipulator_group: Option<&ManipulatorGroup> = None; for (i, &(manipulator_index, group)) in points.iter().enumerate() { - if manipulator_index == 0 { + if manipulator_index == 0 && !subpath.closed { last_manipulator_index = manipulator_index + 1; last_manipulator_group = Some(group); continue; @@ -729,15 +729,15 @@ impl ShapeState { if subpath.closed && i == 0 { to_extend_with_last_group = Some(segment); } else { - new_subpaths.push(bezier_rs::Subpath::new(segment, false)); + broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); } last_manipulator_index = manipulator_index + 1; last_manipulator_group = Some(group); } - if last_manipulator_index == subpath.len() { - return; + if last_manipulator_index == subpath.len() && !subpath.closed { + continue; } let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); @@ -747,23 +747,91 @@ impl ShapeState { final_segment.extend(group); } - new_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); - - debug!("123"); + broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); } } } responses.add(GraphOperationMessage::Vector { layer, - modification: VectorDataModification::UpdateSubpaths { subpaths: new_subpaths }, + modification: VectorDataModification::UpdateSubpaths { subpaths: broken_subpaths }, }); } } /// Delete point and its adjacent segments, then break path. pub fn delete_point_and_break_path(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { - debug!("delete point and break path"); + for (&layer, state) in &self.selected_shape_state { + let Some(subpaths) = get_subpaths(layer, document_network) else { + continue; + }; + + let mut broken_subpaths = Vec::>::new(); + + for subpath in subpaths { + let mut points: Vec<_> = state + .selected_points + .iter() + .filter_map(|&point| { + let Some(manipulator_index) = subpath.manipulator_index_from_id(point.group) else { + return None; + }; + let Some(manipulator) = subpath.manipulator_from_id(point.group) else { + return None; + }; + Some((manipulator_index, manipulator)) + }) + .collect(); + + points.sort_by(|&a, &b| { + return match a.0 > b.0 { + true => std::cmp::Ordering::Greater, + false => std::cmp::Ordering::Less, + }; + }); + + match points.len() { + 0 => broken_subpaths.push(subpath.clone()), + _ => { + let mut last_manipulator_index = 0; + let mut to_extend_with_last_group: Option>> = None; + // let mut last_manipulator_group: Option<&ManipulatorGroup> = None; + for (i, &(manipulator_index, _)) in points.iter().enumerate() { + if (manipulator_index == 0 || manipulator_index == 1) && !subpath.closed { + last_manipulator_index = manipulator_index + 1; + continue; + } + + let segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); + if subpath.closed && i == 0 { + to_extend_with_last_group = Some(segment); + } else { + broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); + } + + last_manipulator_index = manipulator_index + 1; + } + + if (last_manipulator_index == subpath.len() || last_manipulator_index == subpath.len() - 1) && !subpath.closed { + continue; + } + + let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); + + if let Some(group) = to_extend_with_last_group { + final_segment.extend(group); + } + + broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); + } + } + } + + responses.add(GraphOperationMessage::Vector { + layer, + modification: VectorDataModification::UpdateSubpaths { subpaths: broken_subpaths }, + }); + } } /// Toggle if the handles should mirror angle across the anchor position. From 77b641480f39f39c97f6880cfaaa833d33dfd4d6 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Mon, 5 Feb 2024 00:55:38 +0800 Subject: [PATCH 7/9] fix: rm unused --- .../document/node_graph/graph_operation_message.rs | 1 - .../graph_operation_message_handler/transform_utils.rs | 10 ---------- libraries/bezier-rs/src/subpath/manipulators.rs | 5 ----- 3 files changed, 16 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs index 9ad80c7147d..d98bf98ed56 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs @@ -133,7 +133,6 @@ pub enum VectorDataModification { SetClosed { index: usize, closed: bool }, SetManipulatorHandleMirroring { id: ManipulatorGroupId, mirror_angle: bool }, SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 }, - ShiftManipulatorGroup { id: ManipulatorGroupId }, ToggleManipulatorHandleMirroring { id: ManipulatorGroupId }, UpdateSubpaths { subpaths: Vec> }, } diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs index 9ccd324707f..33bdbb0ccb4 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs @@ -229,15 +229,6 @@ impl<'a> VectorModificationState<'a> { } } - fn shift_group_to_head(&mut self, id: ManipulatorGroupId) { - for subpath in self.subpaths.iter_mut() { - if let Some(index) = subpath.manipulator_index_from_id(id) { - subpath.shift_manipulator_group_to_head(index); - break; - } - } - } - fn remove_point(&mut self, point: ManipulatorPointId) { for subpath in self.subpaths.iter_mut() { if point.manipulator_type == SelectedType::Anchor { @@ -309,7 +300,6 @@ impl<'a> VectorModificationState<'a> { VectorDataModification::SetClosed { index, closed } => self.subpaths[index].set_closed(closed), VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle } => self.set_mirror(id, mirror_angle), VectorDataModification::SetManipulatorPosition { point, position } => self.set_position(point, position), - VectorDataModification::ShiftManipulatorGroup { id } => self.shift_group_to_head(id), VectorDataModification::ToggleManipulatorHandleMirroring { id } => self.toggle_mirror(id), VectorDataModification::UpdateSubpaths { subpaths } => *self.subpaths = subpaths, } diff --git a/libraries/bezier-rs/src/subpath/manipulators.rs b/libraries/bezier-rs/src/subpath/manipulators.rs index ebaead2dcdf..336b81c8f90 100644 --- a/libraries/bezier-rs/src/subpath/manipulators.rs +++ b/libraries/bezier-rs/src/subpath/manipulators.rs @@ -51,11 +51,6 @@ impl Subpath { self.manipulator_groups.remove(index) } - /// shift the vec to let the element at index to be the first element. - pub fn shift_manipulator_group_to_head(&mut self, index: usize) { - self.manipulator_groups.rotate_left(index); - } - /// Inserts a `ManipulatorGroup` at a certain point along the subpath based on the parametric `t`-value provided. /// Expects `t` to be within the inclusive range `[0, 1]`. pub fn insert(&mut self, t: SubpathTValue) { From ce131b284ab0d2708c1e24ded5a921583672e590 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sun, 4 Feb 2024 17:45:48 -0800 Subject: [PATCH 8/9] First code review pass --- .../messages/input_mapper/default_mapping.rs | 10 +- .../tool/common_functionality/shape_editor.rs | 152 +++++++++--------- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index ccf495654ef..2aed6d6671a 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -174,13 +174,13 @@ pub fn default_mapping() -> Mapping { entry!(KeyUp(Lmb); action_dispatch=LineToolMessage::DragStop), entry!(KeyDown(Rmb); action_dispatch=LineToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=LineToolMessage::Abort), - entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=LineToolMessage::PointerMove { center: Alt, lock_angle: Control, snap_angle: Shift }), + entry!(PointerMove; refresh_keys=[Alt, Control, Shift], action_dispatch=LineToolMessage::PointerMove { center: Alt, lock_angle: Control, snap_angle: Shift }), // // PathToolMessage - entry!(KeyDown(Delete); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), - entry!(KeyDown(Backspace); modifiers=[Shift, Control], action_dispatch=PathToolMessage::BreakPath), - entry!(KeyDown(Delete); modifiers=[Control], action_dispatch=PathToolMessage::DeleteAndBreakPath), - entry!(KeyDown(Backspace); modifiers=[Control], action_dispatch=PathToolMessage::DeleteAndBreakPath), + entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath), + entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath), + entry!(KeyDown(Delete); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath), + entry!(KeyDown(Backspace); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Lmb); action_dispatch=PathToolMessage::DragStart { add_to_selection: Shift }), entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 3ea698e9669..d715db5a2c5 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -675,7 +675,6 @@ impl ShapeState { } } - /// Break path at selected points. pub fn break_path_at_selected_point(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { let Some(subpaths) = get_subpaths(layer, document_network) else { @@ -699,57 +698,55 @@ impl ShapeState { }) .collect(); - points.sort_by(|&a, &b| { - return match a.0 > b.0 { - true => std::cmp::Ordering::Greater, - false => std::cmp::Ordering::Less, - }; - }); + if points.is_empty() { + broken_subpaths.push(subpath.clone()); + continue; + } - match points.len() { - 0 => broken_subpaths.push(subpath.clone()), - _ => { - let mut last_manipulator_index = 0; - let mut to_extend_with_last_group: Option>> = None; - let mut last_manipulator_group: Option<&ManipulatorGroup> = None; - for (i, &(manipulator_index, group)) in points.iter().enumerate() { - if manipulator_index == 0 && !subpath.closed { - last_manipulator_index = manipulator_index + 1; - last_manipulator_group = Some(group); - continue; - } + points.sort_by(|&a, &b| match a.0 > b.0 { + true => std::cmp::Ordering::Greater, + false => std::cmp::Ordering::Less, + }); - let mut segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); - if i != 0 { - segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); - } + let mut last_manipulator_index = 0; + let mut to_extend_with_last_group: Option>> = None; + let mut last_manipulator_group: Option<&ManipulatorGroup> = None; + for (i, &(manipulator_index, group)) in points.iter().enumerate() { + if manipulator_index == 0 && !subpath.closed { + last_manipulator_index = manipulator_index + 1; + last_manipulator_group = Some(group); + continue; + } - segment.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); + let mut segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); + if i != 0 { + segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); + } - if subpath.closed && i == 0 { - to_extend_with_last_group = Some(segment); - } else { - broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); - } + segment.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); - last_manipulator_index = manipulator_index + 1; - last_manipulator_group = Some(group); - } + if subpath.closed && i == 0 { + to_extend_with_last_group = Some(segment); + } else { + broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); + } - if last_manipulator_index == subpath.len() && !subpath.closed { - continue; - } + last_manipulator_index = manipulator_index + 1; + last_manipulator_group = Some(group); + } - let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); - final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); + if last_manipulator_index == subpath.len() && !subpath.closed { + continue; + } - if let Some(group) = to_extend_with_last_group { - final_segment.extend(group); - } + let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); + final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); - broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); - } + if let Some(group) = to_extend_with_last_group { + final_segment.extend(group); } + + broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); } responses.add(GraphOperationMessage::Vector { @@ -759,17 +756,17 @@ impl ShapeState { } } - /// Delete point and its adjacent segments, then break path. + /// Delete point(s) and adjacent segments, which breaks a closed path as open, or an open path into multiple. pub fn delete_point_and_break_path(&self, document_network: &NodeNetwork, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { let Some(subpaths) = get_subpaths(layer, document_network) else { continue; }; - let mut broken_subpaths = Vec::>::new(); + let mut broken_subpaths = Vec::>::with_capacity(subpaths.len()); for subpath in subpaths { - let mut points: Vec<_> = state + let mut selected_points: Vec<_> = state .selected_points .iter() .filter_map(|&point| { @@ -783,48 +780,45 @@ impl ShapeState { }) .collect(); - points.sort_by(|&a, &b| { - return match a.0 > b.0 { - true => std::cmp::Ordering::Greater, - false => std::cmp::Ordering::Less, - }; - }); + if selected_points.is_empty() { + broken_subpaths.push(subpath.clone()); + continue; + } - match points.len() { - 0 => broken_subpaths.push(subpath.clone()), - _ => { - let mut last_manipulator_index = 0; - let mut to_extend_with_last_group: Option>> = None; - // let mut last_manipulator_group: Option<&ManipulatorGroup> = None; - for (i, &(manipulator_index, _)) in points.iter().enumerate() { - if (manipulator_index == 0 || manipulator_index == 1) && !subpath.closed { - last_manipulator_index = manipulator_index + 1; - continue; - } + selected_points.sort_by(|&a, &b| match a.0 > b.0 { + true => std::cmp::Ordering::Greater, + false => std::cmp::Ordering::Less, + }); - let segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); - if subpath.closed && i == 0 { - to_extend_with_last_group = Some(segment); - } else { - broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); - } + let mut last_manipulator_index = 0; + let mut to_extend_with_last_group: Option>> = None; + for (i, &(manipulator_index, _)) in selected_points.iter().enumerate() { + if (manipulator_index == 0 || manipulator_index == 1) && !subpath.closed { + last_manipulator_index = manipulator_index + 1; + continue; + } - last_manipulator_index = manipulator_index + 1; - } + let segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); + if subpath.closed && i == 0 { + to_extend_with_last_group = Some(segment); + } else { + broken_subpaths.push(bezier_rs::Subpath::new(segment, false)); + } - if (last_manipulator_index == subpath.len() || last_manipulator_index == subpath.len() - 1) && !subpath.closed { - continue; - } + last_manipulator_index = manipulator_index + 1; + } - let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); + if (last_manipulator_index == subpath.len() || last_manipulator_index == subpath.len() - 1) && !subpath.closed { + continue; + } - if let Some(group) = to_extend_with_last_group { - final_segment.extend(group); - } + let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); - broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); - } + if let Some(group) = to_extend_with_last_group { + final_segment.extend(group); } + + broken_subpaths.push(bezier_rs::Subpath::new(final_segment, false)); } responses.add(GraphOperationMessage::Vector { From c408f94c0909de20668aa98e6b9745235130bca3 Mon Sep 17 00:00:00 2001 From: zhiyuang Date: Mon, 5 Feb 2024 10:22:18 +0800 Subject: [PATCH 9/9] fix: closed eclipse handles after breaking path --- .../tool/common_functionality/shape_editor.rs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index d715db5a2c5..a2f37c6afab 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -720,7 +720,7 @@ impl ShapeState { let mut segment = subpath.manipulator_groups()[last_manipulator_index..manipulator_index].to_vec(); if i != 0 { - segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); + segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().out_handle)); } segment.push(ManipulatorGroup::new(group.anchor, group.in_handle, None)); @@ -740,7 +740,7 @@ impl ShapeState { } let mut final_segment = subpath.manipulator_groups()[last_manipulator_index..].to_vec(); - final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().in_handle)); + final_segment.insert(0, ManipulatorGroup::new(last_manipulator_group.unwrap().anchor, None, last_manipulator_group.unwrap().out_handle)); if let Some(group) = to_extend_with_last_group { final_segment.extend(group); @@ -766,33 +766,21 @@ impl ShapeState { let mut broken_subpaths = Vec::>::with_capacity(subpaths.len()); for subpath in subpaths { - let mut selected_points: Vec<_> = state - .selected_points - .iter() - .filter_map(|&point| { - let Some(manipulator_index) = subpath.manipulator_index_from_id(point.group) else { - return None; - }; - let Some(manipulator) = subpath.manipulator_from_id(point.group) else { - return None; - }; - Some((manipulator_index, manipulator)) - }) - .collect(); + let mut selected_points: Vec<_> = state.selected_points.iter().filter_map(|&point| subpath.manipulator_index_from_id(point.group)).collect(); if selected_points.is_empty() { broken_subpaths.push(subpath.clone()); continue; } - selected_points.sort_by(|&a, &b| match a.0 > b.0 { + selected_points.sort_by(|&a, &b| match a > b { true => std::cmp::Ordering::Greater, false => std::cmp::Ordering::Less, }); let mut last_manipulator_index = 0; let mut to_extend_with_last_group: Option>> = None; - for (i, &(manipulator_index, _)) in selected_points.iter().enumerate() { + for (i, &manipulator_index) in selected_points.iter().enumerate() { if (manipulator_index == 0 || manipulator_index == 1) && !subpath.closed { last_manipulator_index = manipulator_index + 1; continue;