mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 12:13:19 +00:00
Merge pull request #5904 from mockydevs/fix/blend-spine-review
Blend spine: moveAnchor refuses `with` alongside `handle`; spine built once per overlay and drag
This commit is contained in:
@@ -205,7 +205,8 @@ pub(super) fn spine_move_anchor(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
};
|
||||
let independent = switch_or(p, "independent", false, C)?;
|
||||
let with = spine_points_param(p, C)?;
|
||||
if handle.is_some() && !with.is_empty() {
|
||||
// Any `with`, even an empty list, asks to move points, which a handle drag doesn't.
|
||||
if handle.is_some() && p.get("with").is_some_and(|v| !v.is_null()) {
|
||||
return Err(bad(C, "`with` moves points, not a handle"));
|
||||
}
|
||||
edit_spine(s, p, C, "Reshape Spine", |sp, anchors, keys| {
|
||||
|
||||
@@ -76,6 +76,7 @@ fn spine_points_move_together() {
|
||||
let before = spec(&s, g);
|
||||
assert!(s.execute("object.blend.spine.moveAnchor", &json!({"anchor": 1, "x": 55, "y": 45, "with": [9]})).is_err());
|
||||
assert!(s.execute("object.blend.spine.moveAnchor", &json!({"anchor": 1, "x": 55, "y": 45, "with": [2], "handle": "in"})).is_err());
|
||||
assert!(s.execute("object.blend.spine.moveAnchor", &json!({"anchor": 1, "x": 55, "y": 45, "with": [], "handle": "in"})).is_err());
|
||||
assert!(s.execute("object.blend.spine.moveAnchor", &json!({"anchor": 1, "x": 55, "y": 45, "with": "2"})).is_err());
|
||||
assert_eq!(spec(&s, g), before, "refused requests change nothing");
|
||||
s.execute("object.blend.spine.moveAnchor", &json!({"anchor": 1, "x": 55, "y": 45, "with": [2, 1]})).unwrap();
|
||||
|
||||
@@ -396,8 +396,13 @@ fn hit_spine_handle(cx: &ToolContext, sel: (NodeId, usize), p: Point, tol: f64)
|
||||
|
||||
/// Anchor `sel.1` of blend `sel.0`'s spine.
|
||||
fn spine_anchor(cx: &ToolContext, (id, ai): (NodeId, usize)) -> Option<vectorcraft_geom::Anchor> {
|
||||
spine_path(cx, id)?.subpaths.first()?.anchors.get(ai).copied()
|
||||
}
|
||||
|
||||
/// Blend `id`'s spine, built once for callers that look up several of its points.
|
||||
fn spine_path(cx: &ToolContext, id: NodeId) -> Option<PathData> {
|
||||
match &cx.doc.node(id)?.kind {
|
||||
NodeKind::Blend { children, spec } => vectorcraft_doc::live::blend_spine(children, spec)?.0.subpaths.first()?.anchors.get(ai).copied(),
|
||||
NodeKind::Blend { children, spec } => Some(vectorcraft_doc::live::blend_spine(children, spec)?.0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -897,7 +902,9 @@ impl Tool for DirectSelectionTool {
|
||||
}
|
||||
let q = from + move_delta(start, p, ev.mods.shift, cx.constrain_angle);
|
||||
let mut params = json!({"id": id.0, "anchor": anchor, "x": q.x, "y": q.y});
|
||||
let with: Vec<usize> = self.spine_more.iter().copied().filter(|a| spine_anchor(cx, (id, *a)).is_some()).collect();
|
||||
let spine = spine_path(cx, id);
|
||||
let count = spine.as_ref().and_then(|sp| sp.subpaths.first()).map_or(0, |sp| sp.anchors.len());
|
||||
let with: Vec<usize> = self.spine_more.iter().copied().filter(|a| *a < count).collect();
|
||||
if !with.is_empty() {
|
||||
params["with"] = json!(with);
|
||||
}
|
||||
@@ -1164,7 +1171,9 @@ impl DirectSelectionTool {
|
||||
/// The clicked spine point, filled, with its handles.
|
||||
fn spine_overlays(&self, cx: &ToolContext) -> Vec<Overlay> {
|
||||
let Some(sel) = self.spine.filter(|(id, _)| cx.selection.contains(*id)) else { return vec![] };
|
||||
let Some(a) = spine_anchor(cx, sel) else { return vec![] };
|
||||
let Some(spine) = spine_path(cx, sel.0) else { return vec![] };
|
||||
let Some(points) = spine.subpaths.first().map(|sp| &sp.anchors) else { return vec![] };
|
||||
let Some(a) = points.get(sel.1).copied() else { return vec![] };
|
||||
let color = cx.doc.layer_color(sel.0);
|
||||
let mut out = vec![];
|
||||
for h in [a.h_in, a.h_out].into_iter().filter(|h| h.distance(a.p) > 1e-6) {
|
||||
@@ -1172,12 +1181,7 @@ impl DirectSelectionTool {
|
||||
out.push(Overlay::Handle { p: h, color });
|
||||
}
|
||||
out.push(Overlay::Anchor { p: a.p, color, filled: true, size: 5.0 });
|
||||
out.extend(self.spine_more.iter().filter_map(|i| spine_anchor(cx, (sel.0, *i))).map(|m| Overlay::Anchor {
|
||||
p: m.p,
|
||||
color,
|
||||
filled: true,
|
||||
size: 5.0,
|
||||
}));
|
||||
out.extend(self.spine_more.iter().filter_map(|i| points.get(*i)).map(|m| Overlay::Anchor { p: m.p, color, filled: true, size: 5.0 }));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user