Skip to main content

argui_animation/
schedule.rs

1use crate::Duration;
2
3#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
4pub struct CueId(usize);
5
6impl CueId {
7    #[must_use]
8    pub const fn from_index(index: usize) -> Self {
9        Self(index)
10    }
11
12    #[must_use]
13    pub const fn index(self) -> usize {
14        self.0
15    }
16}
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub struct Cue {
20    pub start: Duration,
21    pub duration: Duration,
22}
23
24impl Cue {
25    #[must_use]
26    pub fn progress(self, elapsed: Duration) -> f32 {
27        if self.duration == Duration::ZERO {
28            return (elapsed >= self.start) as u8 as f32;
29        }
30        let local = elapsed.as_nanos().saturating_sub(self.start.as_nanos());
31        (local as f32 / self.duration.as_nanos() as f32).clamp(0.0, 1.0)
32    }
33}
34
35#[derive(Clone, Debug, Default, Eq, PartialEq)]
36pub struct Schedule {
37    cues: Box<[Cue]>,
38    duration: Duration,
39}
40
41impl Schedule {
42    #[must_use]
43    pub fn sequence(durations: impl IntoIterator<Item = Duration>) -> Self {
44        let mut builder = ScheduleBuilder::new();
45        for duration in durations {
46            builder.then(duration);
47        }
48        builder.build()
49    }
50
51    #[must_use]
52    pub fn parallel(durations: impl IntoIterator<Item = Duration>) -> Self {
53        let cues: Vec<_> = durations
54            .into_iter()
55            .map(|duration| Cue {
56                start: Duration::ZERO,
57                duration,
58            })
59            .collect();
60        Self::from_cues(cues)
61    }
62
63    #[must_use]
64    pub fn stagger(count: usize, duration: Duration, interval: Duration) -> Self {
65        let cues = (0..count)
66            .map(|index| Cue {
67                start: Duration::from_nanos(interval.as_nanos().saturating_mul(index as u64)),
68                duration,
69            })
70            .collect();
71        Self::from_cues(cues)
72    }
73
74    fn from_cues(cues: Vec<Cue>) -> Self {
75        let duration = cues
76            .iter()
77            .map(|cue| cue.start + cue.duration)
78            .max()
79            .unwrap_or(Duration::ZERO);
80        Self {
81            cues: cues.into_boxed_slice(),
82            duration,
83        }
84    }
85
86    #[must_use]
87    pub fn cue(&self, id: CueId) -> Option<Cue> {
88        self.cues.get(id.0).copied()
89    }
90
91    #[must_use]
92    pub const fn duration(&self) -> Duration {
93        self.duration
94    }
95
96    #[must_use]
97    pub fn len(&self) -> usize {
98        self.cues.len()
99    }
100
101    #[must_use]
102    pub fn is_empty(&self) -> bool {
103        self.cues.is_empty()
104    }
105}
106
107#[derive(Clone, Debug, Default)]
108pub struct ScheduleBuilder {
109    cues: Vec<Cue>,
110    cursor: Duration,
111    group_start: Duration,
112}
113
114impl ScheduleBuilder {
115    #[must_use]
116    pub const fn new() -> Self {
117        Self {
118            cues: Vec::new(),
119            cursor: Duration::ZERO,
120            group_start: Duration::ZERO,
121        }
122    }
123
124    pub fn then(&mut self, duration: Duration) -> CueId {
125        self.group_start = self.cursor;
126        let id = self.push(self.cursor, duration);
127        self.cursor += duration;
128        id
129    }
130
131    pub fn with(&mut self, duration: Duration) -> CueId {
132        let id = self.push(self.group_start, duration);
133        self.cursor = self.cursor.max(self.group_start + duration);
134        id
135    }
136
137    pub fn after(&mut self, dependency: CueId, delay: Duration, duration: Duration) -> CueId {
138        let start = self
139            .cues
140            .get(dependency.0)
141            .map_or(delay, |cue| cue.start + cue.duration + delay);
142        let id = self.push(start, duration);
143        self.cursor = self.cursor.max(start + duration);
144        id
145    }
146
147    fn push(&mut self, start: Duration, duration: Duration) -> CueId {
148        let id = CueId(self.cues.len());
149        self.cues.push(Cue { start, duration });
150        id
151    }
152
153    #[must_use]
154    pub fn build(self) -> Schedule {
155        Schedule::from_cues(self.cues)
156    }
157}