diff --git a/crates/effects/src/lib.rs b/crates/effects/src/lib.rs index af7bd437..2304ce45 100644 --- a/crates/effects/src/lib.rs +++ b/crates/effects/src/lib.rs @@ -354,8 +354,8 @@ pub trait EffectHost: Sync { self.layer(id, false) } /// The effect's own layer at another **layer time** `layer_time` (seconds): its source with - /// masks applied, followed by the first `effects` effects of its stack (0 = none, which is - /// what After Effects' Time effects see). `None` when unavailable (no host, recursion + /// masks applied, followed by the first `effects` effects of its stack (0 = none; the host + /// never goes past the effect being rendered). `None` when unavailable (no host, recursion /// limit, adjustment layers). Time effects use this to read neighbouring frames. fn self_at(&self, _layer_time: f64, _effects: usize) -> Option { None diff --git a/crates/effects/src/sim.rs b/crates/effects/src/sim.rs index 2eddd974..2c7b97d1 100644 --- a/crates/effects/src/sim.rs +++ b/crates/effects/src/sim.rs @@ -1196,18 +1196,9 @@ fn particle_world_plan(ctx: &EffectCtx, b: &Buf) -> Option { /// The CC particle systems' sprites with their Transfer Mode (Composite, Screen, Add, Black /// Background). fn particle_sprites(sprites: Vec, mode: u32) -> SpritePlan { - SpritePlan { - sprites, - tints: vec![], - acc: if mode == 2 { Acc::Add } else { Acc::Over }, - post: Post::Combine(match mode { - 0 => 0, - 1 => 1, - 2 => 2, - 3 => 5, - _ => 0, - }), - } + // The particles replace the layer (After Effects shows no trace of the source in any + // Transfer Mode, which sets how the particles meet each other). + SpritePlan { sprites, tints: vec![], acc: if mode == 2 { Acc::Add } else { Acc::Over }, post: Post::Replace } } // ------------------------------------------------------------------ CC Particle Systems II @@ -1920,8 +1911,35 @@ mod tests { let blank = Image::new(48, 48); let y = mass_y(&a, &blank); assert!((0.0..24.0).contains(&y), "{y}"); + // The particles replace the layer: with none born it is empty. let none = run("ec.sim.ccparticlesystems2", &[("birthRate", num(0.0))], grey(20, 20), 1.0); - assert_eq!(none, grey(20, 20)); + assert_eq!(none, Image::new(20, 20)); + } + + /// CC Particle World and CC Particle Systems II replace their layer with the particles, in + /// every Transfer Mode (which only sets how particles meet each other). SceneYard frames from + /// After Effects 2026 show no trace of the source solid: "Task Preparation UI" (black solids + /// under fireworks hid a white page), "Burnout matchstick" (a red rectangle around the + /// flame), "Documentary Man Falling" (a black block over the light). + #[test] + fn cc_particles_replace_their_layer() { + let red = || { + let mut img = Image::new(48, 48); + img.data.iter_mut().for_each(|p| *p = [0.7, 0.1, 0.1, 1.0]); + img + }; + for id in ["ec.sim.ccparticlesystems2", "ec.sim.ccparticleworld"] { + for mode in 0..4 { + let v = vec![("birthRate", num(3.0)), ("particle/transferMode", Value::Enum(mode))]; + let _ = run(id, &v, red(), 0.5); + let out = run(id, &v, red(), 1.0); + // No source left: the corners, far from the producer, are clear. + for (x, y) in [(0, 0), (47, 0), (0, 47), (47, 47)] { + assert!(out.get(x, y)[3] < 1e-6, "{id} mode {mode} ({x},{y}): {:?}", out.get(x, y)); + } + assert!(out.data.iter().any(|p| p[3] > 0.05), "{id} mode {mode}: particles drawn"); + } + } } #[test] diff --git a/crates/effects/src/time_fx.rs b/crates/effects/src/time_fx.rs index d455ce03..27d2264e 100644 --- a/crates/effects/src/time_fx.rs +++ b/crates/effects/src/time_fx.rs @@ -1,10 +1,11 @@ //! Time effects (Effect > Time): Echo, Posterize Time, Time Difference, Time Displacement, //! Timewarp, CC Force Motion Blur, CC Wide Time and Pixel Motion Blur. //! -//! These read the layer at *other* layer times through [`EffectHost::self_at`] with zero -//! preceding effects: like After Effects' Time effects they see the layer's source with its -//! masks and ignore effects applied before them (precompose to include those). Without a host -//! (tests, thumbnails) they pass their input through. +//! These read the layer at *other* layer times through [`EffectHost::self_at`]: its source with +//! its masks and the effects applied before them, as After Effects renders them (SceneYard: +//! Echo after CC Particle Systems II echoes the fireworks, not the black solid under them; +//! Posterize Time after Turbulent Displace steps the displacement). Without a host (tests, +//! thumbnails) they pass their input through. //! //! Timewarp's Pixel Motion estimates block-matching optical flow between neighbouring frames //! ([`effectcraft_raster::flow`]); Pixel Motion Blur averages sub-frame samples. @@ -169,7 +170,8 @@ fn fetch(ctx: &EffectCtx, times: &[f64]) -> Option { let mut uniq: Vec = times.iter().map(|t| (t * 1e6).round() as i64).collect(); uniq.sort_unstable(); uniq.dedup(); - let bufs: Vec<(i64, Option)> = uniq.iter().map(|&k| (k, host.self_at(k as f64 / 1e6, 0))).collect(); + // Every effect before this one (the host bounds the count to them). + let bufs: Vec<(i64, Option)> = uniq.iter().map(|&k| (k, host.self_at(k as f64 / 1e6, usize::MAX))).collect(); let first = bufs.iter().find_map(|(_, b)| b.as_ref())?; let scale = first.scale; // Union of every frame's extent in layer pixels (offset = where layer (0,0) sits). @@ -1014,7 +1016,8 @@ mod tests { None } fn self_at(&self, t: f64, effects: usize) -> Option { - assert_eq!(effects, 0); + // Time effects see every effect before them. + assert_eq!(effects, usize::MAX); let v = if self.static_layer { 0.4 } else { t as f32 }; Some(Buf { img: Image::filled(6, 4, [v, v, v, 1.0]), offset: [0.0; 2], scale: 1.0 }) } diff --git a/crates/effects/tests/particle_golden.rs b/crates/effects/tests/particle_golden.rs index c9078d1a..0dbf19d7 100644 --- a/crates/effects/tests/particle_golden.rs +++ b/crates/effects/tests/particle_golden.rs @@ -334,34 +334,34 @@ fn particle_effects_golden_hashes() { /// Pinned hashes (aarch64 macOS). const GOLDEN: &[(&str, u64)] = &[ - ("ec.sim.ccparticleworld/0/8bpc/t0.5", 0x263d8f88e7ae651b), - ("ec.sim.ccparticleworld/0/8bpc/t1.5", 0xb7697683e3cc6d43), - ("ec.sim.ccparticleworld/0/8bpc/t0.8", 0xb5065326c8bd674a), - ("ec.sim.ccparticleworld/0/8bpc/t2.2", 0x7f8eb5f2e8d38506), - ("ec.sim.ccparticleworld/0/32bpc/t0.5", 0x6141abad89cd53ca), - ("ec.sim.ccparticleworld/0/32bpc/t1.5", 0x2b5682e32bb3e916), - ("ec.sim.ccparticleworld/0/32bpc/t0.8", 0xe6aaed9716d15c42), - ("ec.sim.ccparticleworld/0/32bpc/t2.2", 0xa6e9a6f2acad94c0), - ("ec.sim.ccparticleworld/0/8bpc/half/t0.5", 0xbb0b6803512a6b99), - ("ec.sim.ccparticleworld/0/8bpc/half/t1.5", 0x70c578ea73992099), - ("ec.sim.ccparticleworld/0/8bpc/half/t0.8", 0x2d26cb3f73a26b4f), - ("ec.sim.ccparticleworld/0/8bpc/half/t2.2", 0x2b474438a48ee0e4), - ("ec.sim.ccparticleworld/1/8bpc/t0.5", 0x294502dc827b0326), - ("ec.sim.ccparticleworld/1/8bpc/t1.5", 0xf7874709f4012aaa), - ("ec.sim.ccparticleworld/1/8bpc/t0.8", 0x87664ce9a05e67f9), - ("ec.sim.ccparticleworld/1/8bpc/t2.2", 0x46e1b5162a0284f4), - ("ec.sim.ccparticleworld/1/32bpc/t0.5", 0x02d8499e93133cb9), - ("ec.sim.ccparticleworld/1/32bpc/t1.5", 0x97218172dec765c6), - ("ec.sim.ccparticleworld/1/32bpc/t0.8", 0xee7da06e0b16e3b7), - ("ec.sim.ccparticleworld/1/32bpc/t2.2", 0x6b9da0aa7d2a6abb), - ("ec.sim.ccparticleworld/2/8bpc/t0.5", 0x71468ce82bba3cb2), - ("ec.sim.ccparticleworld/2/8bpc/t1.5", 0x48595f55e8741a7e), - ("ec.sim.ccparticleworld/2/8bpc/t0.8", 0x5ebc35e9eb02c8d7), - ("ec.sim.ccparticleworld/2/8bpc/t2.2", 0xf9a0f63c72bd35f0), - ("ec.sim.ccparticleworld/2/32bpc/t0.5", 0x757f80d45a2159b1), - ("ec.sim.ccparticleworld/2/32bpc/t1.5", 0x7924f1cb9c8bf8c7), - ("ec.sim.ccparticleworld/2/32bpc/t0.8", 0x22f507420a062c87), - ("ec.sim.ccparticleworld/2/32bpc/t2.2", 0x41171121719a307a), + ("ec.sim.ccparticleworld/0/8bpc/t0.5", 0x4444ad969d8a228e), + ("ec.sim.ccparticleworld/0/8bpc/t1.5", 0xcad2aad73a6d603e), + ("ec.sim.ccparticleworld/0/8bpc/t0.8", 0x513b356b746dacd0), + ("ec.sim.ccparticleworld/0/8bpc/t2.2", 0x6c1915f3f8282023), + ("ec.sim.ccparticleworld/0/32bpc/t0.5", 0x41943ef09dec3b46), + ("ec.sim.ccparticleworld/0/32bpc/t1.5", 0x9f90b72b4f674214), + ("ec.sim.ccparticleworld/0/32bpc/t0.8", 0xecd3343277ff0ea8), + ("ec.sim.ccparticleworld/0/32bpc/t2.2", 0x7aff8cf24f352f11), + ("ec.sim.ccparticleworld/0/8bpc/half/t0.5", 0x80dc4ec079743731), + ("ec.sim.ccparticleworld/0/8bpc/half/t1.5", 0x79b651a929d56eba), + ("ec.sim.ccparticleworld/0/8bpc/half/t0.8", 0xc537e2eae2138c98), + ("ec.sim.ccparticleworld/0/8bpc/half/t2.2", 0x6dec97915b8a85a0), + ("ec.sim.ccparticleworld/1/8bpc/t0.5", 0xbe5fff3969b604b1), + ("ec.sim.ccparticleworld/1/8bpc/t1.5", 0x0505795960f35d94), + ("ec.sim.ccparticleworld/1/8bpc/t0.8", 0x7337a55ab762142d), + ("ec.sim.ccparticleworld/1/8bpc/t2.2", 0x82e9d79c843b8f73), + ("ec.sim.ccparticleworld/1/32bpc/t0.5", 0x85e6f2dfa2cc2342), + ("ec.sim.ccparticleworld/1/32bpc/t1.5", 0x3fc16468de930ff4), + ("ec.sim.ccparticleworld/1/32bpc/t0.8", 0x0c8eede3cae44849), + ("ec.sim.ccparticleworld/1/32bpc/t2.2", 0xabbc670cfdf0bedf), + ("ec.sim.ccparticleworld/2/8bpc/t0.5", 0x5dd6f8961dc8aa01), + ("ec.sim.ccparticleworld/2/8bpc/t1.5", 0x6632451f98c2bb66), + ("ec.sim.ccparticleworld/2/8bpc/t0.8", 0x9c9840445d995203), + ("ec.sim.ccparticleworld/2/8bpc/t2.2", 0xaa29bed036be7d9d), + ("ec.sim.ccparticleworld/2/32bpc/t0.5", 0x7507310c57e5bc42), + ("ec.sim.ccparticleworld/2/32bpc/t1.5", 0x6f0216e5fe64f20c), + ("ec.sim.ccparticleworld/2/32bpc/t0.8", 0x0def6a02489d3bc7), + ("ec.sim.ccparticleworld/2/32bpc/t2.2", 0xb0d1af988e47a2d5), ("ec.sim.ccparticleworld/3/8bpc/t0.5", 0xbca7e3d574cf5e37), ("ec.sim.ccparticleworld/3/8bpc/t1.5", 0x5c4347a6c70b48ec), ("ec.sim.ccparticleworld/3/8bpc/t0.8", 0xc79131dbaa8ff129), @@ -370,74 +370,74 @@ const GOLDEN: &[(&str, u64)] = &[ ("ec.sim.ccparticleworld/3/32bpc/t1.5", 0xf9751b9225a373a4), ("ec.sim.ccparticleworld/3/32bpc/t0.8", 0xb6026343f72dfaeb), ("ec.sim.ccparticleworld/3/32bpc/t2.2", 0x2f0e149de9623753), - ("ec.sim.ccparticleworld/4/8bpc/t0.5", 0xd53dad7a75976197), - ("ec.sim.ccparticleworld/4/8bpc/t1.5", 0x2cd0f3cbed038289), - ("ec.sim.ccparticleworld/4/8bpc/t0.8", 0x46c089373ed50088), - ("ec.sim.ccparticleworld/4/8bpc/t2.2", 0xfb17c5c3ba44657c), - ("ec.sim.ccparticleworld/4/32bpc/t0.5", 0x80b338b7bb9794b1), - ("ec.sim.ccparticleworld/4/32bpc/t1.5", 0x86811f65519e9790), - ("ec.sim.ccparticleworld/4/32bpc/t0.8", 0x6aad5efc09bc64ff), - ("ec.sim.ccparticleworld/4/32bpc/t2.2", 0x9957090d6fc97293), - ("ec.sim.ccparticleworld/5/8bpc/t0.5", 0x56f989c5d7fc1eeb), - ("ec.sim.ccparticleworld/5/8bpc/t1.5", 0x128313333a936e16), - ("ec.sim.ccparticleworld/5/8bpc/t0.8", 0xa0efb8bdc0157788), - ("ec.sim.ccparticleworld/5/8bpc/t2.2", 0x4af31455e2fe2631), - ("ec.sim.ccparticleworld/5/32bpc/t0.5", 0xf3057d8cf6540f4a), - ("ec.sim.ccparticleworld/5/32bpc/t1.5", 0x28203947d78e5ee3), - ("ec.sim.ccparticleworld/5/32bpc/t0.8", 0xa6d8a9a848443344), - ("ec.sim.ccparticleworld/5/32bpc/t2.2", 0xade682a437a4cba1), - ("ec.sim.ccparticleworld/6/8bpc/t0.5", 0xa65b30a36baee78a), - ("ec.sim.ccparticleworld/6/8bpc/t1.5", 0xbf7e4675fec18c48), - ("ec.sim.ccparticleworld/6/8bpc/t0.8", 0xf98c7d9af9d6c9e2), - ("ec.sim.ccparticleworld/6/8bpc/t2.2", 0xdf090357b8e4f471), - ("ec.sim.ccparticleworld/6/32bpc/t0.5", 0x1721a9f149c922de), - ("ec.sim.ccparticleworld/6/32bpc/t1.5", 0xf378db3b55dff048), - ("ec.sim.ccparticleworld/6/32bpc/t0.8", 0x72a5d8dc0ea744ad), - ("ec.sim.ccparticleworld/6/32bpc/t2.2", 0xfbb8e14088bb51ef), - ("ec.sim.ccparticleworld/7/8bpc/t0.5", 0xc56801146ef15811), - ("ec.sim.ccparticleworld/7/8bpc/t1.5", 0x3ca9b694e5e55f82), - ("ec.sim.ccparticleworld/7/8bpc/t0.8", 0x2d72c095e0be6f47), - ("ec.sim.ccparticleworld/7/8bpc/t2.2", 0x135b14387e6ea480), - ("ec.sim.ccparticleworld/7/32bpc/t0.5", 0xad79b2f2d63d0c45), - ("ec.sim.ccparticleworld/7/32bpc/t1.5", 0x902c6b68c6ed615d), - ("ec.sim.ccparticleworld/7/32bpc/t0.8", 0x45045d7931c23788), - ("ec.sim.ccparticleworld/7/32bpc/t2.2", 0xd8f8883374a3784f), - ("ec.sim.ccparticleworld/8/8bpc/t0.5", 0xb681fe6e20dccbf3), - ("ec.sim.ccparticleworld/8/8bpc/t1.5", 0xfa78174cb4fa5cd4), - ("ec.sim.ccparticleworld/8/8bpc/t0.8", 0xd4f486333d843f09), - ("ec.sim.ccparticleworld/8/8bpc/t2.2", 0xbee37590ce17b8b4), - ("ec.sim.ccparticleworld/8/32bpc/t0.5", 0xccf4339bc42c3c01), - ("ec.sim.ccparticleworld/8/32bpc/t1.5", 0xc51a8ee4e3c4a826), - ("ec.sim.ccparticleworld/8/32bpc/t0.8", 0xee3d66e0c4ffb0ec), - ("ec.sim.ccparticleworld/8/32bpc/t2.2", 0x449fc6d506e837fb), - ("ec.sim.ccparticlesystems2/0/8bpc/t0.5", 0x3edb24bfeab4b00a), - ("ec.sim.ccparticlesystems2/0/8bpc/t1.5", 0x78cb639f211cbd5b), - ("ec.sim.ccparticlesystems2/0/8bpc/t0.8", 0x634c71924e4c68f1), - ("ec.sim.ccparticlesystems2/0/8bpc/t2.2", 0xe09d41dcb6286423), - ("ec.sim.ccparticlesystems2/0/32bpc/t0.5", 0xacdb6aec643a9bba), - ("ec.sim.ccparticlesystems2/0/32bpc/t1.5", 0x4c50991034336999), - ("ec.sim.ccparticlesystems2/0/32bpc/t0.8", 0xaacc2ea722f2e9e3), - ("ec.sim.ccparticlesystems2/0/32bpc/t2.2", 0x1a5f7fc66db90a83), - ("ec.sim.ccparticlesystems2/0/8bpc/half/t0.5", 0x8c89f83a2829dba1), - ("ec.sim.ccparticlesystems2/0/8bpc/half/t1.5", 0x9239cd8895757d17), - ("ec.sim.ccparticlesystems2/0/8bpc/half/t0.8", 0x91a16fb0a382d8e1), - ("ec.sim.ccparticlesystems2/0/8bpc/half/t2.2", 0xc86d8250fe1402c5), - ("ec.sim.ccparticlesystems2/1/8bpc/t0.5", 0xabade8d131837f05), - ("ec.sim.ccparticlesystems2/1/8bpc/t1.5", 0x6a3566257b9a0bad), - ("ec.sim.ccparticlesystems2/1/8bpc/t0.8", 0xc1199f209bad5c33), - ("ec.sim.ccparticlesystems2/1/8bpc/t2.2", 0xaf337959d8264a5f), - ("ec.sim.ccparticlesystems2/1/32bpc/t0.5", 0xb1816c2e5562be88), - ("ec.sim.ccparticlesystems2/1/32bpc/t1.5", 0x02413f0ec8284e39), - ("ec.sim.ccparticlesystems2/1/32bpc/t0.8", 0xd9bc645b90055a9a), - ("ec.sim.ccparticlesystems2/1/32bpc/t2.2", 0x2c788bb5816167a3), - ("ec.sim.ccparticlesystems2/2/8bpc/t0.5", 0xe18fd350f7155fc8), - ("ec.sim.ccparticlesystems2/2/8bpc/t1.5", 0x956e03e2c83c3899), - ("ec.sim.ccparticlesystems2/2/8bpc/t0.8", 0x7d16738562170622), - ("ec.sim.ccparticlesystems2/2/8bpc/t2.2", 0x4e5838737bda7317), - ("ec.sim.ccparticlesystems2/2/32bpc/t0.5", 0x27d909b24d165f6c), - ("ec.sim.ccparticlesystems2/2/32bpc/t1.5", 0x47fb5492c0bcb748), - ("ec.sim.ccparticlesystems2/2/32bpc/t0.8", 0x7dd63be8a11f4d71), - ("ec.sim.ccparticlesystems2/2/32bpc/t2.2", 0x139259fc5f942375), + ("ec.sim.ccparticleworld/4/8bpc/t0.5", 0xdfd4cff821add47f), + ("ec.sim.ccparticleworld/4/8bpc/t1.5", 0x4ed5d9c97c8b323e), + ("ec.sim.ccparticleworld/4/8bpc/t0.8", 0x13c8b1929529a4b1), + ("ec.sim.ccparticleworld/4/8bpc/t2.2", 0xd9702ad6bbd34752), + ("ec.sim.ccparticleworld/4/32bpc/t0.5", 0xabb4834618b86cb8), + ("ec.sim.ccparticleworld/4/32bpc/t1.5", 0x54de0787c35112ff), + ("ec.sim.ccparticleworld/4/32bpc/t0.8", 0xfe9ed5ed6c1fd8bd), + ("ec.sim.ccparticleworld/4/32bpc/t2.2", 0x8278e2b011770e33), + ("ec.sim.ccparticleworld/5/8bpc/t0.5", 0x11abba82d18e4540), + ("ec.sim.ccparticleworld/5/8bpc/t1.5", 0x1be834f3d8d64464), + ("ec.sim.ccparticleworld/5/8bpc/t0.8", 0xf3ad76cb0f38394d), + ("ec.sim.ccparticleworld/5/8bpc/t2.2", 0x64e10ae00b8e48e5), + ("ec.sim.ccparticleworld/5/32bpc/t0.5", 0xc5d6ad16b9b6f662), + ("ec.sim.ccparticleworld/5/32bpc/t1.5", 0xcb7b7777e8ba0ea1), + ("ec.sim.ccparticleworld/5/32bpc/t0.8", 0x09692a677eed64fc), + ("ec.sim.ccparticleworld/5/32bpc/t2.2", 0x63e921f1dd7b52bc), + ("ec.sim.ccparticleworld/6/8bpc/t0.5", 0xa954ad832566044b), + ("ec.sim.ccparticleworld/6/8bpc/t1.5", 0x27fd580241299755), + ("ec.sim.ccparticleworld/6/8bpc/t0.8", 0x8749025b3c953f71), + ("ec.sim.ccparticleworld/6/8bpc/t2.2", 0xd01aff9a0712e02d), + ("ec.sim.ccparticleworld/6/32bpc/t0.5", 0xd2213ab758ed6cb9), + ("ec.sim.ccparticleworld/6/32bpc/t1.5", 0xa7287da977d06904), + ("ec.sim.ccparticleworld/6/32bpc/t0.8", 0xd02d00dc0e651852), + ("ec.sim.ccparticleworld/6/32bpc/t2.2", 0xc8930de7171f5ab3), + ("ec.sim.ccparticleworld/7/8bpc/t0.5", 0x9166865a7c9ac035), + ("ec.sim.ccparticleworld/7/8bpc/t1.5", 0x93901b6547233c1f), + ("ec.sim.ccparticleworld/7/8bpc/t0.8", 0xe5d835162c42b6a5), + ("ec.sim.ccparticleworld/7/8bpc/t2.2", 0xe81b1c734e88c7cb), + ("ec.sim.ccparticleworld/7/32bpc/t0.5", 0x27924fd1804d9a61), + ("ec.sim.ccparticleworld/7/32bpc/t1.5", 0x9daca8989bbd99f1), + ("ec.sim.ccparticleworld/7/32bpc/t0.8", 0xcd2932f1ee19e946), + ("ec.sim.ccparticleworld/7/32bpc/t2.2", 0x886af8fedcd62969), + ("ec.sim.ccparticleworld/8/8bpc/t0.5", 0xe08dd245cbd88371), + ("ec.sim.ccparticleworld/8/8bpc/t1.5", 0xcbe7b22563e912f4), + ("ec.sim.ccparticleworld/8/8bpc/t0.8", 0x81bcded7ad281386), + ("ec.sim.ccparticleworld/8/8bpc/t2.2", 0x5d176ad19ac06da2), + ("ec.sim.ccparticleworld/8/32bpc/t0.5", 0x1dff22388b30e96d), + ("ec.sim.ccparticleworld/8/32bpc/t1.5", 0x6f29f64ba5e160df), + ("ec.sim.ccparticleworld/8/32bpc/t0.8", 0xfba8c8d708e417e4), + ("ec.sim.ccparticleworld/8/32bpc/t2.2", 0xf3b2f2a1ea8c71d6), + ("ec.sim.ccparticlesystems2/0/8bpc/t0.5", 0x01d2a42a3a56c69b), + ("ec.sim.ccparticlesystems2/0/8bpc/t1.5", 0x953091e15d03438f), + ("ec.sim.ccparticlesystems2/0/8bpc/t0.8", 0x668ea154b17b2eb0), + ("ec.sim.ccparticlesystems2/0/8bpc/t2.2", 0x1862446b0c54b86c), + ("ec.sim.ccparticlesystems2/0/32bpc/t0.5", 0xa1fb78a64db56ef3), + ("ec.sim.ccparticlesystems2/0/32bpc/t1.5", 0x20ec7913584e6b71), + ("ec.sim.ccparticlesystems2/0/32bpc/t0.8", 0xab390d348d0a66fd), + ("ec.sim.ccparticlesystems2/0/32bpc/t2.2", 0x82f78af53a7c0096), + ("ec.sim.ccparticlesystems2/0/8bpc/half/t0.5", 0xf7cf34b83bd3af18), + ("ec.sim.ccparticlesystems2/0/8bpc/half/t1.5", 0x461020f3a3a4410d), + ("ec.sim.ccparticlesystems2/0/8bpc/half/t0.8", 0xcd190eb7b38db0e8), + ("ec.sim.ccparticlesystems2/0/8bpc/half/t2.2", 0x7f35000229186023), + ("ec.sim.ccparticlesystems2/1/8bpc/t0.5", 0xf711d4d5244de44b), + ("ec.sim.ccparticlesystems2/1/8bpc/t1.5", 0x61ea1dd478d0f90e), + ("ec.sim.ccparticlesystems2/1/8bpc/t0.8", 0x1d23c2c8bb26577c), + ("ec.sim.ccparticlesystems2/1/8bpc/t2.2", 0x0ca22baad9654c06), + ("ec.sim.ccparticlesystems2/1/32bpc/t0.5", 0x6dc7b6570e174ee2), + ("ec.sim.ccparticlesystems2/1/32bpc/t1.5", 0x491a30b9f0dac574), + ("ec.sim.ccparticlesystems2/1/32bpc/t0.8", 0xfd9c7f0d0abd959e), + ("ec.sim.ccparticlesystems2/1/32bpc/t2.2", 0x95ea630041185e8b), + ("ec.sim.ccparticlesystems2/2/8bpc/t0.5", 0x165e69bf2a5f7a6a), + ("ec.sim.ccparticlesystems2/2/8bpc/t1.5", 0xc5ce39b6beb3a4a1), + ("ec.sim.ccparticlesystems2/2/8bpc/t0.8", 0x635bf9e56db3087d), + ("ec.sim.ccparticlesystems2/2/8bpc/t2.2", 0x03bb626e929e6cdd), + ("ec.sim.ccparticlesystems2/2/32bpc/t0.5", 0x92b5a331852e40fe), + ("ec.sim.ccparticlesystems2/2/32bpc/t1.5", 0xae453b6fd4bbaeed), + ("ec.sim.ccparticlesystems2/2/32bpc/t0.8", 0xfa4621a685214620), + ("ec.sim.ccparticlesystems2/2/32bpc/t2.2", 0x84c180368e4411ad), ("ec.sim.ccparticlesystems2/3/8bpc/t0.5", 0xf14aad048c3b4181), ("ec.sim.ccparticlesystems2/3/8bpc/t1.5", 0xc261983ac6805eda), ("ec.sim.ccparticlesystems2/3/8bpc/t0.8", 0xf93ad1d6a9c493a5), @@ -446,22 +446,22 @@ const GOLDEN: &[(&str, u64)] = &[ ("ec.sim.ccparticlesystems2/3/32bpc/t1.5", 0x1c415c633baec738), ("ec.sim.ccparticlesystems2/3/32bpc/t0.8", 0x81a765f6e435e762), ("ec.sim.ccparticlesystems2/3/32bpc/t2.2", 0x7810e7f2ae55f619), - ("ec.sim.ccparticlesystems2/4/8bpc/t0.5", 0xd246be64fea5db5f), - ("ec.sim.ccparticlesystems2/4/8bpc/t1.5", 0x78ca37c0d19e87cf), - ("ec.sim.ccparticlesystems2/4/8bpc/t0.8", 0xa5eacfbc0cc28a7c), - ("ec.sim.ccparticlesystems2/4/8bpc/t2.2", 0xa527d1d2cdce5c96), - ("ec.sim.ccparticlesystems2/4/32bpc/t0.5", 0x41c8b38886ae9652), - ("ec.sim.ccparticlesystems2/4/32bpc/t1.5", 0x1e6d16247bb9c7a7), - ("ec.sim.ccparticlesystems2/4/32bpc/t0.8", 0x05b125bdcd9a1794), - ("ec.sim.ccparticlesystems2/4/32bpc/t2.2", 0x4752ed7777528cc8), - ("ec.sim.ccparticlesystems2/5/8bpc/t0.5", 0x8ff7adb3b1720dbe), - ("ec.sim.ccparticlesystems2/5/8bpc/t1.5", 0x2bb835296a78fccb), - ("ec.sim.ccparticlesystems2/5/8bpc/t0.8", 0x674810581e32f80a), - ("ec.sim.ccparticlesystems2/5/8bpc/t2.2", 0xecba1a3a740f4325), - ("ec.sim.ccparticlesystems2/5/32bpc/t0.5", 0x30f53c7cb869066c), - ("ec.sim.ccparticlesystems2/5/32bpc/t1.5", 0x1eb455f68b9decc8), - ("ec.sim.ccparticlesystems2/5/32bpc/t0.8", 0xf396c8bdd516ef26), - ("ec.sim.ccparticlesystems2/5/32bpc/t2.2", 0x7d0a6e30425ea5c3), + ("ec.sim.ccparticlesystems2/4/8bpc/t0.5", 0x0106a8009fa18e2f), + ("ec.sim.ccparticlesystems2/4/8bpc/t1.5", 0x5f12647281ea7ec9), + ("ec.sim.ccparticlesystems2/4/8bpc/t0.8", 0x9f2655dbad770023), + ("ec.sim.ccparticlesystems2/4/8bpc/t2.2", 0x44ca687ef817c0a1), + ("ec.sim.ccparticlesystems2/4/32bpc/t0.5", 0x895b20656b6d3c98), + ("ec.sim.ccparticlesystems2/4/32bpc/t1.5", 0x3a8b85da736ae6f0), + ("ec.sim.ccparticlesystems2/4/32bpc/t0.8", 0x139f04a2dcf67d69), + ("ec.sim.ccparticlesystems2/4/32bpc/t2.2", 0x02c844b7371128ac), + ("ec.sim.ccparticlesystems2/5/8bpc/t0.5", 0x7b4a7112b6c8bb11), + ("ec.sim.ccparticlesystems2/5/8bpc/t1.5", 0xf16ff18c451dbf63), + ("ec.sim.ccparticlesystems2/5/8bpc/t0.8", 0xdedadbd43089dafe), + ("ec.sim.ccparticlesystems2/5/8bpc/t2.2", 0x1e6fdb272dabf94c), + ("ec.sim.ccparticlesystems2/5/32bpc/t0.5", 0xd17e5a60f476d0f7), + ("ec.sim.ccparticlesystems2/5/32bpc/t1.5", 0xbfd43b69b45cd67c), + ("ec.sim.ccparticlesystems2/5/32bpc/t0.8", 0x79fb89759eea8a6a), + ("ec.sim.ccparticlesystems2/5/32bpc/t2.2", 0x0a70bc480c847d7e), ("ec.sim.particleplayground/0/8bpc/t0.5", 0x1842226399d43e19), ("ec.sim.particleplayground/0/8bpc/t1.5", 0xd5676389299c50e9), ("ec.sim.particleplayground/0/8bpc/t0.8", 0x30517e3b37e099d5), diff --git a/crates/render/src/tests_time.rs b/crates/render/src/tests_time.rs index 31ed9db3..8739140d 100644 --- a/crates/render/src/tests_time.rs +++ b/crates/render/src/tests_time.rs @@ -64,6 +64,22 @@ fn echo_blends_neighbouring_frames() { assert!((a - want).abs() < 0.01, "{a} vs {want}"); } +/// Time effects see the effects applied before them, as After Effects renders them (SceneYard +/// "Task Preparation UI": Echo after CC Particle Systems II echoed the black solid under the +/// fireworks and blacked out the white page). Fill (red) then Echo: the echoes are red. +#[test] +fn time_effects_see_the_effects_before_them() { + let (mut p, cid) = scene(Some(("ec.generate.fill", &[]))); + let spec = effectcraft_effects::find("ec.time.echo").unwrap(); + let mut next = p.next_id; + let mut g = effectcraft_effects::instantiate(spec, &mut Ids(&mut next), spec.name, [200.0, 100.0]); + g.prop_mut("numberOfEchoes").unwrap().value = Value::Scalar(0.0); + p.next_id = next; + p.comp_mut(cid).unwrap().layers[0].props.sub_mut("effects").unwrap().children.push(g.into()); + let c = render(&p, cid, 1.5, None).get(100, 50); + assert!(c[0] > 0.99 && c[1] < 0.01 && c[2] < 0.01 && c[3] > 0.99, "{c:?}"); +} + #[test] fn time_difference_of_static_layer_is_black() { let mut p = Project::default();