Add-ons: Path Precision Tools — Corner Tool and Slide Anchor

Path Precision Tools gains two tools, kept out of the toolbars (All Tools
drawer and Object › Plug-ins › Path Precision Tools, no shortcuts):

- Corner Tool (cornerTool): editable corners on any path through the
  existing Live Corners. Hover shows an eligible anchor, a click cuts a
  corner with the tool's radius and kind, a drag sizes it (Shift: whole
  values), a corner's marker selects it and loads its values, resizes it
  and removes it at 0 or on a double-click; a corner never grows past
  what fits; an Alt-marquee cuts every eligible anchor inside.
- Corners section in the Anchor Inspector: radius, kind, selected count,
  Apply to Selected Anchors.
- Slide Anchor Along Path (slideAnchor): a drag runs an anchor along its
  curve, the neighbouring segments refitted by the keep-shape kernel;
  Shift snaps where the tangent is a multiple of 45° from the constrain
  angle.
- Commands addon.pathtools.cornerSet, cornerRemove and slideAnchor, one
  undo step each, params validated and covered by the command sweep.
- Cursor stand-ins, catalog rows in every language, docs and tests.
This commit is contained in:
bflatastic
2026-10-31 04:21:26 +07:00
parent 0494f505d9
commit c9dc4fa857
55 changed files with 1939 additions and 47 deletions
+24 -9
View File
@@ -1,3 +1,6 @@
use vectorcraft_doc::AnchorRef;
use vectorcraft_geom::PathData;
use super::spans::Span;
use super::{
BTreeSet, CornerKind, EngineError, LiveCorners, LiveShape, NodeId, NodeKind, Result, Session, Value, bad, check_numbers, check_switches,
@@ -249,17 +252,29 @@ pub(super) fn set_live_shape(s: &mut Session, p: &Value) -> Result<Value> {
Some(k) => k.clone(),
None => c.picked(partial.as_deref()),
};
let (selected, layout) = (partial.as_deref().map(|a| c.corners_of(a)), c.sources().to_vec());
set_corners(path, live, &picked, radius, kind);
// Direct-Selected corners stay selected as they gain or lose anchors.
if let (Some(anchors), Some(selected)) = (partial, selected)
&& let Some(c) = LiveCorners::new(path, live.as_deref())
&& c.sources() != layout
{
*anchors = c.anchors_of(&selected);
}
recut(path, live, partial, &picked, radius, kind);
}
Ok(())
})?;
ok()
}
/// [`set_corners`] with the Direct-Selected corners (`partial`, the path's selected anchors)
/// staying selected as they gain or lose anchors.
pub(in crate::cmd) fn recut(
path: &mut PathData,
live: &mut Option<Box<LiveShape>>,
partial: Option<&mut BTreeSet<AnchorRef>>,
picked: &BTreeSet<usize>,
radius: Option<f64>,
kind: Option<CornerKind>,
) {
let before = LiveCorners::new(path, live.as_deref()).map(|c| (partial.as_deref().map(|a| c.corners_of(a)), c.sources().to_vec()));
set_corners(path, live, picked, radius, kind);
if let (Some(anchors), Some((Some(selected), layout))) = (partial, before)
&& let Some(c) = LiveCorners::new(path, live.as_deref())
&& c.sources() != layout
{
*anchors = c.anchors_of(&selected);
}
}