Skip to main content

ranim_core/animation/compose/
sequence.rs

1//! Dynamic sequential animation container.
2
3use std::any::type_name;
4
5use crate::core_item::DynItem;
6
7use crate::animation::build::{IntoAnimNode, Unplaced};
8use crate::animation::node::{AnimNode, NodeContent, static_cell};
9
10/// Dynamic sequential animation container.
11///
12/// `push` erases each direct child's Rust type while retaining its runtime
13/// composition hierarchy in this sequence's local coordinates.
14#[derive(Default)]
15pub struct AnimSequence {
16    pub(super) animations: Vec<AnimNode>,
17    pub(super) cursor_sec: f64,
18}
19
20impl AnimSequence {
21    /// Create an empty sequence.
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    fn eval_at_content_sec(&self, target_sec: f64, output: &mut Vec<DynItem>) {
27        if let Some(animation) = self
28            .animations
29            .iter()
30            .rev()
31            .find(|animation| animation.contains_sec(target_sec, self.cursor_sec))
32        {
33            animation.eval_at(target_sec, output);
34        }
35    }
36
37    /// Append an animation at the current cursor and advance by its local extent.
38    pub fn push<A: Unplaced + 'static>(&mut self, animation: A) -> &mut Self {
39        let mut animation = animation.into_anim_node();
40        let duration_secs = animation.duration_secs();
41        animation.shift_by(self.cursor_sec);
42        self.animations.push(animation);
43        self.cursor_sec += duration_secs;
44        self
45    }
46
47    /// Append another sequence's direct children at the current cursor.
48    pub fn extend(&mut self, mut sequence: AnimSequence) -> &mut Self {
49        for animation in &mut sequence.animations {
50            animation.shift_by(self.cursor_sec);
51        }
52        self.animations.extend(sequence.animations);
53        self.cursor_sec += sequence.cursor_sec;
54        self
55    }
56
57    /// Advance the cursor without adding an animation.
58    pub fn forward(&mut self, secs: f64) -> &mut Self {
59        assert!(
60            secs.is_finite() && secs >= 0.0,
61            "forward duration must be finite and non-negative"
62        );
63        self.cursor_sec += secs;
64        self
65    }
66
67    /// Advance the cursor to `target_sec` without adding an animation.
68    pub fn forward_to(&mut self, target_sec: f64) -> &mut Self {
69        assert!(
70            target_sec.is_finite() && target_sec >= 0.0,
71            "forward target must be finite and non-negative"
72        );
73        if target_sec > self.cursor_sec {
74            self.forward(target_sec - self.cursor_sec);
75        }
76        self
77    }
78
79    /// Advance the cursor while holding the state immediately before it.
80    pub fn hold(&mut self, secs: f64) -> &mut Self {
81        assert!(
82            secs.is_finite() && secs >= 0.0,
83            "hold duration must be finite and non-negative"
84        );
85        if secs == 0.0 {
86            return self;
87        }
88
89        let mut state = Vec::new();
90        self.eval_at_content_sec(self.cursor_sec, &mut state);
91
92        if !state.is_empty() {
93            self.animations
94                .push(static_cell(state, self.cursor_sec..self.cursor_sec + secs));
95        }
96        self.cursor_sec += secs;
97        self
98    }
99
100    /// Advance the cursor to `target_sec` while holding its current state.
101    pub fn hold_to(&mut self, target_sec: f64) -> &mut Self {
102        assert!(
103            target_sec.is_finite() && target_sec >= 0.0,
104            "hold target must be finite and non-negative"
105        );
106        if target_sec > self.cursor_sec {
107            self.hold(target_sec - self.cursor_sec);
108        }
109        self
110    }
111
112    /// Current cursor position.
113    pub fn cursor_sec(&self) -> f64 {
114        self.cursor_sec
115    }
116
117    /// Current sequence duration.
118    pub fn duration_secs(&self) -> f64 {
119        self.cursor_sec
120    }
121
122    /// Borrow the direct child animations in local sequence coordinates.
123    pub fn built_animations(&self) -> &[AnimNode] {
124        &self.animations
125    }
126
127    /// Consume this sequence into its direct child animations.
128    pub fn into_built_animations(self) -> Vec<AnimNode> {
129        self.animations
130    }
131}
132
133impl Unplaced for AnimSequence {}
134impl IntoAnimNode for AnimSequence {
135    fn into_anim_node(self) -> AnimNode {
136        let duration_secs = self.cursor_sec;
137        AnimNode {
138            content: NodeContent::Sequence(self.animations),
139            internal_time_secs: duration_secs,
140            rate_func: None,
141            time_range: 0.0..duration_secs,
142            enabled: true,
143            anim_name: type_name::<Self>(),
144        }
145    }
146}
147
148/// Construct an [`AnimSequence`] by playing each animation in order.
149#[macro_export]
150macro_rules! seq {
151    ($($animation:expr),* $(,)?) => {
152        {
153            #[allow(unused_mut)]
154            let mut sequence = $crate::animation::compose::sequence::AnimSequence::new();
155            $(sequence.push($animation);)*
156            sequence
157        }
158    };
159}
160
161impl<A: Unplaced + 'static> FromIterator<A> for AnimSequence {
162    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
163        let mut sequence = AnimSequence::new();
164        for animation in iter {
165            sequence.push(animation);
166        }
167        sequence
168    }
169}