ranim_core/
lib.rs

1//! The core of ranim.
2//!
3//!
4#![warn(missing_docs)]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![allow(rustdoc::private_intra_doc_links)]
7#![doc(
8    html_logo_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg",
9    html_favicon_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg"
10)]
11/// Fondation of animation
12pub mod animation;
13/// Color
14pub mod color;
15/// Component data
16pub mod components;
17/// The structure to encode animation spans
18pub mod timeline;
19/// Fondamental traits
20pub mod traits;
21/// Utils
22pub mod utils;
23
24/// The core primitives
25pub mod primitives;
26/// The [`CoreItem`] store
27pub mod store;
28
29pub use glam;
30
31/// Prelude
32pub mod prelude {
33    pub use crate::color::prelude::*;
34    pub use crate::traits::*;
35
36    pub use crate::primitives::camera_frame::CameraFrame;
37    pub use crate::timeline::{TimelineFunc, TimelinesFunc};
38    pub use crate::{ItemId, RanimScene, TimeMark};
39}
40
41use crate::primitives::{CoreItem, Primitive, Primitives};
42#[cfg(target_arch = "wasm32")]
43use wasm_bindgen::prelude::*;
44
45/// Extract a [`Extract::Target`] from reference.
46pub trait Extract {
47    /// The extraction result
48    type Target: Primitive + Clone;
49    /// Extract a [`Extract::Target`] from reference.
50    fn extract(&self) -> Vec<Self::Target>;
51    /// Extract to [`Primitive`] from reference.
52    fn extract_to_primitives(&self) -> Primitives {
53        Self::Target::build_primitives(self.extract())
54    }
55}
56
57use crate::timeline::{AnimationInfo, ItemDynTimelines, ItemTimeline, TimelineFunc, TimelinesFunc};
58use itertools::Itertools;
59use tracing::trace;
60
61use std::{any::TypeId, fmt::Debug, ops::Deref};
62
63// MARK: Dylib part
64#[doc(hidden)]
65#[derive(Clone)]
66#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
67pub struct Scene {
68    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
69    pub name: &'static str,
70    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
71    pub constructor: fn(&mut RanimScene),
72    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
73    pub config: SceneConfig,
74    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
75    pub outputs: &'static [Output],
76}
77
78pub use inventory;
79
80inventory::collect!(Scene);
81
82#[doc(hidden)]
83#[unsafe(no_mangle)]
84pub extern "C" fn get_scene(idx: usize) -> *const Scene {
85    inventory::iter::<Scene>().skip(idx).take(1).next().unwrap()
86}
87
88#[doc(hidden)]
89#[unsafe(no_mangle)]
90pub extern "C" fn scene_cnt() -> usize {
91    inventory::iter::<Scene>().count()
92}
93
94/// Return a scene with matched name
95#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
96pub fn find_scene(name: &str) -> Option<Scene> {
97    inventory::iter::<Scene>().find(|s| s.name == name).cloned()
98}
99
100/// Scene config
101#[derive(Debug, Clone)]
102pub struct SceneConfig {
103    /// The height of the frame
104    ///
105    /// This will be the coordinate in the scene. The width is calculated by the aspect ratio from [`Output::width`] and [`Output::height`].
106    pub frame_height: f64,
107    /// The clear color
108    pub clear_color: &'static str,
109}
110
111impl Default for SceneConfig {
112    fn default() -> Self {
113        Self {
114            frame_height: 8.0,
115            clear_color: "#333333ff",
116        }
117    }
118}
119
120/// The output of a scene
121#[derive(Debug, Clone)]
122#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
123pub struct Output {
124    /// The width of the output texture in pixels.
125    pub width: u32,
126    /// The height of the output texture in pixels.
127    pub height: u32,
128    /// The frame rate of the output video.
129    pub fps: u32,
130    /// Whether to save the frames.
131    pub save_frames: bool,
132    /// The directory to save the output
133    ///
134    /// Related to the `output` folder, Or absolute.
135    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
136    pub dir: &'static str,
137}
138
139impl Default for Output {
140    fn default() -> Self {
141        Self::DEFAULT
142    }
143}
144
145impl Output {
146    /// 1920x1080 60fps save_frames=false dir="./"
147    pub const DEFAULT: Self = Self {
148        width: 1920,
149        height: 1080,
150        fps: 60,
151        save_frames: false,
152        dir: "./",
153    };
154}
155
156/// TimeMark
157#[derive(Debug, Clone)]
158pub enum TimeMark {
159    /// Capture a picture with a name
160    Capture(String),
161}
162
163// MARK: ItemId<T>
164/// An item id.
165pub struct ItemId<T> {
166    id: usize,
167    _phantom: std::marker::PhantomData<T>,
168}
169
170impl<T> Debug for ItemId<T> {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        f.debug_struct("ItemId")
173            .field("id", &self.id)
174            .field("type", &std::any::type_name::<T>())
175            .finish()
176    }
177}
178
179impl<T> Deref for ItemId<T> {
180    type Target = usize;
181    fn deref(&self) -> &Self::Target {
182        &self.id
183    }
184}
185
186impl<T> ItemId<T> {
187    /// Get the inner id.
188    pub fn inner(&self) -> usize {
189        self.id
190    }
191    pub(crate) fn new(id: usize) -> Self {
192        Self {
193            id,
194            _phantom: std::marker::PhantomData,
195        }
196    }
197}
198
199// MARK: SceneConstructor
200// ANCHOR: SceneConstructor
201/// A scene constructor
202///
203/// It can be a simple fn pointer of `fn(&mut RanimScene)`,
204/// or any type implements `Fn(&mut RanimScene) + Send + Sync`.
205pub trait SceneConstructor: Send + Sync {
206    /// The construct logic
207    fn construct(&self, r: &mut RanimScene);
208
209    /// Use the constructor to build a [`SealedRanimScene`]
210    fn build_scene(&self) -> SealedRanimScene {
211        let mut scene = RanimScene::new();
212        self.construct(&mut scene);
213        scene.seal()
214    }
215}
216// ANCHOR_END: SceneConstructor
217
218impl<F: Fn(&mut RanimScene) + Send + Sync> SceneConstructor for F {
219    fn construct(&self, r: &mut RanimScene) {
220        self(r);
221    }
222}
223
224// MARK: RanimScene
225/// The main struct that offers the ranim's API, and encodes animations
226/// The rabjects insert to it will hold a reference to it, so it has interior mutability
227#[derive(Default)]
228pub struct RanimScene {
229    // Timeline<CameraFrame> or Timeline<Item>
230    pub(crate) timelines: Vec<ItemDynTimelines>,
231    pub(crate) time_marks: Vec<(f64, TimeMark)>,
232}
233
234impl RanimScene {
235    /// Seals the scene to [`SealedRanimScene`].
236    pub fn seal(mut self) -> SealedRanimScene {
237        let total_secs = self.timelines.max_total_secs();
238        self.timelines.forward_to(total_secs);
239        self.timelines.seal();
240        SealedRanimScene {
241            total_secs,
242            timelines: self.timelines,
243            time_marks: self.time_marks,
244        }
245    }
246    /// Create a new [`RanimScene`]
247    pub fn new() -> Self {
248        Self::default()
249    }
250
251    /// Use the item state to create a new [`ItemDynTimelines`] and returns the [`ItemId`].
252    ///
253    /// Note that, the new timeline is hidden by default, use [`ItemTimeline::forward`] and
254    /// [`ItemTimeline::forward_to`] to modify the start time of the first anim, and use
255    /// [`ItemTimeline::show`] to start encoding and static anim.
256    pub fn insert<T: Extract + Clone + 'static>(&mut self, state: T) -> ItemId<T> {
257        self.insert_and(state, |_| {})
258    }
259    /// Use the item state to create a new [`ItemDynTimelines`], and call [`ItemTimeline::show`] on it.
260    pub fn insert_and_show<T: Extract + Clone + 'static>(&mut self, state: T) -> ItemId<T> {
261        self.insert_and(state, |timeline| {
262            timeline.show();
263        })
264    }
265    /// Use the item state to create a new [`ItemDynTimelines`], and call `f` on it.
266    pub fn insert_and<T: Extract + Clone + 'static>(
267        &mut self,
268        state: T,
269        f: impl FnOnce(&mut ItemTimeline<T>),
270    ) -> ItemId<T> {
271        let id = ItemId::new(self.timelines.len());
272        trace!("insert_and type of {:?}, id: {id:?}", TypeId::of::<T>());
273        let mut item_timeline = ItemTimeline::<T>::new(state);
274        f(&mut item_timeline);
275
276        let mut timeline = ItemDynTimelines::new();
277        timeline.push(item_timeline);
278        self.timelines.push(timeline);
279        id
280    }
281    /// Consumes an [`ItemId<T>`], and convert it into [`ItemId<E>`].
282    ///
283    /// This insert inserts an [`ItemTimeline<E>`] into the corresponding [`ItemDynTimelines`]
284    pub fn map<T: Extract + Clone + 'static, E: Extract + Clone + 'static>(
285        &mut self,
286        item_id: ItemId<T>,
287        map_fn: impl FnOnce(T) -> E,
288    ) -> ItemId<E> {
289        trace!(
290            "map {item_id:?} {:?} -> {:?}",
291            TypeId::of::<T>(),
292            TypeId::of::<E>()
293        );
294        // let dyn_item_timeline = self
295        //     .timelines
296        //     .iter_mut()
297        //     .find(|timeline| timeline.id == *item_id)
298        //     .unwrap();
299        let dyn_item_timeline = self.timelines.get_mut(*item_id).unwrap();
300        dyn_item_timeline.apply_map(map_fn);
301        ItemId::new(item_id.inner())
302    }
303
304    /// Get reference of all timelines in the type erased [`ItemDynTimelines`] type.
305    pub fn timelines(&self) -> &[ItemDynTimelines] {
306        trace!("timelines");
307        &self.timelines
308    }
309    /// Get mutable reference of all timelines in the type erased [`ItemDynTimelines`] type.
310    pub fn timelines_mut(&mut self) -> &mut [ItemDynTimelines] {
311        trace!("timelines_mut");
312        &mut self.timelines
313    }
314    /// Get the reference of timeline(s) by the [`TimelineIndex`].
315    pub fn timeline<'a, T: TimelineIndex<'a>>(&'a self, index: &T) -> T::RefOutput {
316        index.timeline(self)
317    }
318    /// Get the mutable reference of timeline(s) by the [`TimelineIndex`].
319    pub fn timeline_mut<'a, T: TimelineIndex<'a>>(&'a mut self, index: &T) -> T::MutOutput {
320        index.timeline_mut(self)
321    }
322    /// Inserts an [`TimeMark`]
323    pub fn insert_time_mark(&mut self, sec: f64, time_mark: TimeMark) {
324        self.time_marks.push((sec, time_mark));
325    }
326}
327
328/// The information of an [`ItemDynTimelines`].
329pub struct ItemDynTimelinesInfo {
330    /// The inner id value of the [`ItemId`]
331    pub id: usize,
332    /// The animation infos.
333    pub animation_infos: Vec<AnimationInfo>,
334}
335
336impl Debug for RanimScene {
337    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338        f.write_fmt(format_args!("Timeline: {} timelines", self.timelines.len()))?;
339        Ok(())
340    }
341}
342
343// MARK: SealedRanimScene
344/// The sealed [`RanimScene`].
345///
346/// the timelines and time marks cannot be modified after sealed. And
347/// once the [`RanimScene`] is sealed, it can be used for evaluating.
348pub struct SealedRanimScene {
349    pub(crate) total_secs: f64,
350    pub(crate) timelines: Vec<ItemDynTimelines>,
351    pub(crate) time_marks: Vec<(f64, TimeMark)>,
352}
353
354impl SealedRanimScene {
355    /// Get the total seconds of the [`SealedRanimScene`].
356    pub fn total_secs(&self) -> f64 {
357        self.total_secs
358    }
359    /// Get time marks
360    pub fn time_marks(&self) -> &[(f64, TimeMark)] {
361        &self.time_marks
362    }
363
364    /// Get the iterator of timelines
365    pub fn timelines_iter(&self) -> impl Iterator<Item = &ItemDynTimelines> {
366        self.timelines.iter()
367    }
368
369    /// Get the count of timelines
370    pub fn timelines_cnt(&self) -> usize {
371        self.timelines.len()
372    }
373
374    /// Get timeline infos.
375    pub fn get_timeline_infos(&self) -> Vec<ItemDynTimelinesInfo> {
376        // const MAX_TIMELINE_CNT: usize = 100;
377        self.timelines
378            .iter()
379            .enumerate()
380            // .take(MAX_TIMELINE_CNT)
381            .map(|(id, timeline)| ItemDynTimelinesInfo {
382                id,
383                animation_infos: timeline.get_animation_infos(),
384            })
385            .collect()
386    }
387
388    /// Eval primitives
389    pub fn eval_at_sec(&self, target_sec: f64) -> impl Iterator<Item = CoreItem> {
390        self.timelines_iter()
391            .filter_map(move |t| {
392                t.eval_primitives_at_sec(target_sec)
393                    .map(|(res, _id_hash)| res.to_owned().boom())
394            })
395            .flatten()
396    }
397
398    /// Eval primitives
399    pub fn eval_at_alpha(&self, alpha: f64) -> impl Iterator<Item = CoreItem> {
400        self.eval_at_sec(self.total_secs() * alpha)
401    }
402}
403
404// MARK: TimelineIndex
405/// A trait for indexing timeline(s)
406///
407/// [`RanimScene::timeline`] and [`RanimScene::timeline_mut`] uses the
408/// reference of [`TimelineIndex`] to index the timeline(s).
409///
410/// | Index Type | Output Type |
411/// |------------|-------------|
412/// |   `usize`    | `&ItemDynTimelines` and `&mut ItemDynTimelines` |
413/// |   `ItemId<T>`    | `&ItemTimeline<T>` & and `&mut ItemTimeline<T>` |
414/// |   `[&ItemId<T>; N]`    | `[&ItemTimeline<T>; N]` and `[&mut ItemTimeline<T>; N]` |
415pub trait TimelineIndex<'a> {
416    /// Output of [`TimelineIndex::timeline`]
417    type RefOutput;
418    /// Output of [`TimelineIndex::timeline_mut`]
419    type MutOutput;
420    /// Get the reference of timeline(s) from [`RanimScene`] by the [`TimelineIndex`].
421    fn timeline(&self, timeline: &'a RanimScene) -> Self::RefOutput;
422    /// Get the mutable reference of timeline(s) from [`RanimScene`] by the [`TimelineIndex`].
423    fn timeline_mut(&self, timeline: &'a mut RanimScene) -> Self::MutOutput;
424}
425
426impl<'a> TimelineIndex<'a> for usize {
427    type RefOutput = Option<&'a ItemDynTimelines>;
428    type MutOutput = Option<&'a mut ItemDynTimelines>;
429    fn timeline(&self, r: &'a RanimScene) -> Self::RefOutput {
430        r.timelines.get(*self)
431        // timeline
432        //     .timelines()
433        //     .iter()
434        //     .find(|timeline| *self == timeline.id)
435    }
436    fn timeline_mut(&self, r: &'a mut RanimScene) -> Self::MutOutput {
437        r.timelines.get_mut(*self)
438        // timeline
439        //     .timelines_mut()
440        //     .iter_mut()
441        //     .find(|timeline| *self == timeline.id)
442    }
443}
444
445impl<'a, T: 'static> TimelineIndex<'a> for ItemId<T> {
446    type RefOutput = &'a ItemTimeline<T>;
447    type MutOutput = &'a mut ItemTimeline<T>;
448    fn timeline(&self, r: &'a RanimScene) -> Self::RefOutput {
449        r.timelines.get(**self).unwrap().get()
450        // timeline
451        //     .timelines()
452        //     .iter()
453        //     .find(|timeline| **self == timeline.id)
454        //     .map(|timeline| timeline.get())
455        //     .unwrap()
456    }
457    fn timeline_mut(&self, r: &'a mut RanimScene) -> Self::MutOutput {
458        r.timelines.get_mut(**self).unwrap().get_mut()
459        // r
460        //     .timelines_mut()
461        //     .iter_mut()
462        //     .find(|timeline| **self == timeline.id)
463        //     .map(|timeline| timeline.get_mut())
464        //     .unwrap()
465    }
466}
467
468impl<'a, T: 'static, const N: usize> TimelineIndex<'a> for [&ItemId<T>; N] {
469    type RefOutput = [&'a ItemTimeline<T>; N];
470    type MutOutput = [&'a mut ItemTimeline<T>; N];
471    fn timeline(&self, r: &'a RanimScene) -> Self::RefOutput {
472        // TODO: the order is not stable
473        let mut timelines = r
474            .timelines()
475            .iter()
476            .enumerate()
477            .filter(|(id, _)| self.iter().any(|target_id| ***target_id == *id))
478            .collect_array::<N>()
479            .unwrap();
480        timelines.sort_by_key(|(id, _)| {
481            self.iter()
482                .position(|target_id| ***target_id == *id)
483                .unwrap()
484        });
485        timelines.map(|(_, timeline)| timeline.get())
486    }
487    fn timeline_mut(&self, r: &'a mut RanimScene) -> Self::MutOutput {
488        // TODO: the order is not stable
489        let mut timelines = r
490            .timelines_mut()
491            .iter_mut()
492            .enumerate()
493            .filter(|(id, _)| self.iter().any(|target_id| ***target_id == *id))
494            .collect_array::<N>()
495            .unwrap();
496        timelines.sort_by_key(|(id, _)| {
497            self.iter()
498                .position(|target_id| ***target_id == *id)
499                .unwrap()
500        });
501        timelines.map(|(_, timeline)| timeline.get_mut())
502    }
503}