Skip to main content

aura_anim_core/
traits.rs

1//! Core traits implemented by animatable values and animation sources.
2
3use crate::{
4    field::{Fields, FieldsAnimation},
5    interpolate::InterpolationProgress,
6    timeline::{Hold, Sequence},
7    timing::Duration,
8};
9
10/// Interpolates values of the same type.
11pub trait Interpolate: Sized {
12    /// Interpolates from `self` to `other` using normalized progress.
13    #[must_use]
14    fn lerp(&self, other: &Self, progress: f32) -> Self {
15        Self::interpolate_progress(self, other, InterpolationProgress::new(progress))
16    }
17
18    /// Interpolates from `from` to `to` using normalized progress.
19    #[must_use]
20    fn interpolate(from: &Self, to: &Self, progress: f32) -> Self {
21        Self::interpolate_progress(from, to, InterpolationProgress::new(progress))
22    }
23
24    /// Interpolates or extrapolates from `from` to `to` without clamping finite progress.
25    #[must_use]
26    fn extrapolate(from: &Self, to: &Self, progress: f32) -> Self {
27        Self::interpolate_progress(from, to, InterpolationProgress::extrapolated(progress))
28    }
29
30    /// Interpolates from `from` to `to` using a prepared progress value.
31    #[must_use]
32    fn interpolate_progress(from: &Self, to: &Self, progress: InterpolationProgress) -> Self;
33}
34
35/// Marker trait for cloneable, owned values that can be animated.
36pub trait Animatable: Interpolate + Clone + 'static {}
37
38impl<T: Interpolate + Clone + 'static> Animatable for T {}
39
40/// Lifecycle state of an animation.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum AnimationState {
43    /// The animation has not started.
44    Idle,
45    /// The animation is advancing.
46    Running,
47    /// The animation is suspended and retains its current value.
48    Paused,
49    /// The animation reached its final value.
50    Completed,
51    /// The animation was canceled before completion.
52    Canceled,
53}
54
55/// A stateful source that produces animated values over time.
56pub trait Animation<T: Animatable>: 'static {
57    /// Returns the animation's current value.
58    #[must_use]
59    fn value(&self) -> &T;
60
61    /// Returns the animation's lifecycle state.
62    #[must_use]
63    fn state(&self) -> AnimationState;
64
65    /// Returns the total duration when it is finite and known.
66    #[must_use]
67    fn duration(&self) -> Option<Duration> {
68        None
69    }
70
71    /// Advances the animation by `delta`.
72    fn tick(&mut self, delta: Duration);
73
74    /// Advances the animation and returns any unconsumed duration.
75    fn advance(&mut self, delta: Duration) -> Duration {
76        self.tick(delta);
77        Duration::ZERO
78    }
79
80    /// Pauses a running animation.
81    fn pause(&mut self);
82
83    /// Resumes a paused animation.
84    fn resume(&mut self);
85
86    /// Cancels the animation.
87    fn cancel(&mut self);
88
89    /// Seeks to normalized progress within the animation.
90    fn seek(&mut self, progress: f32);
91
92    /// Moves the animation to its completed state.
93    fn finish(&mut self);
94
95    /// Attempts to continue the animation toward a new target.
96    ///
97    /// Returns `true` when the animation supports retargeting.
98    #[must_use]
99    fn retarget(&mut self, _target: &T) -> bool {
100        false
101    }
102
103    /// Returns whether the animation is currently running.
104    #[must_use]
105    fn is_active(&self) -> bool {
106        self.state() == AnimationState::Running
107    }
108
109    /// Updates playback rate by adjusting the animation's stored durations.
110    ///
111    /// The default implementation does nothing, which is appropriate for
112    /// animations such as springs that are not duration-based.
113    fn set_rate(&mut self, _rate: f64) {}
114
115    /// Returns this animation with its playback rate adjusted.
116    ///
117    /// A rate of `2.0` halves duration, while `0.5` doubles it. Repeated calls
118    /// compound because implementations update duration directly instead of
119    /// storing a separate rate value.
120    #[must_use]
121    fn rate(mut self, rate: f64) -> Self
122    where
123        Self: Sized,
124    {
125        self.set_rate(rate);
126        self
127    }
128
129    /// Consumes the animation and returns its current value.
130    ///
131    /// The default implementation clones the sampled value so custom
132    /// animations remain source-compatible. Implementations that own their
133    /// sampled value should override this method to move it without cloning.
134    #[must_use]
135    fn into_value(self: Box<Self>) -> T {
136        self.value().clone()
137    }
138}
139
140/// A type-erased animation source.
141pub type BoxAnimation<T> = Box<dyn Animation<T>>;
142
143/// Convenience methods for concrete animation sources.
144pub trait AnimationExt<T: Animatable>: Animation<T> + Sized {
145    /// Type-erases this animation for use in transition factories.
146    #[must_use]
147    fn boxed(self) -> BoxAnimation<T> {
148        Box::new(self)
149    }
150
151    /// Runs this animation followed by `next`.
152    #[must_use]
153    fn then(self, next: impl Animation<T>) -> Sequence<T> {
154        let initial = self.value().clone();
155        Sequence::new(initial).then(self).then(next)
156    }
157
158    /// Delays the start of this animation while holding its current value.
159    #[must_use]
160    fn delay(self, duration: impl Into<Duration>) -> Sequence<T> {
161        let initial = self.value().clone();
162        Sequence::new(initial.clone())
163            .then(Hold::new(initial, duration))
164            .then(self)
165    }
166}
167
168impl<T: Animatable, A: Animation<T> + Sized> AnimationExt<T> for A {}
169
170/// Dispatch marker for playing an already constructed animation.
171#[doc(hidden)]
172pub enum DirectAnimation {}
173
174/// Dispatch marker for playing a deferred field animation plan.
175#[doc(hidden)]
176pub enum FieldAnimationPlan {}
177
178/// Dispatch marker for building an animation from the current sampled value.
179#[doc(hidden)]
180pub enum AnimationFactory {}
181
182mod private {
183    use super::{Animatable, Animation, AnimationFactory, DirectAnimation, FieldAnimationPlan};
184    use crate::Fields;
185
186    pub trait Sealed<T: Animatable, Kind> {}
187
188    impl<T, A> Sealed<T, DirectAnimation> for A
189    where
190        T: Animatable,
191        A: Animation<T>,
192    {
193    }
194
195    impl<T: Animatable> Sealed<T, FieldAnimationPlan> for Fields<T> {}
196
197    impl<T, A, F> Sealed<T, AnimationFactory> for F
198    where
199        T: Animatable,
200        A: Animation<T>,
201        F: FnOnce(T) -> A,
202    {
203    }
204}
205
206/// Converts a playback value into an animation for an existing motion.
207///
208/// This trait is implemented for every [`Animation`], for factories receiving
209/// the current sampled value, and for [`Fields`]. It exists so
210/// [`crate::Motion::play`] can accept both already constructed and deferred
211/// animation sources.
212pub trait IntoMotionAnimation<T: Animatable, Kind>: private::Sealed<T, Kind> {
213    /// The concrete animation stored by the runtime.
214    type Animation: Animation<T>;
215
216    /// Builds the animation from the motion's current sampled value.
217    #[doc(hidden)]
218    fn into_motion_animation(self, current: &T) -> Self::Animation;
219}
220
221impl<T, A> IntoMotionAnimation<T, DirectAnimation> for A
222where
223    T: Animatable,
224    A: Animation<T>,
225{
226    type Animation = A;
227
228    fn into_motion_animation(self, _current: &T) -> Self::Animation {
229        self
230    }
231}
232
233impl<T: Animatable> IntoMotionAnimation<T, FieldAnimationPlan> for Fields<T> {
234    type Animation = FieldsAnimation<T>;
235
236    fn into_motion_animation(self, current: &T) -> Self::Animation {
237        self.build(current)
238    }
239}
240
241impl<T, A, F> IntoMotionAnimation<T, AnimationFactory> for F
242where
243    T: Animatable,
244    A: Animation<T>,
245    F: FnOnce(T) -> A,
246{
247    type Animation = A;
248
249    fn into_motion_animation(self, current: &T) -> Self::Animation {
250        self(current.clone())
251    }
252}
253
254impl<Source: ?Sized, T: Animatable> Animation<T> for Box<Source>
255where
256    Source: Animation<T>,
257{
258    fn value(&self) -> &T {
259        (**self).value()
260    }
261
262    fn state(&self) -> AnimationState {
263        (**self).state()
264    }
265
266    fn duration(&self) -> Option<Duration> {
267        (**self).duration()
268    }
269
270    fn tick(&mut self, delta: Duration) {
271        (**self).tick(delta);
272    }
273
274    fn advance(&mut self, delta: Duration) -> Duration {
275        (**self).advance(delta)
276    }
277
278    fn pause(&mut self) {
279        (**self).pause();
280    }
281
282    fn resume(&mut self) {
283        (**self).resume();
284    }
285
286    fn cancel(&mut self) {
287        (**self).cancel();
288    }
289
290    fn seek(&mut self, progress: f32) {
291        (**self).seek(progress);
292    }
293
294    fn finish(&mut self) {
295        (**self).finish();
296    }
297
298    fn retarget(&mut self, target: &T) -> bool {
299        (**self).retarget(target)
300    }
301
302    fn is_active(&self) -> bool {
303        (**self).is_active()
304    }
305
306    fn set_rate(&mut self, rate: f64) {
307        (**self).set_rate(rate);
308    }
309
310    fn into_value(self: Box<Self>) -> T {
311        (*self).into_value()
312    }
313}