mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 15:54:53 +00:00
Blend: a blend without a spine shows the Appearance widget
A blend made with Create Spine set to Never now shows the Appearance widget with the Selection and Blend tools, so its color Shift can be dragged on the canvas as on a blend with a spine. The Easing widget stays hidden, since easing needs a spine. The widget sits 16 px above the halfway point, by length, of the straight lines between the key centers that the steps are laid out along, and a drag follows those lines' direction there. The lines are not drawn. `blend_layout_line` exposes that line; `blend_spine` still returns `None` for such a blend.
This commit is contained in:
@@ -1369,6 +1369,14 @@ pub fn blend_spine(keys: &[Arc<Node>], spec: &BlendSpec) -> Option<(PathData, Op
|
||||
layout_spine(keys, spec)
|
||||
}
|
||||
|
||||
/// The line the steps of a blend of `keys` are placed along: [`blend_spine`]'s path for a blend
|
||||
/// with a spine, and the straight lines between the key centers, in key order, for a blend made
|
||||
/// without one ([`BlendSpec::no_spine`]). For a blend without a spine the line places the steps
|
||||
/// only; it is not a spine and is not drawn as one. `None` with fewer than two keys.
|
||||
pub fn blend_layout_line(keys: &[Arc<Node>], spec: &BlendSpec) -> Option<PathData> {
|
||||
layout_spine(keys, spec).map(|(path, _)| path)
|
||||
}
|
||||
|
||||
/// The spine `spec` stores, unless the blend was made without one.
|
||||
fn stored_spine(spec: &BlendSpec) -> Option<&PathData> {
|
||||
spec.spine.as_ref().filter(|_| !spec.no_spine)
|
||||
@@ -1840,6 +1848,28 @@ mod tests {
|
||||
assert_eq!(moved.subpaths[0].anchors[2].p, Point::new(305.0, 5.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_blend_without_a_spine_has_a_layout_line_through_its_key_centers() {
|
||||
let at =
|
||||
|x: f64, y: f64| Arc::new(Node::path(NodeId(1), shapes::rectangle(Rect::new(x - 5.0, y - 5.0, x + 5.0, y + 5.0)), Appearance::default()));
|
||||
let line_of = |path: &PathData| path.subpaths.iter().flat_map(|s| s.anchors.iter().map(|a| a.p)).collect::<Vec<_>>();
|
||||
// A stored spine is ignored once the blend is made without one.
|
||||
let stored = PathData::single(SubPath::polyline(&[Point::new(0.0, 50.0), Point::new(300.0, 50.0)], false));
|
||||
let spec = BlendSpec { no_spine: true, spine: Some(stored), ..Default::default() };
|
||||
for keys in [vec![at(10.0, 20.0), at(110.0, 20.0)], vec![at(10.0, 20.0), at(110.0, 20.0), at(110.0, 220.0), at(-40.0, 0.0)]] {
|
||||
assert!(blend_spine(&keys, &spec).is_none(), "still no spine");
|
||||
let line = blend_layout_line(&keys, &spec).unwrap();
|
||||
assert_eq!(line.subpaths.len(), 1);
|
||||
assert!(!line.subpaths[0].closed);
|
||||
let centers: Vec<Point> = keys.iter().map(|k| center_of(k)).collect();
|
||||
assert_eq!(line_of(&line), centers, "in key order");
|
||||
}
|
||||
// With a spine, the layout line is the spine.
|
||||
let keys = vec![key(0.0), key(100.0), key(300.0)];
|
||||
assert_eq!(blend_layout_line(&keys, &BlendSpec::default()), blend_spine(&keys, &BlendSpec::default()).map(|(p, _)| p));
|
||||
assert!(blend_layout_line(&keys[..1], &spec).is_none(), "one key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn steps_follow_a_bent_spine() {
|
||||
let keys = vec![key(0.0), key(100.0)];
|
||||
|
||||
@@ -39,7 +39,7 @@ fn fifty() -> f64 {
|
||||
// =====================================================================================
|
||||
|
||||
pub use crate::blend::{
|
||||
Spine, blend_expand, blend_spine, blend_step_count, lerp_appearance, lerp_color, lerp_node, lerp_paint, lerp_path, pin_spine,
|
||||
Spine, blend_expand, blend_layout_line, blend_spine, blend_step_count, lerp_appearance, lerp_color, lerp_node, lerp_paint, lerp_path, pin_spine,
|
||||
steps_knockout_shows,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user