mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 21:56:24 +00:00
Blend: moving an end key of a closed replaced spine moves its end (#6809)
On a closed spine from Replace Spine both end keys sit on its first point, so moving one moved that point onto it and then back onto the other. Once a key is moved off, the spine is now opened at its first point, with a copy of it at the end taking the closing segment and the last key.
This commit is contained in:
+17
-1
@@ -1085,8 +1085,9 @@ fn layout_spine(keys: &[Arc<Node>], spec: &BlendSpec) -> Option<(PathData, Optio
|
||||
if Spine::new(&path).is_none_or(|sp| keys_spread_along(keys, &sp)) {
|
||||
return Some((path, None));
|
||||
}
|
||||
let Some(ka) = spread_anchors(&mut path, keys.len()) else { return Some((path, None)) };
|
||||
let Some(mut ka) = spread_anchors(&mut path, keys.len()) else { return Some((path, None)) };
|
||||
let mut first = path.subpaths.into_iter().next()?;
|
||||
open_at_start(&mut first, &mut ka);
|
||||
for (k, &ai) in keys.iter().zip(&ka) {
|
||||
if let Some(a) = first.anchors.get_mut(ai) {
|
||||
let d = center_of(k) - a.p;
|
||||
@@ -1132,6 +1133,21 @@ fn keys_spread_along(keys: &[Arc<Node>], spine: &Spine) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
/// Open closed spine `sp` at its first anchor when the last key sits there with the first (as
|
||||
/// [`spread_anchors`] puts them): a copy of that anchor at the end takes the closing segment and
|
||||
/// the last key, so the two end keys move their ends of the spine apart.
|
||||
fn open_at_start(sp: &mut SubPath, ka: &mut [usize]) {
|
||||
let Some(&start) = sp.anchors.first() else { return };
|
||||
if !sp.closed || ka.len() < 2 {
|
||||
return;
|
||||
}
|
||||
if let Some(last) = ka.last_mut().filter(|a| **a == 0) {
|
||||
*last = sp.anchors.len();
|
||||
sp.anchors.push(start);
|
||||
sp.closed = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// The anchors of `path`'s first subpath that `k` keys spread evenly along it by arc length sit
|
||||
/// on, splitting segments where none is.
|
||||
fn spread_anchors(path: &mut PathData, k: usize) -> Option<Vec<usize>> {
|
||||
|
||||
@@ -455,6 +455,35 @@ fn moving_a_key_of_a_replaced_spine_blend_moves_what_is_drawn() {
|
||||
assert!(center(&back[1]).distance(center(&before[1])) < 1e-6);
|
||||
}
|
||||
|
||||
/// #6809 on a closed spine, where both end keys start on its first point: moving the first key
|
||||
/// moves its end of the spine without dragging the last key's end along.
|
||||
#[test]
|
||||
fn moving_the_first_key_of_a_closed_replaced_spine_blend_moves_what_is_drawn() {
|
||||
let mut s = session();
|
||||
let (a, _, g) = blend(&mut s, json!({"steps": 5}));
|
||||
let square = id_of(&s.execute("path.create", &json!({"d": "M100 100 L300 100 L300 300 L100 300 Z"})).unwrap());
|
||||
sel(&mut s, &[g, square]);
|
||||
s.execute("object.blend.replaceSpine", &json!({})).unwrap();
|
||||
let center = |n: &Node| n.geometric_bounds().unwrap().center();
|
||||
let before = steps_of(&s, g);
|
||||
assert!(center(&before[0]).distance(Point::new(100.0, 100.0)) < 1e-6);
|
||||
sel(&mut s, &[a]);
|
||||
s.execute("object.move", &json!({"dx": -50, "dy": -40})).unwrap();
|
||||
let key = center(&node(&s, a));
|
||||
assert!(key.distance(Point::new(50.0, 60.0)) < 1e-6, "{key:?}");
|
||||
let after = steps_of(&s, g);
|
||||
assert_eq!(after.len(), before.len());
|
||||
let drawn = center(&after[0]);
|
||||
assert!(drawn.distance(key) < 1e-6, "the key is drawn where it was moved to: {drawn:?}, outline at {key:?}");
|
||||
assert!(center(&after[1]).distance(center(&before[1])) > 1.0, "the first step follows the moved key");
|
||||
let last = center(&after[after.len() - 1]);
|
||||
assert!(last.distance(Point::new(100.0, 100.0)) < 1e-6, "the other key stays put: {last:?}");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
let back = steps_of(&s, g);
|
||||
assert!(center(&back[0]).distance(Point::new(100.0, 100.0)) < 1e-6);
|
||||
assert!(center(&back[1]).distance(center(&before[1])) < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blend_align_to_path_rotates_steps() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user