ranim_core/
timeline.rs

1use std::{any::Any, sync::Arc};
2
3use crate::{
4    Extract,
5    animation::{AnimationSpan, EvalResult, Evaluator},
6    primitives::Primitives,
7    utils::calculate_hash,
8};
9
10// MARK: TimelineFunc
11/// Functions for a timeline
12pub trait TimelineFunc: Any {
13    /// The start sec of the timeline(the start sec of the first animation.)
14    fn start_sec(&self) -> Option<f64>;
15    /// The end sec of the timeline(the end sec of the last animation.)
16    fn end_sec(&self) -> Option<f64>;
17    /// The range of the timeline.
18    fn range_sec(&self) -> Option<std::ops::Range<f64>> {
19        let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
20            return None;
21        };
22        Some(start..end)
23    }
24    /// Seal the timeline func(submit the planning static anim)
25    fn seal(&mut self);
26    /// The current sec of the timeline.
27    fn cur_sec(&self) -> f64;
28    /// Forward the timeline by `secs`
29    fn forward(&mut self, secs: f64);
30    /// Forward the timeline to `target_sec`
31    fn forward_to(&mut self, target_sec: f64) {
32        let duration = target_sec - self.cur_sec();
33        if duration > 0.0 {
34            self.forward(duration);
35        }
36    }
37    /// Show the item
38    fn show(&mut self);
39    /// Hide the item
40    fn hide(&mut self);
41    /// Get the animation infos
42    fn get_animation_infos(&self) -> Vec<AnimationInfo>;
43    /// The type name of the timeline
44    fn type_name(&self) -> &str;
45
46    // fn eval_sec_any(&self, target_sec: f64) -> Option<(EvalResult<dyn Any>, usize)>;
47    /// Evaluate timeline's primitives at target sec
48    fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(EvalResult<Primitives>, u64)>;
49}
50
51// MARK: TimelinesFunc
52/// Functions for timelines
53pub trait TimelinesFunc {
54    /// Seal timelines
55    fn seal(&mut self);
56    /// Get the max end_sec of the timelines
57    fn max_total_secs(&self) -> f64;
58    /// Sync the timelines
59    fn sync(&mut self);
60    /// Forward all timelines by `sec`
61    fn forward(&mut self, secs: f64);
62    /// Forward all timelines to `target_sec`
63    fn forward_to(&mut self, target_sec: f64);
64}
65
66impl<I: ?Sized, T: TimelineFunc> TimelinesFunc for I
67where
68    for<'a> &'a mut I: IntoIterator<Item = &'a mut T>,
69    for<'a> &'a I: IntoIterator<Item = &'a T>,
70{
71    fn seal(&mut self) {
72        self.into_iter().for_each(|timeline: &mut T| {
73            timeline.seal();
74        });
75    }
76    fn max_total_secs(&self) -> f64 {
77        self.into_iter()
78            .map(|timeline: &T| timeline.cur_sec())
79            .max_by(|a, b| a.partial_cmp(b).unwrap())
80            .unwrap()
81    }
82    fn sync(&mut self) {
83        let max_elapsed_secs = self.max_total_secs();
84        self.into_iter().for_each(|timeline: &mut T| {
85            timeline.forward_to(max_elapsed_secs);
86        });
87    }
88    fn forward(&mut self, secs: f64) {
89        self.into_iter()
90            .for_each(|timeline: &mut T| timeline.forward(secs));
91    }
92    fn forward_to(&mut self, target_sec: f64) {
93        self.into_iter().for_each(|timeline: &mut T| {
94            timeline.forward_to(target_sec);
95        });
96    }
97}
98
99impl TimelineFunc for Box<dyn TimelineFunc> {
100    fn start_sec(&self) -> Option<f64> {
101        self.as_ref().start_sec()
102    }
103    fn end_sec(&self) -> Option<f64> {
104        self.as_ref().end_sec()
105    }
106    fn seal(&mut self) {
107        self.as_mut().seal()
108    }
109    fn cur_sec(&self) -> f64 {
110        self.as_ref().cur_sec()
111    }
112    fn forward(&mut self, secs: f64) {
113        self.as_mut().forward(secs)
114    }
115    fn show(&mut self) {
116        self.as_mut().show()
117    }
118    fn hide(&mut self) {
119        self.as_mut().hide()
120    }
121    fn get_animation_infos(&self) -> Vec<AnimationInfo> {
122        self.as_ref().get_animation_infos()
123    }
124    fn type_name(&self) -> &str {
125        self.as_ref().type_name()
126    }
127    // fn eval_sec_any(&self, target_sec: f64) -> Option<(EvalResult<dyn Any>, usize)> {
128    //     self.as_ref().eval_sec_any(target_sec)
129    // }
130    fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(EvalResult<Primitives>, u64)> {
131        self.as_ref().eval_primitives_at_sec(target_sec)
132    }
133}
134
135// MARK: ItemDynTimelines
136// ANCHOR: ItemDynTimelines
137/// A item timeline which contains multiple `Box<dyn TimelineFunc>`, so
138/// that it can contains multiple [`ItemTimeline<T>`] in different type of `T`.
139#[derive(Default)]
140pub struct ItemDynTimelines {
141    inner: Vec<Box<dyn TimelineFunc>>,
142}
143
144impl TimelineFunc for ItemDynTimelines {
145    fn start_sec(&self) -> Option<f64> {
146        self.get_dyn().start_sec()
147    }
148    fn end_sec(&self) -> Option<f64> {
149        self.get_dyn().end_sec()
150    }
151    fn seal(&mut self) {
152        self.get_dyn_mut().seal();
153    }
154    fn cur_sec(&self) -> f64 {
155        self.get_dyn().cur_sec()
156    }
157    fn forward(&mut self, duration_secs: f64) {
158        self.get_dyn_mut().forward(duration_secs);
159    }
160    fn show(&mut self) {
161        self.get_dyn_mut().show();
162    }
163    fn hide(&mut self) {
164        self.get_dyn_mut().hide();
165    }
166    fn get_animation_infos(&self) -> Vec<AnimationInfo> {
167        self.inner
168            .iter()
169            .flat_map(|timeline| timeline.get_animation_infos())
170            .collect()
171    }
172    fn type_name(&self) -> &str {
173        self.get_dyn().type_name()
174    }
175    fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(EvalResult<Primitives>, u64)> {
176        self.eval_primitives_at_sec(target_sec)
177    }
178}
179
180impl ItemDynTimelines {
181    /// Create a new [`ItemDynTimelines`]
182    pub fn new() -> Self {
183        Self::default()
184    }
185    /// Push a new [`ItemTimeline<T>`] to the end of the timelines
186    pub fn push<T: Extract + Clone + 'static>(&mut self, timeline: ItemTimeline<T>) {
187        self.inner.push(Box::new(timeline));
188    }
189    /// Evaluate the timeline and extract to primitives at `alpha`
190    pub fn eval_primitives_at_alpha(&self, alpha: f64) -> Option<(EvalResult<Primitives>, u64)> {
191        let target_sec = self.inner.max_total_secs() * alpha;
192        self.eval_primitives_at_sec(target_sec)
193    }
194    /// Evaluate the timeline at `target_sec`
195    pub fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(EvalResult<Primitives>, u64)> {
196        // println!("len: {}", self.timelines.len());
197        // println!("ItemDynTimelines::eval_sec_extracted_any: {}", target_sec);
198
199        let (timeline_idx, timeline) = self.inner.iter().enumerate().find(|(idx, timeline)| {
200            timeline
201                .range_sec()
202                .map(|range| {
203                    range.contains(&target_sec)
204                        || *idx == self.inner.len() - 1 && range.end == target_sec
205                })
206                .unwrap_or(false)
207        })?;
208
209        timeline
210            .eval_primitives_at_sec(target_sec)
211            .map(|(res, idx)| (res, calculate_hash(&(timeline_idx, idx))))
212    }
213}
214
215impl ItemDynTimelines {
216    /// As a reference of [`TimelineFunc`]
217    pub fn get_dyn(&self) -> &dyn TimelineFunc {
218        // TODO: make this unwrap better
219        self.inner.last().unwrap()
220    }
221    /// As a mutable reference of [`TimelineFunc`]
222    pub fn get_dyn_mut(&mut self) -> &mut dyn TimelineFunc {
223        // TODO: make this unwrap better
224        self.inner.last_mut().unwrap()
225    }
226    /// As a reference of [`ItemTimeline<T>`]
227    ///
228    /// Should make sure that the last timeline in it is of type `T`
229    pub fn get<T: 'static>(&self) -> &ItemTimeline<T> {
230        // TODO: make this unwrap better
231        (self.inner.last().unwrap().as_ref() as &dyn Any)
232            .downcast_ref()
233            .unwrap()
234    }
235    /// As a mutable reference of [`ItemTimeline<T>`]
236    ///
237    /// Should make sure that the last timeline in it is of type `T`
238    pub fn get_mut<T: 'static>(&mut self) -> &mut ItemTimeline<T> {
239        // TODO: make this unwrap better
240        (self.inner.last_mut().unwrap().as_mut() as &mut dyn Any)
241            .downcast_mut()
242            .unwrap()
243    }
244    /// Apply a map function.
245    ///
246    /// This will use the last timeline's state, which is of type `T` to
247    /// construct a mapped state of type `E`, then use this mapped state to
248    /// create a new [`ItemTimeline<E>`] and push to the end of the timleines.
249    ///
250    /// If there is a planning static anim, it will get submitted and the new
251    /// timeline will start planning a static anim immediately just like the
252    /// static anim goes "cross" two timeline of different type.
253    pub fn apply_map<T: Extract + Clone + 'static, E: Extract + Clone + 'static>(
254        &mut self,
255        map_fn: impl FnOnce(T) -> E,
256    ) {
257        let (state, end_sec, is_showing) = {
258            let timeline = self.get_mut::<T>();
259            let is_showing = timeline.planning_static_start_sec.is_some();
260            timeline.seal();
261            (
262                timeline.snapshot().clone(),
263                timeline.end_sec().unwrap_or(timeline.cur_sec()),
264                is_showing,
265            )
266        };
267        let new_state = map_fn(state);
268        let mut new_timeline = ItemTimeline::new(new_state);
269        new_timeline.forward_to(end_sec);
270        if is_showing {
271            new_timeline.show();
272        }
273        self.inner.push(Box::new(new_timeline));
274    }
275}
276
277// MARK: ItemTimeline<T>
278/// `ItemTimeline<T>` is used to encode animations for a single type `T`,
279/// it contains a list of [`AnimationSpan<T>`] and the corresponding metadata for each span.
280pub struct ItemTimeline<T> {
281    type_name: String,
282    anims: Vec<(AnimationSpan<T>, std::ops::Range<f64>)>,
283
284    // Followings are states use while constructing
285    cur_sec: f64,
286    /// The state used for static anim.
287    state: T,
288    /// The start time of the planning static anim.
289    /// When it is true, it means that it is showing.
290    planning_static_start_sec: Option<f64>,
291}
292// ANCHOR_END: ItemTimeline
293
294impl<T: 'static> ItemTimeline<T> {
295    /// Create a new timeline with the initial state
296    ///
297    /// The timeline is hidden by default, because we don't know when the first anim starts.
298    /// And this allow us to use [`ItemTimeline::forward`] and [`ItemTimeline::forward_to`]
299    /// to adjust the start time of the first anim.
300    pub fn new(state: T) -> Self {
301        Self {
302            type_name: std::any::type_name::<T>().to_string(),
303            anims: vec![],
304            // extractor: None,
305            cur_sec: 0.0,
306            state,
307            planning_static_start_sec: None,
308        }
309    }
310}
311
312/// Info of an animation
313pub struct AnimationInfo {
314    /// The name of the animation
315    pub anim_name: String,
316    /// The time range of the animation
317    pub range: std::ops::Range<f64>,
318}
319
320impl<T: Extract + Any + Clone + 'static> TimelineFunc for ItemTimeline<T> {
321    fn start_sec(&self) -> Option<f64> {
322        self.start_sec()
323    }
324    fn end_sec(&self) -> Option<f64> {
325        self.end_sec()
326    }
327    fn seal(&mut self) {
328        // println!("seal");
329        self._submit_planning_static_anim();
330    }
331    fn cur_sec(&self) -> f64 {
332        self.cur_sec
333    }
334    /// The [`ItemTimeline::state`] should be `Some`
335    fn show(&mut self) {
336        self.show();
337    }
338    fn hide(&mut self) {
339        self.hide();
340    }
341    fn forward(&mut self, duration_secs: f64) {
342        self.forward(duration_secs);
343    }
344    fn get_animation_infos(&self) -> Vec<AnimationInfo> {
345        // const MAX_INFO_CNT: usize = 100;
346        self.anims
347            .iter()
348            .map(|(anim, range)| AnimationInfo {
349                anim_name: anim.type_name().to_string(),
350                range: range.clone(),
351            })
352            // .take(MAX_INFO_CNT)
353            .collect()
354    }
355    fn type_name(&self) -> &str {
356        &self.type_name
357    }
358    // fn eval_sec_any(&self, target_sec: f64) -> Option<(EvalResult<dyn Any>, usize)> {
359    //     self.eval_sec(target_sec)
360    //         .map(|(res, idx)| (res.into_any(), idx))
361    // }
362    fn eval_primitives_at_sec(&self, target_sec: f64) -> Option<(EvalResult<Primitives>, u64)> {
363        self.eval_at_sec(target_sec)
364            .map(|(res, idx)| (res.map(|res| res.extract_to_primitives()), idx))
365    }
366}
367
368impl<T: Clone + 'static> ItemTimeline<T> {
369    /// Get the start sec
370    pub fn start_sec(&self) -> Option<f64> {
371        self.anims.first().map(|(_, range)| range.start)
372    }
373    /// Get the end sec
374    pub fn end_sec(&self) -> Option<f64> {
375        self.anims.last().map(|(_, range)| range.end)
376    }
377    /// Get the current second
378    pub fn cur_sec(&self) -> f64 {
379        self.cur_sec
380    }
381    /// Get the reference of current item state
382    pub fn snapshot_ref(&self) -> &T {
383        &self.state
384    }
385    /// Get the current item state
386    pub fn snapshot(&self) -> T {
387        self.state.clone()
388    }
389    /// Do something on the timeline with current snapshot captured
390    pub fn with_snapshot<R>(&mut self, f: impl Fn(&mut Self, T) -> R) -> R {
391        let state = self.snapshot();
392        f(self, state)
393    }
394    /// Update the state
395    pub fn update(&mut self, state: T) -> &mut Self {
396        self.update_with(|s| *s = state)
397    }
398    /// Update the state with `update_func`
399    pub fn update_with(&mut self, update_func: impl FnOnce(&mut T)) -> &mut Self {
400        let showing = self._submit_planning_static_anim();
401        update_func(&mut self.state);
402        if showing {
403            self.show();
404        }
405        self
406    }
407    /// Show the item.
408    ///
409    /// This will start planning an static anim if there isn't an planning static anim.
410    pub fn show(&mut self) -> &mut Self {
411        if self.planning_static_start_sec.is_none() {
412            self.planning_static_start_sec = Some(self.cur_sec)
413        }
414        self
415    }
416    /// Hide the item.
417    ///
418    /// This will submit a static anim if there is an planning static anim.
419    pub fn hide(&mut self) -> &mut Self {
420        // println!("hide");
421        self._submit_planning_static_anim();
422        self
423    }
424    /// Forward the timeline by `secs`
425    pub fn forward(&mut self, secs: f64) -> &mut Self {
426        self.cur_sec += secs;
427        self
428    }
429    /// Forward the timeline to `target_sec` if the current sec is smaller than it.
430    pub fn forward_to(&mut self, target_sec: f64) -> &mut Self {
431        if target_sec > self.cur_sec {
432            self.forward(target_sec - self.cur_sec);
433        }
434        self
435    }
436    fn _submit_planning_static_anim(&mut self) -> bool {
437        // println!("{:?}", self.planning_static_start_sec);
438        if let Some(start) = self.planning_static_start_sec.take() {
439            self.anims.push((
440                AnimationSpan::from_evaluator(Evaluator::Static(Arc::new(self.state.clone()))),
441                start..self.cur_sec,
442            ));
443            return true;
444        }
445        false
446    }
447    /// Plays an anim with `anim_func`.
448    pub fn play_with(&mut self, anim_func: impl FnOnce(T) -> AnimationSpan<T>) -> &mut Self {
449        self.play(anim_func(self.state.clone()))
450    }
451    /// Plays an anim.
452    pub fn play(&mut self, anim: AnimationSpan<T>) -> &mut Self {
453        self._submit_planning_static_anim();
454        let res = anim.eval_alpha(1.0).into_owned();
455        let duration = anim.duration_secs;
456        let end = self.cur_sec + duration;
457        self.anims.push((anim, self.cur_sec..end));
458        self.cur_sec = end;
459        self.update(res);
460        self.show();
461        self
462    }
463    /// Evaluate the state at `alpha`
464    pub fn eval_at_alpha(&self, alpha: f64) -> Option<(EvalResult<T>, u64)> {
465        let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
466            return None;
467        };
468        self.eval_at_sec(alpha * (end - start) + start)
469    }
470    /// Evaluate the state at `target_sec`
471    pub fn eval_at_sec(&self, target_sec: f64) -> Option<(EvalResult<T>, u64)> {
472        let (Some(start), Some(end)) = (self.start_sec(), self.end_sec()) else {
473            return None;
474        };
475
476        if !(start..=end).contains(&target_sec) {
477            return None;
478        }
479
480        self.anims
481            .iter()
482            .enumerate()
483            .find_map(|(idx, (anim, range))| {
484                if range.contains(&target_sec)
485                    || (idx == self.anims.len() - 1 && target_sec == range.end)
486                {
487                    Some((idx, anim, range))
488                } else {
489                    None
490                }
491            })
492            .map(|(idx, anim, range)| {
493                let alpha = (target_sec - range.start) / (range.end - range.start);
494                (anim.eval_alpha(alpha), idx as u64)
495            })
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502    use crate::{primitives::vitem::VItemPrimitive, timeline::ItemTimeline};
503
504    #[test]
505    fn test_item_timeline() {
506        let vitem = VItemPrimitive {
507            points2d: vec![],
508            fill_rgbas: vec![],
509            stroke_rgbas: vec![],
510            stroke_widths: vec![],
511        };
512        let mut timeline = ItemTimeline::new(vitem.clone());
513        assert!(timeline.eval_at_alpha(0.0).is_none());
514        timeline.show();
515        timeline.forward(1.0);
516        timeline.seal();
517
518        assert_eq!(timeline.start_sec(), Some(0.0));
519        assert_eq!(timeline.end_sec(), Some(1.0));
520
521        let (res, _) = timeline.eval_at_alpha(0.0).unwrap();
522        assert_eq!(res.as_ref(), &vitem);
523        let (res, _) = timeline.eval_at_alpha(0.5).unwrap();
524        assert_eq!(res.as_ref(), &vitem);
525        let (res, _) = timeline.eval_at_alpha(1.0).unwrap();
526        assert_eq!(res.as_ref(), &vitem);
527    }
528
529    #[test]
530    fn test_item_dyn_timelines() {
531        let vitem = VItemPrimitive {
532            points2d: vec![],
533            fill_rgbas: vec![],
534            stroke_rgbas: vec![],
535            stroke_widths: vec![],
536        };
537        let mut timeline = ItemDynTimelines::new();
538        timeline.push(ItemTimeline::new(vitem.clone()));
539        assert!(timeline.eval_primitives_at_alpha(0.0).is_none());
540
541        timeline.get_dyn_mut().show();
542        timeline.get_dyn_mut().forward(1.0);
543        timeline.get_dyn_mut().seal();
544
545        assert_eq!(timeline.get_dyn().start_sec(), Some(0.0));
546        assert_eq!(timeline.get_dyn().end_sec(), Some(1.0));
547
548        let (res, _) = timeline.eval_primitives_at_alpha(0.0).unwrap();
549        assert_eq!(res.as_ref(), &Primitives::VItemPrimitive(vec![vitem]));
550    }
551}