mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 04:57:54 -06:00
Shapes: Wiggle Paths has Spatial Phase, which slides its wiggle along the path
Wiggle Paths had Size, Detail, Points, Wiggles/Second, Correlation, Temporal Phase and Random Seed, but no Spatial Phase, which After Effects lists among its properties (ADBE Vector Spatial Phase). Spatial Phase now moves each point's own displacement along its subpath: 360 degrees is once around, and a fraction of a point is interpolated between neighbors. At 0 degrees a path draws as before. A project saved without the property gets it at 0 degrees, after Temporal Phase, when it opens. Scripts can read and set every Wiggle Paths property by its match name.
This commit is contained in:
@@ -1313,7 +1313,9 @@ impl Session {
|
||||
/// (renamed / regrouped / added parameters, reordered popups; see
|
||||
/// [`effectcraft_effects::migrate`]), and a project from before
|
||||
/// [`effectcraft_project::SCHEMA_EFFECT_SPACE`] into effect space
|
||||
/// ([`effectcraft_effects::migrate::to_effect_space`]). Stamps the current schema.
|
||||
/// ([`effectcraft_effects::migrate::to_effect_space`]). Shape path operations get the
|
||||
/// properties added since ([`effectcraft_project::build::upgrade_shape_contents`]). Stamps the
|
||||
/// current schema.
|
||||
pub fn upgrade_effects(p: &mut Project) {
|
||||
let to_effect_space = p.schema < effectcraft_project::SCHEMA_EFFECT_SPACE;
|
||||
// Effect bounds per layer: (size, top-left in layer coordinates).
|
||||
@@ -1336,6 +1338,9 @@ pub fn upgrade_effects(p: &mut Project) {
|
||||
Some(id) => bounds.get(&(*cid, effectcraft_project::LayerId(id))).map_or([0.0; 2], |b| shift(b.1)),
|
||||
None => d,
|
||||
};
|
||||
if let Some(contents) = l.props.sub_mut("contents") {
|
||||
effectcraft_project::build::upgrade_shape_contents(&mut effectcraft_project::build::Ids(&mut next), contents);
|
||||
}
|
||||
let Some(fx) = l.props.sub_mut("effects") else { continue };
|
||||
for n in &mut fx.children {
|
||||
let effectcraft_project::Node::Group(g) = n else { continue };
|
||||
|
||||
@@ -280,6 +280,31 @@ fn toolbar_fill_and_stroke_paint_selected_shape_layers() {
|
||||
let _ = s.render(cid, s.time(), Default::default());
|
||||
}
|
||||
|
||||
/// A Wiggle Paths saved without Spatial Phase gets the property, at 0°, after Temporal Phase when
|
||||
/// the project opens.
|
||||
#[test]
|
||||
fn opening_a_wiggle_paths_without_spatial_phase_adds_it() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"width": 400, "height": 300, "duration": 2})).unwrap();
|
||||
let sh = s.execute("layer.newShape", json!({"kind": "rect", "fill": "#ff0000"})).unwrap()["layer"].as_u64().unwrap();
|
||||
let uid = s.execute("layer.addShapeItem", json!({"layer": sh, "kind": "wiggle"})).unwrap()["uid"].as_u64().unwrap();
|
||||
let cid = s.active_comp_id().unwrap();
|
||||
let mut old = effectcraft_project::Project::from_json(&s.project.to_file_json().unwrap()).unwrap();
|
||||
let l = old.comp_mut(cid).unwrap().layer_mut(effectcraft_project::LayerId(sh)).unwrap();
|
||||
let g = l.props.find_group_mut(uid).unwrap();
|
||||
g.children.retain(|c| c.match_id() != "spatialPhase");
|
||||
assert_eq!(g.children.len(), 7);
|
||||
let next = old.next_id;
|
||||
s.replace_project(old, None);
|
||||
let l = s.active_comp().unwrap().layer(effectcraft_project::LayerId(sh)).unwrap().clone();
|
||||
let g = l.props.find_group(uid).unwrap();
|
||||
let ids: Vec<&str> = g.children.iter().map(|c| c.match_id()).collect();
|
||||
assert_eq!(ids, ["size", "detail", "points", "speed", "correlation", "phase", "spatialPhase", "seed"]);
|
||||
let sp = g.get("spatialPhase").unwrap();
|
||||
assert_eq!((sp.name.as_str(), &sp.value), ("Spatial Phase", &effectcraft_keyframe::Value::Scalar(0.0)));
|
||||
assert!(sp.uid >= next && s.project.next_id > sp.uid, "a fresh uid: {} (next {next})", sp.uid);
|
||||
}
|
||||
|
||||
/// The toolbar's Fill Options / Stroke Options change a selected shape layer's paint kind (#732):
|
||||
/// None switches the fill off, a gradient turns the Fill into a Gradient Fill that keeps its
|
||||
/// opacity, Solid Color turns it back, and a Stroke kind adds a stroke where there is none.
|
||||
|
||||
Reference in New Issue
Block a user