Skip to main content

tween/
lib.rs

1#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
2#![cfg_attr(not(feature = "std"), doc = "no-std stand in")]
3#![deny(unsafe_code)]
4#![deny(rust_2018_idioms)]
5#![deny(missing_docs)]
6#![deny(rustdoc::broken_intra_doc_links)]
7#![no_std]
8
9#[cfg(feature = "std")]
10#[macro_use]
11extern crate std;
12
13#[cfg(all(feature = "std", feature = "libm"))]
14compile_error!("Please disable feature `libm` or disable default features -- both cannot be active at once.");
15
16#[macro_use]
17mod macros;
18
19mod math;
20mod tweener;
21mod tweens;
22
23pub use tweener::*;
24pub use tweens::*;
25
26/// This is the core trait of the Library, which all tweens implement.
27pub trait Tween<Value> {
28    /// Returns a new value based on the value_delta and the percent.
29    ///
30    /// [Linear], for example, is implemented simply as:
31    /// ```no_test
32    /// value_delta.scale(percent)
33    /// ```
34    /// which is just `value_delta * percent`.
35    fn tween(&mut self, value_delta: Value, percent: f32) -> Value;
36
37    /// All Tweens in this library use this default method, except [Looper] and [Oscillator], which
38    /// which are both unbounded (because they never stop returning values), and [Extrapolator],
39    /// which simply unbounds tweens.
40    ///
41    /// This is used by [Tweener] and [FixedTweener] to determine when to clamp and when a tween
42    /// will return true for [Tweener::is_finished].
43    ///
44    /// If you have a [Tween] which returns valid values at all percentage ranges at all times, you
45    /// should return [false].
46    ///
47    /// If you would like to extrapolate a tween *beyond* its bounds, you can wrap it in
48    /// [Extrapolator].
49    #[inline(always)]
50    fn is_finite(&self) -> bool {
51        true
52    }
53}
54
55#[cfg(test)]
56static_assertions::assert_obj_safe!(Tween<i32>);
57
58#[cfg(feature = "std")]
59impl<Value> Tween<Value> for &'_ mut dyn Tween<Value>
60where
61    Value: TweenValue,
62{
63    #[inline(always)]
64    fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
65        (**self).tween(value_delta, percent)
66    }
67
68    fn is_finite(&self) -> bool {
69        true
70    }
71}
72
73impl_tween_for_box!();
74impl_tween_for_box!(Send);
75impl_tween_for_box!(Sync);
76impl_tween_for_box!(Send, Sync);
77impl_tween_for_box!(Unpin);
78impl_tween_for_box!(Send, Unpin);
79impl_tween_for_box!(Send, Sync, Unpin);
80
81impl<Value, F> Tween<Value> for F
82where
83    F: FnMut(Value, f32) -> Value,
84    Value: TweenValue,
85{
86    #[inline(always)]
87    fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
88        self(value_delta, percent)
89    }
90}
91
92/// A `TweenValue` is a value which *can* be Tweened. The library fundamentally outputs
93/// `TweenValue` eventually.
94///
95/// If you want to implement your own values to be tweened (for example, your favorite color lib),
96/// then you'll need to implement this trait.
97///
98/// For now, we require `Copy`, but can reduce this to a `Clone` implementation. Please file an
99/// issue if that is needed for your workflow.
100pub trait TweenValue: Copy + core::fmt::Debug + core::ops::Add<Output = Self> + core::ops::Sub<Output = Self> {
101    /// This should be implemented as a simple multiplication. For f64, for example,
102    /// it's implemented as `(self as f32 * scale) as f64`.
103    fn scale(self, scale: f32) -> Self;
104}
105
106/// A `TweenTime` is a representation of Time. The two most common will be `f32`/`f64` for
107/// seconds and `u32`/`u64`/`usize` for frames.
108///
109/// If you want to implement your own time for duration, then you'll need to implement this
110/// trait.
111pub trait TweenTime:
112    Copy
113    + PartialEq
114    + PartialOrd
115    + core::fmt::Debug
116    + core::ops::Add<Output = Self>
117    + core::ops::AddAssign
118    + core::ops::Rem<Output = Self>
119    + core::ops::Sub<Output = Self>
120{
121    /// The ZERO value. This is 0 or 0.0.
122    const ZERO: Self;
123
124    /// Converts the given number to an `f32`.
125    fn to_f32(self) -> f32;
126}
127
128declare_time!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
129
130impl TweenTime for f32 {
131    const ZERO: Self = 0.0;
132
133    #[inline(always)]
134    fn to_f32(self) -> f32 {
135        self
136    }
137}
138impl TweenTime for f64 {
139    const ZERO: Self = 0.0;
140
141    #[inline(always)]
142    fn to_f32(self) -> f32 {
143        self as f32
144    }
145}
146
147declare_value!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
148
149impl TweenValue for f32 {
150    #[inline(always)]
151    fn scale(self, scale: f32) -> Self {
152        self * scale
153    }
154}
155
156impl TweenValue for f64 {
157    #[inline(always)]
158    fn scale(self, scale: f32) -> Self {
159        (self as f32 * scale) as Self
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn lambda_test() {
169        // this tweener will always return `100` for `4` frames!
170        let (start, end) = (0, 1); // this is basically ignored by the Lambda Tween
171        let length = 4;
172        let mut pct_50_or_over = false;
173        let mut tweener = Tweener::new(start, end, length, |_vd, pct| {
174            if pct >= 0.5 {
175                pct_50_or_over = true;
176            }
177            100
178        });
179        assert_eq!(tweener.move_by(1), 100);
180        assert_eq!(tweener.move_by(1), 100);
181        assert_eq!(tweener.move_by(1), 100);
182        assert_eq!(tweener.move_by(1), 100);
183        // because we're clamped to the tween's original bounds!
184        assert_eq!(tweener.move_by(1), 1);
185        assert!(tweener.is_finished());
186        assert!(pct_50_or_over);
187    }
188
189    #[test]
190    fn cubic_bezier() {
191        use core::ops::{Add, Sub};
192        #[derive(Debug, Clone, Copy, PartialEq)]
193        struct Point(f32, f32);
194        impl Add for Point {
195            type Output = Self;
196
197            fn add(self, rhs: Self) -> Self::Output {
198                Self(self.0 + rhs.0, self.1 + rhs.1)
199            }
200        }
201        impl Sub for Point {
202            type Output = Self;
203
204            fn sub(self, rhs: Self) -> Self::Output {
205                Self(self.0 - rhs.0, self.1 - rhs.1)
206            }
207        }
208        impl TweenValue for Point {
209            fn scale(self, scale: f32) -> Self {
210                Self(self.0 * scale, self.1 * scale)
211            }
212        }
213
214        let start = Point(0.0, 0.0);
215        let destination = Point(10.0, 0.0);
216
217        let ctrl_one = Point(2.5, 10.0);
218        let ctrl_two = Point(7.5, 10.0);
219
220        // decasteljau for simplicity
221        let mut tweener = Tweener::new(start, destination, 10.0, |delta, t| {
222            fn lerp(a: Point, b: Point, t: f32) -> Point {
223                a.scale(1.0 - t) + b.scale(t)
224            }
225
226            let a = lerp(Point(0.0, 0.0), ctrl_one, t);
227            let b = lerp(ctrl_two, ctrl_one, t);
228            let c = lerp(ctrl_two, delta, t);
229
230            let d = lerp(a, b, t);
231            let e = lerp(b, c, t);
232
233            lerp(d, e, t)
234        });
235
236        assert_eq!(tweener.move_to(5.0), Point(5.0, 7.5));
237        assert_eq!(tweener.move_to(10.0), Point(10.0, 0.0));
238    }
239}