1use crate::{
2 Direction, Duration, Easing, FillMode, Interpolate, Iterations, Keyframe, Keyframes, Time,
3 Timing, TimingError,
4};
5
6#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
7pub enum PlaybackState {
8 #[default]
9 Idle,
10 Running,
11 Paused,
12 Finished,
13 Canceled,
14}
15
16#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
17pub struct TimelineEvents {
18 pub started: bool,
19 pub iterations: u64,
20 pub finished: bool,
21 pub canceled: bool,
22}
23
24#[derive(Clone, Debug)]
25pub struct TimelineSample<T> {
26 pub value: Option<T>,
27 pub events: TimelineEvents,
28 pub state: PlaybackState,
29}
30
31#[derive(Clone, Debug)]
32pub struct Timeline<T> {
33 keyframes: Keyframes<T>,
34 timing: Timing,
35 state: PlaybackState,
36 anchor_time: Time,
37 anchor_position: f64,
38 playback_rate: f64,
39 started_emitted: bool,
40 iteration_marker: u64,
41 pending_finished: bool,
42 pending_canceled: bool,
43}
44
45impl<T> Timeline<T> {
46 pub fn new(keyframes: Keyframes<T>, timing: Timing) -> Result<Self, TimingError> {
47 let timing = timing.validate()?;
48 Ok(Self {
49 keyframes,
50 timing,
51 state: PlaybackState::Idle,
52 anchor_time: Time::ZERO,
53 anchor_position: 0.0,
54 playback_rate: timing.playback_rate,
55 started_emitted: false,
56 iteration_marker: 0,
57 pending_finished: false,
58 pending_canceled: false,
59 })
60 }
61
62 #[must_use]
63 pub const fn state(&self) -> PlaybackState {
64 self.state
65 }
66
67 #[must_use]
68 pub const fn timing(&self) -> Timing {
69 self.timing
70 }
71
72 #[must_use]
73 pub fn needs_frame(&self) -> bool {
74 self.state == PlaybackState::Running
75 }
76
77 pub fn play(&mut self, now: Time) {
78 match self.state {
79 PlaybackState::Running => return,
80 PlaybackState::Paused => {
81 self.anchor_time = now;
82 self.state = PlaybackState::Running;
83 return;
84 }
85 PlaybackState::Idle | PlaybackState::Finished | PlaybackState::Canceled => {}
86 }
87 self.reset_position();
88 self.anchor_time = now;
89 self.state = PlaybackState::Running;
90 }
91
92 pub fn pause(&mut self, now: Time) {
93 if self.state == PlaybackState::Running {
94 self.anchor_position = self.position(now);
95 self.anchor_time = now;
96 self.state = PlaybackState::Paused;
97 }
98 }
99
100 pub fn resume(&mut self, now: Time) {
101 if self.state == PlaybackState::Paused {
102 self.anchor_time = now;
103 self.state = PlaybackState::Running;
104 }
105 }
106
107 pub fn restart(&mut self, now: Time) {
108 self.reset_position();
109 self.anchor_time = now;
110 self.state = PlaybackState::Running;
111 }
112
113 pub fn seek(&mut self, position: Duration, now: Time) {
114 self.anchor_position = self.clamp_position(position.as_secs_f64());
115 self.anchor_time = now;
116 self.reset_events_for_position();
117 }
118
119 pub fn set_playback_rate(&mut self, playback_rate: f64, now: Time) -> Result<(), TimingError> {
120 if !playback_rate.is_finite() || playback_rate == 0.0 {
121 return Err(TimingError::InvalidPlaybackRate);
122 }
123 self.anchor_position = self.position(now);
124 self.anchor_time = now;
125 self.playback_rate = playback_rate;
126 Ok(())
127 }
128
129 pub fn reverse(&mut self, now: Time) {
130 let rate = -self.playback_rate;
131 if matches!(
132 self.state,
133 PlaybackState::Idle | PlaybackState::Finished | PlaybackState::Canceled
134 ) {
135 self.playback_rate = rate;
136 self.restart(now);
137 } else {
138 self.anchor_position = self.position(now);
139 self.anchor_time = now;
140 self.playback_rate = rate;
141 }
142 }
143
144 pub fn finish(&mut self) {
145 self.anchor_position = if self.playback_rate >= 0.0 {
146 self.timing.total_seconds()
147 } else {
148 0.0
149 };
150 self.state = PlaybackState::Finished;
151 self.pending_finished = true;
152 }
153
154 pub fn cancel(&mut self) {
155 self.state = PlaybackState::Canceled;
156 self.pending_canceled = true;
157 }
158
159 fn position(&self, now: Time) -> f64 {
160 if self.state != PlaybackState::Running {
161 return self.anchor_position;
162 }
163 let elapsed = now.duration_since(self.anchor_time).as_secs_f64();
164 self.clamp_position(self.anchor_position + elapsed * self.playback_rate)
165 }
166
167 fn clamp_position(&self, position: f64) -> f64 {
168 position.max(0.0).min(self.timing.total_seconds())
169 }
170
171 fn reset_position(&mut self) {
172 self.anchor_position = if self.playback_rate >= 0.0 {
173 0.0
174 } else {
175 self.timing.total_seconds()
176 };
177 self.started_emitted = false;
178 self.iteration_marker = if self.playback_rate >= 0.0 {
179 0
180 } else {
181 self.maximum_iteration_events()
182 };
183 self.pending_finished = false;
184 self.pending_canceled = false;
185 }
186
187 fn reset_events_for_position(&mut self) {
188 let active = (self.anchor_position - self.timing.delay.as_secs_f64()).max(0.0);
189 self.started_emitted = active > 0.0;
190 self.iteration_marker = self.iteration_events_at(active);
191 self.pending_finished = false;
192 self.pending_canceled = false;
193 }
194
195 fn maximum_iteration_events(&self) -> u64 {
196 match self.timing.iterations {
197 Iterations::Finite(iterations) => iterations.ceil().max(1.0) as u64 - 1,
198 Iterations::Infinite => u64::MAX,
199 }
200 }
201
202 fn iteration_events_at(&self, active_seconds: f64) -> u64 {
203 let completed = (active_seconds / self.timing.duration.as_secs_f64()).floor();
204 (completed as u64).min(self.maximum_iteration_events())
205 }
206}
207
208impl<T: Clone + Interpolate> Timeline<T> {
209 pub(crate) fn terminal_value(&self) -> T {
210 let progress = if self.playback_rate >= 0.0 {
211 self.final_progress()
212 } else {
213 self.directed_progress(0.0, 0)
214 };
215 self.keyframes.sample(progress)
216 }
217
218 pub fn retarget(
219 &mut self,
220 target: T,
221 duration: Duration,
222 easing: Easing,
223 now: Time,
224 ) -> Result<(), TimingError> {
225 let current = self
226 .sample(now)
227 .value
228 .unwrap_or_else(|| self.keyframes.sample(0.0));
229 self.keyframes = Keyframes::new(vec![
230 Keyframe::new(0.0, current).easing(easing),
231 Keyframe::new(1.0, target),
232 ])?;
233 self.timing = Timing::new(duration).fill(FillMode::Forwards).validate()?;
234 self.playback_rate = 1.0;
235 self.restart(now);
236 Ok(())
237 }
238
239 #[must_use]
240 pub fn sample(&mut self, now: Time) -> TimelineSample<T> {
241 let mut events = TimelineEvents {
242 finished: std::mem::take(&mut self.pending_finished),
243 canceled: std::mem::take(&mut self.pending_canceled),
244 ..TimelineEvents::default()
245 };
246 if matches!(self.state, PlaybackState::Idle | PlaybackState::Canceled) {
247 return TimelineSample {
248 value: None,
249 events,
250 state: self.state,
251 };
252 }
253
254 let mut position = self.position(now);
255 let delay = self.timing.delay.as_secs_f64();
256 let active_end = delay + self.timing.active_seconds();
257 if position >= delay && !self.started_emitted {
258 self.started_emitted = true;
259 events.started = true;
260 }
261 if position >= delay {
262 let marker =
263 self.iteration_events_at((position - delay).min(self.timing.active_seconds()));
264 events.iterations = marker.abs_diff(self.iteration_marker);
265 self.iteration_marker = marker;
266 }
267
268 if self.state == PlaybackState::Running && self.reached_end(position) {
269 position = if self.playback_rate >= 0.0 {
270 self.timing.total_seconds()
271 } else {
272 0.0
273 };
274 self.anchor_position = position;
275 self.anchor_time = now;
276 self.state = PlaybackState::Finished;
277 events.finished = true;
278 }
279
280 let inside_active = position < active_end
281 || (position == active_end && self.state != PlaybackState::Finished);
282 let progress = if position < delay {
283 self.timing
284 .fill
285 .fills_before()
286 .then(|| self.directed_progress(0.0, 0))
287 } else if inside_active {
288 Some(self.progress_at((position - delay).max(0.0)))
289 } else {
290 self.timing
291 .fill
292 .fills_after()
293 .then(|| self.final_progress())
294 };
295 TimelineSample {
296 value: progress.map(|progress| self.keyframes.sample(progress)),
297 events,
298 state: self.state,
299 }
300 }
301
302 fn reached_end(&self, position: f64) -> bool {
303 const TIME_EPSILON: f64 = 1.0e-9;
304 if self.playback_rate >= 0.0 {
305 position + TIME_EPSILON >= self.timing.total_seconds()
306 } else {
307 position <= TIME_EPSILON
308 }
309 }
310
311 fn progress_at(&self, active_seconds: f64) -> f32 {
312 let duration = self.timing.duration.as_secs_f64();
313 let iterations = active_seconds / duration;
314 let index = iterations.floor() as u64;
315 let fraction = iterations.fract();
316 if active_seconds == self.timing.active_seconds() {
317 return self.final_progress();
318 }
319 self.directed_progress(fraction as f32, index)
320 }
321
322 fn final_progress(&self) -> f32 {
323 let iterations = match self.timing.iterations {
324 Iterations::Finite(iterations) => iterations,
325 Iterations::Infinite => return 1.0,
326 };
327 let fraction = iterations.fract();
328 let index = iterations.ceil().max(1.0) as u64 - 1;
329 self.directed_progress(
330 if fraction == 0.0 {
331 1.0
332 } else {
333 fraction as f32
334 },
335 index,
336 )
337 }
338
339 fn directed_progress(&self, progress: f32, iteration: u64) -> f32 {
340 let reverse = match self.timing.direction {
341 Direction::Normal => false,
342 Direction::Reverse => true,
343 Direction::Alternate => !iteration.is_multiple_of(2),
344 Direction::AlternateReverse => iteration.is_multiple_of(2),
345 };
346 if reverse { 1.0 - progress } else { progress }
347 }
348}