mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 04:57:54 -06:00
Merge pull request #1513 from vangel76/gap-behaviors-presets
#1510: Behaviors animation presets (22 original, expression-driven)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! EffectCraft's own animation preset library (Effects & Presets ▸ * Animation Presets): the
|
||||
//! Transitions category (original presets built on our Transition effects and layer opacity) and
|
||||
//! the Backgrounds category (generators, noise and colour effects driven by expressions).
|
||||
//! the Backgrounds category (generators, noise and colour effects driven by expressions) and the
|
||||
//! Behaviors category (transform and opacity driven by expressions, no effects).
|
||||
//!
|
||||
//! Each preset adds effects (with values and keyframes) and/or keys layer properties, starting
|
||||
//! at the current time, on every selected layer. The library is data
|
||||
@@ -26,13 +27,17 @@ pub const TRANSITIONS_JSON: &str = include_str!("../../presets/transitions.json"
|
||||
/// The original background presets (`presets/backgrounds.json`).
|
||||
pub const BACKGROUNDS_JSON: &str = include_str!("../../presets/backgrounds.json");
|
||||
|
||||
/// The original behavior presets (`presets/behaviors.json`): expression-driven motion on a layer's
|
||||
/// transform and opacity, no effects.
|
||||
pub const BEHAVIORS_JSON: &str = include_str!("../../presets/behaviors.json");
|
||||
|
||||
/// Keys per animated property, so a hostile inline preset can't allocate without bound.
|
||||
const MAX_KEYS: usize = 256;
|
||||
/// Longest expression text an inline preset may carry.
|
||||
const MAX_EXPR: usize = 4096;
|
||||
|
||||
fn presets() -> Vec<Value> {
|
||||
[TRANSITIONS_JSON, BACKGROUNDS_JSON]
|
||||
[TRANSITIONS_JSON, BACKGROUNDS_JSON, BEHAVIORS_JSON]
|
||||
.iter()
|
||||
.flat_map(|src| serde_json::from_str::<Value>(src).ok().and_then(|v| v.get("presets").and_then(Value::as_array).cloned()).unwrap_or_default())
|
||||
.collect()
|
||||
|
||||
@@ -78,7 +78,7 @@ fn transition_preset_by_name_and_bad_input() {
|
||||
#[test]
|
||||
fn preset_catalogue_is_unique_and_categorised() {
|
||||
let all = crate::animation_presets();
|
||||
let known = ["Transitions", "Backgrounds"];
|
||||
let known = ["Transitions", "Backgrounds", "Behaviors"];
|
||||
let mut ids = std::collections::HashSet::new();
|
||||
let mut names = std::collections::HashSet::new();
|
||||
for (id, name, cat) in &all {
|
||||
@@ -90,7 +90,7 @@ fn preset_catalogue_is_unique_and_categorised() {
|
||||
assert!(!ids.contains(&id.to_lowercase()) && !names.contains(&name.to_lowercase()), "text preset {id} clashes");
|
||||
}
|
||||
let n = |c: &str| all.iter().filter(|(_, _, k)| k == c).count();
|
||||
assert!(n("Backgrounds") >= 15 && n("Transitions") >= 20);
|
||||
assert!(n("Backgrounds") >= 15 && n("Transitions") >= 20 && n("Behaviors") >= 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -126,3 +126,34 @@ fn preset_expressions_and_point_fractions() {
|
||||
assert_eq!(ramp.prop("end").unwrap().value, effectcraft_keyframe::Value::Vec2([80.0, 90.0]));
|
||||
assert_eq!(ramp.prop("startColor").unwrap().keys.len(), 2);
|
||||
}
|
||||
|
||||
/// #1510: Behaviors presets add no effects; each puts an enabled expression on a transform
|
||||
/// property, and undo removes it again.
|
||||
#[test]
|
||||
fn every_behavior_preset_applies_and_undoes() {
|
||||
let (mut s, solid) = session();
|
||||
let list = s.execute("anim.builtinPresets", json!({"category": "Behaviors"})).unwrap();
|
||||
let list = list.as_array().unwrap().clone();
|
||||
assert!(list.len() >= 20, "{}", list.len());
|
||||
let driven = |s: &Session| {
|
||||
let l = &s.active_comp().unwrap().layers[0];
|
||||
["transform/position", "transform/scale", "transform/rotation", "transform/opacity"]
|
||||
.iter()
|
||||
.filter(|p| l.props.prop(p).is_some_and(|pr| pr.has_expression()))
|
||||
.copied()
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
for p in &list {
|
||||
let id = p["id"].as_str().unwrap();
|
||||
let r = s.execute("anim.applyBuiltinPreset", json!({"layers": [solid], "preset": id})).unwrap();
|
||||
assert_eq!(r["skipped"], json!([]), "{id}: every path and expression applies");
|
||||
assert_eq!(r["effects"], json!([]), "{id}: behaviors add no effects");
|
||||
let applied = driven(&s);
|
||||
assert_eq!(applied.len(), 1, "{id}: exactly one property is driven");
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert!(driven(&s).is_empty(), "{id}: undo removes the expression");
|
||||
s.execute("edit.redo", json!({})).unwrap();
|
||||
assert_eq!(driven(&s), applied, "{id}: redo restores the expression");
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user