Skip to main content

aura_anim_iced/timeline/
parallel.rs

1use super::{Hold, Sequence, TimelineStep, Track, duration::max_duration};
2use crate::{keyframes::Keyframes, property::PropertySnapshot, timing::Duration};
3
4/// A parallel timeline group.
5#[derive(Debug, Clone, PartialEq, Default)]
6pub struct Parallel {
7    steps: Vec<TimelineStep>,
8}
9
10impl Parallel {
11    /// Creates an empty parallel group.
12    #[must_use]
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    /// Creates a parallel group from steps.
18    #[must_use]
19    pub fn from_steps(steps: impl IntoIterator<Item = TimelineStep>) -> Self {
20        Self {
21            steps: steps.into_iter().collect(),
22        }
23    }
24
25    /// Returns the parallel steps in insertion order.
26    #[must_use]
27    pub fn steps(&self) -> &[TimelineStep] {
28        &self.steps
29    }
30
31    /// Appends a timeline step.
32    pub fn push_step(&mut self, step: impl Into<TimelineStep>) {
33        self.steps.push(step.into());
34    }
35
36    /// Appends a timeline step and returns the updated parallel group.
37    #[must_use]
38    pub fn then(mut self, step: impl Into<TimelineStep>) -> Self {
39        self.push_step(step);
40        self
41    }
42
43    /// Appends a keyframe track.
44    #[must_use]
45    pub fn track(self, track: impl Into<Track>) -> Self {
46        self.then(track.into())
47    }
48
49    /// Appends raw keyframes as a track.
50    #[must_use]
51    pub fn keyframes(self, keyframes: Keyframes) -> Self {
52        self.track(keyframes)
53    }
54
55    /// Appends a hold segment.
56    #[must_use]
57    pub fn hold(self, duration: impl Into<Duration>) -> Self {
58        self.then(Hold::new(duration.into()))
59    }
60
61    /// Appends a sequence.
62    #[must_use]
63    pub fn sequence(self, sequence: Sequence) -> Self {
64        self.then(sequence)
65    }
66
67    /// Appends a nested parallel group.
68    #[must_use]
69    pub fn parallel_step(self, parallel: Parallel) -> Self {
70        self.then(parallel)
71    }
72
73    /// Returns the finite maximum step duration, or `None` if any step is infinite.
74    #[must_use]
75    pub fn total_duration(&self) -> Option<Duration> {
76        max_duration(self.steps.iter().map(TimelineStep::total_duration))
77    }
78
79    /// Samples this parallel group at local timeline `offset`.
80    #[must_use]
81    pub fn sample_at(&self, offset: impl Into<Duration>) -> Option<PropertySnapshot> {
82        let offset = offset.into();
83        let mut merged = PropertySnapshot::with_capacity(
84            self.steps.iter().map(TimelineStep::sample_entry_hint).sum(),
85        );
86
87        for step in &self.steps {
88            if let Some(snapshot) = step.sample_at(offset) {
89                merged.merge_unsorted(snapshot);
90            }
91        }
92
93        if merged.is_empty() {
94            None
95        } else {
96            merged.sort_by_composition_key();
97            Some(merged)
98        }
99    }
100
101    /// Samples the final visual state of all child steps.
102    #[must_use]
103    pub fn completion_snapshot(&self) -> Option<PropertySnapshot> {
104        let mut merged = PropertySnapshot::new();
105
106        for step in &self.steps {
107            if let Some(snapshot) = step.completion_snapshot() {
108                merged.merge(snapshot);
109            }
110        }
111
112        if merged.is_empty() {
113            None
114        } else {
115            merged.sort_by_composition_key();
116            Some(merged)
117        }
118    }
119}