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::Children(self.animations),
61            internal_time_secs: duration_secs,
62            rate_func: None,
63            time_range: 0.0..duration_secs,
64            end_inclusive: true,
65            enabled: true,
66            anim_name: type_name::<Self>(),
67        }
68    }
69}
70
71/// Construct an [`AnimStack`] by pushing each animation at the same origin.
72#[macro_export]
73macro_rules! stack {
74    ($($animation:expr),* $(,)?) => {
75        {
76            #[allow(unused_mut)]
77            let mut stack = $crate::animation::compose::stack::AnimStack::new();
78            $(stack.push($animation);)*
79            stack
80        }
81    };
82}
83
84impl<A: IntoAnimNode + 'static> FromIterator<A> for AnimStack {
85    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
86        let mut stack = AnimStack::new();
87        for animation in iter {
88            stack.push(animation);
89        }
90        stack
91    }
92}