mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 16:38:07 +00:00
Merge pull request #5538 from dotCooCoo/fix/convert-replaced-spine-handles-only
Blends: Convert on a replaced spine changes only its handles
This commit is contained in:
@@ -112,7 +112,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Convert Anchor Points",
|
||||
[],
|
||||
None,
|
||||
"{to: \"corner\"|\"smooth\"} convert the direct-selected anchors (all anchors of fully selected paths, every point of a selected blend's spine) as one undo step: corner retracts both handles, smooth pulls handles in line with the neighbours (smooth anchors keep theirs)",
|
||||
"{to: \"corner\"|\"smooth\"} convert the direct-selected anchors (all anchors of fully selected paths, every point of a selected blend's spine; a spine from Replace Spine changes only its handles, its points and the keys' spread along it staying as they are) as one undo step: corner retracts both handles, smooth pulls handles in line with the neighbours (smooth anchors keep theirs)",
|
||||
has_selection,
|
||||
convert_anchors
|
||||
),
|
||||
@@ -1155,11 +1155,7 @@ fn convert_anchors(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in &blends {
|
||||
let Some(NodeKind::Blend { children, spec }) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
let Some((mut path, anchors)) = vectorcraft_doc::live::pin_spine(children, spec) else { continue };
|
||||
let pinned = path.clone();
|
||||
let Some(sp) = path.subpaths.first_mut() else { continue };
|
||||
let convert = |sp: &mut SubPath| {
|
||||
for ai in 0..sp.anchors.len() {
|
||||
if smooth {
|
||||
sp.smooth_anchor(ai);
|
||||
@@ -1167,6 +1163,21 @@ fn convert_anchors(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
a.retract();
|
||||
}
|
||||
}
|
||||
};
|
||||
for id in &blends {
|
||||
let Some(NodeKind::Blend { children, spec }) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
// A spine from Replace Spine keeps its points and its keys spread along it: only the
|
||||
// handles change, and the keys are not pinned to points.
|
||||
if vectorcraft_doc::live::blend_spine(children, spec).is_some_and(|(_, anchors)| anchors.is_none()) {
|
||||
if let Some(sp) = spec.spine.as_mut().and_then(|p| p.subpaths.first_mut()) {
|
||||
convert(sp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let Some((mut path, anchors)) = vectorcraft_doc::live::pin_spine(children, spec) else { continue };
|
||||
let pinned = path.clone();
|
||||
let Some(sp) = path.subpaths.first_mut() else { continue };
|
||||
convert(sp);
|
||||
// A spine the conversion leaves as it was stays as the blend has it.
|
||||
if path != pinned {
|
||||
spec.key_anchors = anchors.iter().map(|a| u32::try_from(*a).unwrap_or(u32::MAX)).collect();
|
||||
|
||||
@@ -144,6 +144,42 @@ fn converting_anchors_of_a_selected_blend_converts_its_spine() {
|
||||
assert_eq!(spec(&s, g).spine.unwrap().subpaths[0].anchors[1], middle, "one undo step");
|
||||
}
|
||||
|
||||
/// Converting the points of a blend whose spine came from Replace Spine changes only the spine's
|
||||
/// handles: its points stay where they are, the keys are not pinned to points, and the steps stay
|
||||
/// spread along the spine instead of the spine's ends moving onto the keys.
|
||||
#[test]
|
||||
fn converting_anchors_of_a_replaced_spine_changes_only_its_handles() {
|
||||
let mut s = session();
|
||||
let (_, _, g) = blend(&mut s);
|
||||
let spine = id_of(&s.execute("path.create", &json!({"d": "M100 300 L200 200 L300 300"})).unwrap());
|
||||
s.execute("select.set", &json!({"ids": [g.0, spine.0]})).unwrap();
|
||||
s.execute("object.blend.replaceSpine", &json!({})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": [g.0]})).unwrap();
|
||||
let points = |s: &Session| spec(s, g).spine.unwrap().subpaths[0].anchors.iter().map(|a| a.p).collect::<Vec<_>>();
|
||||
let before = points(&s);
|
||||
let ends = |s: &Session| {
|
||||
let st = steps(s, g);
|
||||
(center(&st[0]), center(&st[st.len() - 1]))
|
||||
};
|
||||
let ends_before = ends(&s);
|
||||
s.execute("path.convertAnchors", &json!({"to": "smooth"})).unwrap();
|
||||
let sp = spec(&s, g);
|
||||
assert!(sp.key_anchors.is_empty(), "{:?}", sp.key_anchors);
|
||||
assert_eq!(points(&s), before, "the spine's points stay put");
|
||||
let middle = sp.spine.as_ref().unwrap().subpaths[0].anchors[1];
|
||||
assert!(middle.has_in() && middle.has_out(), "{middle:?}");
|
||||
assert_eq!(ends(&s), ends_before, "the end steps stay at the spine's ends");
|
||||
assert!(center(&steps(&s, g)[1]).distance(Point::new(200.0, 200.0)) < 1e-6, "the step stays on the spine");
|
||||
|
||||
s.execute("path.convertAnchors", &json!({"to": "corner"})).unwrap();
|
||||
let sp = spec(&s, g);
|
||||
assert!(sp.key_anchors.is_empty(), "{:?}", sp.key_anchors);
|
||||
assert!(sp.spine.unwrap().subpaths[0].anchors.iter().all(|a| !a.has_in() && !a.has_out()));
|
||||
assert_eq!(points(&s), before);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(spec(&s, g).spine.unwrap().subpaths[0].anchors[1], middle, "one undo step");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moving_a_key_point_moves_the_key_and_the_other_way_round() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user