ranim_core/
scene_evaluator.rs1use crate::{
19 Extract, SealedRanimScene, TimeMark,
20 animation::{AnimationCell, AnimationInfo},
21 core_item::CoreItem,
22};
23
24pub type EvaluatedFrame = Vec<((usize, usize), CoreItem)>;
26
27pub struct SceneEvaluator {
29 cells: Vec<AnimationCell>,
30 total_secs: f64,
31 time_marks: Vec<(f64, TimeMark)>,
32 logic_fps: f64,
33 clock: f64,
34}
35
36impl SceneEvaluator {
37 pub fn new(scene: SealedRanimScene, logic_fps: f64) -> Self {
43 assert!(
44 logic_fps.is_finite() && logic_fps > 0.0,
45 "logic_fps must be finite and positive"
46 );
47 Self {
48 cells: scene.animations,
49 total_secs: scene.total_secs,
50 time_marks: scene.time_marks,
51 logic_fps,
52 clock: 0.0,
53 }
54 }
55
56 pub fn total_secs(&self) -> f64 {
58 self.total_secs
59 }
60
61 pub fn time_marks(&self) -> &[(f64, TimeMark)] {
63 &self.time_marks
64 }
65
66 pub fn animation_infos(&self) -> Vec<AnimationInfo> {
68 self.cells
69 .iter()
70 .map(AnimationCell::animation_info)
71 .collect()
72 }
73
74 pub fn clock(&self) -> f64 {
76 self.clock
77 }
78
79 pub fn advance_to(&mut self, render_secs: f64) {
85 let target = (render_secs * self.logic_fps).floor() / self.logic_fps;
86 while self.clock + 1e-9 < target {
87 let prev_tick_secs = self.clock;
88 let tick_secs = prev_tick_secs + 1.0 / self.logic_fps;
89 self.clock = tick_secs;
90 for cell in &mut self.cells {
91 cell.step_at_sec(tick_secs, prev_tick_secs);
92 }
93 }
94 self.clock = target;
95 }
96
97 pub fn seek(&mut self, render_secs: f64) {
99 self.clock = 0.0;
100 for cell in &mut self.cells {
101 cell.reset_entered();
102 }
103 self.advance_to(render_secs);
104 }
105
106 pub fn sample_into(&self, out: &mut EvaluatedFrame) {
112 for (animation_id, cell) in self.cells.iter().enumerate() {
113 if !cell.active_at(self.clock) {
114 continue;
115 }
116 let mut items = Vec::new();
117 cell.sample_at_sec(self.clock, &mut items);
118 for (part, item) in items
119 .into_iter()
120 .flat_map(|item| item.extract())
121 .enumerate()
122 {
123 out.push(((animation_id, part), item));
124 }
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::{
133 RanimScene,
134 animation::{AnimationExt, Eval, Placeable, SegmentTime},
135 core_item::vitem::VItem,
136 seq,
137 };
138
139 struct ConstantVelocity {
141 v: f64,
142 x: f64,
143 }
144
145 impl Eval for ConstantVelocity {
146 type Output = VItem;
147
148 fn sample(&self, _time: &SegmentTime) -> VItem {
149 let mut item = VItem::default();
150 item.points[0].x = self.x as f32;
151 item
152 }
153
154 fn reset(&mut self) {
155 self.x = 0.0;
156 }
157
158 fn step(&mut self, time: &SegmentTime) {
159 self.x += self.v * time.local_delta_secs;
160 }
161 }
162
163 fn xs_of(frame: &EvaluatedFrame) -> Vec<f32> {
164 frame
165 .iter()
166 .filter_map(|(_, item)| match item {
167 CoreItem::VItem(v) => Some(v.points[0].x),
168 _ => None,
169 })
170 .collect()
171 }
172
173 #[test]
174 fn functional_scene_matches_pure_eval() {
175 let mut scene = RanimScene::new();
176 scene.play(
177 (move |alpha| {
178 let mut item = VItem::default();
179 item.points[0].x = alpha as f32;
180 item
181 })
182 .with_duration(2.0),
183 );
184 let sealed = scene.seal();
185
186 let mut ev = SceneEvaluator::new(sealed, 120.0);
187 for sec in [0.0, 0.25, 0.5, 1.0, 1.5, 2.0] {
188 let mut frame = EvaluatedFrame::new();
189 ev.advance_to(sec);
190 ev.sample_into(&mut frame);
191 let expected = (sec / 2.0) as f32;
193 let got = xs_of(&frame);
194 assert_eq!(got, vec![expected], "at sec={sec}");
195 }
196 }
197
198 #[test]
199 fn iterative_segment_steps_along_logic_grid() {
200 let mut scene = RanimScene::new();
201 scene.play(
202 ConstantVelocity { v: 1.0, x: 0.0 }
203 .with_duration(2.0)
204 .at(0.0),
205 );
206 let mut ev = SceneEvaluator::new(scene.seal(), 120.0);
207
208 for sec in [0.0, 0.5, 1.0, 1.5, 2.0] {
209 let mut frame = EvaluatedFrame::new();
210 ev.advance_to(sec);
211 ev.sample_into(&mut frame);
212 assert_eq!(xs_of(&frame), vec![sec as f32], "at sec={sec}");
213 }
214 }
215
216 #[test]
217 fn iterative_eval_is_deterministic_and_seek_matches_forward() {
218 fn build_scene() -> SealedRanimScene {
219 let mut scene = RanimScene::new();
220 scene.play(
221 ConstantVelocity { v: 2.0, x: 0.0 }
222 .with_duration(3.0)
223 .at(0.0),
224 );
225 scene.seal()
226 }
227
228 let run = |seek_first: bool| {
229 let mut ev = SceneEvaluator::new(build_scene(), 120.0);
230 let mut trace = Vec::new();
231 for sec in [0.3, 0.7, 1.1, 1.9, 2.6] {
232 if seek_first {
233 ev.seek(sec);
234 } else {
235 ev.advance_to(sec);
236 }
237 let mut frame = EvaluatedFrame::new();
238 ev.sample_into(&mut frame);
239 trace.push(xs_of(&frame));
240 }
241 trace
242 };
243
244 let forward = run(false);
245 let seek = run(true);
246 assert_eq!(forward, seek);
247 assert_eq!(forward[2], vec![(2.0 * 1.1) as f32]);
249 }
250
251 #[test]
252 fn iterative_leaf_inside_sequence_steps() {
253 let mut scene = RanimScene::new();
254 scene.play(
255 seq![
256 ConstantVelocity { v: 1.0, x: 0.0 }.with_duration(1.0),
257 ConstantVelocity { v: 1.0, x: 0.0 }.with_duration(1.0),
258 ]
259 .at(0.0),
260 );
261
262 let mut ev = SceneEvaluator::new(scene.seal(), 120.0);
263 let mut frame = EvaluatedFrame::new();
266 ev.advance_to(1.5);
267 ev.sample_into(&mut frame);
268 assert_eq!(xs_of(&frame), vec![0.5]);
269
270 let mut frame = EvaluatedFrame::new();
272 ev.advance_to(2.0);
273 ev.sample_into(&mut frame);
274 assert_eq!(xs_of(&frame), vec![1.0]);
275 }
276}