graphics_shapes/
lerp.rs

1use crate::Coord;
2
3pub trait Lerp {
4    /// calculate the point at `percent` between `self` and `end`
5    ///
6    /// # Usage
7    /// ```rust
8    ///# use graphics_shapes::lerp::Lerp;
9    /// assert_eq!(10.lerp(20, 0.5), 15);
10    /// assert_eq!(10.lerp(20, 0.1), 11);
11    /// ```
12    ///
13    /// internally the values are cast as f32 and rounded before being returned
14    #[must_use]
15    fn lerp(self, end: Self, percent: f32) -> Self;
16
17    /// calculate the percent for `point` between `self` and `end`
18    ///
19    /// # Usage
20    /// ```rust
21    ///# use graphics_shapes::lerp::Lerp;
22    /// assert_eq!(10.inv_lerp(20, 15), 0.5);
23    /// assert_eq!(10.inv_lerp(20, 11), 0.1);
24    /// ```
25    ///
26    /// internally the values are cast as f32 and rounded before being returned
27    #[must_use]
28    fn inv_lerp(self, end: Self, point: Self) -> f32;
29}
30
31/// This method has to be separate and named differently because
32/// f32::lerp already exists but is unstable
33///
34/// see [f32::lerp]
35///
36/// calculate the point at `percent` between `self` and `end`
37///
38/// # Usage
39/// ```rust
40///# use graphics_shapes::lerp::flerp;
41/// assert_eq!(flerp(10.0, 20.0, 0.5), 15.0);
42/// assert_eq!(flerp(10.0, 20.0, 0.1), 11.0);
43/// ```
44#[inline]
45#[must_use]
46pub fn flerp(start: f32, end: f32, percent: f32) -> f32 {
47    start + ((end - start) * percent)
48}
49
50/// calculate the percent for `point` between `self` and `end`
51///
52/// # Usage
53/// ```rust
54///# use graphics_shapes::lerp::inv_flerp;
55/// assert_eq!(inv_flerp(10.0, 20.0, 15.0), 0.5);
56/// assert_eq!(inv_flerp(10.0, 20.0, 11.0), 0.1);
57/// ```
58#[inline]
59#[must_use]
60pub fn inv_flerp(start: f32, end: f32, point: f32) -> f32 {
61    if point == start {
62        return 0.0;
63    }
64    if point == end {
65        return 1.0;
66    }
67    (point - start) / (end - start)
68}
69
70macro_rules! impl_lerp {
71    ($num_type: ty) => {
72        impl Lerp for $num_type {
73            #[inline]
74            fn lerp(self, end: $num_type, percent: f32) -> $num_type {
75                let start = self as f32;
76                let end = end as f32;
77                flerp(start, end, percent).round() as $num_type
78            }
79
80            #[inline]
81            fn inv_lerp(self, end: $num_type, point: $num_type) -> f32 {
82                let start = self as f32;
83                let end = end as f32;
84                let point = point as f32;
85                inv_flerp(start, end, point)
86            }
87        }
88    };
89}
90
91impl_lerp!(u8);
92impl_lerp!(i8);
93impl_lerp!(u16);
94impl_lerp!(i16);
95impl_lerp!(u32);
96impl_lerp!(i32);
97impl_lerp!(u64);
98impl_lerp!(i64);
99impl_lerp!(u128);
100impl_lerp!(i128);
101impl_lerp!(usize);
102impl_lerp!(isize);
103
104impl Lerp for Coord {
105    #[inline]
106    fn lerp(self, end: Coord, percent: f32) -> Coord {
107        Coord {
108            x: self.x.lerp(end.x, percent),
109            y: self.y.lerp(end.y, percent),
110        }
111    }
112
113    #[inline]
114    fn inv_lerp(self, end: Coord, point: Coord) -> f32 {
115        (self.x.inv_lerp(end.x, point.x) + self.y.inv_lerp(end.y, point.y)) / 2.0
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use super::*;
122
123    #[test]
124    fn isize_simple() {
125        assert_eq!(0_isize.lerp(10, 0.), 0);
126        assert_eq!(0_isize.lerp(10, 0.5), 5);
127        assert_eq!(0_isize.lerp(10, 1.), 10);
128        assert_eq!(0_isize.lerp(10, 0.2), 2);
129
130        assert_eq!(5_isize.lerp(10, 0.), 5);
131        assert_eq!(5_isize.lerp(10, 1.), 10);
132
133        assert_eq!(785_isize.lerp(787, 0.), 785);
134        assert_eq!(785_isize.lerp(787, 0.5), 786);
135        assert_eq!(785_isize.lerp(787, 1.), 787);
136
137        assert_eq!(21_isize.lerp(21, 0.), 21);
138        assert_eq!(21_isize.lerp(21, 0.5), 21);
139        assert_eq!(21_isize.lerp(21, 1.), 21);
140
141        assert_eq!(10_isize.lerp(1, 1.), 1);
142        assert_eq!(10_isize.lerp(1, 0.5), 6);
143        assert_eq!(10_isize.lerp(1, 0.), 10);
144
145        assert_eq!((-5_isize).lerp(5, 1.), 5);
146        assert_eq!((-5_isize).lerp(5, 0.5), 0);
147        assert_eq!((-5_isize).lerp(5, 0.), -5);
148
149        assert_eq!(5_isize.lerp(-5, 1.), -5);
150        assert_eq!(5_isize.lerp(-5, 0.5), 0);
151        assert_eq!(5_isize.lerp(-5, 0.), 5);
152
153        assert_eq!(0_isize.inv_lerp(10, 0), 0.);
154        assert_eq!(0_isize.inv_lerp(10, 5), 0.5);
155        assert_eq!(0_isize.inv_lerp(10, 10), 1.);
156        assert_eq!(0_isize.inv_lerp(10, 2), 0.2);
157
158        assert_eq!(5_isize.inv_lerp(10, 5), 0.);
159        assert_eq!(5_isize.inv_lerp(10, 10), 1.);
160
161        assert_eq!(785_isize.inv_lerp(787, 785), 0.);
162        assert_eq!(785_isize.inv_lerp(787, 786), 0.5);
163        assert_eq!(785_isize.inv_lerp(787, 787), 1.);
164
165        assert_eq!(21_isize.inv_lerp(21, 21), 0.);
166
167        assert_eq!(10_isize.inv_lerp(1, 1), 1.);
168        assert_eq!(10_isize.inv_lerp(1, 6), 0.44444445);
169        assert_eq!(10_isize.inv_lerp(1, 10), 0.);
170
171        assert_eq!((-5_isize).inv_lerp(5, 5), 1.);
172        assert_eq!((-5_isize).inv_lerp(5, 0), 0.5);
173        assert_eq!((-5_isize).inv_lerp(5, -5), 0.);
174
175        assert_eq!(5_isize.inv_lerp(-5, -5), 1.);
176        assert_eq!(5_isize.inv_lerp(-5, 0), 0.5);
177        assert_eq!(5_isize.inv_lerp(-5, 5), 0.);
178    }
179
180    #[test]
181    fn point_simple() {
182        let start1 = Coord { x: 0, y: 0 };
183        let end1 = Coord { x: 10, y: 10 };
184
185        let start2 = Coord { x: -1, y: -1 };
186        let end2 = Coord { x: 1, y: 1 };
187
188        let start3 = Coord { x: 1, y: -1 };
189        let end3 = Coord { x: -1, y: 1 };
190
191        assert_eq!(start1.lerp(end1, 0.), Coord { x: 0, y: 0 });
192        assert_eq!(start1.lerp(end1, 0.5), Coord { x: 5, y: 5 });
193        assert_eq!(start1.lerp(end1, 1.), Coord { x: 10, y: 10 });
194
195        assert_eq!(end1.lerp(start1, 0.), Coord { x: 10, y: 10 });
196        assert_eq!(end1.lerp(start1, 0.5), Coord { x: 5, y: 5 });
197        assert_eq!(end1.lerp(start1, 1.), Coord { x: 0, y: 0 });
198
199        assert_eq!(start2.lerp(end2, 0.), Coord { x: -1, y: -1 });
200        assert_eq!(start2.lerp(end2, 0.5), Coord { x: 0, y: 0 });
201        assert_eq!(start2.lerp(end2, 1.), Coord { x: 1, y: 1 });
202
203        assert_eq!(end2.lerp(start2, 0.), Coord { x: 1, y: 1 });
204        assert_eq!(end2.lerp(start2, 0.5), Coord { x: 0, y: 0 });
205        assert_eq!(end2.lerp(start2, 1.), Coord { x: -1, y: -1 });
206
207        assert_eq!(start3.lerp(end3, 0.), Coord { x: 1, y: -1 });
208        assert_eq!(start3.lerp(end3, 0.5), Coord { x: 0, y: 0 });
209        assert_eq!(start3.lerp(end3, 1.), Coord { x: -1, y: 1 });
210
211        assert_eq!(end3.lerp(start3, 0.), Coord { x: -1, y: 1 });
212        assert_eq!(end3.lerp(start3, 0.5), Coord { x: 0, y: 0 });
213        assert_eq!(end3.lerp(start3, 1.), Coord { x: 1, y: -1 });
214
215        assert_eq!(start1.lerp(end1, 2.), Coord { x: 20, y: 20 });
216        assert_eq!(start1.lerp(end1, -1.), Coord { x: -10, y: -10 });
217
218        assert_eq!(start1.inv_lerp(end1, Coord { x: 0, y: 0 }), 0.);
219        assert_eq!(start1.inv_lerp(end1, Coord { x: 5, y: 5 }), 0.5);
220        assert_eq!(start1.inv_lerp(end1, Coord { x: 10, y: 10 }), 1.);
221
222        assert_eq!(end1.inv_lerp(start1, Coord { x: 10, y: 10 }), 0.);
223        assert_eq!(end1.inv_lerp(start1, Coord { x: 5, y: 5 }), 0.5);
224        assert_eq!(end1.inv_lerp(start1, Coord { x: 0, y: 0 }), 1.);
225
226        assert_eq!(start2.inv_lerp(end2, Coord { x: -1, y: -1 }), 0.);
227        assert_eq!(start2.inv_lerp(end2, Coord { x: 0, y: 0 }), 0.5);
228        assert_eq!(start2.inv_lerp(end2, Coord { x: 1, y: 1 }), 1.);
229
230        assert_eq!(end2.inv_lerp(start2, Coord { x: 1, y: 1 }), 0.);
231        assert_eq!(end2.inv_lerp(start2, Coord { x: 0, y: 0 }), 0.5);
232        assert_eq!(end2.inv_lerp(start2, Coord { x: -1, y: -1 }), 1.);
233
234        assert_eq!(start3.inv_lerp(end3, Coord { x: 1, y: -1 }), 0.);
235        assert_eq!(start3.inv_lerp(end3, Coord { x: 0, y: 0 }), 0.5);
236        assert_eq!(start3.inv_lerp(end3, Coord { x: -1, y: 1 }), 1.);
237
238        assert_eq!(end3.inv_lerp(start3, Coord { x: -1, y: 1 }), 0.);
239        assert_eq!(end3.inv_lerp(start3, Coord { x: 0, y: 0 }), 0.5);
240        assert_eq!(end3.inv_lerp(start3, Coord { x: 1, y: -1 }), 1.);
241
242        assert_eq!(start1.inv_lerp(end1, Coord { x: 20, y: 20 }), 2.);
243        assert_eq!(start1.inv_lerp(end1, Coord { x: -10, y: -10 }), -1.);
244    }
245}