Skip to main content

ferroui_base/rendering/composition/animations/
interpolators.rs

1//! Interpolation of the value types composition animations can animate.
2
3use crate::media::Color;
4use crate::numerics::{Quaternion, Vector2, Vector3, Vector4};
5use crate::{RelativePoint, RelativeScalar, Vector, Vector3D};
6
7/// An interface to define interpolation logic for a particular type
8pub trait IInterpolator<T> {
9    fn interpolate(&self, from: T, to: T, progress: f32) -> T;
10}
11
12macro_rules! instance {
13    ($ty:ident) => {
14        impl $ty {
15            /// The shared instance.
16            pub fn instance() -> &'static $ty {
17                static INSTANCE: $ty = $ty;
18                &INSTANCE
19            }
20        }
21    };
22}
23
24/// Interpolates single-precision scalars.
25#[derive(Clone, Copy, Debug, Default)]
26pub struct ScalarInterpolator;
27
28impl IInterpolator<f32> for ScalarInterpolator {
29    fn interpolate(&self, from: f32, to: f32, progress: f32) -> f32 {
30        from + (to - from) * progress
31    }
32}
33
34instance!(ScalarInterpolator);
35
36/// Interpolates double-precision scalars.
37#[derive(Clone, Copy, Debug, Default)]
38pub struct DoubleInterpolator;
39
40impl IInterpolator<f64> for DoubleInterpolator {
41    fn interpolate(&self, from: f64, to: f64, progress: f32) -> f64 {
42        from + (to - from) * progress as f64
43    }
44}
45
46instance!(DoubleInterpolator);
47
48/// Interpolates [`Vector2`] values.
49#[derive(Clone, Copy, Debug, Default)]
50pub struct Vector2Interpolator;
51
52impl IInterpolator<Vector2> for Vector2Interpolator {
53    fn interpolate(&self, from: Vector2, to: Vector2, progress: f32) -> Vector2 {
54        Vector2::lerp(from, to, progress)
55    }
56}
57
58instance!(Vector2Interpolator);
59
60/// Interpolates [`Vector`] values.
61#[derive(Clone, Copy, Debug, Default)]
62pub struct VectorInterpolator;
63
64impl IInterpolator<Vector> for VectorInterpolator {
65    fn interpolate(&self, from: Vector, to: Vector, progress: f32) -> Vector {
66        Vector::new(
67            DoubleInterpolator::instance().interpolate(from.x, to.x, progress),
68            DoubleInterpolator::instance().interpolate(from.y, to.y, progress),
69        )
70    }
71}
72
73instance!(VectorInterpolator);
74
75/// Interpolates [`Vector3`] values.
76#[derive(Clone, Copy, Debug, Default)]
77pub struct Vector3Interpolator;
78
79impl IInterpolator<Vector3> for Vector3Interpolator {
80    fn interpolate(&self, from: Vector3, to: Vector3, progress: f32) -> Vector3 {
81        Vector3::lerp(from, to, progress)
82    }
83}
84
85instance!(Vector3Interpolator);
86
87/// Interpolates [`Vector3D`] values.
88#[derive(Clone, Copy, Debug, Default)]
89pub struct Vector3DInterpolator;
90
91impl IInterpolator<Vector3D> for Vector3DInterpolator {
92    fn interpolate(&self, from: Vector3D, to: Vector3D, progress: f32) -> Vector3D {
93        Vector3D::new(
94            DoubleInterpolator::instance().interpolate(from.x, to.x, progress),
95            DoubleInterpolator::instance().interpolate(from.y, to.y, progress),
96            DoubleInterpolator::instance().interpolate(from.z, to.z, progress),
97        )
98    }
99}
100
101instance!(Vector3DInterpolator);
102
103/// Interpolates [`Vector4`] values.
104#[derive(Clone, Copy, Debug, Default)]
105pub struct Vector4Interpolator;
106
107impl IInterpolator<Vector4> for Vector4Interpolator {
108    fn interpolate(&self, from: Vector4, to: Vector4, progress: f32) -> Vector4 {
109        Vector4::lerp(from, to, progress)
110    }
111}
112
113instance!(Vector4Interpolator);
114
115/// Interpolates [`Quaternion`] values (a normalized linear interpolation).
116#[derive(Clone, Copy, Debug, Default)]
117pub struct QuaternionInterpolator;
118
119impl IInterpolator<Quaternion> for QuaternionInterpolator {
120    fn interpolate(&self, from: Quaternion, to: Quaternion, progress: f32) -> Quaternion {
121        Quaternion::lerp(from, to, progress)
122    }
123}
124
125instance!(QuaternionInterpolator);
126
127/// Interpolates [`Color`] values per ARGB channel.
128#[derive(Clone, Copy, Debug, Default)]
129pub struct ColorInterpolator;
130
131impl ColorInterpolator {
132    fn lerp(a: f32, b: f32, p: f32) -> u8 {
133        // The conversion truncates; the value is already within the byte range
134        // (a NaN becomes zero).
135        ((a * (1.0 - p)) + (b * p)).clamp(0.0, 255.0) as u8
136    }
137
138    /// Interpolates each of the alpha, red, green and blue channels linearly.
139    pub fn lerp_rgb(to: Color, from: Color, progress: f32) -> Color {
140        Color::new(
141            Self::lerp(to.a as f32, from.a as f32, progress),
142            Self::lerp(to.r as f32, from.r as f32, progress),
143            Self::lerp(to.g as f32, from.g as f32, progress),
144            Self::lerp(to.b as f32, from.b as f32, progress),
145        )
146    }
147}
148
149impl IInterpolator<Color> for ColorInterpolator {
150    fn interpolate(&self, from: Color, to: Color, progress: f32) -> Color {
151        Self::lerp_rgb(from, to, progress)
152    }
153}
154
155instance!(ColorInterpolator);
156
157/// "Interpolates" booleans: the value switches when the animation completes.
158#[derive(Clone, Copy, Debug, Default)]
159pub struct BooleanInterpolator;
160
161impl IInterpolator<bool> for BooleanInterpolator {
162    fn interpolate(&self, from: bool, to: bool, progress: f32) -> bool {
163        if progress >= 1.0 {
164            to
165        } else {
166            from
167        }
168    }
169}
170
171instance!(BooleanInterpolator);
172
173/// Interpolates [`RelativePoint`] values of the same unit; points of
174/// different units switch halfway.
175#[derive(Clone, Copy, Debug, Default)]
176pub struct RelativePointInterpolator;
177
178impl IInterpolator<RelativePoint> for RelativePointInterpolator {
179    fn interpolate(&self, from: RelativePoint, to: RelativePoint, progress: f32) -> RelativePoint {
180        if from.unit != to.unit {
181            return if progress >= 0.5 { to } else { from };
182        }
183
184        let x = DoubleInterpolator::instance().interpolate(from.point.x, to.point.x, progress);
185        let y = DoubleInterpolator::instance().interpolate(from.point.y, to.point.y, progress);
186        RelativePoint::new(x, y, from.unit)
187    }
188}
189
190instance!(RelativePointInterpolator);
191
192/// Interpolates [`RelativeScalar`] values of the same unit; scalars of
193/// different units switch halfway.
194#[derive(Clone, Copy, Debug, Default)]
195pub struct RelativeScalarInterpolator;
196
197impl IInterpolator<RelativeScalar> for RelativeScalarInterpolator {
198    fn interpolate(&self, from: RelativeScalar, to: RelativeScalar, progress: f32) -> RelativeScalar {
199        if from.unit != to.unit {
200            return if progress >= 0.5 { to } else { from };
201        }
202        RelativeScalar::new(from.scalar + (to.scalar - from.scalar) * progress as f64, from.unit)
203    }
204}
205
206instance!(RelativeScalarInterpolator);
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211    use crate::RelativeUnit;
212
213    #[test]
214    fn scalars() {
215        assert_eq!(ScalarInterpolator::instance().interpolate(2.0, 4.0, 0.25), 2.5);
216        assert_eq!(ScalarInterpolator::instance().interpolate(2.0, 4.0, 1.5), 5.0);
217        assert_eq!(DoubleInterpolator::instance().interpolate(2.0, 4.0, 0.25), 2.5);
218        assert_eq!(DoubleInterpolator::instance().interpolate(4.0, 2.0, 0.5), 3.0);
219    }
220
221    #[test]
222    fn vectors() {
223        assert_eq!(
224            Vector2Interpolator::instance().interpolate(Vector2::new(0.0, 2.0), Vector2::new(4.0, 4.0), 0.5),
225            Vector2::new(2.0, 3.0)
226        );
227        assert_eq!(
228            VectorInterpolator::instance().interpolate(Vector::new(0.0, 2.0), Vector::new(4.0, 4.0), 0.5),
229            Vector::new(2.0, 3.0)
230        );
231        assert_eq!(
232            Vector3Interpolator::instance().interpolate(
233                Vector3::new(0.0, 2.0, -2.0),
234                Vector3::new(4.0, 4.0, 2.0),
235                0.5
236            ),
237            Vector3::new(2.0, 3.0, 0.0)
238        );
239        assert_eq!(
240            Vector3DInterpolator::instance().interpolate(
241                Vector3D::new(0.0, 2.0, -2.0),
242                Vector3D::new(4.0, 4.0, 2.0),
243                0.5
244            ),
245            Vector3D::new(2.0, 3.0, 0.0)
246        );
247        assert_eq!(
248            Vector4Interpolator::instance().interpolate(
249                Vector4::new(0.0, 2.0, -2.0, 1.0),
250                Vector4::new(4.0, 4.0, 2.0, 1.0),
251                0.5
252            ),
253            Vector4::new(2.0, 3.0, 0.0, 1.0)
254        );
255    }
256
257    #[test]
258    fn quaternion_is_a_normalized_lerp() {
259        let from = Quaternion::create_from_axis_angle(Vector3::UNIT_Z, 0.0);
260        let to = Quaternion::create_from_axis_angle(Vector3::UNIT_Z, 1.0);
261        let actual = QuaternionInterpolator::instance().interpolate(from, to, 0.5);
262        assert_eq!(actual, Quaternion::lerp(from, to, 0.5));
263        assert!((actual.length() - 1.0).abs() < 1e-6);
264    }
265
266    #[test]
267    fn color_channels_are_interpolated_and_truncated() {
268        let from = Color::from_argb(255, 10, 0, 255);
269        let to = Color::from_argb(0, 200, 1, 255);
270        let i = ColorInterpolator::instance();
271        // (10 + 200) / 2 = 105; 127.5 and 0.5 truncate.
272        assert_eq!(i.interpolate(from, to, 0.5), Color::from_argb(127, 105, 0, 255));
273        assert_eq!(i.interpolate(from, to, 0.0), from);
274        assert_eq!(i.interpolate(from, to, 1.0), to);
275        // Out of range progress is clamped per channel.
276        assert_eq!(i.interpolate(from, to, 2.0), Color::from_argb(0, 255, 2, 255));
277        assert_eq!(i.interpolate(from, to, -1.0), Color::from_argb(255, 0, 0, 255));
278        assert_eq!(ColorInterpolator::lerp_rgb(from, to, 0.5), i.interpolate(from, to, 0.5));
279    }
280
281    #[test]
282    fn boolean_switches_at_the_end() {
283        let i = BooleanInterpolator::instance();
284        assert!(!i.interpolate(false, true, 0.0));
285        assert!(!i.interpolate(false, true, 0.999));
286        assert!(i.interpolate(false, true, 1.0));
287        assert!(i.interpolate(true, false, 0.5));
288    }
289
290    #[test]
291    fn relative_values() {
292        let p = RelativePointInterpolator::instance();
293        let from = RelativePoint::new(0.0, 1.0, RelativeUnit::Relative);
294        let to = RelativePoint::new(1.0, 0.0, RelativeUnit::Relative);
295        assert_eq!(
296            p.interpolate(from, to, 0.25),
297            RelativePoint::new(0.25, 0.75, RelativeUnit::Relative)
298        );
299        let absolute = RelativePoint::new(10.0, 20.0, RelativeUnit::Absolute);
300        assert_eq!(p.interpolate(from, absolute, 0.25), from);
301        assert_eq!(p.interpolate(from, absolute, 0.5), absolute);
302
303        let s = RelativeScalarInterpolator::instance();
304        let from = RelativeScalar::new(0.0, RelativeUnit::Relative);
305        let to = RelativeScalar::new(1.0, RelativeUnit::Relative);
306        assert_eq!(
307            s.interpolate(from, to, 0.25),
308            RelativeScalar::new(0.25, RelativeUnit::Relative)
309        );
310        let absolute = RelativeScalar::new(10.0, RelativeUnit::Absolute);
311        assert_eq!(s.interpolate(from, absolute, 0.25), from);
312        assert_eq!(s.interpolate(from, absolute, 0.75), absolute);
313    }
314}