1use crate::{
11 Extract, SealedRanimScene, TimeMark,
12 animation::node::{AnimNode, AnimationInfo},
13 audio::MASTER_SAMPLE_RATE,
14 core_item::CoreItem,
15};
16
17pub type EvaluatedFrame = Vec<((usize, usize), CoreItem)>;
19
20pub struct SceneEvaluator {
22 cells: Vec<AnimNode>,
23 total_secs: f64,
24 audio: std::sync::Arc<[f32]>,
25 time_marks: Vec<(f64, TimeMark)>,
26 clock: f64,
27}
28
29impl SceneEvaluator {
30 #[allow(unused_variables)]
35 pub fn new(scene: SealedRanimScene, logic_fps: f64) -> Self {
36 Self {
37 cells: scene.animations,
38 total_secs: scene.total_secs,
39 audio: scene.audio,
40 time_marks: scene.time_marks,
41 clock: 0.0,
42 }
43 }
44
45 pub fn total_secs(&self) -> f64 {
47 self.total_secs
48 }
49
50 #[cfg(test)]
55 pub(crate) fn cells(&self) -> &[AnimNode] {
56 &self.cells
57 }
58
59 pub fn has_audio(&self) -> bool {
61 self.cells.iter().any(AnimNode::has_audio)
62 }
63
64 pub fn audio(&self) -> &std::sync::Arc<[f32]> {
68 &self.audio
69 }
70
71 pub fn mix_audio(&self, out_secs: f64, sample_rate: u32) -> Vec<f32> {
78 assert_eq!(
79 sample_rate, MASTER_SAMPLE_RATE,
80 "the baked audio lives at the master sample rate"
81 );
82 let out_frames = (out_secs * sample_rate as f64).ceil() as usize;
83 let baked_frames = self.audio.len() / 2;
84 let copy = out_frames.min(baked_frames);
85 let mut out = Vec::with_capacity(out_frames * 2);
86 out.extend_from_slice(&self.audio[..copy * 2]);
87 out.resize(out_frames * 2, 0.0);
88 out
89 }
90
91 pub fn time_marks(&self) -> &[(f64, TimeMark)] {
93 &self.time_marks
94 }
95
96 pub fn animation_infos(&self) -> Vec<AnimationInfo> {
98 self.cells.iter().map(AnimNode::animation_info).collect()
99 }
100
101 pub fn clock(&self) -> f64 {
103 self.clock
104 }
105
106 pub fn sample_at(&mut self, render_secs: f64, out: &mut EvaluatedFrame) {
112 for (animation_id, cell) in self.cells.iter().enumerate() {
113 let mut items = Vec::new();
114 cell.eval_at(render_secs, &mut items);
115 for (part, item) in items
116 .into_iter()
117 .flat_map(|item| item.extract())
118 .enumerate()
119 {
120 out.push(((animation_id, part), item));
121 }
122 }
123 self.clock = render_secs;
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::{
131 RanimScene, SealedRanimScene,
132 animation::{
133 build::{PlaybackExt, Unplaced},
134 eval::Eval,
135 },
136 core_item::vitem::VItem,
137 seq,
138 };
139
140 struct ConstantVelocity {
145 v: f64,
146 logical_secs: f64,
147 sim_step: f64,
148 state: std::cell::RefCell<(f64, f64)>, }
150
151 impl Eval for ConstantVelocity {
152 type Output = VItem;
153
154 fn eval_alpha(&self, target: f64) -> VItem {
155 let mut s = self.state.borrow_mut();
156 if target < s.1 {
157 s.0 = 0.0;
158 s.1 = 0.0;
159 }
160 let start_idx = (s.1 / self.sim_step).floor() as usize;
161 let end_idx = (target / self.sim_step).floor() as usize;
162 for _ in start_idx..end_idx {
163 s.0 += self.v * self.sim_step * self.logical_secs;
164 }
165 s.1 = target;
166 let mut item = VItem::default();
167 item.points[0].x = s.0 as f32;
168 item
169 }
170 }
171
172 struct ProgressX;
174
175 impl Eval for ProgressX {
176 type Output = VItem;
177
178 fn eval_alpha(&self, alpha: f64) -> VItem {
179 let mut item = VItem::default();
180 item.points[0].x = alpha as f32;
181 item
182 }
183 }
184
185 fn xs_of(frame: &EvaluatedFrame) -> Vec<f32> {
186 frame
187 .iter()
188 .filter_map(|(_, item)| match item {
189 CoreItem::VItem(v) => Some(v.points[0].x),
190 _ => None,
191 })
192 .collect()
193 }
194
195 fn cv(v: f64, logical_secs: f64) -> ConstantVelocity {
196 ConstantVelocity {
197 v,
198 logical_secs,
199 sim_step: 1.0 / 120.0,
200 state: std::cell::RefCell::new((0.0, 0.0)),
201 }
202 }
203
204 #[test]
205 fn functional_scene_matches_pure_eval() {
206 let mut scene = RanimScene::new();
207 scene.play(ProgressX.with_duration(2.0));
208 let sealed = scene.seal();
209
210 let mut ev = SceneEvaluator::new(sealed, 120.0);
211 for sec in [0.0, 0.25, 0.5, 1.0, 1.5, 2.0] {
212 let mut frame = EvaluatedFrame::new();
213 ev.sample_at(sec, &mut frame);
214 let expected = (sec / 2.0) as f32;
215 let got = xs_of(&frame);
216 assert_eq!(got, vec![expected], "at sec={sec}");
217 }
218 }
219
220 #[test]
221 fn iterative_leaves_step_along_the_logic_grid() {
222 let mut scene = RanimScene::new();
223 scene.play(cv(1.0, 2.0).with_duration(2.0).at(0.0));
224 let mut ev = SceneEvaluator::new(scene.seal(), 120.0);
225 for sec in [0.0, 0.5, 1.0, 1.5, 2.0] {
226 let mut frame = EvaluatedFrame::new();
227 ev.sample_at(sec, &mut frame);
228 assert_eq!(xs_of(&frame), vec![sec as f32], "at sec={sec}");
229 }
230
231 let mut scene = RanimScene::new();
233 scene.play(
234 seq![
235 cv(1.0, 1.0).with_duration(1.0),
236 cv(1.0, 1.0).with_duration(1.0)
237 ]
238 .at(0.0),
239 );
240 let mut ev = SceneEvaluator::new(scene.seal(), 120.0);
241 for (sec, expected) in [(1.5, 0.5), (2.0, 1.0)] {
242 let mut frame = EvaluatedFrame::new();
243 ev.sample_at(sec, &mut frame);
244 assert_eq!(xs_of(&frame), vec![expected], "at sec={sec}");
245 }
246 }
247
248 #[test]
249 fn iterative_seek_matches_forward_and_resets_nested_leaves() {
250 fn run(scene: SealedRanimScene, backward: bool) -> Vec<Vec<f32>> {
251 let mut ev = SceneEvaluator::new(scene, 120.0);
252 let mut trace = Vec::new();
253 for sec in [0.3, 0.7, 1.1, 1.9, 2.6] {
254 if backward {
255 ev.sample_at(0.2, &mut EvaluatedFrame::new());
258 }
259 let mut frame = EvaluatedFrame::new();
260 ev.sample_at(sec, &mut frame);
261 trace.push(xs_of(&frame));
262 }
263 trace
264 }
265
266 let single = || {
267 let mut scene = RanimScene::new();
268 scene.play(cv(2.0, 3.0).with_duration(3.0).at(0.0));
269 scene.seal()
270 };
271 let forward = run(single(), false);
272 assert_eq!(forward, run(single(), true));
273 assert_eq!(forward[2], vec![(2.0 * 1.1) as f32]);
274
275 let nested = || {
276 let mut scene = RanimScene::new();
277 scene.play(
278 seq![
279 cv(1.0, 1.0).with_duration(1.0),
280 cv(1.0, 1.0).with_duration(1.0)
281 ]
282 .at(0.0),
283 );
284 scene.seal()
285 };
286 assert_eq!(run(nested(), false), run(nested(), true));
287 }
288}