Skip to main content

argui_animation/
controller.rs

1use crate::{
2    Composition, Duration, Easing, FillMode, Interpolate, Keyframe, Keyframes, MotionValue,
3    PlaybackState, Spring, SpringConfig, Time, Timeline, Timing, TimingError,
4};
5use std::{
6    fmt,
7    sync::{Arc, Mutex, MutexGuard, PoisonError},
8};
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct Tween {
12    pub duration: Duration,
13    pub delay: Duration,
14    pub easing: Easing,
15}
16
17impl Tween {
18    #[must_use]
19    pub const fn new(duration: Duration) -> Self {
20        Self {
21            duration,
22            delay: Duration::ZERO,
23            easing: Easing::Linear,
24        }
25    }
26
27    #[must_use]
28    pub const fn delay(mut self, delay: Duration) -> Self {
29        self.delay = delay;
30        self
31    }
32
33    #[must_use]
34    pub fn easing(mut self, easing: Easing) -> Self {
35        self.easing = easing;
36        self
37    }
38}
39
40#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
41pub enum MotionState {
42    #[default]
43    Idle,
44    Running,
45    Paused,
46    Finished,
47    Canceled,
48}
49
50#[derive(Clone)]
51pub struct Motion<T>(Arc<Mutex<MotionInner<T>>>);
52
53#[derive(Clone, Debug, PartialEq)]
54pub struct MotionBinding<T> {
55    pub motion: Motion<T>,
56    pub composition: Composition,
57    pub priority: i32,
58}
59
60impl<T> MotionBinding<T> {
61    #[must_use]
62    pub fn new(motion: Motion<T>) -> Self {
63        Self {
64            motion,
65            composition: Composition::Replace,
66            priority: 0,
67        }
68    }
69
70    #[must_use]
71    pub const fn composition(mut self, composition: Composition) -> Self {
72        self.composition = composition;
73        self
74    }
75
76    #[must_use]
77    pub const fn priority(mut self, priority: i32) -> Self {
78        self.priority = priority;
79        self
80    }
81}
82
83impl<T> From<Motion<T>> for MotionBinding<T> {
84    fn from(motion: Motion<T>) -> Self {
85        Self::new(motion)
86    }
87}
88
89struct MotionInner<T> {
90    value: T,
91    target: T,
92    driver: Driver<T>,
93    state: MotionState,
94    last_frame: Option<Time>,
95    resume_pending: bool,
96    completed_iterations: u64,
97}
98
99enum Driver<T> {
100    None,
101    PendingTween { from: T, tween: Tween },
102    Timeline(Timeline<T>),
103    Spring(Spring<T>),
104}
105
106impl<T: fmt::Debug> fmt::Debug for Motion<T> {
107    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
108        let inner = self.lock();
109        formatter
110            .debug_struct("Motion")
111            .field("value", &inner.value)
112            .field("target", &inner.target)
113            .field("state", &inner.state)
114            .finish_non_exhaustive()
115    }
116}
117
118impl<T> PartialEq for Motion<T> {
119    fn eq(&self, other: &Self) -> bool {
120        Arc::ptr_eq(&self.0, &other.0)
121    }
122}
123
124impl<T> Motion<T> {
125    fn lock(&self) -> MutexGuard<'_, MotionInner<T>> {
126        self.0.lock().unwrap_or_else(PoisonError::into_inner)
127    }
128
129    #[must_use]
130    pub fn new(value: T) -> Self
131    where
132        T: Clone,
133    {
134        Self(Arc::new(Mutex::new(MotionInner {
135            value: value.clone(),
136            target: value,
137            driver: Driver::None,
138            state: MotionState::Idle,
139            last_frame: None,
140            resume_pending: false,
141            completed_iterations: 0,
142        })))
143    }
144
145    #[must_use]
146    pub fn value(&self) -> T
147    where
148        T: Clone,
149    {
150        self.lock().value.clone()
151    }
152
153    #[must_use]
154    pub fn target(&self) -> T
155    where
156        T: Clone,
157    {
158        self.lock().target.clone()
159    }
160
161    #[must_use]
162    pub fn state(&self) -> MotionState {
163        self.lock().state
164    }
165
166    #[must_use]
167    pub fn is_active(&self) -> bool {
168        self.state() == MotionState::Running
169    }
170
171    #[must_use]
172    pub fn completed_iterations(&self) -> u64 {
173        self.lock().completed_iterations
174    }
175
176    #[must_use]
177    pub fn identity(&self) -> usize {
178        Arc::as_ptr(&self.0).cast::<()>() as usize
179    }
180
181    pub fn set(&self, value: T)
182    where
183        T: Clone,
184    {
185        let mut inner = self.lock();
186        inner.value = value.clone();
187        inner.target = value;
188        inner.driver = Driver::None;
189        inner.state = MotionState::Idle;
190        inner.last_frame = None;
191        inner.completed_iterations = 0;
192    }
193
194    pub fn cancel(&self) {
195        let mut inner = self.lock();
196        inner.driver = Driver::None;
197        inner.state = MotionState::Canceled;
198        inner.last_frame = None;
199    }
200
201    pub fn finish(&self)
202    where
203        T: Clone,
204    {
205        let mut inner = self.lock();
206        inner.value = inner.target.clone();
207        inner.driver = Driver::None;
208        inner.state = MotionState::Finished;
209        inner.last_frame = None;
210    }
211
212    pub fn pause(&self) {
213        let mut inner = self.lock();
214        if inner.state != MotionState::Running {
215            return;
216        }
217        let last_frame = inner.last_frame;
218        if let (Driver::Timeline(timeline), Some(now)) = (&mut inner.driver, last_frame) {
219            timeline.pause(now);
220        }
221        inner.state = MotionState::Paused;
222    }
223
224    pub fn resume(&self) {
225        let mut inner = self.lock();
226        if inner.state == MotionState::Paused {
227            inner.state = MotionState::Running;
228            inner.resume_pending = true;
229            inner.last_frame = None;
230        }
231    }
232
233    pub fn play(&self, timeline: Timeline<T>)
234    where
235        T: Clone + Interpolate,
236    {
237        let mut inner = self.lock();
238        inner.target = timeline.terminal_value();
239        inner.driver = Driver::Timeline(timeline);
240        inner.state = MotionState::Running;
241        inner.last_frame = None;
242        inner.resume_pending = true;
243        inner.completed_iterations = 0;
244    }
245}
246
247impl<T: Clone + Interpolate + PartialEq> Motion<T> {
248    pub fn animate_to(&self, target: T, tween: Tween) {
249        let mut inner = self.lock();
250        if tween.duration == Duration::ZERO {
251            inner.value = target.clone();
252            inner.target = target;
253            inner.driver = Driver::None;
254            inner.state = MotionState::Finished;
255            inner.last_frame = None;
256            inner.completed_iterations = 0;
257            return;
258        }
259        let from = inner.value.clone();
260        inner.target = target;
261        inner.driver = Driver::PendingTween { from, tween };
262        inner.state = MotionState::Running;
263        inner.last_frame = None;
264        inner.resume_pending = false;
265        inner.completed_iterations = 0;
266    }
267
268    pub fn restart(&self, from: T, target: T, tween: Tween) {
269        self.set(from);
270        self.animate_to(target, tween);
271    }
272
273    pub fn advance(&self, now: Time) -> Result<bool, TimingError> {
274        let mut inner = self.lock();
275        if inner.state != MotionState::Running {
276            return Ok(false);
277        }
278        if let Driver::PendingTween { from, tween } = &inner.driver {
279            let frames = Keyframes::new(vec![
280                Keyframe::new(0.0, from.clone()).easing(tween.easing.clone()),
281                Keyframe::new(1.0, inner.target.clone()),
282            ])?;
283            let mut timeline = Timeline::new(
284                frames,
285                Timing::new(tween.duration)
286                    .delay(tween.delay)
287                    .fill(FillMode::Both),
288            )?;
289            timeline.play(now);
290            inner.driver = Driver::Timeline(timeline);
291        }
292        if inner.resume_pending {
293            if let Driver::Timeline(timeline) = &mut inner.driver {
294                match timeline.state() {
295                    PlaybackState::Idle | PlaybackState::Finished | PlaybackState::Canceled => {
296                        timeline.play(now);
297                    }
298                    PlaybackState::Paused => timeline.resume(now),
299                    PlaybackState::Running => {}
300                }
301            }
302            inner.resume_pending = false;
303        }
304        let sample = match &mut inner.driver {
305            Driver::Timeline(timeline) => Some(timeline.sample(now)),
306            _ => None,
307        };
308        let mut changed = false;
309        if let Some(sample) = sample {
310            inner.completed_iterations = inner
311                .completed_iterations
312                .saturating_add(sample.events.iterations);
313            if let Some(value) = sample.value {
314                changed = value != inner.value;
315                inner.value = value;
316            }
317            if sample.state == PlaybackState::Finished {
318                inner.value = inner.target.clone();
319                inner.driver = Driver::None;
320                inner.state = MotionState::Finished;
321                inner.last_frame = None;
322                return Ok(true);
323            }
324        }
325        inner.last_frame = Some(now);
326        Ok(changed)
327    }
328}
329
330impl<T: MotionValue> Motion<T> {
331    #[must_use]
332    pub fn velocity(&self) -> T {
333        match &self.lock().driver {
334            Driver::Spring(spring) => spring.velocity(),
335            _ => T::zero(),
336        }
337    }
338
339    pub fn spring_to(&self, target: T, config: SpringConfig) -> Result<(), crate::PhysicsError> {
340        let mut inner = self.lock();
341        let velocity = match &inner.driver {
342            Driver::Spring(spring) => spring.velocity(),
343            _ => T::zero(),
344        };
345        let spring = Spring::new(inner.value, target, velocity, config)?;
346        inner.target = target;
347        inner.state = if spring.is_active() {
348            MotionState::Running
349        } else {
350            MotionState::Finished
351        };
352        inner.driver = if spring.is_active() {
353            Driver::Spring(spring)
354        } else {
355            Driver::None
356        };
357        inner.last_frame = None;
358        inner.completed_iterations = 0;
359        Ok(())
360    }
361
362    pub fn restart_spring(
363        &self,
364        from: T,
365        target: T,
366        config: SpringConfig,
367    ) -> Result<(), crate::PhysicsError> {
368        self.set(from);
369        self.spring_to(target, config)
370    }
371
372    pub fn advance_spring(&self, now: Time) -> bool {
373        let mut inner = self.lock();
374        if inner.state != MotionState::Running {
375            return false;
376        }
377        let elapsed = inner
378            .last_frame
379            .map_or(Duration::ZERO, |last| now.duration_since(last));
380        let (changed, value, active) = match &mut inner.driver {
381            Driver::Spring(spring) => {
382                let changed = spring.advance(elapsed);
383                (changed, spring.value(), spring.is_active())
384            }
385            _ => return false,
386        };
387        inner.value = value;
388        inner.last_frame = Some(now);
389        if !active {
390            inner.driver = Driver::None;
391            inner.state = MotionState::Finished;
392            inner.last_frame = None;
393        }
394        changed
395    }
396}
397
398pub trait MotionTrack {
399    fn identity(&self) -> usize;
400    fn is_active(&self) -> bool;
401    fn advance(&self, now: Time) -> bool;
402    fn finish(&self);
403    fn cancel(&self);
404}
405
406impl<T> MotionTrack for Motion<T>
407where
408    T: MotionValue + Clone + Interpolate + 'static,
409{
410    fn identity(&self) -> usize {
411        self.identity()
412    }
413
414    fn is_active(&self) -> bool {
415        self.is_active()
416    }
417
418    fn advance(&self, now: Time) -> bool {
419        let is_spring = matches!(&self.lock().driver, Driver::Spring(_));
420        if is_spring {
421            self.advance_spring(now)
422        } else {
423            self.advance(now).unwrap_or_else(|_| {
424                self.cancel();
425                false
426            })
427        }
428    }
429
430    fn finish(&self) {
431        self.finish();
432    }
433
434    fn cancel(&self) {
435        self.cancel();
436    }
437}