Skip to main content

inset_animation/
tween.rs

1//! Flutter counterpart: `animation/tween.dart`.
2
3use std::fmt::{self, Debug};
4use std::marker::PhantomData;
5use std::rc::Rc;
6
7use inset_embedder::{Color, Offset, Rect, Size};
8use inset_foundation::{App, Handle, Listener, RetainedHandle};
9
10use crate::animation::{Animation, AnimationStatus, AnimationStatusListener, AnyAnimation};
11use crate::curves::Curve;
12
13/// An object that can produce a value of type `T` given an `Animation<double>`
14/// as input.
15///
16/// Typically, the values of the input animation are nominally in the range 0.0
17/// to 1.0. In principle, however, any value could be provided.
18///
19/// The main implementor of [`Animatable`] is [`Tween`].
20pub trait Animatable<T> {
21    /// Returns the value of the object at point `t`.
22    ///
23    /// The value of `t` is nominally a fraction in the range 0.0 to 1.0,
24    /// though in practice it may extend outside this range.
25    fn transform(&self, app: &App, t: f64) -> T;
26
27    /// The current value of this object for the given animation.
28    ///
29    /// This function is implemented by deferring to [`transform`]. Implementors
30    /// that want to provide custom behavior should override [`transform`], not
31    /// [`evaluate`].
32    ///
33    /// [`transform`]: Animatable::transform
34    /// [`evaluate`]: Animatable::evaluate
35    fn evaluate(&self, app: &App, animation: AnyAnimation<f64>) -> T
36    where
37        Self: Sized,
38    {
39        self.transform(app, animation.value(app))
40    }
41
42    /// Returns a new [`AnyAnimation`] that is driven by the given animation but
43    /// that takes on values determined by this object.
44    fn animate(self, app: &mut App, parent: AnyAnimation<f64>) -> AnyAnimation<T>
45    where
46        Self: Sized + Clone + 'static,
47        T: 'static,
48    {
49        let parent = app.retain(parent);
50        app.create(AnimatedEvaluation {
51            parent,
52            evaluatable: self,
53            _value: PhantomData,
54        })
55        .as_animation()
56    }
57
58    /// Returns a new [`Animatable`] whose value is determined by first
59    /// evaluating the given parent and then evaluating this object at the
60    /// result.
61    fn chain<P>(self, parent: P) -> ChainedEvaluation<P, Self>
62    where
63        Self: Sized + Clone,
64        P: Animatable<f64> + Clone,
65    {
66        ChainedEvaluation {
67            parent,
68            evaluatable: self,
69        }
70    }
71}
72
73/// Dart's `_CallbackAnimatable<T>`.
74#[derive(Clone)]
75pub struct CallbackAnimatable<T> {
76    callback: Rc<dyn Fn(f64) -> T>,
77}
78
79impl<T> CallbackAnimatable<T> {
80    /// Dart's `Animatable.fromCallback`.
81    pub fn from_callback(callback: impl Fn(f64) -> T + 'static) -> CallbackAnimatable<T> {
82        CallbackAnimatable {
83            callback: Rc::new(callback),
84        }
85    }
86}
87
88impl<T> Animatable<T> for CallbackAnimatable<T> {
89    fn transform(&self, _app: &App, t: f64) -> T {
90        (self.callback)(t)
91    }
92}
93
94impl<T> Debug for CallbackAnimatable<T> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.write_str("CallbackAnimatable")
97    }
98}
99
100/// Dart's `_AnimatedEvaluation<T>` — the animation returned by
101/// [`Animatable::animate`].
102struct AnimatedEvaluation<T, A> {
103    parent: RetainedHandle<AnyAnimation<f64>>,
104    evaluatable: A,
105    _value: PhantomData<fn() -> T>,
106}
107
108impl<T: 'static, A: Animatable<T> + Clone + 'static> Animation<T> for AnimatedEvaluation<T, A> {
109    fn add_listener(self: Handle<Self>, app: &mut App, listener: Listener) {
110        app.get(self).parent.add_listener(app, listener);
111    }
112
113    fn remove_listener(self: Handle<Self>, app: &mut App, listener: &Listener) {
114        app.get(self).parent.remove_listener(app, listener);
115    }
116
117    fn add_status_listener(self: Handle<Self>, app: &mut App, listener: AnimationStatusListener) {
118        app.get(self).parent.add_status_listener(app, listener);
119    }
120
121    fn remove_status_listener(
122        self: Handle<Self>,
123        app: &mut App,
124        listener: &AnimationStatusListener,
125    ) {
126        app.get(self).parent.remove_status_listener(app, listener);
127    }
128
129    fn status(self: Handle<Self>, app: &App) -> AnimationStatus {
130        app.get(self).parent.status(app)
131    }
132
133    fn value(self: Handle<Self>, app: &App) -> T {
134        let parent = app.get(self).parent.get();
135        let evaluatable = app.get(self).evaluatable.clone();
136        evaluatable.evaluate(app, parent)
137    }
138}
139
140/// Dart's `_ChainedEvaluation<T>`.
141#[derive(Clone)]
142pub struct ChainedEvaluation<P, C> {
143    parent: P,
144    evaluatable: C,
145}
146
147impl<P: Animatable<f64>, C: Animatable<T>, T> Animatable<T> for ChainedEvaluation<P, C> {
148    fn transform(&self, app: &App, t: f64) -> T {
149        self.evaluatable
150            .transform(app, self.parent.transform(app, t))
151    }
152}
153
154impl<P: Debug, C: Debug> Debug for ChainedEvaluation<P, C> {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "{:?}\u{27A9}{:?}", self.parent, self.evaluatable)
157    }
158}
159
160/// Types that [`Tween<T>`] can interpolate with `+`, `-`, and `*`.
161///
162/// Dart's `Tween.lerp` is `begin + (end - begin) * t` on `dynamic`, and its assert rejects a
163/// type that does not carry the three operators (`Rect` is the type it names: use
164/// [`RectTween`]). This trait is the set of types that do.
165pub trait TweenLerp: Clone {
166    /// Dart's `Tween.lerp`.
167    fn lerp(begin: &Self, end: &Self, t: f64) -> Self;
168}
169
170impl TweenLerp for f64 {
171    fn lerp(begin: &f64, end: &f64, t: f64) -> f64 {
172        begin + (end - begin) * t
173    }
174}
175
176impl TweenLerp for Offset {
177    fn lerp(begin: &Offset, end: &Offset, t: f64) -> Offset {
178        *begin + (*end - *begin) * t
179    }
180}
181
182impl TweenLerp for Size {
183    fn lerp(begin: &Size, end: &Size, t: f64) -> Size {
184        // Dart: `Size - Size` is an `Offset`, and `Size + Offset` is a `Size`.
185        *begin + (*end - *begin) * t
186    }
187}
188
189/// A linear interpolation between a beginning and ending value.
190///
191/// Tweens are mutable: [`set_begin`] / [`set_end`] after [`Animatable::animate`]
192/// is visible to the driven animation, as in Dart.
193///
194/// [`set_begin`]: Tween::set_begin
195/// [`set_end`]: Tween::set_end
196#[derive(Debug)]
197pub struct Tween<T> {
198    /// The value this variable has at the beginning of the animation.
199    pub begin: Option<T>,
200
201    /// The value this variable has at the end of the animation.
202    pub end: Option<T>,
203}
204
205impl<T: 'static> Tween<T> {
206    /// Creates a tween.
207    ///
208    /// The [`begin`] and [`end`] properties must be non-null before the tween is
209    /// first used, but the arguments can be null if the values are going to be
210    /// filled in later.
211    ///
212    /// [`begin`]: Tween::begin
213    /// [`end`]: Tween::end
214    pub fn new(app: &mut App, begin: Option<T>, end: Option<T>) -> Handle<Tween<T>> {
215        app.create(Tween { begin, end })
216    }
217
218    /// The value this variable has at the beginning of the animation.
219    pub fn begin(self: Handle<Self>, app: &App) -> Option<&T> {
220        app.get(self).begin.as_ref()
221    }
222
223    /// Dart's `begin` setter.
224    pub fn set_begin(self: Handle<Self>, app: &mut App, begin: Option<T>) {
225        app.get_mut(self).begin = begin;
226    }
227
228    /// The value this variable has at the end of the animation.
229    pub fn end(self: Handle<Self>, app: &App) -> Option<&T> {
230        app.get(self).end.as_ref()
231    }
232
233    /// Dart's `end` setter.
234    pub fn set_end(self: Handle<Self>, app: &mut App, end: Option<T>) {
235        app.get_mut(self).end = end;
236    }
237}
238
239fn tween_transform<T: Clone>(
240    begin: &Option<T>,
241    end: &Option<T>,
242    t: f64,
243    lerp: impl FnOnce(f64) -> T,
244) -> T {
245    if t == 0.0 {
246        return begin.clone().expect("Tween.begin must be set before use");
247    }
248    if t == 1.0 {
249        return end.clone().expect("Tween.end must be set before use");
250    }
251    lerp(t)
252}
253
254impl<T: TweenLerp + 'static> Tween<T> {
255    /// Returns the value this variable has at the given animation clock value.
256    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> T {
257        let tween = app.get(self);
258        let begin = tween
259            .begin
260            .as_ref()
261            .expect("Tween.begin must be set before use");
262        let end = tween
263            .end
264            .as_ref()
265            .expect("Tween.end must be set before use");
266        T::lerp(begin, end, t)
267    }
268}
269
270impl<T: TweenLerp + 'static> Animatable<T> for Handle<Tween<T>> {
271    fn transform(&self, app: &App, t: f64) -> T {
272        let this = *self;
273        let tween = app.get(this);
274        tween_transform(&tween.begin, &tween.end, t, |t| this.lerp(app, t))
275    }
276}
277
278/// A [`Tween`] that evaluates its [`parent`] in reverse.
279///
280/// [`parent`]: ReverseTween::parent
281pub struct ReverseTween<T: 'static> {
282    parent: Handle<Tween<T>>,
283}
284
285impl<T: TweenLerp + 'static> ReverseTween<T> {
286    /// Construct a [`Tween`] that evaluates its parent in reverse.
287    pub fn new(app: &mut App, parent: Handle<Tween<T>>) -> Handle<ReverseTween<T>> {
288        app.create(ReverseTween { parent })
289    }
290
291    /// This tween's value is the same as the parent's value evaluated in reverse.
292    pub fn parent(self: Handle<Self>, app: &App) -> Handle<Tween<T>> {
293        app.get(self).parent
294    }
295}
296
297impl<T: TweenLerp + 'static> Animatable<T> for Handle<ReverseTween<T>> {
298    fn transform(&self, app: &App, t: f64) -> T {
299        let this = *self;
300        this.parent(app).lerp(app, 1.0 - t)
301    }
302}
303
304/// An interpolation between two colors.
305pub struct ColorTween {
306    /// The value this variable has at the beginning of the animation.
307    pub begin: Option<Color>,
308
309    /// The value this variable has at the end of the animation.
310    pub end: Option<Color>,
311}
312
313impl ColorTween {
314    /// Creates a [`Color`] tween.
315    ///
316    /// The begin and end properties may be null; the null value is treated as
317    /// transparent.
318    pub fn new(app: &mut App, begin: Option<Color>, end: Option<Color>) -> Handle<ColorTween> {
319        app.create(ColorTween { begin, end })
320    }
321
322    /// The value this variable has at the beginning of the animation.
323    pub fn begin(self: Handle<Self>, app: &App) -> Option<Color> {
324        app.get(self).begin
325    }
326
327    /// Dart's `begin` setter.
328    pub fn set_begin(self: Handle<Self>, app: &mut App, begin: Option<Color>) {
329        app.get_mut(self).begin = begin;
330    }
331
332    /// The value this variable has at the end of the animation.
333    pub fn end(self: Handle<Self>, app: &App) -> Option<Color> {
334        app.get(self).end
335    }
336
337    /// Dart's `end` setter.
338    pub fn set_end(self: Handle<Self>, app: &mut App, end: Option<Color>) {
339        app.get_mut(self).end = end;
340    }
341
342    /// Returns the value this variable has at the given animation clock value.
343    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> Option<Color> {
344        let tween = app.get(self);
345        Color::lerp(tween.begin, tween.end, t)
346    }
347}
348
349impl Animatable<Option<Color>> for Handle<ColorTween> {
350    fn transform(&self, app: &App, t: f64) -> Option<Color> {
351        let this = *self;
352        if t == 0.0 {
353            return this.begin(app);
354        }
355        if t == 1.0 {
356            return this.end(app);
357        }
358        this.lerp(app, t)
359    }
360}
361
362/// An interpolation between two sizes.
363pub struct SizeTween {
364    /// The value this variable has at the beginning of the animation.
365    pub begin: Option<Size>,
366
367    /// The value this variable has at the end of the animation.
368    pub end: Option<Size>,
369}
370
371impl SizeTween {
372    /// Creates a [`Size`] tween.
373    pub fn new(app: &mut App, begin: Option<Size>, end: Option<Size>) -> Handle<SizeTween> {
374        app.create(SizeTween { begin, end })
375    }
376
377    /// Returns the value this variable has at the given animation clock value.
378    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> Option<Size> {
379        let tween = app.get(self);
380        Size::lerp(tween.begin, tween.end, t)
381    }
382}
383
384impl Animatable<Option<Size>> for Handle<SizeTween> {
385    fn transform(&self, app: &App, t: f64) -> Option<Size> {
386        let this = *self;
387        let tween = app.get(this);
388        if t == 0.0 {
389            return tween.begin;
390        }
391        if t == 1.0 {
392            return tween.end;
393        }
394        this.lerp(app, t)
395    }
396}
397
398/// An interpolation between two rectangles.
399pub struct RectTween {
400    /// The value this variable has at the beginning of the animation.
401    pub begin: Option<Rect>,
402
403    /// The value this variable has at the end of the animation.
404    pub end: Option<Rect>,
405}
406
407impl RectTween {
408    /// Creates a [`Rect`] tween.
409    pub fn new(app: &mut App, begin: Option<Rect>, end: Option<Rect>) -> Handle<RectTween> {
410        app.create(RectTween { begin, end })
411    }
412
413    /// Returns the value this variable has at the given animation clock value.
414    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> Option<Rect> {
415        let tween = app.get(self);
416        Rect::lerp(tween.begin, tween.end, t)
417    }
418}
419
420impl Animatable<Option<Rect>> for Handle<RectTween> {
421    fn transform(&self, app: &App, t: f64) -> Option<Rect> {
422        let this = *self;
423        let tween = app.get(this);
424        if t == 0.0 {
425            return tween.begin;
426        }
427        if t == 1.0 {
428            return tween.end;
429        }
430        this.lerp(app, t)
431    }
432}
433
434/// An interpolation between two integers that rounds.
435pub struct IntTween {
436    /// The value this variable has at the beginning of the animation.
437    pub begin: Option<i64>,
438
439    /// The value this variable has at the end of the animation.
440    pub end: Option<i64>,
441}
442
443impl IntTween {
444    /// Creates an int tween.
445    pub fn new(app: &mut App, begin: Option<i64>, end: Option<i64>) -> Handle<IntTween> {
446        app.create(IntTween { begin, end })
447    }
448
449    /// Returns the interpolated integer, rounded.
450    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> i64 {
451        let tween = app.get(self);
452        let begin = tween.begin.expect("IntTween.begin must be set before use");
453        let end = tween.end.expect("IntTween.end must be set before use");
454        (begin as f64 + (end - begin) as f64 * t).round() as i64
455    }
456}
457
458impl Animatable<i64> for Handle<IntTween> {
459    fn transform(&self, app: &App, t: f64) -> i64 {
460        let this = *self;
461        let tween = app.get(this);
462        tween_transform(&tween.begin, &tween.end, t, |t| this.lerp(app, t))
463    }
464}
465
466/// An interpolation between two integers that floors.
467pub struct StepTween {
468    /// The value this variable has at the beginning of the animation.
469    pub begin: Option<i64>,
470
471    /// The value this variable has at the end of the animation.
472    pub end: Option<i64>,
473}
474
475impl StepTween {
476    /// Creates an [`i64`] tween that floors.
477    pub fn new(app: &mut App, begin: Option<i64>, end: Option<i64>) -> Handle<StepTween> {
478        app.create(StepTween { begin, end })
479    }
480
481    /// Returns the interpolated integer, floored.
482    pub fn lerp(self: Handle<Self>, app: &App, t: f64) -> i64 {
483        let tween = app.get(self);
484        let begin = tween.begin.expect("StepTween.begin must be set before use");
485        let end = tween.end.expect("StepTween.end must be set before use");
486        (begin as f64 + (end - begin) as f64 * t).floor() as i64
487    }
488}
489
490impl Animatable<i64> for Handle<StepTween> {
491    fn transform(&self, app: &App, t: f64) -> i64 {
492        let this = *self;
493        let tween = app.get(this);
494        tween_transform(&tween.begin, &tween.end, t, |t| this.lerp(app, t))
495    }
496}
497
498/// A tween with a constant value.
499pub struct ConstantTween<T> {
500    // Dart: `class ConstantTween<T> extends Tween<T>` — the superclass's
501    // fields, with `begin` and `end` both set to the value.
502    tween: Tween<T>,
503}
504
505impl<T: Clone + 'static> ConstantTween<T> {
506    /// Create a tween whose begin and end values equal `value`.
507    pub fn new(app: &mut App, value: T) -> Handle<ConstantTween<T>> {
508        app.create(ConstantTween {
509            tween: Tween {
510                begin: Some(value.clone()),
511                end: Some(value),
512            },
513        })
514    }
515}
516
517impl<T: Clone + 'static> Animatable<T> for Handle<ConstantTween<T>> {
518    fn transform(&self, app: &App, _t: f64) -> T {
519        app.get(*self)
520            .tween
521            .begin
522            .clone()
523            .expect("ConstantTween.begin must be set")
524    }
525}
526
527/// Transforms the value of the given animation by the given curve.
528#[derive(Debug)]
529pub struct CurveTween {
530    /// The curve to use when transforming the value of the animation.
531    pub curve: Rc<dyn Curve>,
532}
533
534impl CurveTween {
535    /// Creates a curve tween.
536    pub fn new(app: &mut App, curve: Rc<dyn Curve>) -> Handle<CurveTween> {
537        app.create(CurveTween { curve })
538    }
539
540    /// The curve to use when transforming the value of the animation.
541    pub fn curve(self: Handle<Self>, app: &App) -> Rc<dyn Curve> {
542        Rc::clone(&app.get(self).curve)
543    }
544
545    /// Dart's `curve` setter.
546    pub fn set_curve(self: Handle<Self>, app: &mut App, curve: Rc<dyn Curve>) {
547        app.get_mut(self).curve = curve;
548    }
549}
550
551impl Animatable<f64> for Handle<CurveTween> {
552    fn transform(&self, app: &App, t: f64) -> f64 {
553        let curve = &app.get(*self).curve;
554        if t == 0.0 || t == 1.0 {
555            debug_assert_eq!(curve.transform(t).round(), t);
556            return t;
557        }
558        curve.transform(t)
559    }
560}
561
562#[cfg(test)]
563mod tests {
564    use super::*;
565    use crate::animation::Animation;
566    use crate::animations::AlwaysStoppedAnimation;
567    use inset_foundation::AppCell;
568
569    #[test]
570    fn a_driven_tween_sees_end_assigned_after_animate() {
571        let cell = AppCell::new();
572        let mut app = cell.borrow_mut();
573        let tween = Tween::new(&mut app, Some(0.0), Some(1.0));
574        let parent = app.create(AlwaysStoppedAnimation::new(1.0)).as_animation();
575        let driven = tween.animate(&mut app, parent);
576
577        assert_eq!(driven.value(&app), 1.0);
578        tween.set_end(&mut app, Some(0.4));
579        assert_eq!(driven.value(&app), 0.4);
580    }
581
582    #[test]
583    fn lerp_walks_the_line() {
584        let cell = AppCell::new();
585        let mut app = cell.borrow_mut();
586        let tween = Tween::new(&mut app, Some(0.0), Some(10.0));
587        assert_eq!(tween.transform(&app, 0.0), 0.0);
588        assert_eq!(tween.transform(&app, 0.25), 2.5);
589        assert_eq!(tween.transform(&app, 1.0), 10.0);
590    }
591
592    #[test]
593    fn an_offset_tween_walks_both_axes() {
594        let cell = AppCell::new();
595        let mut app = cell.borrow_mut();
596        let tween = Tween::new(
597            &mut app,
598            Some(Offset::new(0.0, 10.0)),
599            Some(Offset::new(4.0, -10.0)),
600        );
601        assert_eq!(tween.transform(&app, 0.0), Offset::new(0.0, 10.0));
602        assert_eq!(tween.transform(&app, 0.25), Offset::new(1.0, 5.0));
603        assert_eq!(tween.transform(&app, 1.0), Offset::new(4.0, -10.0));
604    }
605
606    #[test]
607    fn a_size_tween_walks_both_dimensions() {
608        let cell = AppCell::new();
609        let mut app = cell.borrow_mut();
610        let tween = Tween::new(
611            &mut app,
612            Some(Size::new(10.0, 20.0)),
613            Some(Size::new(20.0, 0.0)),
614        );
615        assert_eq!(tween.transform(&app, 0.5), Size::new(15.0, 10.0));
616    }
617}