diff --git a/crates/engine/src/cmd/live.rs b/crates/engine/src/cmd/live.rs index 82e2adbff..e0d71726c 100644 --- a/crates/engine/src/cmd/live.rs +++ b/crates/engine/src/cmd/live.rs @@ -14,7 +14,7 @@ use vectorcraft_doc::live::{ PreserveShape, Spine, }; use vectorcraft_doc::{Appearance, Document, Node, NodeId, NodeKind, Selection}; -use vectorcraft_geom::{Affine, PathData, Point, Vec2}; +use vectorcraft_geom::{Affine, Anchor, PathData, Point, Vec2}; use super::edit::selected_roots; use super::expand::gradient_sampler; @@ -96,6 +96,15 @@ pub fn specs() -> Vec { has_doc, spine_move_anchor ), + cmd!( + "object.blend.spine.convertAnchor", + "Convert Spine Point", + [], + None, + "{id? (default: the selected blend), anchor: index, to: smooth|corner, x?, y?} convert a blend spine point: dragging a corner creates symmetric smooth handles, clicking a point with handles retracts both. Without x/y, smooth uses the neighbouring spine points", + has_doc, + spine_convert_anchor + ), cmd!( "object.blend.spine.reshapeSegment", "Reshape Spine Segment", @@ -653,6 +662,37 @@ fn spine_move_anchor(s: &mut Session, p: &Value) -> Result { ok() } +fn spine_convert_anchor(s: &mut Session, p: &Value) -> Result { + const C: &str = "object.blend.spine.convertAnchor"; + let index = index_param(p, "anchor", C)?; + let smooth = match str_param(p, "to") { + Some("smooth") => true, + Some("corner") => false, + _ => return Err(bad(C, "to must be smooth or corner")), + }; + let target = match (p.get("x"), p.get("y")) { + (None, None) => None, + (Some(_), Some(_)) => Some(finite_point(p, C)?), + _ => return Err(bad(C, "x and y must be given together")), + }; + edit_spine(s, p, C, "Convert Spine Point", |sp, _, _| { + if smooth && target.is_none() { + if index >= sp.anchors.len() { + return Err(bad(C, "no such spine point")); + } + vectorcraft_tools::draw2::curvature_smooth(sp, index); + return Ok(()); + } + let a = sp.anchors.get_mut(index).ok_or_else(|| bad(C, "no such spine point"))?; + match target { + Some(q) if smooth && q.distance(a.p) > 1e-9 => *a = Anchor::smooth(a.p, q), + _ => a.retract(), + } + Ok(()) + })?; + ok() +} + fn spine_reshape_segment(s: &mut Session, p: &Value) -> Result { const C: &str = "object.blend.spine.reshapeSegment"; let seg = index_param(p, "segment", C)?;