Skip to main content

ranim_core/animation/compose/
stack.rs

1//! Dynamic overlay animation container.
2
3use std::any::type_name;
4
5use crate::animation::build::{IntoAnimNode, Unplaced};
6use crate::animation::node::{AnimNode, NodeContent};
7
8/// Dynamic overlay animation container.
9///
10/// Unlike [`AnimSequence`](super::sequence::AnimSequence), every pushed animation keeps its own local start
11/// time and the stack duration is the maximum child extent.
12#[derive(Default)]
13pub struct AnimStack {
14    animations: Vec<AnimNode>,
15    duration_secs: f64,
16}
17
18impl AnimStack {
19    /// Create an empty dynamic stack.
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Add an animation without advancing the other children.
25    pub fn push<A: IntoAnimNode + 'static>(&mut self, animation: A) -> &mut Self {
26        let animation = animation.into_anim_node();
27        self.duration_secs = self.duration_secs.max(animation.time_range.end);
28        self.animations.push(animation);
29        self
30    }
31
32    /// Add all direct child animations from another dynamic stack.
33    pub fn extend(&mut self, stack: AnimStack) -> &mut Self {
34        self.duration_secs = self.duration_secs.max(stack.duration_secs);
35        self.animations.extend(stack.animations);
36        self
37    }
38
39    /// Current maximum child extent.
40    pub fn duration_secs(&self) -> f64 {
41        self.duration_secs
42    }
43
44    /// Borrow the direct child animations in local stack coordinates.
45    pub fn built_animations(&self) -> &[AnimNode] {
46        &self.animations
47    }
48
49    /// Consume this stack into its direct child animations.
50    pub fn into_built_animations(self) -> Vec<AnimNode> {
51        self.animations
52    }
53}
54
55impl Unplaced for AnimStack {}
56impl IntoAnimNode for AnimStack {
57    fn into_anim_node(self) -> AnimNode {
58        let duration_secs = self.duration_secs;
59        AnimNode {
60            content: NodeContent::Stack(self.animations),
61            internal_time_secs: duration_secs,
62            rate_func: None,
63            time_range: 0.0..duration_secs,
64            enabled: true,
65            anim_name: type_name::<Self>(),
66        }
67    }
68}
69
70/// Construct an [`AnimStack`] by pushing each animation at the same origin.
71#[macro_export]
72macro_rules! stack {
73    ($($animation:expr),* $(,)?) => {
74        {
75            #[allow(unused_mut)]
76            let mut stack = $crate::animation::compose::stack::AnimStack::new();
77            $(stack.push($animation);)*
78            stack
79        }
80    };
81}
82
83impl<A: IntoAnimNode + 'static> FromIterator<A> for AnimStack {
84    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
85        let mut stack = AnimStack::new();
86        for animation in iter {
87            stack.push(animation);
88        }
89        stack
90    }
91}