mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 12:18:12 +08:00
Support for deleting points to break path (#1593)
* feat: break closed curve * feat: update hotkeys and handles * feat: break an open path * feat: elegantly handle breaking at multi points in a subpath * feat: handle break at end points * feat: ctrl+delete to remove segments and break path * fix: rm unused * First code review pass * fix: closed eclipse handles after breaking path --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
a4a2680ac4
commit
a412a77062
@@ -675,6 +675,147 @@ impl ShapeState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn break_path_at_selected_point(&self, document_network: &NodeNetwork, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(subpaths) = get_subpaths(layer, document_network) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut broken_subpaths = Vec::<bezier_rs::Subpath<ManipulatorGroupId>>::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();
|
||||
|
||||
if points.is_empty() {
|
||||
broken_subpaths.push(subpath.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
points.sort_by(|&a, &b| match a.0 > b.0 {
|
||||
true => std::cmp::Ordering::Greater,
|
||||
false => std::cmp::Ordering::Less,
|
||||
});
|
||||
|
||||
let mut last_manipulator_index = 0;
|
||||
let mut to_extend_with_last_group: Option<Vec<ManipulatorGroup<ManipulatorGroupId>>> = None;
|
||||
let mut last_manipulator_group: Option<&ManipulatorGroup<ManipulatorGroupId>> = 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;
|
||||
}
|
||||
|
||||
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().out_handle));
|
||||
}
|
||||
|
||||
segment.push(ManipulatorGroup::new(group.anchor, group.in_handle, None));
|
||||
|
||||
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;
|
||||
last_manipulator_group = Some(group);
|
||||
}
|
||||
|
||||
if last_manipulator_index == subpath.len() && !subpath.closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
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().out_handle));
|
||||
|
||||
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 },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(subpaths) = get_subpaths(layer, document_network) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut broken_subpaths = Vec::<bezier_rs::Subpath<ManipulatorGroupId>>::with_capacity(subpaths.len());
|
||||
|
||||
for subpath in subpaths {
|
||||
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 > 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<Vec<ManipulatorGroup<ManipulatorGroupId>>> = 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;
|
||||
}
|
||||
|
||||
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.
|
||||
pub fn toggle_handle_mirroring_on_selected(&self, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
|
||||
Reference in New Issue
Block a user