#3534, #3536: pin analysis keys saved by 0.24.1

A fixture keyed by the current release: Warp Stabilizer, 3D Camera Tracker
and Roto Brush keys keep matching through edits that keep the frames, undo
and save/reopen, and real changes still clear them.
This commit is contained in:
arnav-hiwarkar committed 2026-11-12 13:56:48 +05:30
1 parent 24df676af5
commit d361a6cb4e
5 files changed
+105

No files matched your search

+1
View File
@@ -133,6 +133,7 @@ configuration from these Apache-2.0 projects:
| Asset | Author | Source | Licence |
|---|---|---|---|
| `crates/engine/src/tests/fixtures/analysed-0.24.ecproj` | arnav-hiwarkar | Original work, synthetic project keyed by EffectCraft 0.24.1 | MIT OR Apache-2.0 |
| `crates/engine/src/tests/fixtures/object-matte-0.22.ecproj` | lisn0 | Original work, synthetic project saved by EffectCraft 0.22.0 | MIT OR Apache-2.0 |
| `crates/engine/src/tests/fixtures/tracked-0.22.ecproj` | arnav-hiwarkar | Original work, synthetic project saved by EffectCraft 0.22.0 | MIT OR Apache-2.0 |
+1
View File
@@ -27,6 +27,7 @@ mod key_metadata_order;
mod key_time;
mod keyframe_text;
mod keylight;
mod analysis_signature;
mod legacy_analysis_keys;
mod limits;
mod lottie;
@@ -0,0 +1,94 @@
//! Warp Stabilizer, 3D Camera Tracker and Roto Brush keys saved by 0.24 keep matching their
//! layer's frames after edits that don't change them, and a real change still clears them.
use effectcraft_effects::camera_tracker::{TRACKS, TRACKS_KEY};
use effectcraft_effects::roto::{FROZEN, INPUT_KEY};
use effectcraft_effects::warp_stab::{ANALYSIS, ANALYSIS_KEY};
use effectcraft_keyframe::Value;
use effectcraft_project::{ItemId, LayerId, Project};
use serde_json::json;
use crate::Session;
const COMP: ItemId = ItemId(2);
const CLIP: LayerId = LayerId(3);
const WARP: u64 = 26;
const TRACKER: u64 = 54;
const ROTO: u64 = 86;
/// Saved by 0.24.1 (before #3534/#3536): the clip of `tracked-0.22.ecproj` with Time Remap, a
/// mask, a Gaussian Blur and a Roto Brush, its analyses replaced by short stand-ins. The keys
/// are the ones that version computed for it.
const ANALYSED_0_24: &str = include_str!("fixtures/analysed-0.24.ecproj");
const SIGNATURE_0_24: &str = "03efb7c4b0f27b65";
const ROTO_INPUT_0_24: &str = "7b319ce93027617c";
fn open(json: &str) -> Session {
let mut s = Session::default();
s.replace_project(Project::from_json(json).unwrap(), None);
s.open_comp(COMP);
s
}
/// The (data, key) of the Warp Stabilizer, the 3D Camera Tracker and the Roto Brush.
fn saved(s: &Session) -> [(String, String); 3] {
let layer = s.project.comp(COMP).unwrap().layer(CLIP).unwrap();
let get = |uid: u64, data: &str, key: &str| {
let params = crate::camera_track::static_params(layer.props.find_group(uid).unwrap());
let text = |id: &str| match params.get(id) {
Some(Value::Str(s)) => s.clone(),
other => panic!("{id}: {other:?}"),
};
(text(data), text(key))
};
[get(WARP, ANALYSIS, ANALYSIS_KEY), get(TRACKER, TRACKS, TRACKS_KEY), get(ROTO, FROZEN, INPUT_KEY)]
}
fn add_to_render_queue(s: &mut Session) {
s.execute_checked("renderQueue.add", json!({"comp": COMP.0, "format": "png", "output": "frame.png"})).unwrap();
}
#[test]
fn analyses_saved_by_0_24_survive_edits_that_keep_their_frames() {
let mut s = open(ANALYSED_0_24);
let before = saved(&s);
assert_eq!(before[0], ("saved analysis".into(), SIGNATURE_0_24.into()));
assert_eq!(before[1], ("saved tracks".into(), SIGNATURE_0_24.into()));
assert_eq!(before[2], ("saved frozen".into(), ROTO_INPUT_0_24.into()));
add_to_render_queue(&mut s);
assert_eq!(saved(&s), before);
s.execute_checked("edit.undo", json!({})).unwrap();
assert_eq!(saved(&s), before);
// Saved and reopened, the keys still match.
let mut s = open(&s.project.to_json());
add_to_render_queue(&mut s);
assert_eq!(saved(&s), before);
// Other frames: all three are cleared (the Roto Brush gets a new key), and undo brings them back.
s.execute_checked("layer.timing", json!({"layer": CLIP.0, "out": 0.25})).unwrap();
let after = saved(&s);
assert_eq!(after[0], (String::new(), String::new()));
assert_eq!(after[1], (String::new(), String::new()));
assert_eq!(after[2].0, "");
assert_ne!(after[2].1, ROTO_INPUT_0_24);
s.execute_checked("edit.undo", json!({})).unwrap();
assert_eq!(saved(&s), before);
// Time Remap is part of the frames.
s.execute_checked("prop.set", json!({"layer": CLIP.0, "path": "timeRemap", "value": 0.3})).unwrap();
assert!(saved(&s).iter().all(|(data, _)| data.is_empty()));
s.execute_checked("edit.undo", json!({})).unwrap();
// The Roto Brush's input also has the layer's masks and the effects above it.
s.execute_checked("prop.set", json!({"layer": CLIP.0, "path": "effects/#3/blurriness", "value": 4})).unwrap();
let after = saved(&s);
assert_eq!(after[2].0, "");
assert_ne!(after[2].1, ROTO_INPUT_0_24);
s.execute_checked("edit.undo", json!({})).unwrap();
s.execute_checked("prop.set", json!({"layer": CLIP.0, "path": "masks/#1/expansion", "value": 3})).unwrap();
assert_eq!(saved(&s)[2].0, "");
s.execute_checked("edit.undo", json!({})).unwrap();
assert_eq!(saved(&s), before);
}
+1
View File
@@ -0,0 +1 @@
{"schema":2,"settings":{"bit_depth":"Bpc8","linearize":false,"working_space":null,"blend_linear":false,"time_display":"Timecode","frame_start":0,"audio_sample_rate":48000,"gpu_acceleration":true,"color_engine":"Adobe","hdr":"Clip","output_space":null,"working_gamma":2.2},"items":{"1":{"id":1,"name":"effectcraft-3456-shake.mp4","label":"Aqua","comment":"","parent":null,"kind":{"type":"Footage","path":"/tmp/effectcraft-3456-shake.mp4","kind":"Video","width":160,"height":120,"pixel_aspect":1.0,"frame_rate":{"num":12,"den":1},"native_rate":null,"duration":254016000000,"has_video":true,"has_audio":false,"alpha":"Ignore","premul_color":[0.0,0.0,0.0],"loop_count":1,"codec":"H.264 (High)","missing":false,"color_profile":"Rec709"}},"2":{"id":2,"name":"effectcraft-3456-shake","label":"Sandstone","comment":"","parent":null,"kind":{"type":"Comp","width":160,"height":120,"pixel_aspect":1.0,"frame_rate":{"num":12,"den":1},"duration":254016000000,"display_start":0,"background":[0.0,0.0,0.0],"work_area":[0,254016000000],"layers":[{"id":3,"name":"effectcraft-3456-shake.mp4","source":{"type":"Footage","item":1},"label":"Aqua","comment":"","start_time":0,"in_point":0,"out_point":254016000000,"stretch":100.0,"switches":{"video":true,"audio":true,"solo":false,"locked":false,"shy":false,"collapse":false,"quality":"Best","sampling":"Bilinear","effects":true,"frame_blend":"Off","motion_blur":false,"adjustment":false,"three_d":false,"guide":false},"blend_mode":"Normal","preserve_transparency":false,"track_matte":null,"parent":null,"markers":[],"auto_orient":"Off","props":{"uid":4,"match":"layer","name":"effectcraft-3456-shake.mp4","kind":{"kind":"Plain"},"enabled":true,"children":[{"node":"Prop","uid":76,"match":"timeRemap","name":"Time Remap","value":{"t":"Scalar","v":0.0},"keys":[{"time":0,"value":{"t":"Scalar","v":0.2},"in_interp":"Linear","out_interp":"Linear","in_ease":[],"out_ease":[],"continuous":false,"auto_bezier":false,"spatial_in":[0.0,0.0,0.0],"spatial_out":[0.0,0.0,0.0],"spatial_auto":true,"spatial_continuous":false,"roving":false},{"time":254016000000,"value":{"t":"Scalar","v":1.0},"in_interp":"Linear","out_interp":"Linear","in_ease":[],"out_ease":[],"continuous":false,"auto_bezier":false,"spatial_in":[0.0,0.0,0.0],"spatial_out":[0.0,0.0,0.0],"spatial_auto":true,"spatial_continuous":false,"roving":false}],"ui":{"kind":"Number"},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Group","uid":5,"match":"masks","name":"Masks","kind":{"kind":"Plain"},"enabled":true,"children":[{"node":"Group","uid":77,"match":"mask","name":"Mask 1","kind":{"kind":"Mask","mode":"Add","inverted":false,"color":[177,180,79],"locked":false,"motion_blur":"SameAsLayer","feather_falloff":"Smooth","roto_bezier":false},"enabled":true,"children":[{"node":"Prop","uid":78,"match":"path","name":"Mask Path","value":{"t":"Path","v":{"vertices":[[10.0,10.0],[120.0,10.0],[120.0,100.0],[10.0,100.0]],"in_tangents":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]],"out_tangents":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]],"closed":false}},"ui":{"kind":"Path"},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Prop","uid":79,"match":"feather","name":"Mask Feather","value":{"t":"Vec2","v":[0.0,0.0]},"ui":{"kind":"Pixels"},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Prop","uid":80,"match":"opacity","name":"Mask Opacity","value":{"t":"Scalar","v":100.0},"ui":{"kind":"Slider","min":0.0,"max":100.0,"slider_min":0.0,"slider_max":100.0,"decimals":0},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Prop","uid":81,"match":"expansion","name":"Mask Expansion","value":{"t":"Scalar","v":0.0},"ui":{"kind":"Pixels"},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false}]}]},{"node":"Group","uid":6,"match":"effects","name":"Effects","kind":{"kind":"Plain"},"enabled":true,"children":[{"node":"Group","uid":26,"match":"ec.distort.warpstabilizer","name":"Warp Stabilizer","kind":{"kind":"Effect","effect":"ec.distort.warpstabilizer"},"enabled":true,"children":[{"node":"Group","uid":28,"match":"stabilization","name":"Stabilization","kind":{"kind":"Plain"},"enabled":true,"children":[{"node":"Prop","uid":27,"match":"result","name":"Result","value":{"t":"Enum","v":0},"ui":{"kind":"Popup","options":["Smooth Motion","No Motion"]},"spatial":false,"hold_only":true,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Prop","uid":29,"match":"smoothness","name":"Smoothness","value":{"t":"Scalar","v":50.0},"ui":{"kind":"Slider","min":0.0,"max":1000.0,"slider_min":0.0,"slider_max":100.0,"decimals":0},"spatial":false,"hold_only":false,"three_d_only":false,"two_d_only":false,"shown_dims":0,"static_only":false},{"node":"Prop","uid":30,"match":"mLine truncated
@@ -0,0 +1,8 @@
asset: analysed-0.24.ecproj
title: The tracked-0.22 clip with Time Remap, a mask, Gaussian Blur and Roto Brush, keyed by EffectCraft 0.24.1
author: arnav-hiwarkar
source: original work
license: MIT OR Apache-2.0
license-file: ../../../../../LICENSE-MIT
added: 2026-11-12 by arnav-hiwarkar
notes: Made from tracked-0.22.ecproj with 0.24.1 engine commands (enable Time Remap, a rectangle mask, Gaussian Blur and Roto Brush), its saved analyses replaced by short stand-in strings, and the Warp Stabilizer, 3D Camera Tracker and Roto Brush keys that 0.24.1 computed for it. Written as compact JSON. No media is needed: the tests only check that the keys are kept or cleared.