Skip to main content

ff_filter/animation/
lerp.rs

1/// A value that supports linear component-wise interpolation.
2///
3/// `t = 0.0` returns a clone of `a`; `t = 1.0` returns a clone of `b`.
4///
5/// Implementations for `(f64, f64)` and `(f64, f64, f64)` are added
6/// in issue #351.
7pub trait Lerp: Clone {
8    /// Linearly interpolates between `a` and `b` by the factor `t`.
9    fn lerp(a: &Self, b: &Self, t: f64) -> Self;
10}
11
12impl Lerp for f64 {
13    fn lerp(a: &Self, b: &Self, t: f64) -> Self {
14        a + (b - a) * t
15    }
16}
17
18impl Lerp for (f64, f64) {
19    fn lerp(a: &Self, b: &Self, t: f64) -> Self {
20        (f64::lerp(&a.0, &b.0, t), f64::lerp(&a.1, &b.1, t))
21    }
22}
23
24impl Lerp for (f64, f64, f64) {
25    fn lerp(a: &Self, b: &Self, t: f64) -> Self {
26        (
27            f64::lerp(&a.0, &b.0, t),
28            f64::lerp(&a.1, &b.1, t),
29            f64::lerp(&a.2, &b.2, t),
30        )
31    }
32}
33
34// ── Tests ─────────────────────────────────────────────────────────────────────
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn lerp_tuple2d_should_interpolate_both_components() {
42        let a = (0.0_f64, 10.0_f64);
43        let b = (100.0_f64, 50.0_f64);
44
45        let mid = <(f64, f64)>::lerp(&a, &b, 0.5);
46        assert!(
47            (mid.0 - 50.0).abs() < f64::EPSILON,
48            "x: expected 50.0, got {}",
49            mid.0
50        );
51        assert!(
52            (mid.1 - 30.0).abs() < f64::EPSILON,
53            "y: expected 30.0, got {}",
54            mid.1
55        );
56
57        let start = <(f64, f64)>::lerp(&a, &b, 0.0);
58        assert!((start.0 - 0.0).abs() < f64::EPSILON);
59        assert!((start.1 - 10.0).abs() < f64::EPSILON);
60
61        let end = <(f64, f64)>::lerp(&a, &b, 1.0);
62        assert!((end.0 - 100.0).abs() < f64::EPSILON);
63        assert!((end.1 - 50.0).abs() < f64::EPSILON);
64    }
65
66    #[test]
67    fn lerp_tuple3d_should_interpolate_all_components() {
68        let a = (0.0_f64, 0.0_f64, 0.0_f64);
69        let b = (255.0_f64, 128.0_f64, 64.0_f64);
70
71        let mid = <(f64, f64, f64)>::lerp(&a, &b, 0.5);
72        assert!(
73            (mid.0 - 127.5).abs() < f64::EPSILON,
74            "r: expected 127.5, got {}",
75            mid.0
76        );
77        assert!(
78            (mid.1 - 64.0).abs() < f64::EPSILON,
79            "g: expected 64.0, got {}",
80            mid.1
81        );
82        assert!(
83            (mid.2 - 32.0).abs() < f64::EPSILON,
84            "b: expected 32.0, got {}",
85            mid.2
86        );
87
88        let start = <(f64, f64, f64)>::lerp(&a, &b, 0.0);
89        assert!((start.0).abs() < f64::EPSILON);
90        assert!((start.1).abs() < f64::EPSILON);
91        assert!((start.2).abs() < f64::EPSILON);
92
93        let end = <(f64, f64, f64)>::lerp(&a, &b, 1.0);
94        assert!((end.0 - 255.0).abs() < f64::EPSILON);
95        assert!((end.1 - 128.0).abs() < f64::EPSILON);
96        assert!((end.2 - 64.0).abs() < f64::EPSILON);
97    }
98}