1#![warn(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![allow(rustdoc::private_intra_doc_links)]
6#![doc(
7 html_logo_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg",
8 html_favicon_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg"
9)]
10
11pub mod anchor;
13pub mod animation;
14pub mod audio;
16pub mod color;
18pub mod components;
20pub mod core_item;
22pub mod scene_evaluator;
24pub mod time;
26pub mod traits;
28pub use scene_evaluator::SceneEvaluator;
29pub mod utils;
31
32pub use glam;
33use std::fmt::Debug;
34use std::sync::Arc;
35
36use animation::{
37 build::IntoAnimNode,
38 compose::stack::AnimStack,
39 node::{AnimNode, AnimationInfo, bake_audio},
40};
41use core_item::CoreItem;
42
43pub mod prelude {
45 pub use crate::color::prelude::*;
46 pub use crate::traits::*;
47
48 pub use crate::animation::build::{IntoAnimNode, PlaybackExt, Unplaced};
49 pub use crate::animation::compose::{
50 AnimIterExt,
51 lagged::{AnimLagged, LaggedFill},
52 sequence::AnimSequence,
53 stack::AnimStack,
54 };
55 pub use crate::animation::eval::{
56 Eval, EvalExt, Static, StaticAnim,
57 iterative::{Iterative, IterativeEval, IterativeFn},
58 pure::Pure,
59 };
60 pub use crate::animation::sound::Sound;
61 pub use crate::audio::{AudioClip, AudioTrack};
62 pub use crate::core_item::CoreItem;
63 pub use crate::core_item::camera_frame::CameraFrame;
64 pub use crate::core_item::transformed::{Transformed, TransformedExt};
65 pub use crate::{Extract, RanimScene, TimeMark};
66}
67
68pub trait Extract {
70 type Target: Clone;
72 fn extract_into(&self, buf: &mut Vec<Self::Target>);
74 fn extract(&self) -> Vec<Self::Target> {
76 let mut buf = Vec::new();
77 self.extract_into(&mut buf);
78 buf
79 }
80}
81
82mod sealed {
89 pub trait IntoExtractIter {
90 type Item;
91 }
92 impl<E> IntoExtractIter for Vec<E> {
93 type Item = E;
94 }
95 impl<E> IntoExtractIter for std::collections::VecDeque<E> {
96 type Item = E;
97 }
98 impl<E> IntoExtractIter for std::collections::LinkedList<E> {
99 type Item = E;
100 }
101 impl<E> IntoExtractIter for std::collections::HashSet<E> {
102 type Item = E;
103 }
104 impl<E> IntoExtractIter for std::collections::BTreeSet<E> {
105 type Item = E;
106 }
107 impl<E> IntoExtractIter for std::collections::BinaryHeap<E> {
108 type Item = E;
109 }
110 impl<E> IntoExtractIter for Option<E> {
111 type Item = E;
112 }
113 impl<E, const N: usize> IntoExtractIter for [E; N] {
114 type Item = E;
115 }
116}
117use sealed::IntoExtractIter;
118
119impl<I: IntoExtractIter> Extract for I
120where
121 I::Item: Extract,
122 for<'a> &'a I: IntoIterator<Item = &'a I::Item>,
123{
124 type Target = <I::Item as Extract>::Target;
125
126 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
127 for element in self {
128 element.extract_into(buf);
129 }
130 }
131}
132
133macro_rules! impl_extract_for_tuple {
136 ($(($E:ident, $e:ident)),*) => {
137 impl<T: Clone, $($E: Extract<Target = T>),*> Extract for ($($E,)*) {
138 type Target = T;
139
140 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
141 let ($($e,)*) = self;
142 $($e.extract_into(buf);)*
143 }
144 }
145 };
146}
147
148variadics_please::all_tuples!(impl_extract_for_tuple, 1, 15, E, e);
150
151#[derive(Debug, Clone)]
153pub enum TimeMark {
154 Capture(String),
156}
157
158#[derive(Default)]
164pub struct RanimScene {
165 pub root: AnimStack,
171 time_marks: Vec<(f64, TimeMark)>,
172}
173
174impl RanimScene {
175 pub fn new() -> Self {
177 Self::default()
178 }
179
180 pub fn play<A: IntoAnimNode + 'static>(&mut self, animation: A) -> &mut Self {
182 self.root.push(animation);
183 self
184 }
185
186 pub fn insert_time_mark(&mut self, sec: f64, time_mark: TimeMark) {
188 self.time_marks.push((sec, time_mark));
189 }
190
191 pub fn seal(self) -> SealedRanimScene {
198 let total_secs = self.root.duration_secs();
199 let animations = self.root.into_built_animations();
200 let sample_rate = crate::audio::MASTER_SAMPLE_RATE as f64;
201 let audio: Arc<[f32]> = bake_audio(&animations, total_secs, sample_rate).into();
202 SealedRanimScene {
203 total_secs,
204 animations,
205 audio,
206 time_marks: self.time_marks,
207 }
208 }
209}
210
211impl Debug for RanimScene {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("RanimScene")
214 .field("animations", &self.root.built_animations().len())
215 .field("duration_secs", &self.root.duration_secs())
216 .finish()
217 }
218}
219
220pub struct SealedRanimScene {
222 total_secs: f64,
223 animations: Vec<AnimNode>,
224 audio: Arc<[f32]>,
228 time_marks: Vec<(f64, TimeMark)>,
229}
230
231impl SealedRanimScene {
232 pub fn into_evaluator(self, logic_fps: f64) -> SceneEvaluator {
237 SceneEvaluator::new(self, logic_fps)
238 }
239
240 pub fn audio(&self) -> &Arc<[f32]> {
244 &self.audio
245 }
246
247 pub fn total_secs(&self) -> f64 {
249 self.total_secs
250 }
251
252 pub fn time_marks(&self) -> &[(f64, TimeMark)] {
254 &self.time_marks
255 }
256
257 pub fn get_animation_infos(&self) -> Vec<AnimationInfo> {
259 self.animations
260 .iter()
261 .map(AnimNode::animation_info)
262 .collect()
263 }
264
265 pub fn eval_at_sec(&self, target_sec: f64) -> impl Iterator<Item = ((usize, usize), CoreItem)> {
270 self.animations
271 .iter()
272 .enumerate()
273 .filter_map(move |(animation_id, animation)| {
274 if !animation.enabled() {
275 return None;
276 }
277
278 let mut items = Vec::new();
279 animation.eval_at(target_sec, &mut items);
280 (!items.is_empty()).then_some((animation_id, items))
281 })
282 .flat_map(|(animation_id, items)| {
283 items
284 .into_iter()
285 .flat_map(|item| item.extract())
286 .enumerate()
287 .map(move |(part, item)| ((animation_id, part), item))
288 })
289 }
290
291 pub fn eval_at_alpha(&self, alpha: f64) -> impl Iterator<Item = ((usize, usize), CoreItem)> {
293 self.eval_at_sec(self.total_secs * alpha)
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::{
301 animation::{
302 build::{PlaybackExt, Unplaced},
303 compose::sequence::AnimSequence,
304 eval::Static,
305 },
306 core_item::vitem::VItem,
307 };
308
309 fn leaf(duration: f64) -> impl Unplaced {
310 Static(VItem::default()).with_duration(duration)
311 }
312
313 #[test]
314 fn scene_play_places_sequences_on_the_root_timeline() {
315 let mut scene = RanimScene::new();
317 scene.play(seq![leaf(2.0), leaf(3.0)]);
318 let sealed = scene.seal();
319 assert_eq!(sealed.total_secs(), 5.0);
320 let infos = sealed.get_animation_infos();
321 assert_eq!(infos[0].range, 0.0..5.0);
322 assert_eq!(infos[0].children[0].range, 0.0..2.0);
323 assert_eq!(infos[0].children[1].range, 2.0..5.0);
324
325 let mut reusable = AnimSequence::new();
328 reusable.push(leaf(2.0)).forward(1.0).push(leaf(1.0));
329 let mut scene = RanimScene::new();
330 scene.play(reusable);
331 scene.root.push(leaf(5.0));
332 let sealed = scene.seal();
333 assert_eq!(sealed.total_secs(), 5.0);
334 let infos = sealed.get_animation_infos();
335 assert_eq!(infos[0].range, 0.0..4.0);
336 assert_eq!(infos[0].children[1].range, 3.0..4.0);
337 assert_eq!(infos[1].range, 0.0..5.0);
338 }
339
340 #[test]
341 fn extracted_items_have_unique_semantic_part_ids() {
342 let mut scene = RanimScene::new();
343 scene.play(Static(vec![VItem::default(), VItem::default()]).with_duration(1.0));
344 let sealed = scene.seal();
345
346 let ids = sealed
347 .eval_at_sec(0.5)
348 .map(|(id, _)| id)
349 .collect::<Vec<_>>();
350
351 assert_eq!(ids, [(0, 0), (0, 1)]);
352 }
353
354 #[test]
355 fn heterogeneous_tuples_extract_to_core_items() {
356 use crate::core_item::{camera_frame::CameraFrame, mesh_item::MeshItem};
357
358 let camera = CameraFrame::default();
359 let vitem = VItem::default();
360 let mesh = MeshItem::default();
361
362 let items = (camera.clone(), vitem.clone()).extract();
364 assert_eq!(items.len(), 2);
365 assert!(matches!(&items[0], CoreItem::CameraFrame(_)));
366 assert!(matches!(&items[1], CoreItem::VItem(_)));
367
368 let items = (camera.clone(), vitem.clone(), mesh.clone()).extract();
370 assert_eq!(items.len(), 3);
371
372 let items = vec![
374 (camera.clone(), vitem.clone()),
375 (camera.clone(), vitem.clone()),
376 ]
377 .extract();
378 assert_eq!(items.len(), 4);
379
380 let t13 = (
382 camera.clone(),
383 vitem.clone(),
384 mesh.clone(),
385 camera.clone(),
386 vitem.clone(),
387 mesh.clone(),
388 camera.clone(),
389 vitem.clone(),
390 mesh.clone(),
391 camera.clone(),
392 vitem.clone(),
393 mesh.clone(),
394 camera.clone(),
395 );
396 assert_eq!(t13.extract().len(), 13);
397 }
398}