1use crate::media::{Color, HslColor};
4use crate::numerics::{Quaternion, Vector2, Vector3, Vector4};
5use crate::{RelativePoint, RelativeScalar, Vector, Vector3D};
6use std::any::Any;
7
8pub trait IInterpolator<T> {
10 fn interpolate(&self, from: T, to: T, progress: f32) -> T;
11}
12
13macro_rules! instance {
14 ($ty:ident) => {
15 impl $ty {
16 pub fn instance() -> &'static $ty {
18 static INSTANCE: $ty = $ty;
19 &INSTANCE
20 }
21 }
22 };
23}
24
25#[derive(Clone, Copy, Debug, Default)]
27pub struct ScalarInterpolator;
28
29impl IInterpolator<f32> for ScalarInterpolator {
30 fn interpolate(&self, from: f32, to: f32, progress: f32) -> f32 {
31 from + (to - from) * progress
32 }
33}
34
35instance!(ScalarInterpolator);
36
37#[derive(Clone, Copy, Debug, Default)]
39pub struct DoubleInterpolator;
40
41impl IInterpolator<f64> for DoubleInterpolator {
42 fn interpolate(&self, from: f64, to: f64, progress: f32) -> f64 {
43 from + (to - from) * progress as f64
44 }
45}
46
47instance!(DoubleInterpolator);
48
49#[derive(Clone, Copy, Debug, Default)]
51pub struct Vector2Interpolator;
52
53impl IInterpolator<Vector2> for Vector2Interpolator {
54 fn interpolate(&self, from: Vector2, to: Vector2, progress: f32) -> Vector2 {
55 Vector2::lerp(from, to, progress)
56 }
57}
58
59instance!(Vector2Interpolator);
60
61#[derive(Clone, Copy, Debug, Default)]
63pub struct VectorInterpolator;
64
65impl IInterpolator<Vector> for VectorInterpolator {
66 fn interpolate(&self, from: Vector, to: Vector, progress: f32) -> Vector {
67 Vector::new(
68 DoubleInterpolator::instance().interpolate(from.x, to.x, progress),
69 DoubleInterpolator::instance().interpolate(from.y, to.y, progress),
70 )
71 }
72}
73
74instance!(VectorInterpolator);
75
76#[derive(Clone, Copy, Debug, Default)]
78pub struct Vector3Interpolator;
79
80impl IInterpolator<Vector3> for Vector3Interpolator {
81 fn interpolate(&self, from: Vector3, to: Vector3, progress: f32) -> Vector3 {
82 Vector3::lerp(from, to, progress)
83 }
84}
85
86instance!(Vector3Interpolator);
87
88#[derive(Clone, Copy, Debug, Default)]
90pub struct Vector3DInterpolator;
91
92impl IInterpolator<Vector3D> for Vector3DInterpolator {
93 fn interpolate(&self, from: Vector3D, to: Vector3D, progress: f32) -> Vector3D {
94 Vector3D::new(
95 DoubleInterpolator::instance().interpolate(from.x, to.x, progress),
96 DoubleInterpolator::instance().interpolate(from.y, to.y, progress),
97 DoubleInterpolator::instance().interpolate(from.z, to.z, progress),
98 )
99 }
100}
101
102instance!(Vector3DInterpolator);
103
104#[derive(Clone, Copy, Debug, Default)]
106pub struct Vector4Interpolator;
107
108impl IInterpolator<Vector4> for Vector4Interpolator {
109 fn interpolate(&self, from: Vector4, to: Vector4, progress: f32) -> Vector4 {
110 Vector4::lerp(from, to, progress)
111 }
112}
113
114instance!(Vector4Interpolator);
115
116#[derive(Clone, Copy, Debug, Default)]
118pub struct QuaternionInterpolator;
119
120impl IInterpolator<Quaternion> for QuaternionInterpolator {
121 fn interpolate(&self, from: Quaternion, to: Quaternion, progress: f32) -> Quaternion {
122 Quaternion::lerp(from, to, progress)
123 }
124}
125
126instance!(QuaternionInterpolator);
127
128#[derive(Clone, Copy, Debug, Default)]
130pub struct ColorInterpolator;
131
132impl ColorInterpolator {
133 fn lerp(a: f32, b: f32, p: f32) -> u8 {
134 ((a * (1.0 - p)) + (b * p)).clamp(0.0, 255.0) as u8
137 }
138
139 pub fn lerp_rgb(to: Color, from: Color, progress: f32) -> Color {
141 Color::new(
142 Self::lerp(to.a as f32, from.a as f32, progress),
143 Self::lerp(to.r as f32, from.r as f32, progress),
144 Self::lerp(to.g as f32, from.g as f32, progress),
145 Self::lerp(to.b as f32, from.b as f32, progress),
146 )
147 }
148}
149
150impl IInterpolator<Color> for ColorInterpolator {
151 fn interpolate(&self, from: Color, to: Color, progress: f32) -> Color {
152 Self::lerp_rgb(from, to, progress)
153 }
154}
155
156instance!(ColorInterpolator);
157
158#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
162pub enum CompositionColorSpace {
163 #[default]
168 Auto,
169 Hsl,
171 Rgb,
173 HslLinear,
175 RgbLinear,
177}
178
179fn lerp_f64(a: f64, b: f64, p: f64) -> f64 {
181 (a * (1.0 - p)) + (b * p)
182}
183
184fn srgb_to_linear(c: f64) -> f64 {
186 if c <= 0.04045 {
187 c / 12.92
188 } else {
189 ((c + 0.055) / 1.055).powf(2.4)
190 }
191}
192
193fn linear_to_srgb(c: f64) -> f64 {
195 let c = c.clamp(0.0, 1.0);
196 if c <= 0.003_130_8 {
197 c * 12.92
198 } else {
199 1.055 * c.powf(1.0 / 2.4) - 0.055
200 }
201}
202
203fn unit_to_byte(c: f64) -> u8 {
205 (c.clamp(0.0, 1.0) * 255.0).round() as u8
206}
207
208fn rgb_to_hsl(r: f64, g: f64, b: f64) -> (f64, f64, f64) {
211 let max = r.max(g).max(b);
212 let min = r.min(g).min(b);
213 let l = (max + min) / 2.0;
214 let chroma = max - min;
215 if chroma <= 0.0 {
216 return (0.0, 0.0, l);
217 }
218 let s = chroma / (1.0 - (2.0 * l - 1.0).abs());
219 let h = if max == r {
220 60.0 * (((g - b) / chroma).rem_euclid(6.0))
221 } else if max == g {
222 60.0 * (((b - r) / chroma) + 2.0)
223 } else {
224 60.0 * (((r - g) / chroma) + 4.0)
225 };
226 (h, s.clamp(0.0, 1.0), l)
227}
228
229fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (f64, f64, f64) {
232 let h = h.rem_euclid(360.0);
233 let s = s.clamp(0.0, 1.0);
234 let l = l.clamp(0.0, 1.0);
235 let chroma = (1.0 - (2.0 * l - 1.0).abs()) * s;
236 let h1 = h / 60.0;
237 let x = chroma * (1.0 - ((h1 % 2.0) - 1.0).abs());
238 let (r1, g1, b1) = if h1 < 1.0 {
239 (chroma, x, 0.0)
240 } else if h1 < 2.0 {
241 (x, chroma, 0.0)
242 } else if h1 < 3.0 {
243 (0.0, chroma, x)
244 } else if h1 < 4.0 {
245 (0.0, x, chroma)
246 } else if h1 < 5.0 {
247 (x, 0.0, chroma)
248 } else {
249 (chroma, 0.0, x)
250 };
251 let m = l - chroma / 2.0;
252 (r1 + m, g1 + m, b1 + m)
253}
254
255fn hue_path(from_hue: f64, from_saturation: f64, to_hue: f64, to_saturation: f64) -> (f64, f64) {
259 let (from_hue, to_hue) = match (from_saturation == 0.0, to_saturation == 0.0) {
260 (true, false) => (to_hue, to_hue),
261 (false, true) => (from_hue, from_hue),
262 _ => (from_hue, to_hue),
263 };
264 let mut delta = to_hue - from_hue;
265 if delta > 180.0 {
266 delta -= 360.0;
267 } else if delta < -180.0 {
268 delta += 360.0;
269 }
270 (from_hue, delta)
271}
272
273impl ColorInterpolator {
274 pub fn lerp_hsl(from: Color, to: Color, progress: f32) -> Color {
286 let a = from.to_hsl();
287 let b = to.to_hsl();
288 let p = progress as f64;
289 let (from_hue, delta) = hue_path(a.h, a.s, b.h, b.s);
290 HslColor::hsl_to_rgb(
291 from_hue + delta * p,
292 lerp_f64(a.s, b.s, p),
293 lerp_f64(a.l, b.l, p),
294 lerp_f64(a.a, b.a, p),
295 )
296 }
297
298 pub fn lerp_rgb_linear(from: Color, to: Color, progress: f32) -> Color {
303 let p = progress as f64;
304 let channel = |a: u8, b: u8| {
305 let a = srgb_to_linear(a as f64 / 255.0);
306 let b = srgb_to_linear(b as f64 / 255.0);
307 unit_to_byte(linear_to_srgb(lerp_f64(a, b, p)))
308 };
309 let alpha = unit_to_byte(lerp_f64(from.a as f64 / 255.0, to.a as f64 / 255.0, p));
310 Color::new(alpha, channel(from.r, to.r), channel(from.g, to.g), channel(from.b, to.b))
311 }
312
313 pub fn lerp_hsl_linear(from: Color, to: Color, progress: f32) -> Color {
318 let p = progress as f64;
319 let linear = |c: Color| {
320 rgb_to_hsl(
321 srgb_to_linear(c.r as f64 / 255.0),
322 srgb_to_linear(c.g as f64 / 255.0),
323 srgb_to_linear(c.b as f64 / 255.0),
324 )
325 };
326 let (ah, as_, al) = linear(from);
327 let (bh, bs, bl) = linear(to);
328 let (from_hue, delta) = hue_path(ah, as_, bh, bs);
329 let (r, g, b) = hsl_to_rgb(from_hue + delta * p, lerp_f64(as_, bs, p), lerp_f64(al, bl, p));
330 let alpha = unit_to_byte(lerp_f64(from.a as f64 / 255.0, to.a as f64 / 255.0, p));
331 Color::new(
332 alpha,
333 unit_to_byte(linear_to_srgb(r)),
334 unit_to_byte(linear_to_srgb(g)),
335 unit_to_byte(linear_to_srgb(b)),
336 )
337 }
338}
339
340#[derive(Clone, Copy, Debug, Default)]
343pub struct ColorHslInterpolator;
344
345impl IInterpolator<Color> for ColorHslInterpolator {
346 fn interpolate(&self, from: Color, to: Color, progress: f32) -> Color {
347 ColorInterpolator::lerp_hsl(from, to, progress)
348 }
349}
350
351instance!(ColorHslInterpolator);
352
353#[derive(Clone, Copy, Debug, Default)]
356pub struct ColorRgbLinearInterpolator;
357
358impl IInterpolator<Color> for ColorRgbLinearInterpolator {
359 fn interpolate(&self, from: Color, to: Color, progress: f32) -> Color {
360 ColorInterpolator::lerp_rgb_linear(from, to, progress)
361 }
362}
363
364instance!(ColorRgbLinearInterpolator);
365
366#[derive(Clone, Copy, Debug, Default)]
369pub struct ColorHslLinearInterpolator;
370
371impl IInterpolator<Color> for ColorHslLinearInterpolator {
372 fn interpolate(&self, from: Color, to: Color, progress: f32) -> Color {
373 ColorInterpolator::lerp_hsl_linear(from, to, progress)
374 }
375}
376
377instance!(ColorHslLinearInterpolator);
378
379pub(crate) fn interpolator_for_color_space<T: 'static>(
383 default: &'static (dyn IInterpolator<T> + Sync),
384 color_space: CompositionColorSpace,
385) -> &'static (dyn IInterpolator<T> + Sync) {
386 static HSL: &(dyn IInterpolator<Color> + Sync) = &ColorHslInterpolator;
387 static RGB_LINEAR: &(dyn IInterpolator<Color> + Sync) = &ColorRgbLinearInterpolator;
388 static HSL_LINEAR: &(dyn IInterpolator<Color> + Sync) = &ColorHslLinearInterpolator;
389 let chosen: &'static dyn Any = match color_space {
390 CompositionColorSpace::Auto | CompositionColorSpace::Rgb => return default,
391 CompositionColorSpace::Hsl => &HSL,
392 CompositionColorSpace::RgbLinear => &RGB_LINEAR,
393 CompositionColorSpace::HslLinear => &HSL_LINEAR,
394 };
395 chosen.downcast_ref::<&'static (dyn IInterpolator<T> + Sync)>().copied().unwrap_or(default)
398}
399
400#[derive(Clone, Copy, Debug, Default)]
402pub struct BooleanInterpolator;
403
404impl IInterpolator<bool> for BooleanInterpolator {
405 fn interpolate(&self, from: bool, to: bool, progress: f32) -> bool {
406 if progress >= 1.0 {
407 to
408 } else {
409 from
410 }
411 }
412}
413
414instance!(BooleanInterpolator);
415
416#[derive(Clone, Copy, Debug, Default)]
419pub struct RelativePointInterpolator;
420
421impl IInterpolator<RelativePoint> for RelativePointInterpolator {
422 fn interpolate(&self, from: RelativePoint, to: RelativePoint, progress: f32) -> RelativePoint {
423 if from.unit != to.unit {
424 return if progress >= 0.5 { to } else { from };
425 }
426
427 let x = DoubleInterpolator::instance().interpolate(from.point.x, to.point.x, progress);
428 let y = DoubleInterpolator::instance().interpolate(from.point.y, to.point.y, progress);
429 RelativePoint::new(x, y, from.unit)
430 }
431}
432
433instance!(RelativePointInterpolator);
434
435#[derive(Clone, Copy, Debug, Default)]
438pub struct RelativeScalarInterpolator;
439
440impl IInterpolator<RelativeScalar> for RelativeScalarInterpolator {
441 fn interpolate(&self, from: RelativeScalar, to: RelativeScalar, progress: f32) -> RelativeScalar {
442 if from.unit != to.unit {
443 return if progress >= 0.5 { to } else { from };
444 }
445 RelativeScalar::new(from.scalar + (to.scalar - from.scalar) * progress as f64, from.unit)
446 }
447}
448
449instance!(RelativeScalarInterpolator);
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use crate::RelativeUnit;
455
456 #[test]
457 fn scalars() {
458 assert_eq!(ScalarInterpolator::instance().interpolate(2.0, 4.0, 0.25), 2.5);
459 assert_eq!(ScalarInterpolator::instance().interpolate(2.0, 4.0, 1.5), 5.0);
460 assert_eq!(DoubleInterpolator::instance().interpolate(2.0, 4.0, 0.25), 2.5);
461 assert_eq!(DoubleInterpolator::instance().interpolate(4.0, 2.0, 0.5), 3.0);
462 }
463
464 #[test]
465 fn vectors() {
466 assert_eq!(
467 Vector2Interpolator::instance().interpolate(Vector2::new(0.0, 2.0), Vector2::new(4.0, 4.0), 0.5),
468 Vector2::new(2.0, 3.0)
469 );
470 assert_eq!(
471 VectorInterpolator::instance().interpolate(Vector::new(0.0, 2.0), Vector::new(4.0, 4.0), 0.5),
472 Vector::new(2.0, 3.0)
473 );
474 assert_eq!(
475 Vector3Interpolator::instance().interpolate(
476 Vector3::new(0.0, 2.0, -2.0),
477 Vector3::new(4.0, 4.0, 2.0),
478 0.5
479 ),
480 Vector3::new(2.0, 3.0, 0.0)
481 );
482 assert_eq!(
483 Vector3DInterpolator::instance().interpolate(
484 Vector3D::new(0.0, 2.0, -2.0),
485 Vector3D::new(4.0, 4.0, 2.0),
486 0.5
487 ),
488 Vector3D::new(2.0, 3.0, 0.0)
489 );
490 assert_eq!(
491 Vector4Interpolator::instance().interpolate(
492 Vector4::new(0.0, 2.0, -2.0, 1.0),
493 Vector4::new(4.0, 4.0, 2.0, 1.0),
494 0.5
495 ),
496 Vector4::new(2.0, 3.0, 0.0, 1.0)
497 );
498 }
499
500 #[test]
501 fn quaternion_is_a_normalized_lerp() {
502 let from = Quaternion::create_from_axis_angle(Vector3::UNIT_Z, 0.0);
503 let to = Quaternion::create_from_axis_angle(Vector3::UNIT_Z, 1.0);
504 let actual = QuaternionInterpolator::instance().interpolate(from, to, 0.5);
505 assert_eq!(actual, Quaternion::lerp(from, to, 0.5));
506 assert!((actual.length() - 1.0).abs() < 1e-6);
507 }
508
509 #[test]
510 fn color_channels_are_interpolated_and_truncated() {
511 let from = Color::from_argb(255, 10, 0, 255);
512 let to = Color::from_argb(0, 200, 1, 255);
513 let i = ColorInterpolator::instance();
514 assert_eq!(i.interpolate(from, to, 0.5), Color::from_argb(127, 105, 0, 255));
516 assert_eq!(i.interpolate(from, to, 0.0), from);
517 assert_eq!(i.interpolate(from, to, 1.0), to);
518 assert_eq!(i.interpolate(from, to, 2.0), Color::from_argb(0, 255, 2, 255));
520 assert_eq!(i.interpolate(from, to, -1.0), Color::from_argb(255, 0, 0, 255));
521 assert_eq!(ColorInterpolator::lerp_rgb(from, to, 0.5), i.interpolate(from, to, 0.5));
522 }
523
524 #[test]
526 fn hsl_interpolation_turns_the_hue_the_short_way() {
527 let red = Color::from_argb(255, 255, 0, 0);
528 let blue = Color::from_argb(255, 0, 0, 255);
529 assert_eq!(ColorInterpolator::lerp_hsl(red, blue, 0.5), Color::from_argb(255, 255, 0, 255));
531 assert_eq!(ColorHslInterpolator::instance().interpolate(red, blue, 0.0), red);
532 assert_eq!(ColorHslInterpolator::instance().interpolate(red, blue, 1.0), blue);
533 assert_eq!(ColorInterpolator::instance().interpolate(red, blue, 0.5), Color::from_argb(255, 127, 0, 127));
535 let grey = Color::from_argb(255, 128, 128, 128);
537 let mid = ColorInterpolator::lerp_hsl(grey, red, 0.5);
538 assert!(mid.r > mid.g && mid.g == mid.b, "{mid:?}");
539 }
540
541 #[test]
542 fn linear_rgb_interpolation_is_brighter_in_the_middle() {
543 let black = Color::from_argb(255, 0, 0, 0);
544 let white = Color::from_argb(255, 255, 255, 255);
545 let mid = ColorRgbLinearInterpolator::instance().interpolate(black, white, 0.5);
547 assert_eq!(mid, Color::from_argb(255, 188, 188, 188));
548 assert_eq!(ColorInterpolator::lerp_rgb_linear(black, white, 0.0), black);
549 assert_eq!(ColorInterpolator::lerp_rgb_linear(black, white, 1.0), white);
550 let transparent = Color::from_argb(0, 255, 0, 0);
551 assert_eq!(ColorInterpolator::lerp_rgb_linear(transparent, white, 0.5).a, 128);
552 }
553
554 #[test]
555 fn linear_hsl_interpolation_keeps_the_end_points() {
556 let red = Color::from_argb(255, 255, 0, 0);
557 let blue = Color::from_argb(255, 0, 0, 255);
558 let i = ColorHslLinearInterpolator::instance();
559 assert_eq!(i.interpolate(red, blue, 0.0), red);
560 assert_eq!(i.interpolate(red, blue, 1.0), blue);
561 assert_eq!(i.interpolate(red, blue, 0.5), Color::from_argb(255, 255, 0, 255));
562 let dark = Color::from_argb(255, 40, 80, 120);
563 assert_eq!(i.interpolate(dark, dark, 0.3), dark);
564 }
565
566 #[test]
567 fn color_space_selects_the_interpolator_of_colours_only() {
568 let red = Color::from_argb(255, 255, 0, 0);
569 let blue = Color::from_argb(255, 0, 0, 255);
570 let hsl = interpolator_for_color_space::<Color>(ColorInterpolator::instance(), CompositionColorSpace::Hsl);
571 assert_eq!(hsl.interpolate(red, blue, 0.5), Color::from_argb(255, 255, 0, 255));
572 let auto = interpolator_for_color_space::<Color>(ColorInterpolator::instance(), CompositionColorSpace::Auto);
573 assert_eq!(auto.interpolate(red, blue, 0.5), Color::from_argb(255, 127, 0, 127));
574 let scalar = interpolator_for_color_space::<f32>(ScalarInterpolator::instance(), CompositionColorSpace::Hsl);
575 assert_eq!(scalar.interpolate(2.0, 4.0, 0.5), 3.0);
576 }
577
578 #[test]
579 fn boolean_switches_at_the_end() {
580 let i = BooleanInterpolator::instance();
581 assert!(!i.interpolate(false, true, 0.0));
582 assert!(!i.interpolate(false, true, 0.999));
583 assert!(i.interpolate(false, true, 1.0));
584 assert!(i.interpolate(true, false, 0.5));
585 }
586
587 #[test]
588 fn relative_values() {
589 let p = RelativePointInterpolator::instance();
590 let from = RelativePoint::new(0.0, 1.0, RelativeUnit::Relative);
591 let to = RelativePoint::new(1.0, 0.0, RelativeUnit::Relative);
592 assert_eq!(
593 p.interpolate(from, to, 0.25),
594 RelativePoint::new(0.25, 0.75, RelativeUnit::Relative)
595 );
596 let absolute = RelativePoint::new(10.0, 20.0, RelativeUnit::Absolute);
597 assert_eq!(p.interpolate(from, absolute, 0.25), from);
598 assert_eq!(p.interpolate(from, absolute, 0.5), absolute);
599
600 let s = RelativeScalarInterpolator::instance();
601 let from = RelativeScalar::new(0.0, RelativeUnit::Relative);
602 let to = RelativeScalar::new(1.0, RelativeUnit::Relative);
603 assert_eq!(
604 s.interpolate(from, to, 0.25),
605 RelativeScalar::new(0.25, RelativeUnit::Relative)
606 );
607 let absolute = RelativeScalar::new(10.0, RelativeUnit::Absolute);
608 assert_eq!(s.interpolate(from, absolute, 0.25), from);
609 assert_eq!(s.interpolate(from, absolute, 0.75), absolute);
610 }
611}