Curvature: deleting a point re-curves the path through the remaining points

Deleting a point of a finished path with the Curvature tool sent
path.removeAnchor, which refits the two segments that met at the point so
one curve follows them. The path kept the bend of the deleted point
instead of curving through the points that remain.

The tool now sends path.removeAnchor with curvature: true, which uses the
new curvature_remove. Each smooth neighbor takes new handles from its new
neighbors, and an end beside one of them takes its end handle again. A
corner neighbor keeps its handle directions, and its handle on the new
segment scales with the segment's chord. With two points left, the points
that curve lose their handles. Object > Path > Remove Anchor Points and
the Delete Anchor Point tool still refit the curve.
This commit is contained in:
dotCooCoo
2026-10-29 10:00:30 +07:00
committed by bflatastic
parent 4661615af4
commit 686cd1f2f5
6 changed files with 217 additions and 11 deletions
+12 -5
View File
@@ -109,13 +109,14 @@ pub(super) fn remove_anchor(s: &mut Session, p: &Value) -> Result<Value> {
let id = id_param(p, "id").ok_or_else(|| bad(C, "missing id"))?;
let si = usize_opt(p, "subpath", C)?.unwrap_or(0);
let ai = usize_req(p, "anchor", C)?;
let curvature = switch_or(p, "curvature", false, C)?;
let removed = s.edit("Delete Anchor Point", |d, sel| {
let sp = path_mut(d, id)?.subpaths.get(si).ok_or_else(|| EngineError::Other("no such subpath".into()))?;
if ai >= sp.anchors.len() {
return Err(EngineError::Other("no such anchor".into()));
}
let mut emptied = Beside::replacing(d, &[id]);
let removed = remove_anchors_of(d, sel, &mut emptied, id, [(si, ai)])?;
let removed = remove_anchors_of(d, sel, &mut emptied, id, [(si, ai)], curvature)?;
emptied.finish(d)?;
Ok(removed)
})?;
@@ -141,7 +142,7 @@ pub(super) fn remove_anchors(s: &mut Session, _: &Value) -> Result<Value> {
let shape: Vec<(usize, bool)> =
d.node(id).and_then(Node::path_data).map(|p| p.subpaths.iter().map(|sp| (sp.anchors.len(), sp.closed)).collect()).unwrap_or_default();
let next = following_anchors(&shape, &anchors);
if remove_anchors_of(d, sel, &mut emptied, id, anchors)? {
if remove_anchors_of(d, sel, &mut emptied, id, anchors, false)? {
removed += 1;
} else if !next.is_empty() {
// The next anchor along is selected, so the next Remove Anchor Points goes on from
@@ -180,14 +181,16 @@ fn following_anchors(shape: &[(usize, bool)], removed: &[(usize, usize)]) -> BTr
}
/// Remove `anchors` (subpath, anchor) of path `id` without opening it, refitting the curve round
/// each one. A subpath left with one anchor is removed, and a path left with no subpath is removed
/// through `emptied`. True when the path went.
/// each one, or with `curvature` re-curving it as the Curvature tool curves its points
/// ([`vectorcraft_tools::draw2::curvature_remove`]). A subpath left with one anchor is removed,
/// and a path left with no subpath is removed through `emptied`. True when the path went.
fn remove_anchors_of(
d: &mut Document,
sel: &mut Selection,
emptied: &mut Beside,
id: NodeId,
anchors: impl IntoIterator<Item = (usize, usize)>,
curvature: bool,
) -> Result<bool> {
let mut anchors: Vec<(usize, usize)> = anchors.into_iter().collect();
// The last first, so the indexes still to come stay valid.
@@ -196,7 +199,11 @@ fn remove_anchors_of(
let path = path_mut(d, id)?;
for (si, ai) in anchors {
if let Some(sp) = path.subpaths.get_mut(si) {
po::remove_anchor(sp, ai);
if curvature {
vectorcraft_tools::draw2::curvature_remove(sp, ai);
} else {
po::remove_anchor(sp, ai);
}
}
}
path.subpaths.retain(|sp| sp.anchors.len() >= 2);
+1 -1
View File
@@ -38,7 +38,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Delete Anchor Point",
[],
None,
"{id, subpath?, anchor} remove one anchor, re-fitting the curve",
"{id, subpath?, anchor, curvature?} remove one anchor, re-fitting the curve (with curvature: true, the curve where it was follows the remaining anchors, as the Curvature tool curves its points)",
has_doc,
remove_anchor
),