mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-27 19:47:12 -06:00
114 lines
4.8 KiB
Rust
114 lines
4.8 KiB
Rust
//! Key attributes preserve the order used by the next stagger gesture.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use effectcraft_keyframe::Interp;
|
|
use effectcraft_project::LayerId;
|
|
use serde_json::json;
|
|
|
|
use crate::{KeyRef, Session, time::FrameRate};
|
|
|
|
fn setup() -> (Session, u64) {
|
|
let mut s = Session::default();
|
|
s.execute_checked("comp.new", json!({"width": 64, "height": 64, "frameRate": 30, "duration": 4})).unwrap();
|
|
let layer = s.execute_checked("layer.newSolid", json!({"name": "Box", "color": "#fff"})).unwrap()["layer"].as_u64().unwrap();
|
|
(s, layer)
|
|
}
|
|
|
|
fn add(s: &mut Session, layer: u64, path: &str, time: i64, value: serde_json::Value) {
|
|
s.execute_checked("prop.addKey", json!({"layer": layer, "path": path, "time": time, "value": value})).unwrap();
|
|
}
|
|
|
|
fn uid(s: &Session, layer: u64, path: &str) -> u64 {
|
|
s.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.prop(path).unwrap().uid
|
|
}
|
|
|
|
fn key(layer: u64, prop: u64, frame: i64) -> KeyRef {
|
|
KeyRef { layer: LayerId(layer), prop, time: FrameRate::FPS_30.tick_of(frame) }
|
|
}
|
|
|
|
#[test]
|
|
fn easing_preserves_interleaved_layer_and_property_order_with_one_undo() {
|
|
let (mut s, first) = setup();
|
|
let second = s.execute_checked("layer.newSolid", json!({"name": "Other", "color": "#fff"})).unwrap()["layer"].as_u64().unwrap();
|
|
for layer in [first, second] {
|
|
for time in [0, 1] {
|
|
add(&mut s, layer, "transform/opacity", time, json!(time * 25));
|
|
}
|
|
}
|
|
add(&mut s, first, "transform/position", 1, json!([10, 20]));
|
|
let a = uid(&s, first, "transform/opacity");
|
|
let b = uid(&s, second, "transform/opacity");
|
|
let position = uid(&s, first, "transform/position");
|
|
s.execute_checked(
|
|
"keys.select",
|
|
json!({"keys": [
|
|
{"layer": second, "prop": b, "time": 1},
|
|
{"layer": first, "prop": position, "time": 1},
|
|
{"layer": first, "prop": a, "time": 0},
|
|
{"layer": second, "prop": b, "time": 0}
|
|
]}),
|
|
)
|
|
.unwrap();
|
|
let order = s.state.selected_keys.clone();
|
|
let before = s.project.clone();
|
|
s.history.clear();
|
|
s.execute_checked("keys.easyEase", json!({})).unwrap();
|
|
assert_eq!(s.state.selected_keys, order);
|
|
assert_eq!(s.history.undo.len(), 1);
|
|
for selected in &order {
|
|
let keys = &s.active_comp().unwrap().layer(selected.layer).unwrap().props.find(selected.prop).unwrap().keys;
|
|
assert_eq!(keys.iter().find(|k| k.time == selected.time).unwrap().out_interp, Interp::Bezier);
|
|
}
|
|
s.execute_checked("edit.undo", json!({})).unwrap();
|
|
assert!(Arc::ptr_eq(&s.project, &before));
|
|
assert_eq!(s.state.selected_keys, order);
|
|
}
|
|
|
|
#[test]
|
|
fn roving_keeps_the_selected_key_identity_and_arbitrary_order() {
|
|
let (mut s, layer) = setup();
|
|
for (time, x) in [(0, 0), (1, 25), (2, 100)] {
|
|
add(&mut s, layer, "transform/position", time, json!([x, 0]));
|
|
}
|
|
let prop = uid(&s, layer, "transform/position");
|
|
s.execute_checked(
|
|
"keys.select",
|
|
json!({"keys": [
|
|
{"layer": layer, "prop": prop, "time": 1},
|
|
{"layer": layer, "prop": prop, "time": 2},
|
|
{"layer": layer, "prop": prop, "time": 0}
|
|
]}),
|
|
)
|
|
.unwrap();
|
|
s.execute_checked("keys.interpolation", json!({"roving": true})).unwrap();
|
|
assert_eq!(s.state.selected_keys, vec![key(layer, prop, 15), key(layer, prop, 60), key(layer, prop, 0)]);
|
|
let position = s.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.find(prop).unwrap();
|
|
assert_eq!(position.keys[1].value.as_vec2(), [25.0, 0.0]);
|
|
assert_eq!(position.keys[1].time, FrameRate::FPS_30.tick_of(15));
|
|
assert!(position.keys[1].roving);
|
|
assert!(!position.keys[0].roving && !position.keys[2].roving);
|
|
}
|
|
|
|
#[test]
|
|
fn attribute_edit_deduplicates_legacy_refs_and_drops_stale_refs_without_snapping() {
|
|
let (mut s, layer) = setup();
|
|
for time in [0, 1] {
|
|
add(&mut s, layer, "transform/opacity", time, json!(20));
|
|
}
|
|
let prop = uid(&s, layer, "transform/opacity");
|
|
let first = key(layer, prop, 0);
|
|
let second = key(layer, prop, 30);
|
|
s.state.selected_keys = vec![second, key(layer, prop, 10), first, second, key(layer, u64::MAX, 0), first, key(u64::MAX, prop, 0)];
|
|
s.history.clear();
|
|
let result = s.execute_checked("keys.toggleHold", json!({})).unwrap();
|
|
assert_eq!(result, json!(2));
|
|
assert_eq!(s.state.selected_keys, vec![second, first]);
|
|
assert_eq!(s.history.undo.len(), 1);
|
|
let opacity = s.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.find(prop).unwrap();
|
|
assert!(opacity.keys.iter().all(|k| k.out_interp == Interp::Hold));
|
|
s.execute_checked("edit.undo", json!({})).unwrap();
|
|
let opacity = s.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.find(prop).unwrap();
|
|
assert!(opacity.keys.iter().all(|k| k.out_interp == Interp::Linear));
|
|
}
|