From fcc8910568a00b892e66586a7359ea3552c840a2 Mon Sep 17 00:00:00 2001
From: Jerome Humbert <djeedai@gmail.com>
Date: Mon, 14 Nov 2022 20:55:32 +0000
Subject: [PATCH] Added total duration and shared implementations (#74)

Exposed the total animation duration via the `TotalDuration` enum and
the `Tweenable<T>::total_duration() -> TotalDuration` API.

Added a default implementation for some methods of `Tweenable<T>`,
namely `set_progress()`, `progress()`, and `times_completed()`. These
shared implementations are used by all built-in tweenables.

Issue: #31
---
 CHANGELOG.md     |   2 +
 src/lib.rs       |  14 +--
 src/tweenable.rs | 230 ++++++++++++++++++-----------------------------
 3 files changed, 97 insertions(+), 149 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 499fec6..8d6dd16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Added `Targetable`, `ComponentTarget`, and `AssetTarget`, which should be considered private even though they appear in the public API. They are a workaround for Bevy 0.8 and will likely be removed in the future once the related Bevy limitation is lifted.
 - Added the missing `Tween::with_completed()` to raise a callback.
 - Added completion event and callback support to `Delay<T>`, similar to what existed for `Tween<T>`.
+- Added `TotalDuration` and a new `Tweenable<T>::total_duration()` method to retrieve the total duration of the animation including looping.
 
 ### Changed
 
@@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Changed the signature of the `component_animator_system()` and `asset_animator_system()` public functions to directly consume a `ResMut<Events<TweenCompleted>>` instead of an `EventWriter<TweenCompleted>`, to work around some internal limitations.
 - Changed `Delay` into `Delay<T>`, taking the animation target type like other tweenables, to be able to emit events and raise callbacks.
 - Changed `CompletedCallback<T>` to take the tweenable type itself, instead of the target type. Users upgrading should replace `CompletedCallback<T>` with `CompletedCallback<Tween<T>>`.
+- The `set_progress()`, `progress()`, and `times_completed()` method of `Tweenable<T>` now have a default implementation, and all built-in tweenables use that implementation.
 
 ### Removed
 
diff --git a/src/lib.rs b/src/lib.rs
index 7d5fc03..f139914 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -164,8 +164,8 @@ pub use lens::Lens;
 pub use plugin::asset_animator_system;
 pub use plugin::{component_animator_system, AnimationSystem, TweeningPlugin};
 pub use tweenable::{
-    BoxedTweenable, Delay, Sequence, Targetable, Tracks, Tween, TweenCompleted, TweenState,
-    Tweenable,
+    BoxedTweenable, Delay, Sequence, Targetable, TotalDuration, Tracks, Tween, TweenCompleted,
+    TweenState, Tweenable,
 };
 
 pub mod lens;
@@ -375,19 +375,19 @@ macro_rules! animator_impl {
         }
 
         /// Set the top-level tweenable item this animator controls.
-        pub fn set_tweenable(&mut self, tween: impl Tweenable<T> + Send + Sync + 'static) {
+        pub fn set_tweenable(&mut self, tween: impl Tweenable<T> + 'static) {
             self.tweenable = Box::new(tween);
         }
 
         /// Get the top-level tweenable this animator is currently controlling.
         #[must_use]
-        pub fn tweenable(&self) -> &(dyn Tweenable<T> + Send + Sync + 'static) {
+        pub fn tweenable(&self) -> &dyn Tweenable<T> {
             self.tweenable.as_ref()
         }
 
         /// Get the top-level mutable tweenable this animator is currently controlling.
         #[must_use]
-        pub fn tweenable_mut(&mut self) -> &mut (dyn Tweenable<T> + Send + Sync + 'static) {
+        pub fn tweenable_mut(&mut self) -> &mut dyn Tweenable<T> {
             self.tweenable.as_mut()
         }
 
@@ -422,7 +422,7 @@ impl<T: Component + std::fmt::Debug> std::fmt::Debug for Animator<T> {
 impl<T: Component> Animator<T> {
     /// Create a new animator component from a single tweenable.
     #[must_use]
-    pub fn new(tween: impl Tweenable<T> + Send + Sync + 'static) -> Self {
+    pub fn new(tween: impl Tweenable<T> + 'static) -> Self {
         Self {
             state: default(),
             tweenable: Box::new(tween),
@@ -457,7 +457,7 @@ impl<T: Asset + std::fmt::Debug> std::fmt::Debug for AssetAnimator<T> {
 impl<T: Asset> AssetAnimator<T> {
     /// Create a new asset animator component from a single tweenable.
     #[must_use]
-    pub fn new(handle: Handle<T>, tween: impl Tweenable<T> + Send + Sync + 'static) -> Self {
+    pub fn new(handle: Handle<T>, tween: impl Tweenable<T> + 'static) -> Self {
         Self {
             state: default(),
             tweenable: Box::new(tween),
diff --git a/src/tweenable.rs b/src/tweenable.rs
index 0fc9ad8..442f4fe 100644
--- a/src/tweenable.rs
+++ b/src/tweenable.rs
@@ -26,17 +26,15 @@ use crate::{EaseMethod, Lens, RepeatCount, RepeatStrategy, TweeningDirection};
 /// ```no_run
 /// # use std::time::Duration;
 /// # use bevy::prelude::{Entity, Events, Mut, Transform};
-/// # use bevy_tweening::{BoxedTweenable, Sequence, Tweenable, TweenCompleted, TweenState, Targetable};
+/// # use bevy_tweening::{BoxedTweenable, Sequence, Tweenable, TweenCompleted, TweenState, Targetable, TotalDuration};
 /// #
 /// # struct MyTweenable;
 /// # impl Tweenable<Transform> for MyTweenable {
 /// #     fn duration(&self) -> Duration  { unimplemented!() }
+/// #     fn total_duration(&self) -> TotalDuration  { unimplemented!() }
 /// #     fn set_elapsed(&mut self, elapsed: Duration)  { unimplemented!() }
 /// #     fn elapsed(&self) -> Duration  { unimplemented!() }
-/// #     fn set_progress(&mut self, progress: f32)  { unimplemented!() }
-/// #     fn progress(&self) -> f32  { unimplemented!() }
 /// #     fn tick<'a>(&mut self, delta: Duration, target: &'a mut dyn Targetable<Transform>, entity: Entity, events: &mut Mut<Events<TweenCompleted>>) -> TweenState  { unimplemented!() }
-/// #     fn times_completed(&self) -> u32  { unimplemented!() }
 /// #     fn rewind(&mut self) { unimplemented!() }
 /// # }
 ///
@@ -52,7 +50,7 @@ use crate::{EaseMethod, Lens, RepeatCount, RepeatStrategy, TweeningDirection};
 ///     }
 /// }
 /// ```
-pub type BoxedTweenable<T> = Box<dyn Tweenable<T> + Send + Sync + 'static>;
+pub type BoxedTweenable<T> = Box<dyn Tweenable<T> + 'static>;
 
 /// Playback state of a [`Tweenable`].
 ///
@@ -161,19 +159,6 @@ impl AnimClock {
         self.elapsed
     }
 
-    fn set_progress(&mut self, progress: f32) -> (TweenState, i32) {
-        self.set_elapsed(self.duration.mul_f32(progress.max(0.)))
-    }
-
-    fn progress(&self) -> f32 {
-        if let TotalDuration::Finite(total_duration) = self.total_duration {
-            if self.elapsed >= total_duration {
-                return 1.;
-            }
-        }
-        fraction_progress(self.elapsed, self.duration)
-    }
-
     fn state(&self) -> TweenState {
         match self.total_duration {
             TotalDuration::Finite(total_duration) => {
@@ -192,9 +177,14 @@ impl AnimClock {
     }
 }
 
-#[derive(Debug)]
-enum TotalDuration {
+/// Possibly infinite duration of an animation.
+///
+/// Used to measure the total duration of an animation including any looping.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum TotalDuration {
+    /// The duration is finite, of the given value.
     Finite(Duration),
+    /// The duration is infinite.
     Infinite,
 }
 
@@ -262,9 +252,7 @@ impl<'a, T: Asset> Targetable<T> for AssetTarget<'a, T> {
 
 /// An animatable entity, either a single [`Tween`] or a collection of them.
 pub trait Tweenable<T>: Send + Sync {
-    /// Get the total duration of the animation.
-    ///
-    /// This is always the duration of a single iteration, even when looping.
+    /// Get the duration of a single iteration of the animation.
     ///
     /// Note that for [`RepeatStrategy::MirroredRepeat`], this is the duration
     /// of a single way, either from start to end or back from end to start.
@@ -272,6 +260,13 @@ pub trait Tweenable<T>: Send + Sync {
     /// same state in this case is the double of the returned value.
     fn duration(&self) -> Duration;
 
+    /// Get the total duration of the entire animation, including looping.
+    ///
+    /// For [`TotalDuration::Finite`], this is the number of repeats times the duration of a single iteration ([`duration()`]).
+    ///
+    /// [`duration()`]: Tweenable::duration
+    fn total_duration(&self) -> TotalDuration;
+
     /// Set the current animation playback elapsed time.
     ///
     /// See [`elapsed()`] for details on the meaning. If `elapsed` is greater
@@ -297,27 +292,6 @@ pub trait Tweenable<T>: Send + Sync {
     /// [`duration()`]: Tweenable::duration
     fn elapsed(&self) -> Duration;
 
-    /// Set the current animation playback progress.
-    ///
-    /// See [`progress()`] for details on the meaning.
-    ///
-    /// Setting the progress seeks the animation to a new position, but does not
-    /// apply that change to the underlying component being animated. To
-    /// force the change to apply, call [`tick()`] with a `delta` of
-    /// `Duration::ZERO`.
-    ///
-    /// [`progress()`]: Tweenable::progress
-    /// [`tick()`]: Tweenable::tick
-    fn set_progress(&mut self, progress: f32);
-
-    /// Get the current progress in \[0:1\] of the animation.
-    ///
-    /// While looping, the exact value `1.0` is never reached, since the
-    /// tweenable loops over to `0.0` immediately when it changes direction at
-    /// either endpoint. Upon completion, the tweenable always reports exactly
-    /// `1.0`.
-    fn progress(&self) -> f32;
-
     /// Tick the animation, advancing it by the given delta time and mutating
     /// the given target component or asset.
     ///
@@ -342,47 +316,71 @@ pub trait Tweenable<T>: Send + Sync {
         events: &mut Mut<Events<TweenCompleted>>,
     ) -> TweenState;
 
-    /// Get the number of times this tweenable completed.
-    ///
-    /// For looping animations, this returns the number of times a single
-    /// playback was completed. In the case of
-    /// [`RepeatStrategy::MirroredRepeat`] this corresponds to a playback in
-    /// a single direction, so tweening from start to end and back to start
-    /// counts as two completed times (one forward, one backward).
-    fn times_completed(&self) -> u32;
-
     /// Rewind the animation to its starting state.
     ///
     /// Note that the starting state depends on the current direction. For
     /// [`TweeningDirection::Forward`] this is the start point of the lens,
     /// whereas for [`TweeningDirection::Backward`] this is the end one.
     fn rewind(&mut self);
-}
 
-impl<T: 'static> From<Delay<T>> for BoxedTweenable<T> {
-    fn from(d: Delay<T>) -> Self {
-        Box::new(d)
+    /// Set the current animation playback progress.
+    ///
+    /// See [`progress()`] for details on the meaning.
+    ///
+    /// Setting the progress seeks the animation to a new position, but does not
+    /// apply that change to the underlying component being animated. To
+    /// force the change to apply, call [`tick()`] with a `delta` of
+    /// `Duration::ZERO`.
+    ///
+    /// [`progress()`]: Tweenable::progress
+    /// [`tick()`]: Tweenable::tick
+    fn set_progress(&mut self, progress: f32) {
+        self.set_elapsed(self.duration().mul_f32(progress.max(0.)));
     }
-}
 
-impl<T: 'static> From<Sequence<T>> for BoxedTweenable<T> {
-    fn from(s: Sequence<T>) -> Self {
-        Box::new(s)
+    /// Get the current progress in \[0:1\] of the animation.
+    ///
+    /// While looping, the exact value `1.0` is never reached, since the
+    /// tweenable loops over to `0.0` immediately when it changes direction at
+    /// either endpoint. Upon completion, the tweenable always reports exactly
+    /// `1.0`.
+    fn progress(&self) -> f32 {
+        let elapsed = self.elapsed();
+        if let TotalDuration::Finite(total_duration) = self.total_duration() {
+            if elapsed >= total_duration {
+                return 1.;
+            }
+        }
+        fraction_progress(elapsed, self.duration())
     }
-}
 
-impl<T: 'static> From<Tracks<T>> for BoxedTweenable<T> {
-    fn from(t: Tracks<T>) -> Self {
-        Box::new(t)
+    /// Get the number of times this tweenable completed.
+    ///
+    /// For looping animations, this returns the number of times a single
+    /// playback was completed. In the case of
+    /// [`RepeatStrategy::MirroredRepeat`] this corresponds to a playback in
+    /// a single direction, so tweening from start to end and back to start
+    /// counts as two completed times (one forward, one backward).
+    fn times_completed(&self) -> u32 {
+        (self.elapsed().as_nanos() / self.duration().as_nanos()) as u32
     }
 }
 
-impl<T: 'static> From<Tween<T>> for BoxedTweenable<T> {
-    fn from(t: Tween<T>) -> Self {
-        Box::new(t)
-    }
+macro_rules! impl_boxed {
+    ($tweenable:ty) => {
+        impl<T: 'static> From<$tweenable> for BoxedTweenable<T> {
+            fn from(t: $tweenable) -> Self {
+                Box::new(t)
+            }
+        }
+    };
 }
 
+impl_boxed!(Tween<T>);
+impl_boxed!(Sequence<T>);
+impl_boxed!(Tracks<T>);
+impl_boxed!(Delay<T>);
+
 /// Type of a callback invoked when a [`Tween`] or [`Delay`] has completed.
 ///
 /// See [`Tween::set_completed()`] or [`Delay::set_completed()`] for usage.
@@ -426,7 +424,7 @@ impl<T: 'static> Tween<T> {
     /// let seq = tween1.then(tween2);
     /// ```
     #[must_use]
-    pub fn then(self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Sequence<T> {
+    pub fn then(self, tween: impl Tweenable<T> + 'static) -> Sequence<T> {
         Sequence::with_capacity(2).then(self).then(tween)
     }
 }
@@ -639,6 +637,10 @@ impl<T> Tweenable<T> for Tween<T> {
         self.clock.duration
     }
 
+    fn total_duration(&self) -> TotalDuration {
+        self.clock.total_duration
+    }
+
     fn set_elapsed(&mut self, elapsed: Duration) {
         self.clock.set_elapsed(elapsed);
     }
@@ -647,14 +649,6 @@ impl<T> Tweenable<T> for Tween<T> {
         self.clock.elapsed()
     }
 
-    fn set_progress(&mut self, progress: f32) {
-        self.clock.set_progress(progress);
-    }
-
-    fn progress(&self) -> f32 {
-        self.clock.progress()
-    }
-
     fn tick<'a>(
         &mut self,
         delta: Duration,
@@ -704,10 +698,6 @@ impl<T> Tweenable<T> for Tween<T> {
         state
     }
 
-    fn times_completed(&self) -> u32 {
-        self.clock.times_completed()
-    }
-
     fn rewind(&mut self) {
         if self.clock.strategy == RepeatStrategy::MirroredRepeat {
             // In mirrored mode, direction alternates each loop. To reset to the original
@@ -733,7 +723,6 @@ pub struct Sequence<T> {
     index: usize,
     duration: Duration,
     elapsed: Duration,
-    times_completed: u32,
 }
 
 impl<T> Sequence<T> {
@@ -754,13 +743,12 @@ impl<T> Sequence<T> {
             index: 0,
             duration,
             elapsed: Duration::ZERO,
-            times_completed: 0,
         }
     }
 
     /// Create a new sequence containing a single tween.
     #[must_use]
-    pub fn from_single(tween: impl Tweenable<T> + Send + Sync + 'static) -> Self {
+    pub fn from_single(tween: impl Tweenable<T> + 'static) -> Self {
         let duration = tween.duration();
         let boxed: BoxedTweenable<T> = Box::new(tween);
         Self {
@@ -768,7 +756,6 @@ impl<T> Sequence<T> {
             index: 0,
             duration,
             elapsed: Duration::ZERO,
-            times_completed: 0,
         }
     }
 
@@ -780,13 +767,12 @@ impl<T> Sequence<T> {
             index: 0,
             duration: Duration::ZERO,
             elapsed: Duration::ZERO,
-            times_completed: 0,
         }
     }
 
     /// Append a [`Tweenable`] to this sequence.
     #[must_use]
-    pub fn then(mut self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Self {
+    pub fn then(mut self, tween: impl Tweenable<T> + 'static) -> Self {
         self.duration += tween.duration();
         self.tweens.push(Box::new(tween));
         self
@@ -810,10 +796,13 @@ impl<T> Tweenable<T> for Sequence<T> {
         self.duration
     }
 
+    fn total_duration(&self) -> TotalDuration {
+        TotalDuration::Finite(self.duration)
+    }
+
     fn set_elapsed(&mut self, elapsed: Duration) {
         // Set the total sequence progress
         self.elapsed = elapsed;
-        self.times_completed = u32::from(elapsed >= self.duration);
 
         // Find which tween is active in the sequence
         let mut accum_duration = Duration::ZERO;
@@ -839,18 +828,6 @@ impl<T> Tweenable<T> for Sequence<T> {
         self.elapsed
     }
 
-    fn set_progress(&mut self, progress: f32) {
-        self.set_elapsed(self.duration.mul_f32(progress.max(0.)))
-    }
-
-    fn progress(&self) -> f32 {
-        if self.elapsed >= self.duration {
-            1.
-        } else {
-            fraction_progress(self.elapsed, self.duration)
-        }
-    }
-
     fn tick<'a>(
         &mut self,
         mut delta: Duration,
@@ -861,7 +838,7 @@ impl<T> Tweenable<T> for Sequence<T> {
         self.elapsed = self.elapsed.saturating_add(delta).min(self.duration);
         while self.index < self.tweens.len() {
             let tween = &mut self.tweens[self.index];
-            let tween_remaining = tween.duration().mul_f32(1.0 - tween.progress());
+            let tween_remaining = tween.duration() - tween.elapsed();
             if let TweenState::Active = tween.tick(delta, target, entity, events) {
                 return TweenState::Active;
             }
@@ -871,18 +848,12 @@ impl<T> Tweenable<T> for Sequence<T> {
             self.index += 1;
         }
 
-        self.times_completed = 1;
         TweenState::Completed
     }
 
-    fn times_completed(&self) -> u32 {
-        self.times_completed
-    }
-
     fn rewind(&mut self) {
         self.elapsed = Duration::ZERO;
         self.index = 0;
-        self.times_completed = 0;
         for tween in &mut self.tweens {
             // or only first?
             tween.rewind();
@@ -895,7 +866,6 @@ pub struct Tracks<T> {
     tracks: Vec<BoxedTweenable<T>>,
     duration: Duration,
     elapsed: Duration,
-    times_completed: u32,
 }
 
 impl<T> Tracks<T> {
@@ -914,7 +884,6 @@ impl<T> Tracks<T> {
             tracks,
             duration,
             elapsed: Duration::ZERO,
-            times_completed: 0,
         }
     }
 }
@@ -924,9 +893,12 @@ impl<T> Tweenable<T> for Tracks<T> {
         self.duration
     }
 
+    fn total_duration(&self) -> TotalDuration {
+        TotalDuration::Finite(self.duration)
+    }
+
     fn set_elapsed(&mut self, elapsed: Duration) {
         self.elapsed = elapsed;
-        self.times_completed = u32::from(elapsed >= self.duration); // not looping
 
         for tweenable in &mut self.tracks {
             tweenable.set_elapsed(elapsed);
@@ -937,18 +909,6 @@ impl<T> Tweenable<T> for Tracks<T> {
         self.elapsed
     }
 
-    fn set_progress(&mut self, progress: f32) {
-        self.set_elapsed(self.duration.mul_f32(progress.max(0.)))
-    }
-
-    fn progress(&self) -> f32 {
-        if self.elapsed >= self.duration {
-            1.
-        } else {
-            fraction_progress(self.elapsed, self.duration)
-        }
-    }
-
     fn tick<'a>(
         &mut self,
         delta: Duration,
@@ -965,18 +925,12 @@ impl<T> Tweenable<T> for Tracks<T> {
         if any_active {
             TweenState::Active
         } else {
-            self.times_completed = 1;
             TweenState::Completed
         }
     }
 
-    fn times_completed(&self) -> u32 {
-        self.times_completed
-    }
-
     fn rewind(&mut self) {
         self.elapsed = Duration::ZERO;
-        self.times_completed = 0;
         for tween in &mut self.tracks {
             tween.rewind();
         }
@@ -999,7 +953,7 @@ impl<T: 'static> Delay<T> {
     /// Chain another [`Tweenable`] after this tween, making a [`Sequence`] with
     /// the two.
     #[must_use]
-    pub fn then(self, tween: impl Tweenable<T> + Send + Sync + 'static) -> Sequence<T> {
+    pub fn then(self, tween: impl Tweenable<T> + 'static) -> Sequence<T> {
         Sequence::with_capacity(2).then(self).then(tween)
     }
 }
@@ -1155,6 +1109,10 @@ impl<T> Tweenable<T> for Delay<T> {
         self.timer.duration()
     }
 
+    fn total_duration(&self) -> TotalDuration {
+        TotalDuration::Finite(self.duration())
+    }
+
     fn set_elapsed(&mut self, elapsed: Duration) {
         // need to reset() to clear finished() unfortunately
         self.timer.reset();
@@ -1167,14 +1125,6 @@ impl<T> Tweenable<T> for Delay<T> {
         self.timer.elapsed()
     }
 
-    fn set_progress(&mut self, progress: f32) {
-        self.set_elapsed(self.timer.duration().mul_f32(progress.max(0.)));
-    }
-
-    fn progress(&self) -> f32 {
-        self.timer.percent()
-    }
-
     fn tick<'a>(
         &mut self,
         delta: Duration,
@@ -1204,10 +1154,6 @@ impl<T> Tweenable<T> for Delay<T> {
         state
     }
 
-    fn times_completed(&self) -> u32 {
-        u32::from(self.is_completed())
-    }
-
     fn rewind(&mut self) {
         self.timer.reset();
     }
-- 
GitLab