1use crate::{
3 Bounds, Corners, DevicePixels, Edges, Percentage, Pixels, Point, Radians, Rems, Size,
4 colors::Colors,
5};
6use palette::rgb::Rgba;
7use std::{
8 fmt::Debug,
9 ops::{Add, Mul, Sub},
10};
11
12pub trait Lerp<Output = Self>
14where
15 Self: Sized,
16{
17 fn lerp(&self, to: &Self, delta: f32) -> Output;
19}
20
21impl Lerp<f32> for bool {
22 fn lerp(&self, to: &Self, delta: f32) -> f32 {
23 lerp(*self as u8 as f32, *to as u8 as f32, delta)
24 }
25}
26
27macro_rules! float_lerps {
28 ( $( $ty:ty ),+ ) => {
29 $(
30 impl Lerp for $ty {
31 fn lerp(&self, to: &Self, delta: f32) -> Self {
32 lerp(*self, *to, delta as $ty)
33 }
34 }
35 )+
36 };
37}
38
39float_lerps!(f32, f64);
40
41macro_rules! int_lerps {
42 ( $( $ty:ident as $ty_into:ident ),+ ) => {
43 $(
44 impl Lerp for $ty {
45 fn lerp(&self, to: &Self, delta: f32) -> Self {
46 lerp(*self as $ty_into, *to as $ty_into, delta as $ty_into) as $ty
47 }
48 }
49 )+
50 };
51}
52
53int_lerps!(
54 usize as f32,
55 u8 as f32,
56 u16 as f32,
57 u32 as f32,
58 u64 as f64,
59 u128 as f64,
60 isize as f32,
61 i8 as f32,
62 i16 as f32,
63 i32 as f32,
64 i64 as f64,
65 i128 as f64
66);
67
68macro_rules! struct_lerps {
69 ( $( $ty:ident $( < $gen:ident > )? { $( $n:ident ),+ } ),+ $(,)? ) => {
70 $(
71 impl$(<$gen: Lerp + Clone + Debug + Default + PartialEq>)? Lerp for $ty$(<$gen>)? {
72 fn lerp(&self, to: &Self, delta: f32) -> Self {
73 $ty$(::<$gen>)? {
74 $(
75 $n: self.$n.lerp(&to.$n, delta)
76 ),+
77 }
78 }
79 }
80 )+
81 };
82}
83
84struct_lerps!(
85 Point<T> { x, y },
86 Size<T> { width, height },
87 Edges<T> { top, right, bottom, left },
88 Corners<T> { top_left, top_right, bottom_right, bottom_left },
89 Bounds<T> { origin, size },
90 Rgba { color, alpha },
91 Colors { text, selected_text, background, disabled, selected, border, separator, container }
92);
93
94impl Lerp for palette::rgb::Rgb {
95 fn lerp(&self, to: &Self, delta: f32) -> Self {
96 Self::new(
97 self.red.lerp(&to.red, delta),
98 self.green.lerp(&to.green, delta),
99 self.blue.lerp(&to.blue, delta),
100 )
101 }
102}
103
104macro_rules! tuple_struct_lerps {
105 ( $( $ty:ident ( $n:ty ) ),+ ) => {
106 $(
107 impl Lerp for $ty {
108 fn lerp(&self, to: &Self, delta: f32) -> Self {
109 $ty(self.0.lerp(&to.0, delta))
110 }
111 }
112 )+
113 };
114}
115
116tuple_struct_lerps!(
117 Radians(f32),
118 Percentage(f32),
119 DevicePixels(i32),
120 Rems(f32),
121 Pixels(f32)
122);
123
124fn lerp<T>(from: T, to: T, alpha: T) -> T
125where
126 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
127{
128 from + (to - from) * alpha
129}
130
131#[cfg(all(test, feature = "test-support"))]
132mod tests {
133 use super::*;
134 use crate::px;
135
136 #[test]
137 fn test_f32_lerp() {
138 let start = 0.0_f32;
139 let end = 100.0_f32;
140
141 assert_eq!(start.lerp(&end, 0.0), 0.0);
142 assert_eq!(start.lerp(&end, 0.5), 50.0);
143 assert_eq!(start.lerp(&end, 1.0), 100.0);
144 assert_eq!(start.lerp(&end, 0.25), 25.0);
145 }
146
147 #[test]
148 fn test_f64_lerp() {
149 let start = 0.0_f64;
150 let end = 100.0_f64;
151
152 assert_eq!(start.lerp(&end, 0.0), 0.0);
153 assert_eq!(start.lerp(&end, 0.5), 50.0);
154 assert_eq!(start.lerp(&end, 1.0), 100.0);
155 }
156
157 #[test]
158 fn test_f32_lerp_negative_values() {
159 let start = -50.0_f32;
160 let end = 50.0_f32;
161
162 assert_eq!(start.lerp(&end, 0.0), -50.0);
163 assert_eq!(start.lerp(&end, 0.5), 0.0);
164 assert_eq!(start.lerp(&end, 1.0), 50.0);
165 }
166
167 #[test]
168 fn test_integer_lerp() {
169 assert_eq!(0_i32.lerp(&100_i32, 0.0), 0);
171 assert_eq!(0_i32.lerp(&100_i32, 0.5), 50);
172 assert_eq!(0_i32.lerp(&100_i32, 1.0), 100);
173
174 assert_eq!(0_u8.lerp(&100_u8, 0.5), 50);
176
177 assert_eq!(0_u16.lerp(&1000_u16, 0.5), 500);
179
180 assert_eq!(0_u32.lerp(&10000_u32, 0.5), 5000);
182
183 assert_eq!(0_u64.lerp(&100000_u64, 0.5), 50000);
185
186 assert_eq!((-50000_i64).lerp(&50000_i64, 0.5), 0);
188
189 assert_eq!(0_usize.lerp(&100_usize, 0.5), 50);
191
192 assert_eq!((-100_isize).lerp(&100_isize, 0.5), 0);
194 }
195
196 #[test]
197 fn test_point_lerp() {
198 let start: Point<f32> = Point { x: 0.0, y: 0.0 };
199 let end: Point<f32> = Point { x: 100.0, y: 200.0 };
200
201 let mid = start.lerp(&end, 0.5);
202 assert_eq!(mid.x, 50.0);
203 assert_eq!(mid.y, 100.0);
204
205 let at_start = start.lerp(&end, 0.0);
206 assert_eq!(at_start.x, 0.0);
207 assert_eq!(at_start.y, 0.0);
208
209 let at_end = start.lerp(&end, 1.0);
210 assert_eq!(at_end.x, 100.0);
211 assert_eq!(at_end.y, 200.0);
212 }
213
214 #[test]
215 fn test_size_lerp() {
216 let start: Size<f32> = Size {
217 width: 10.0,
218 height: 20.0,
219 };
220 let end: Size<f32> = Size {
221 width: 110.0,
222 height: 220.0,
223 };
224
225 let mid = start.lerp(&end, 0.5);
226 assert_eq!(mid.width, 60.0);
227 assert_eq!(mid.height, 120.0);
228 }
229
230 #[test]
231 fn test_edges_lerp() {
232 let start: Edges<f32> = Edges {
233 top: 0.0,
234 right: 0.0,
235 bottom: 0.0,
236 left: 0.0,
237 };
238 let end: Edges<f32> = Edges {
239 top: 10.0,
240 right: 20.0,
241 bottom: 30.0,
242 left: 40.0,
243 };
244
245 let mid = start.lerp(&end, 0.5);
246 assert_eq!(mid.top, 5.0);
247 assert_eq!(mid.right, 10.0);
248 assert_eq!(mid.bottom, 15.0);
249 assert_eq!(mid.left, 20.0);
250 }
251
252 #[test]
253 fn test_corners_lerp() {
254 let start: Corners<f32> = Corners {
255 top_left: 0.0,
256 top_right: 0.0,
257 bottom_right: 0.0,
258 bottom_left: 0.0,
259 };
260 let end: Corners<f32> = Corners {
261 top_left: 4.0,
262 top_right: 8.0,
263 bottom_right: 12.0,
264 bottom_left: 16.0,
265 };
266
267 let mid = start.lerp(&end, 0.5);
268 assert_eq!(mid.top_left, 2.0);
269 assert_eq!(mid.top_right, 4.0);
270 assert_eq!(mid.bottom_right, 6.0);
271 assert_eq!(mid.bottom_left, 8.0);
272 }
273
274 #[test]
275 fn test_bounds_lerp() {
276 let start: Bounds<f32> = Bounds {
277 origin: Point { x: 0.0, y: 0.0 },
278 size: Size {
279 width: 100.0,
280 height: 100.0,
281 },
282 };
283 let end: Bounds<f32> = Bounds {
284 origin: Point { x: 50.0, y: 50.0 },
285 size: Size {
286 width: 200.0,
287 height: 200.0,
288 },
289 };
290
291 let mid = start.lerp(&end, 0.5);
292 assert_eq!(mid.origin.x, 25.0);
293 assert_eq!(mid.origin.y, 25.0);
294 assert_eq!(mid.size.width, 150.0);
295 assert_eq!(mid.size.height, 150.0);
296 }
297
298 #[test]
299 fn test_rgba_lerp() {
300 let start = Rgba::new(0., 0., 0., 1.);
301 let end = Rgba::new(1., 1., 1., 1.);
302
303 let mid = start.lerp(&end, 0.5);
304 assert_eq!(mid.red, 0.5);
305 assert_eq!(mid.green, 0.5);
306 assert_eq!(mid.blue, 0.5);
307 assert_eq!(mid.alpha, 1.0);
308 }
309
310 #[test]
311 fn test_rgba_lerp_with_alpha() {
312 let start = Rgba::new(1., 0., 0., 0.);
313 let end = Rgba::new(0., 0., 1., 1.);
314
315 let mid = start.lerp(&end, 0.5);
316 assert_eq!(mid.red, 0.5);
317 assert_eq!(mid.green, 0.0);
318 assert_eq!(mid.blue, 0.5);
319 assert_eq!(mid.alpha, 0.5);
320 }
321
322 #[test]
323 fn test_pixels_lerp() {
324 let start = px(0.0);
325 let end = px(100.0);
326
327 let mid = start.lerp(&end, 0.5);
328 assert_eq!(mid, px(50.0));
329
330 let at_start = start.lerp(&end, 0.0);
331 assert_eq!(at_start, px(0.0));
332
333 let at_end = start.lerp(&end, 1.0);
334 assert_eq!(at_end, px(100.0));
335 }
336
337 #[test]
338 fn test_rems_lerp() {
339 let start = Rems(0.0);
340 let end = Rems(2.0);
341
342 let mid = start.lerp(&end, 0.5);
343 assert_eq!(mid.0, 1.0);
344 }
345
346 #[test]
347 fn test_device_pixels_lerp() {
348 let start = DevicePixels(0);
349 let end = DevicePixels(100);
350
351 let mid = start.lerp(&end, 0.5);
352 assert_eq!(mid.0, 50);
353 }
354
355 #[test]
356 fn test_percentage_lerp() {
357 let start = Percentage(0.0);
358 let end = Percentage(100.0);
359
360 let mid = start.lerp(&end, 0.5);
361 assert_eq!(mid.0, 50.0);
362 }
363
364 #[test]
365 fn test_radians_lerp() {
366 let start = Radians(0.0);
367 let end = Radians(std::f32::consts::PI);
368
369 let mid = start.lerp(&end, 0.5);
370 assert!((mid.0 - std::f32::consts::FRAC_PI_2).abs() < 0.0001);
371 }
372}