eazy_tweener/
value.rs

1//! Tweenable values for animation interpolation.
2//!
3//! The [`Tweenable`] trait defines types that can be interpolated between
4//! two values using linear interpolation modified by easing functions.
5
6/// A value that can be interpolated between two points.
7///
8/// Types implementing this trait can be animated using [`Tween`].
9///
10/// # Examples
11///
12/// ```rust
13/// use eazy_tweener::Tweenable;
14///
15/// let a = 0.0_f32;
16/// let b = 100.0_f32;
17/// let mid = a.lerp(b, 0.5);
18///
19/// assert_eq!(mid, 50.0);
20/// ```
21pub trait Tweenable: Copy + Send + Sync + 'static {
22  /// Linearly interpolate between `self` and `other` by factor `t`.
23  ///
24  /// When `t = 0.0`, returns `self`.
25  /// When `t = 1.0`, returns `other`.
26  /// Values outside `[0, 1]` extrapolate beyond the endpoints.
27  fn lerp(self, other: Self, t: f32) -> Self;
28}
29
30// --- Scalar Implementations ---
31
32impl Tweenable for f32 {
33  #[inline(always)]
34  fn lerp(self, other: Self, t: f32) -> Self {
35    self + (other - self) * t
36  }
37}
38
39impl Tweenable for f64 {
40  #[inline(always)]
41  fn lerp(self, other: Self, t: f32) -> Self {
42    self + (other - self) * t as f64
43  }
44}
45
46impl Tweenable for i32 {
47  #[inline(always)]
48  fn lerp(self, other: Self, t: f32) -> Self {
49    (self as f32 + (other - self) as f32 * t).round() as i32
50  }
51}
52
53impl Tweenable for u32 {
54  #[inline(always)]
55  fn lerp(self, other: Self, t: f32) -> Self {
56    (self as f32 + (other as f32 - self as f32) * t).round() as u32
57  }
58}
59
60// --- Array Implementations (Vec2, Vec3, Vec4, etc.) ---
61
62impl Tweenable for [f32; 2] {
63  #[inline(always)]
64  fn lerp(self, other: Self, t: f32) -> Self {
65    [
66      self[0] + (other[0] - self[0]) * t,
67      self[1] + (other[1] - self[1]) * t,
68    ]
69  }
70}
71
72impl Tweenable for [f32; 3] {
73  #[inline(always)]
74  fn lerp(self, other: Self, t: f32) -> Self {
75    [
76      self[0] + (other[0] - self[0]) * t,
77      self[1] + (other[1] - self[1]) * t,
78      self[2] + (other[2] - self[2]) * t,
79    ]
80  }
81}
82
83impl Tweenable for [f32; 4] {
84  #[inline(always)]
85  fn lerp(self, other: Self, t: f32) -> Self {
86    [
87      self[0] + (other[0] - self[0]) * t,
88      self[1] + (other[1] - self[1]) * t,
89      self[2] + (other[2] - self[2]) * t,
90      self[3] + (other[3] - self[3]) * t,
91    ]
92  }
93}
94
95impl Tweenable for [f64; 2] {
96  #[inline(always)]
97  fn lerp(self, other: Self, t: f32) -> Self {
98    let t = t as f64;
99
100    [
101      self[0] + (other[0] - self[0]) * t,
102      self[1] + (other[1] - self[1]) * t,
103    ]
104  }
105}
106
107impl Tweenable for [f64; 3] {
108  #[inline(always)]
109  fn lerp(self, other: Self, t: f32) -> Self {
110    let t = t as f64;
111
112    [
113      self[0] + (other[0] - self[0]) * t,
114      self[1] + (other[1] - self[1]) * t,
115      self[2] + (other[2] - self[2]) * t,
116    ]
117  }
118}
119
120impl Tweenable for [f64; 4] {
121  #[inline(always)]
122  fn lerp(self, other: Self, t: f32) -> Self {
123    let t = t as f64;
124
125    [
126      self[0] + (other[0] - self[0]) * t,
127      self[1] + (other[1] - self[1]) * t,
128      self[2] + (other[2] - self[2]) * t,
129      self[3] + (other[3] - self[3]) * t,
130    ]
131  }
132}
133
134// --- Tuple Implementations ---
135
136impl Tweenable for (f32, f32) {
137  #[inline(always)]
138  fn lerp(self, other: Self, t: f32) -> Self {
139    (
140      self.0 + (other.0 - self.0) * t,
141      self.1 + (other.1 - self.1) * t,
142    )
143  }
144}
145
146impl Tweenable for (f32, f32, f32) {
147  #[inline(always)]
148  fn lerp(self, other: Self, t: f32) -> Self {
149    (
150      self.0 + (other.0 - self.0) * t,
151      self.1 + (other.1 - self.1) * t,
152      self.2 + (other.2 - self.2) * t,
153    )
154  }
155}
156
157impl Tweenable for (f32, f32, f32, f32) {
158  #[inline(always)]
159  fn lerp(self, other: Self, t: f32) -> Self {
160    (
161      self.0 + (other.0 - self.0) * t,
162      self.1 + (other.1 - self.1) * t,
163      self.2 + (other.2 - self.2) * t,
164      self.3 + (other.3 - self.3) * t,
165    )
166  }
167}
168
169#[cfg(test)]
170mod tests {
171  use super::*;
172
173  #[test]
174  fn test_f32_lerp() {
175    assert_eq!(0.0_f32.lerp(100.0, 0.0), 0.0);
176    assert_eq!(0.0_f32.lerp(100.0, 0.5), 50.0);
177    assert_eq!(0.0_f32.lerp(100.0, 1.0), 100.0);
178  }
179
180  #[test]
181  fn test_array_lerp() {
182    let a = [0.0_f32, 0.0, 0.0];
183    let b = [100.0_f32, 200.0, 300.0];
184    let mid = a.lerp(b, 0.5);
185
186    assert_eq!(mid, [50.0, 100.0, 150.0]);
187  }
188
189  #[test]
190  fn test_extrapolation() {
191    // t > 1.0 should extrapolate
192    assert_eq!(0.0_f32.lerp(100.0, 1.5), 150.0);
193    // t < 0.0 should extrapolate backward
194    assert_eq!(0.0_f32.lerp(100.0, -0.5), -50.0);
195  }
196}