1use super::color::Color;
8use super::easing::EasingFn;
9use super::vec2::Vec2;
10use super::vec3::Vec3;
11
12pub trait Tweenable: Sized {
17 fn lerp(self, other: Self, t: f32) -> Self;
21
22 #[inline]
27 fn tween(self, other: Self, t: f32, easing: EasingFn) -> Self {
28 self.lerp(other, easing(t))
29 }
30}
31
32impl Tweenable for f32 {
33 #[inline]
34 fn lerp(self, other: Self, t: f32) -> Self {
35 self + (other - self) * t
36 }
37}
38
39impl Tweenable for Vec2 {
40 #[inline]
41 fn lerp(self, other: Self, t: f32) -> Self {
42 self.lerp(other, t)
43 }
44}
45
46impl Tweenable for Vec3 {
47 #[inline]
48 fn lerp(self, other: Self, t: f32) -> Self {
49 self.lerp(other, t)
50 }
51}
52
53impl Tweenable for Color {
54 #[inline]
55 fn lerp(self, other: Self, t: f32) -> Self {
56 self.lerp(other, t)
57 }
58}
59
60#[inline]
62pub fn tween(from: f32, to: f32, t: f32, easing: EasingFn) -> f32 {
63 from + (to - from) * easing(t)
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::core::math::easing::{ease_in, linear};
70
71 const EPSILON: f32 = 1e-5;
72
73 #[test]
78 fn test_f32_lerp_boundaries() {
79 let result_0 = Tweenable::lerp(0.0_f32, 10.0, 0.0);
80 let result_1 = Tweenable::lerp(0.0_f32, 10.0, 1.0);
81 assert!((result_0).abs() < EPSILON);
82 assert!((result_1 - 10.0).abs() < EPSILON);
83 }
84
85 #[test]
86 fn test_f32_lerp_midpoint() {
87 let result = Tweenable::lerp(0.0_f32, 10.0, 0.5);
88 assert!((result - 5.0).abs() < EPSILON);
89 }
90
91 #[test]
92 fn test_f32_tween_linear() {
93 let result = 0.0_f32.tween(10.0, 0.5, linear);
94 assert!((result - 5.0).abs() < EPSILON);
95 }
96
97 #[test]
98 fn test_f32_tween_ease_in() {
99 let result = 0.0_f32.tween(10.0, 0.5, ease_in);
101 assert!((result - 2.5).abs() < EPSILON);
102 }
103
104 #[test]
109 fn test_vec2_lerp_boundaries() {
110 let a = Vec2::new(0.0, 0.0);
111 let b = Vec2::new(10.0, 20.0);
112 let r0 = Tweenable::lerp(a, b, 0.0);
113 let r1 = Tweenable::lerp(a, b, 1.0);
114 assert_eq!(r0, a);
115 assert_eq!(r1, b);
116 }
117
118 #[test]
119 fn test_vec2_tween_linear() {
120 let a = Vec2::new(0.0, 0.0);
121 let b = Vec2::new(10.0, 20.0);
122 let result = a.tween(b, 0.5, linear);
123 assert!((result.x - 5.0).abs() < EPSILON);
124 assert!((result.y - 10.0).abs() < EPSILON);
125 }
126
127 #[test]
128 fn test_vec2_tween_ease_in() {
129 let a = Vec2::new(0.0, 0.0);
130 let b = Vec2::new(10.0, 20.0);
131 let result = a.tween(b, 0.5, ease_in);
132 assert!((result.x - 2.5).abs() < EPSILON);
134 assert!((result.y - 5.0).abs() < EPSILON);
135 }
136
137 #[test]
142 fn test_vec3_lerp_boundaries() {
143 let a = Vec3::new(0.0, 0.0, 0.0);
144 let b = Vec3::new(10.0, 20.0, 30.0);
145 let r0 = Tweenable::lerp(a, b, 0.0);
146 let r1 = Tweenable::lerp(a, b, 1.0);
147 assert_eq!(r0, a);
148 assert_eq!(r1, b);
149 }
150
151 #[test]
152 fn test_vec3_tween_linear() {
153 let a = Vec3::new(0.0, 0.0, 0.0);
154 let b = Vec3::new(10.0, 20.0, 30.0);
155 let result = a.tween(b, 0.5, linear);
156 assert!((result.x - 5.0).abs() < EPSILON);
157 assert!((result.y - 10.0).abs() < EPSILON);
158 assert!((result.z - 15.0).abs() < EPSILON);
159 }
160
161 #[test]
162 fn test_vec3_tween_ease_in() {
163 let a = Vec3::new(0.0, 0.0, 0.0);
164 let b = Vec3::new(10.0, 20.0, 30.0);
165 let result = a.tween(b, 0.5, ease_in);
166 assert!((result.x - 2.5).abs() < EPSILON);
167 assert!((result.y - 5.0).abs() < EPSILON);
168 assert!((result.z - 7.5).abs() < EPSILON);
169 }
170
171 #[test]
176 fn test_color_lerp_boundaries() {
177 let a = Color::BLACK;
178 let b = Color::WHITE;
179 let r0 = Tweenable::lerp(a, b, 0.0);
180 let r1 = Tweenable::lerp(a, b, 1.0);
181 assert_eq!(r0, a);
182 assert_eq!(r1, b);
183 }
184
185 #[test]
186 fn test_color_tween_linear() {
187 let a = Color::BLACK;
188 let b = Color::WHITE;
189 let result = a.tween(b, 0.5, linear);
190 assert!((result.r - 0.5).abs() < EPSILON);
191 assert!((result.g - 0.5).abs() < EPSILON);
192 assert!((result.b - 0.5).abs() < EPSILON);
193 }
194
195 #[test]
196 fn test_color_tween_ease_in() {
197 let a = Color::BLACK;
198 let b = Color::WHITE;
199 let result = a.tween(b, 0.5, ease_in);
200 assert!((result.r - 0.25).abs() < EPSILON);
202 assert!((result.g - 0.25).abs() < EPSILON);
203 assert!((result.b - 0.25).abs() < EPSILON);
204 }
205
206 #[test]
211 fn test_tween_fn_linear() {
212 let result = tween(0.0, 10.0, 0.5, linear);
213 assert!((result - 5.0).abs() < EPSILON);
214 }
215
216 #[test]
217 fn test_tween_fn_ease_in() {
218 let result = tween(0.0, 10.0, 0.5, ease_in);
219 assert!((result - 2.5).abs() < EPSILON);
220 }
221
222 #[test]
223 fn test_tween_fn_with_offset() {
224 let result = tween(5.0, 15.0, 0.5, linear);
225 assert!((result - 10.0).abs() < EPSILON);
226 }
227}