1#![cfg_attr(feature = "nightly", feature(portable_simd))]
5
6use core::ops::*;
7use num_traits::{Float, FromPrimitive};
8
9#[cfg(feature = "nightly")]
10use core::simd::{LaneCount, Mask, Simd, SupportedLaneCount};
11
12#[cfg(feature = "nightly")]
13use std::simd::cmp::{SimdPartialEq, SimdPartialOrd};
14
15#[cfg(feature = "nightly")]
16use std::simd::num::SimdFloat;
17
18#[cfg(feature = "nightly")]
19use std::simd::StdFloat;
20
21trait Scalar: Float + FromPrimitive {}
25impl Scalar for f32 {}
26impl Scalar for f64 {}
27
28mod internal {
29 pub trait Sealed {}
30
31 pub trait CurveParam<T>: Sealed + Copy {
32 fn to_curve(self) -> T;
33 }
34
35 #[cfg(feature = "nightly")]
36 pub trait SimdScalar: core::simd::SimdElement + Copy {
37 fn from_f32_scalar(val: f32) -> Self;
38 fn ln_2() -> Self;
39 }
40}
41
42impl internal::CurveParam<f32> for f32 {
43 fn to_curve(self) -> f32 {
44 self
45 }
46}
47
48impl internal::CurveParam<f64> for f64 {
49 fn to_curve(self) -> f64 {
50 self
51 }
52}
53
54#[cfg(feature = "nightly")]
55impl internal::SimdScalar for f32 {
56 fn from_f32_scalar(val: f32) -> Self {
57 val
58 }
59 fn ln_2() -> Self {
60 2.0f32.ln()
61 }
62}
63
64#[cfg(feature = "nightly")]
65impl internal::SimdScalar for f64 {
66 fn from_f32_scalar(val: f32) -> Self {
67 val as f64
68 }
69 fn ln_2() -> Self {
70 2.0f64.ln()
71 }
72}
73
74#[cfg(feature = "nightly")]
75impl<const N: usize> internal::CurveParam<Simd<f32, N>> for f32
76where
77 LaneCount<N>: SupportedLaneCount,
78 Simd<f32, N>: EasingImplHelper,
79{
80 fn to_curve(self) -> Simd<f32, N> {
81 Simd::splat(self)
82 }
83}
84
85#[cfg(feature = "nightly")]
86impl<const N: usize> internal::CurveParam<Simd<f32, N>> for Simd<f32, N>
87where
88 LaneCount<N>: SupportedLaneCount,
89 Simd<f32, N>: EasingImplHelper,
90{
91 fn to_curve(self) -> Simd<f32, N> {
92 self
93 }
94}
95
96#[cfg(feature = "nightly")]
97impl<const N: usize> internal::CurveParam<Simd<f64, N>> for f64
98where
99 LaneCount<N>: SupportedLaneCount,
100 Simd<f64, N>: EasingImplHelper,
101{
102 fn to_curve(self) -> Simd<f64, N> {
103 Simd::splat(self)
104 }
105}
106
107#[cfg(feature = "nightly")]
108impl<const N: usize> internal::CurveParam<Simd<f64, N>> for Simd<f64, N>
109where
110 LaneCount<N>: SupportedLaneCount,
111 Simd<f64, N>: EasingImplHelper,
112{
113 fn to_curve(self) -> Simd<f64, N> {
114 self
115 }
116}
117
118pub trait EasingArgument: internal::Sealed + Sized + Copy {
126 #[allow(private_bounds)]
130 fn ease_in_quad(self) -> Self
131 where
132 Self: EasingImplHelper,
133 {
134 self.ease_in_pow(2)
135 }
136
137 #[allow(private_bounds)]
141 fn ease_out_quad(self) -> Self
142 where
143 Self: EasingImplHelper,
144 {
145 self.ease_out_pow(2)
146 }
147
148 #[allow(private_bounds)]
152 fn ease_in_out_quad(self) -> Self
153 where
154 Self: EasingImplHelper,
155 {
156 <Self as EasingImplHelper>::ease_in_out_quad(self)
157 }
158
159 #[allow(private_bounds)]
163 fn ease_in_cubic(self) -> Self
164 where
165 Self: EasingImplHelper,
166 {
167 self.ease_in_pow(3)
168 }
169
170 #[allow(private_bounds)]
174 fn ease_out_cubic(self) -> Self
175 where
176 Self: EasingImplHelper,
177 {
178 self.ease_out_pow(3)
179 }
180
181 #[allow(private_bounds)]
185 fn ease_in_out_cubic(self) -> Self
186 where
187 Self: EasingImplHelper,
188 {
189 <Self as EasingImplHelper>::ease_in_out_cubic(self)
190 }
191
192 #[allow(private_bounds)]
196 fn ease_in_quart(self) -> Self
197 where
198 Self: EasingImplHelper,
199 {
200 self.ease_in_pow(4)
201 }
202
203 #[allow(private_bounds)]
207 fn ease_out_quart(self) -> Self
208 where
209 Self: EasingImplHelper,
210 {
211 self.ease_out_pow(4)
212 }
213
214 #[allow(private_bounds)]
218 fn ease_in_out_quart(self) -> Self
219 where
220 Self: EasingImplHelper,
221 {
222 <Self as EasingImplHelper>::ease_in_out_quart(self)
223 }
224
225 #[allow(private_bounds)]
229 fn ease_in_quint(self) -> Self
230 where
231 Self: EasingImplHelper,
232 {
233 self.ease_in_pow(5)
234 }
235
236 #[allow(private_bounds)]
240 fn ease_out_quint(self) -> Self
241 where
242 Self: EasingImplHelper,
243 {
244 self.ease_out_pow(5)
245 }
246
247 #[allow(private_bounds)]
251 fn ease_in_out_quint(self) -> Self
252 where
253 Self: EasingImplHelper,
254 {
255 <Self as EasingImplHelper>::ease_in_out_quint(self)
256 }
257
258 #[allow(private_bounds)]
262 fn ease_in_out_back(self) -> Self
263 where
264 Self: EasingImplHelper,
265 {
266 <Self as EasingImplHelper>::ease_in_out_back(self)
267 }
268
269 #[allow(private_bounds)]
273 fn ease_in_bounce(self) -> Self
274 where
275 Self: EasingImplHelper,
276 {
277 let one = Self::from_f32(1.0);
278 one - <Self as EasingImplHelper>::ease_out_bounce(one - self)
279 }
280
281 #[allow(private_bounds)]
285 fn ease_out_bounce(self) -> Self
286 where
287 Self: EasingImplHelper,
288 {
289 <Self as EasingImplHelper>::ease_out_bounce(self)
290 }
291
292 #[allow(private_bounds)]
296 fn ease_in_out_bounce(self) -> Self
297 where
298 Self: EasingImplHelper,
299 {
300 <Self as EasingImplHelper>::ease_in_out_bounce(self)
301 }
302
303 #[allow(private_bounds)]
307 fn ease_in_expo(self) -> Self
308 where
309 Self: EasingImplHelper,
310 {
311 <Self as EasingImplHelper>::ease_in_expo(self)
312 }
313
314 #[allow(private_bounds)]
318 fn ease_out_expo(self) -> Self
319 where
320 Self: EasingImplHelper,
321 {
322 <Self as EasingImplHelper>::ease_out_expo(self)
323 }
324
325 #[allow(private_bounds)]
329 fn ease_in_out_expo(self) -> Self
330 where
331 Self: EasingImplHelper,
332 {
333 <Self as EasingImplHelper>::ease_in_out_expo(self)
334 }
335
336 #[allow(private_bounds)]
340 fn ease_in_elastic(self) -> Self
341 where
342 Self: EasingImplHelper,
343 {
344 <Self as EasingImplHelper>::ease_in_elastic(self)
345 }
346
347 #[allow(private_bounds)]
351 fn ease_out_elastic(self) -> Self
352 where
353 Self: EasingImplHelper,
354 {
355 <Self as EasingImplHelper>::ease_out_elastic(self)
356 }
357
358 #[allow(private_bounds)]
362 fn ease_in_out_elastic(self) -> Self
363 where
364 Self: EasingImplHelper,
365 {
366 <Self as EasingImplHelper>::ease_in_out_elastic(self)
367 }
368
369 #[allow(private_bounds)]
373 fn ease_in_sine(self) -> Self
374 where
375 Self: EasingImplHelper,
376 {
377 let one = Self::from_f32(1.0);
378 let pi_half = Self::from_f32(std::f32::consts::FRAC_PI_2);
379 one - (self * pi_half).cos()
380 }
381
382 #[allow(private_bounds)]
386 fn ease_out_sine(self) -> Self
387 where
388 Self: EasingImplHelper,
389 {
390 let pi_half = Self::from_f32(std::f32::consts::FRAC_PI_2);
391 (self * pi_half).sin()
392 }
393
394 #[allow(private_bounds)]
398 fn ease_in_out_sine(self) -> Self
399 where
400 Self: EasingImplHelper,
401 {
402 use std::f32::consts::PI;
403 let cos_val = (self * Self::from_f32(PI)).cos();
404 cos_val.mul_add(Self::from_f32(-0.5), Self::from_f32(0.5))
405 }
406
407 #[allow(private_bounds)]
411 fn ease_in_circ(self) -> Self
412 where
413 Self: EasingImplHelper,
414 {
415 let one = Self::from_f32(1.0);
416 one - (one - self.powi(2)).sqrt()
417 }
418
419 #[allow(private_bounds)]
423 fn ease_out_circ(self) -> Self
424 where
425 Self: EasingImplHelper,
426 {
427 let one = Self::from_f32(1.0);
428 (one - (self - one).powi(2)).sqrt()
429 }
430
431 #[allow(private_bounds)]
435 fn ease_in_out_circ(self) -> Self
436 where
437 Self: EasingImplHelper,
438 {
439 <Self as EasingImplHelper>::ease_in_out_circ(self)
440 }
441
442 #[allow(private_bounds)]
446 fn ease_in_back(self) -> Self
447 where
448 Self: EasingImplHelper,
449 {
450 let c1 = Self::from_f32(1.70158);
451 let c3 = Self::from_f32(2.70158);
452
453 c3 * self.powi(3) - c1 * self.powi(2)
454 }
455
456 #[allow(private_bounds)]
460 fn ease_out_back(self) -> Self
461 where
462 Self: EasingImplHelper,
463 {
464 let c1 = Self::from_f32(1.70158);
465 let c3 = Self::from_f32(2.70158);
466 let one = Self::from_f32(1.0);
467
468 one + c3 * (self - one).powi(3) + c1 * (self - one).powi(2)
469 }
470
471 #[allow(private_bounds)]
482 fn ease_in_curve<C>(self, curve: C) -> Self
483 where
484 Self: EasingImplHelper,
485 C: internal::CurveParam<Self>,
486 {
487 <Self as EasingImplHelper>::ease_in_curve(self, curve)
488 }
489
490 #[allow(private_bounds)]
501 fn ease_out_curve<C>(self, curve: C) -> Self
502 where
503 Self: EasingImplHelper,
504 C: internal::CurveParam<Self>,
505 {
506 <Self as EasingImplHelper>::ease_out_curve(self, curve)
507 }
508
509 #[allow(private_bounds)]
521 fn ease_in_out_curve<C>(self, curve: C) -> Self
522 where
523 Self: EasingImplHelper,
524 C: internal::CurveParam<Self>,
525 {
526 <Self as EasingImplHelper>::ease_in_out_curve(self, curve)
527 }
528}
529
530trait EasingImplHelper:
533 Sub<Self, Output = Self>
534 + Add<Self, Output = Self>
535 + Mul<Self, Output = Self>
536 + Div<Self, Output = Self>
537 + Sized
538 + Copy
539{
540 fn from_f32(arg: f32) -> Self;
541 fn sin(self) -> Self;
542 fn cos(self) -> Self;
543 fn powi(self, n: i32) -> Self;
544 #[allow(unused)]
545 fn powf(self, other: Self) -> Self;
546 fn double(self) -> Self {
547 self + self
548 }
549 fn sqrt(self) -> Self;
550 #[allow(unused)]
551 fn exp(self) -> Self;
552 fn mul_add(self, a: Self, b: Self) -> Self;
553
554 fn ease_in_pow(self, n: i32) -> Self {
555 self.powi(n)
556 }
557
558 fn ease_out_pow(self, n: i32) -> Self {
559 let one = Self::from_f32(1.0);
560 one - (one - self).powi(n)
561 }
562
563 fn ease_in_out_quad(self) -> Self;
564 fn ease_in_out_cubic(self) -> Self;
565 fn ease_in_out_quart(self) -> Self;
566 fn ease_in_out_quint(self) -> Self;
567 fn ease_in_out_back(self) -> Self;
568 fn ease_out_bounce(self) -> Self;
569 fn ease_in_out_bounce(self) -> Self;
570 fn ease_in_expo(self) -> Self;
571 fn ease_out_expo(self) -> Self;
572 fn ease_in_out_expo(self) -> Self;
573 fn ease_in_elastic(self) -> Self;
574 fn ease_out_elastic(self) -> Self;
575 fn ease_in_out_elastic(self) -> Self;
576 fn ease_in_out_circ(self) -> Self;
577
578 fn ease_in_curve<C>(self, curve: C) -> Self
579 where
580 C: internal::CurveParam<Self>;
581 fn ease_out_curve<C>(self, curve: C) -> Self
582 where
583 C: internal::CurveParam<Self>;
584 fn ease_in_out_curve<C>(self, curve: C) -> Self
585 where
586 C: internal::CurveParam<Self>;
587}
588
589impl<T: EasingImplHelper> internal::Sealed for T {}
592impl<T: EasingImplHelper> EasingArgument for T {}
593
594impl<T> EasingImplHelper for T
595where
596 T: Scalar,
597{
598 fn from_f32(arg: f32) -> Self {
599 T::from(arg).unwrap()
600 }
601 fn sin(self) -> Self {
602 self.sin()
603 }
604 fn cos(self) -> Self {
605 self.cos()
606 }
607 fn powi(self, n: i32) -> Self {
608 self.powi(n)
609 }
610 fn powf(self, other: Self) -> Self {
611 self.powf(other)
612 }
613 fn sqrt(self) -> Self {
614 self.sqrt()
615 }
616 fn exp(self) -> Self {
617 self.exp()
618 }
619 fn mul_add(self, a: Self, b: Self) -> Self {
620 self.mul_add(a, b)
621 }
622
623 fn ease_in_out_quad(self) -> Self {
624 let half = T::from(0.5).unwrap();
625 let one = T::one();
626 let two = T::from(2.0).unwrap();
627 if self < half {
628 two * self.powi(2)
629 } else {
630 one - ((two * self - two).powi(2) * half)
631 }
632 }
633 fn ease_in_out_cubic(self) -> Self {
634 let half = T::from(0.5).unwrap();
635 if self < half {
636 let cubed = self.powi(3);
637 let doubled = cubed.double();
638 doubled + doubled
639 } else {
640 let one = T::one();
641 let two = T::from(2.0).unwrap();
642 one - (two - self.double()).powi(3) * half
643 }
644 }
645 fn ease_in_out_quart(self) -> Self {
646 let half = T::from(0.5).unwrap();
647 if self < half {
648 T::from(8.0).unwrap() * self.powi(4)
649 } else {
650 let one = T::one();
651 let two = T::from(2.0).unwrap();
652 one - (two - self.double()).powi(4) * half
653 }
654 }
655 fn ease_in_out_quint(self) -> Self {
656 let half = T::from(0.5).unwrap();
657 if self < half {
658 T::from(16.0).unwrap() * self.powi(5)
659 } else {
660 let one = T::one();
661 let two = T::from(2.0).unwrap();
662 one - (two - self.double()).powi(5) * half
663 }
664 }
665 fn ease_in_out_back(self) -> Self {
666 let c2 = T::from(1.70158 * 1.525).unwrap();
667 let half = T::from(0.5).unwrap();
668 let two = T::from(2.0).unwrap();
669 if self < half {
670 let two_x = self.double();
671 let pow_two_x_2 = two_x.powi(2);
672 let inner = (c2 + T::one()).mul_add(two_x, -c2);
673 pow_two_x_2 * inner * half
674 } else {
675 let two_x_minus_2 = self.double() - two;
676 let pow_two_x_minus_2_2 = two_x_minus_2.powi(2);
677 let inner = (c2 + T::one()).mul_add(self.double() - two, c2);
678 pow_two_x_minus_2_2.mul_add(inner, two) * half
679 }
680 }
681 fn ease_out_bounce(self) -> Self {
682 let n1 = T::from(7.5625).unwrap();
683 let one_over_d1 = T::from(1.0 / 2.75).unwrap();
684 let two_over_d1 = T::from(2.0 / 2.75).unwrap();
685 let two_point_five_over_d1 = T::from(2.5 / 2.75).unwrap();
686 if self < one_over_d1 {
687 n1 * self * self
688 } else if self < two_over_d1 {
689 let adjusted = self - T::from(1.5 / 2.75).unwrap();
690 (adjusted * adjusted).mul_add(n1, T::from(0.75).unwrap())
691 } else if self < two_point_five_over_d1 {
692 let adjusted = self - T::from(2.25 / 2.75).unwrap();
693 (adjusted * adjusted).mul_add(n1, T::from(0.9375).unwrap())
694 } else {
695 let adjusted = self - T::from(2.625 / 2.75).unwrap();
696 (adjusted * adjusted).mul_add(n1, T::from(0.984375).unwrap())
697 }
698 }
699 fn ease_in_out_bounce(self) -> Self {
700 let half = T::from(0.5).unwrap();
701 let one = T::one();
702 if self < half {
703 (one - EasingArgument::ease_out_bounce(one - self.double())) * half
704 } else {
705 (one + EasingArgument::ease_out_bounce(self.double() - one)) * half
706 }
707 }
708 fn ease_in_expo(self) -> Self {
709 if self == T::zero() {
710 T::zero()
711 } else {
712 T::from(2.0).unwrap().powf(
713 T::from(10.0)
714 .unwrap()
715 .mul_add(self, -T::from(10.0).unwrap()),
716 )
717 }
718 }
719 fn ease_out_expo(self) -> Self {
720 if self == T::one() {
721 T::one()
722 } else {
723 T::from(2.0)
724 .unwrap()
725 .powf(-T::from(10.0).unwrap() * self)
726 .mul_add(-T::one(), T::one())
727 }
728 }
729 fn ease_in_out_expo(self) -> Self {
730 if self == T::zero() {
731 T::zero()
732 } else if self == T::one() {
733 T::one()
734 } else if self < T::from(0.5).unwrap() {
735 T::from(2.0)
736 .unwrap()
737 .powf(
738 T::from(20.0)
739 .unwrap()
740 .mul_add(self, -T::from(10.0).unwrap()),
741 )
742 .mul_add(T::from(0.5).unwrap(), T::zero())
743 } else {
744 T::from(2.0)
745 .unwrap()
746 .powf(
747 T::from(-20.0)
748 .unwrap()
749 .mul_add(self, T::from(10.0).unwrap()),
750 )
751 .mul_add(-T::from(0.5).unwrap(), T::one())
752 }
753 }
754 fn ease_in_elastic(self) -> Self {
755 if self == T::zero() {
756 T::zero()
757 } else if self == T::one() {
758 T::one()
759 } else {
760 let c4 = T::from(2.094_395_2).unwrap();
761 -T::from(2.0)
762 .unwrap()
763 .powf(T::from(10.0).unwrap() * self - T::from(10.0).unwrap())
764 * (self.mul_add(T::from(10.0).unwrap(), -T::from(10.75).unwrap()) * c4).sin()
765 }
766 }
767 fn ease_out_elastic(self) -> Self {
768 if self == T::zero() {
769 T::zero()
770 } else if self == T::one() {
771 T::one()
772 } else {
773 let c4 = T::from(2.094_395_2).unwrap();
774 T::from(2.0)
775 .unwrap()
776 .powf(-T::from(10.0).unwrap() * self)
777 .mul_add(
778 (self.mul_add(T::from(10.0).unwrap(), -T::from(0.75).unwrap()) * c4).sin(),
779 T::one(),
780 )
781 }
782 }
783 fn ease_in_out_elastic(self) -> Self {
784 if self == T::zero() {
785 T::zero()
786 } else if self == T::one() {
787 T::one()
788 } else if self < T::from(0.5).unwrap() {
789 let c5 = T::from(1.396_263_4).unwrap();
790 -T::from(2.0)
791 .unwrap()
792 .powf(T::from(20.0).unwrap() * self - T::from(10.0).unwrap())
793 * (self.mul_add(T::from(20.0).unwrap(), -T::from(11.125).unwrap()) * c5).sin()
794 * T::from(0.5).unwrap()
795 } else {
796 let c5 = T::from(1.396_263_4).unwrap();
797 T::from(2.0)
798 .unwrap()
799 .powf(-T::from(20.0).unwrap() * self + T::from(10.0).unwrap())
800 .mul_add(
801 (self.mul_add(T::from(20.0).unwrap(), -T::from(11.125).unwrap()) * c5).sin()
802 * T::from(0.5).unwrap(),
803 T::one(),
804 )
805 }
806 }
807 fn ease_in_out_circ(self) -> Self {
808 let half = T::from(0.5).unwrap();
809 let one = T::one();
810 let two = T::from(2.0).unwrap();
811 let double = self.double();
812 if self < half {
813 (one - (one - double.powi(2)).sqrt()) * half
814 } else {
815 ((one - (two - double).powi(2)).sqrt() + one) * half
816 }
817 }
818
819 fn ease_in_curve<C>(self, curve: C) -> Self
820 where
821 C: internal::CurveParam<Self>,
822 {
823 let c = curve.to_curve();
824 if c.abs() < T::from(0.001).unwrap() {
825 self
826 } else {
827 let grow = c.exp();
828 let one = T::one();
829 let a = one / (one - grow);
830 a - (a * grow.powf(self))
831 }
832 }
833
834 fn ease_out_curve<C>(self, curve: C) -> Self
835 where
836 C: internal::CurveParam<Self>,
837 {
838 let one = T::one();
839 one - <Self as EasingImplHelper>::ease_in_curve(one - self, curve)
840 }
841
842 fn ease_in_out_curve<C>(self, curve: C) -> Self
843 where
844 C: internal::CurveParam<Self>,
845 {
846 let half = T::from(0.5).unwrap();
847 if self < half {
848 <Self as EasingImplHelper>::ease_in_curve(self.double(), curve) * half
849 } else {
850 half + <Self as EasingImplHelper>::ease_out_curve((self - half).double(), curve) * half
851 }
852 }
853}
854
855#[cfg(feature = "nightly")]
858impl<T, const N: usize> EasingImplHelper for Simd<T, N>
859where
860 T: internal::SimdScalar + core::simd::SimdElement,
861 T::Mask: core::simd::MaskElement,
862 LaneCount<N>: SupportedLaneCount,
863 Simd<T, N>: StdFloat
864 + SimdFloat
865 + SimdPartialEq<Mask = Mask<T::Mask, N>>
866 + SimdPartialOrd
867 + Add<Output = Simd<T, N>>
868 + Sub<Output = Simd<T, N>>
869 + Mul<Output = Simd<T, N>>
870 + Div<Output = Simd<T, N>>
871 + Neg<Output = Simd<T, N>>,
872{
873 fn from_f32(arg: f32) -> Self {
874 Simd::splat(T::from_f32_scalar(arg))
875 }
876
877 fn sin(self) -> Self {
878 <Self as StdFloat>::sin(self)
879 }
880
881 fn cos(self) -> Self {
882 <Self as StdFloat>::cos(self)
883 }
884
885 fn powi(self, n: i32) -> Self {
886 if n == 1 {
887 self
888 } else if n % 2 == 0 {
889 let tmp = self.powi(n / 2);
890 tmp * tmp
891 } else {
892 self * self.powi(n - 1)
893 }
894 }
895
896 fn powf(self, other: Self) -> Self {
897 <Self as StdFloat>::exp(other * <Self as StdFloat>::ln(self))
898 }
899
900 fn sqrt(self) -> Self {
901 <Self as StdFloat>::sqrt(self)
902 }
903
904 fn exp(self) -> Self {
905 <Self as StdFloat>::exp(self)
906 }
907
908 fn mul_add(self, a: Self, b: Self) -> Self {
909 <Self as StdFloat>::mul_add(self, a, b)
910 }
911
912 fn ease_in_out_quad(self) -> Self {
913 let half = Self::from_f32(0.5);
914 let mask = self.simd_lt(half);
915
916 let lower_half = self.powi(2).double();
917 let upper_half = Self::from_f32(1.0) - (self.double() - Self::from_f32(2.0)).powi(2) * half;
918
919 mask.select(lower_half, upper_half)
920 }
921
922 fn ease_in_out_cubic(self) -> Self {
923 let half = Self::from_f32(0.5);
924 let mask = self.simd_lt(half);
925
926 let lower_half = {
927 let cubed = self.powi(3);
928 let doubled = cubed.double();
929 doubled + doubled
930 };
931
932 let upper_half = {
933 let one = Self::from_f32(1.0);
934 let two = Self::from_f32(2.0);
935 one - (two - self.double()).powi(3) * half
936 };
937
938 mask.select(lower_half, upper_half)
939 }
940
941 fn ease_in_out_quart(self) -> Self {
942 let half = Self::from_f32(0.5);
943 let mask = self.simd_lt(half);
944
945 let lower_half = { Self::from_f32(8.0) * self.powi(4) };
946 let upper_half = {
947 let one = Self::from_f32(1.0);
948 let two = Self::from_f32(2.0);
949 one - (two - self.double()).powi(4) * half
950 };
951 mask.select(lower_half, upper_half)
952 }
953
954 fn ease_in_out_quint(self) -> Self {
955 let half = Self::from_f32(0.5);
956 let mask = self.simd_lt(half);
957
958 let lower_half = { Self::from_f32(16.0) * self.powi(5) };
959 let upper_half = {
960 let one = Self::from_f32(1.0);
961 let two = Self::from_f32(2.0);
962 one - (two - self.double()).powi(5) * half
963 };
964 mask.select(lower_half, upper_half)
965 }
966
967 fn ease_in_out_back(self) -> Self {
968 let c2 = Self::from_f32(1.70158 * 1.525);
969 let half = Self::from_f32(0.5);
970 let mask = self.simd_lt(half);
971
972 let lower_half = {
973 let two_x = self.double();
974 let pow_two_x_2 = two_x.powi(2);
975 let inner = StdFloat::mul_add(c2 + Self::from_f32(1.0), two_x, -c2);
976 pow_two_x_2 * inner
977 };
978 let upper_half = {
979 let two_x_minus_2 = self.double() - Self::from_f32(2.0);
980 let pow_two_x_minus_2_2 = two_x_minus_2.powi(2);
981 let inner = StdFloat::mul_add(
982 c2 + Self::from_f32(1.0),
983 self.double() - Self::from_f32(2.0),
984 c2,
985 );
986 StdFloat::mul_add(pow_two_x_minus_2_2, inner, Self::from_f32(2.0))
987 };
988 mask.select(lower_half, upper_half) * half
989 }
990
991 fn ease_out_bounce(self) -> Self {
992 let n1 = Self::from_f32(7.5625);
993 let one_over_d1 = Self::from_f32(1.0 / 2.75);
994 let two_over_d1 = Self::from_f32(2.0 / 2.75);
995 let two_point_five_over_d1 = Self::from_f32(2.5 / 2.75);
996 let mask1 = self.simd_lt(one_over_d1);
997 let mask2 = self.simd_lt(two_over_d1);
998 let mask3 = self.simd_lt(two_point_five_over_d1);
999 let branch1 = n1 * self * self;
1000 let adjusted2 = self - Self::from_f32(1.5 / 2.75);
1001 let branch2 = StdFloat::mul_add(adjusted2 * adjusted2, n1, Self::from_f32(0.75));
1002 let adjusted3 = self - Self::from_f32(2.25 / 2.75);
1003 let branch3 = StdFloat::mul_add(adjusted3 * adjusted3, n1, Self::from_f32(0.9375));
1004 let adjusted4 = self - Self::from_f32(2.625 / 2.75);
1005 let branch4 = StdFloat::mul_add(adjusted4 * adjusted4, n1, Self::from_f32(0.984375));
1006 mask1.select(
1007 branch1,
1008 mask2.select(branch2, mask3.select(branch3, branch4)),
1009 )
1010 }
1011
1012 fn ease_in_out_bounce(self) -> Self {
1013 let half = Self::from_f32(0.5);
1014 let one = Self::from_f32(1.0);
1015 let mask = self.simd_lt(half);
1016 let lower_half = one - EasingArgument::ease_out_bounce(one - self.double());
1017 let upper_half = one + EasingArgument::ease_out_bounce(self.double() - one);
1018 mask.select(lower_half, upper_half) * half
1019 }
1020
1021 fn ease_in_expo(self) -> Self {
1022 let zero = Self::from_f32(0.0);
1023 let ln2 = Simd::splat(T::ln_2());
1024 let ten = Self::from_f32(10.0);
1025 let mask_zero = self.simd_eq(zero);
1026 let exponent = StdFloat::mul_add(ten, self, -ten);
1027 let normal = <Self as StdFloat>::exp(exponent * ln2);
1028 mask_zero.select(zero, normal)
1029 }
1030
1031 fn ease_out_expo(self) -> Self {
1032 let one = Self::from_f32(1.0);
1033 let ln2 = Simd::splat(T::ln_2());
1034 let neg_ten = Self::from_f32(-10.0);
1035 let mask_one = self.simd_eq(one);
1036 let exponent = neg_ten * self;
1037 let normal = StdFloat::mul_add(
1038 <Self as StdFloat>::exp(exponent * ln2),
1039 -Self::from_f32(1.0),
1040 one,
1041 );
1042 mask_one.select(one, normal)
1043 }
1044
1045 fn ease_in_out_expo(self) -> Self {
1046 let zero = Self::from_f32(0.0);
1047 let one = Self::from_f32(1.0);
1048 let half = Self::from_f32(0.5);
1049 let ln2 = Simd::splat(T::ln_2());
1050 let twenty = Self::from_f32(20.0);
1051 let ten = Self::from_f32(10.0);
1052 let mask_zero = self.simd_eq(zero);
1053 let mask_one = self.simd_eq(one);
1054 let mask_half = self.simd_lt(half);
1055 let exponent_lower = StdFloat::mul_add(twenty, self, -ten);
1056 let branch_lower = <Self as StdFloat>::exp(exponent_lower * ln2) * half;
1057 let exponent_upper = StdFloat::mul_add(-twenty, self, ten);
1058 let branch_upper =
1059 StdFloat::mul_add(<Self as StdFloat>::exp(exponent_upper * ln2), -half, one);
1060 let temp = mask_half.select(branch_lower, branch_upper);
1061 let temp2 = mask_one.select(one, temp);
1062 mask_zero.select(zero, temp2)
1063 }
1064
1065 fn ease_in_elastic(self) -> Self {
1066 let zero = Self::from_f32(0.0);
1067 let one = Self::from_f32(1.0);
1068 let ln2 = Simd::splat(T::ln_2());
1069 let c4 = Self::from_f32(2.094_395_2);
1070 let ten = Self::from_f32(10.0);
1071 let minus_ten_point_75 = Self::from_f32(-10.75);
1072 let mask_zero = self.simd_eq(zero);
1073 let mask_one = self.simd_eq(one);
1074 let exponent = StdFloat::mul_add(ten, self, -ten);
1075 let sin_arg = StdFloat::mul_add(ten, self, minus_ten_point_75) * c4;
1076 let normal = -<Self as StdFloat>::exp(exponent * ln2) * <Self as StdFloat>::sin(sin_arg);
1077 let temp = mask_one.select(one, normal);
1078 mask_zero.select(zero, temp)
1079 }
1080
1081 fn ease_out_elastic(self) -> Self {
1082 let zero = Self::from_f32(0.0);
1083 let one = Self::from_f32(1.0);
1084 let ln2 = Simd::splat(T::ln_2());
1085 let c4 = Self::from_f32(2.094_395_2);
1086 let ten = Self::from_f32(10.0);
1087 let minus_zero_point_75 = Self::from_f32(-0.75);
1088 let mask_zero = self.simd_eq(zero);
1089 let mask_one = self.simd_eq(one);
1090 let exponent = -ten * self;
1091 let sin_arg = StdFloat::mul_add(ten, self, minus_zero_point_75) * c4;
1092 let normal = StdFloat::mul_add(
1093 <Self as StdFloat>::exp(exponent * ln2),
1094 <Self as StdFloat>::sin(sin_arg),
1095 one,
1096 );
1097 let temp = mask_one.select(one, normal);
1098 mask_zero.select(zero, temp)
1099 }
1100
1101 fn ease_in_out_elastic(self) -> Self {
1102 let zero = Self::from_f32(0.0);
1103 let one = Self::from_f32(1.0);
1104 let half = Self::from_f32(0.5);
1105 let ln2 = Simd::splat(T::ln_2());
1106 let c5 = Self::from_f32(1.396_263_4);
1107 let twenty = Self::from_f32(20.0);
1108 let ten = Self::from_f32(10.0);
1109 let minus_eleven_point_125 = Self::from_f32(-11.125);
1110 let mask_zero = self.simd_eq(zero);
1111 let mask_one = self.simd_eq(one);
1112 let mask_half = self.simd_lt(half);
1113 let exponent_lower = StdFloat::mul_add(twenty, self, -ten);
1114 let sin_arg = StdFloat::mul_add(twenty, self, minus_eleven_point_125) * c5;
1115 let branch_lower = -<Self as StdFloat>::exp(exponent_lower * ln2)
1116 * <Self as StdFloat>::sin(sin_arg)
1117 * half;
1118 let exponent_upper = StdFloat::mul_add(-twenty, self, ten);
1119 let branch_upper = StdFloat::mul_add(
1120 <Self as StdFloat>::exp(exponent_upper * ln2),
1121 <Self as StdFloat>::sin(sin_arg) * half,
1122 one,
1123 );
1124 let temp = mask_half.select(branch_lower, branch_upper);
1125 let temp2 = mask_one.select(one, temp);
1126 mask_zero.select(zero, temp2)
1127 }
1128
1129 fn ease_in_out_circ(self) -> Self {
1130 let half = Self::from_f32(0.5);
1131 let mask = self.simd_lt(half);
1132
1133 let one = Self::from_f32(1.0);
1134 let two = Self::from_f32(2.0);
1135 let double = self.double();
1136
1137 let lower_half = one - StdFloat::sqrt(one - double.powi(2));
1138 let upper_half = StdFloat::sqrt(one - (two - double).powi(2)) + one;
1139 mask.select(lower_half, upper_half) * half
1140 }
1141
1142 fn ease_in_curve<C>(self, curve: C) -> Self
1143 where
1144 C: internal::CurveParam<Self>,
1145 {
1146 let c = curve.to_curve();
1147 let abs_curve = SimdFloat::abs(c);
1148 let mask = abs_curve.simd_lt(Self::from_f32(0.001));
1149 let grow = <Self as StdFloat>::exp(c);
1150 let a = Self::from_f32(1.0) / (Self::from_f32(1.0) - grow);
1151 let normal = a - (a * grow.powf(self));
1152 mask.select(self, normal)
1153 }
1154
1155 fn ease_out_curve<C>(self, curve: C) -> Self
1156 where
1157 C: internal::CurveParam<Self>,
1158 {
1159 let one = Self::from_f32(1.0);
1160 one - <Self as EasingImplHelper>::ease_in_curve(one - self, curve)
1161 }
1162
1163 fn ease_in_out_curve<C>(self, curve: C) -> Self
1164 where
1165 C: internal::CurveParam<Self>,
1166 {
1167 let half = Self::from_f32(0.5);
1168 let mask = self.simd_lt(half);
1169 let lower_half = <Self as EasingImplHelper>::ease_in_curve(self.double(), curve) * half;
1170 let upper_half =
1171 half + <Self as EasingImplHelper>::ease_out_curve((self - half).double(), curve) * half;
1172 mask.select(lower_half, upper_half)
1173 }
1174}
1175
1176#[cfg(test)]
1179mod tests {
1180 use super::EasingArgument;
1181 #[cfg(feature = "nightly")]
1182 use std::simd::{Simd, f32x4};
1183
1184 #[cfg(feature = "nightly")]
1185 mod comparison_tests {
1186 use approx::assert_relative_eq;
1187 use paste::paste;
1188
1189 macro_rules! generate_comparison_tests {
1190 ($func:ident) => {
1191 paste! {
1192 #[test]
1193 fn [<$func _f32_vs_f32x4>]() {
1194 use super::EasingArgument;
1195 let points = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
1196 for &x in &points {
1197 let scalar = EasingArgument::$func(x);
1198 let vector = EasingArgument::$func(core::simd::f32x4::splat(x))[0];
1199 assert_relative_eq!(scalar, vector, epsilon = 1e-6);
1200 }
1201 }
1202 }
1203 };
1204 }
1205
1206 generate_comparison_tests!(ease_in_quad);
1207 generate_comparison_tests!(ease_out_quad);
1208 generate_comparison_tests!(ease_in_out_quad);
1209 generate_comparison_tests!(ease_in_cubic);
1210 generate_comparison_tests!(ease_out_cubic);
1211 generate_comparison_tests!(ease_in_out_cubic);
1212 generate_comparison_tests!(ease_in_quart);
1213 generate_comparison_tests!(ease_out_quart);
1214 generate_comparison_tests!(ease_in_out_quart);
1215 generate_comparison_tests!(ease_in_quint);
1216 generate_comparison_tests!(ease_out_quint);
1217 generate_comparison_tests!(ease_in_out_quint);
1218 generate_comparison_tests!(ease_in_sine);
1219 generate_comparison_tests!(ease_out_sine);
1220 generate_comparison_tests!(ease_in_out_sine);
1221 generate_comparison_tests!(ease_in_circ);
1222 generate_comparison_tests!(ease_out_circ);
1223 generate_comparison_tests!(ease_in_out_circ);
1224 generate_comparison_tests!(ease_in_back);
1225 generate_comparison_tests!(ease_out_back);
1226 generate_comparison_tests!(ease_in_out_back);
1227 generate_comparison_tests!(ease_in_bounce);
1228 generate_comparison_tests!(ease_out_bounce);
1229 generate_comparison_tests!(ease_in_out_bounce);
1230 generate_comparison_tests!(ease_in_expo);
1231 generate_comparison_tests!(ease_out_expo);
1232 generate_comparison_tests!(ease_in_out_expo);
1233 generate_comparison_tests!(ease_in_elastic);
1234 generate_comparison_tests!(ease_out_elastic);
1235 generate_comparison_tests!(ease_in_out_elastic);
1236
1237 #[test]
1238 fn ease_in_curve_f32_vs_f32x4() {
1239 use super::EasingArgument;
1240 let points = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
1241 for &x in &points {
1242 let scalar = EasingArgument::ease_in_curve(x, 1.0f32);
1243 let vector = EasingArgument::ease_in_curve(core::simd::f32x4::splat(x), 1.0f32)[0];
1244 assert_relative_eq!(scalar, vector, epsilon = 1e-6);
1245 }
1246 }
1247
1248 #[test]
1249 fn ease_out_curve_f32_vs_f32x4() {
1250 use super::EasingArgument;
1251 let points = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
1252 for &x in &points {
1253 let scalar = EasingArgument::ease_out_curve(x, 1.0f32);
1254 let vector = EasingArgument::ease_out_curve(core::simd::f32x4::splat(x), 1.0f32)[0];
1255 assert_relative_eq!(scalar, vector, epsilon = 1e-6);
1256 }
1257 }
1258
1259 #[test]
1260 fn ease_in_out_curve_f32_vs_f32x4() {
1261 use super::EasingArgument;
1262 let points = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
1263 for &x in &points {
1264 let scalar = EasingArgument::ease_in_out_curve(x, 1.0f32);
1265 let vector =
1266 EasingArgument::ease_in_out_curve(core::simd::f32x4::splat(x), 1.0f32)[0];
1267 assert_relative_eq!(scalar, vector, epsilon = 1e-6);
1268 }
1269 }
1270 }
1271
1272 mod boundary_and_symmetry_tests {
1273 use super::EasingArgument;
1274 use approx::assert_relative_eq;
1275 use paste::paste;
1276
1277 macro_rules! generate_boundary_tests {
1279 ($type:ty, $epsilon:expr) => {
1280 paste! {
1281 #[test]
1282 fn [<boundary_tests_ $type>]() {
1283 let zero: $type = 0.0.into();
1284 let one: $type = 1.0.into();
1285
1286 assert_relative_eq!(zero.ease_in_quad(), zero, epsilon = $epsilon);
1287 assert_relative_eq!(one.ease_in_quad(), one, epsilon = $epsilon);
1288 assert_relative_eq!(zero.ease_out_quad(), zero, epsilon = $epsilon);
1289 assert_relative_eq!(one.ease_out_quad(), one, epsilon = $epsilon);
1290 assert_relative_eq!(zero.ease_in_out_quad(), zero, epsilon = $epsilon);
1291 assert_relative_eq!(one.ease_in_out_quad(), one, epsilon = $epsilon);
1292
1293 assert_relative_eq!(zero.ease_in_cubic(), zero, epsilon = $epsilon);
1294 assert_relative_eq!(one.ease_in_cubic(), one, epsilon = $epsilon);
1295 assert_relative_eq!(zero.ease_out_cubic(), zero, epsilon = $epsilon);
1296 assert_relative_eq!(one.ease_out_cubic(), one, epsilon = $epsilon);
1297 assert_relative_eq!(zero.ease_in_out_cubic(), zero, epsilon = $epsilon);
1298 assert_relative_eq!(one.ease_in_out_cubic(), one, epsilon = $epsilon);
1299
1300 assert_relative_eq!(zero.ease_in_quart(), zero, epsilon = $epsilon);
1301 assert_relative_eq!(one.ease_in_quart(), one, epsilon = $epsilon);
1302 assert_relative_eq!(zero.ease_out_quart(), zero, epsilon = $epsilon);
1303 assert_relative_eq!(one.ease_out_quart(), one, epsilon = $epsilon);
1304 assert_relative_eq!(zero.ease_in_out_quart(), zero, epsilon = $epsilon);
1305 assert_relative_eq!(one.ease_in_out_quart(), one, epsilon = $epsilon);
1306
1307 assert_relative_eq!(zero.ease_in_quint(), zero, epsilon = $epsilon);
1308 assert_relative_eq!(one.ease_in_quint(), one, epsilon = $epsilon);
1309 assert_relative_eq!(zero.ease_out_quint(), zero, epsilon = $epsilon);
1310 assert_relative_eq!(one.ease_out_quint(), one, epsilon = $epsilon);
1311 assert_relative_eq!(zero.ease_in_out_quint(), zero, epsilon = $epsilon);
1312 assert_relative_eq!(one.ease_in_out_quint(), one, epsilon = $epsilon);
1313
1314 assert_relative_eq!(zero.ease_in_sine(), zero, epsilon = $epsilon);
1315 assert_relative_eq!(one.ease_in_sine(), one, epsilon = $epsilon);
1316 assert_relative_eq!(zero.ease_out_sine(), zero, epsilon = $epsilon);
1317 assert_relative_eq!(one.ease_out_sine(), one, epsilon = $epsilon);
1318 assert_relative_eq!(zero.ease_in_out_sine(), zero, epsilon = $epsilon);
1319 assert_relative_eq!(one.ease_in_out_sine(), one, epsilon = $epsilon);
1320
1321 assert_relative_eq!(zero.ease_in_circ(), zero, epsilon = $epsilon);
1322 assert_relative_eq!(one.ease_in_circ(), one, epsilon = $epsilon);
1323 assert_relative_eq!(zero.ease_out_circ(), zero, epsilon = $epsilon);
1324 assert_relative_eq!(one.ease_out_circ(), one, epsilon = $epsilon);
1325 assert_relative_eq!(zero.ease_in_out_circ(), zero, epsilon = $epsilon);
1326 assert_relative_eq!(one.ease_in_out_circ(), one, epsilon = $epsilon);
1327
1328 assert_relative_eq!(zero.ease_in_back(), zero, epsilon = $epsilon);
1329 assert_relative_eq!(one.ease_in_back(), one, epsilon = $epsilon);
1330 assert_relative_eq!(zero.ease_out_back(), zero, epsilon = $epsilon);
1331 assert_relative_eq!(one.ease_out_back(), one, epsilon = $epsilon);
1332 assert_relative_eq!(zero.ease_in_out_back(), zero, epsilon = $epsilon);
1333 assert_relative_eq!(one.ease_in_out_back(), one, epsilon = $epsilon);
1334
1335 assert_relative_eq!(zero.ease_in_bounce(), zero, epsilon = $epsilon);
1336 assert_relative_eq!(one.ease_in_bounce(), one, epsilon = $epsilon);
1337 assert_relative_eq!(zero.ease_out_bounce(), zero, epsilon = $epsilon);
1338 assert_relative_eq!(one.ease_out_bounce(), one, epsilon = $epsilon);
1339 assert_relative_eq!(zero.ease_in_out_bounce(), zero, epsilon = $epsilon);
1340 assert_relative_eq!(one.ease_in_out_bounce(), one, epsilon = $epsilon);
1341
1342 assert_relative_eq!(zero.ease_in_expo(), zero, epsilon = $epsilon);
1343 assert_relative_eq!(one.ease_in_expo(), one, epsilon = $epsilon);
1344 assert_relative_eq!(zero.ease_out_expo(), zero, epsilon = $epsilon);
1345 assert_relative_eq!(one.ease_out_expo(), one, epsilon = $epsilon);
1346 assert_relative_eq!(zero.ease_in_out_expo(), zero, epsilon = $epsilon);
1347 assert_relative_eq!(one.ease_in_out_expo(), one, epsilon = $epsilon);
1348
1349 assert_relative_eq!(zero.ease_in_elastic(), zero, epsilon = $epsilon);
1350 assert_relative_eq!(one.ease_in_elastic(), one, epsilon = $epsilon);
1351 assert_relative_eq!(zero.ease_out_elastic(), zero, epsilon = $epsilon);
1352 assert_relative_eq!(one.ease_out_elastic(), one, epsilon = $epsilon);
1353 assert_relative_eq!(zero.ease_in_out_elastic(), zero, epsilon = $epsilon);
1354 assert_relative_eq!(one.ease_in_out_elastic(), one, epsilon = $epsilon);
1355
1356 assert_relative_eq!(zero.ease_in_curve(1.0), zero, epsilon = $epsilon);
1357 assert_relative_eq!(one.ease_in_curve(1.0), one, epsilon = $epsilon);
1358 assert_relative_eq!(zero.ease_in_curve(-1.0), zero, epsilon = $epsilon);
1359 assert_relative_eq!(one.ease_in_curve(-1.0), one, epsilon = $epsilon);
1360
1361 assert_relative_eq!(zero.ease_out_curve(1.0), zero, epsilon = $epsilon);
1362 assert_relative_eq!(one.ease_out_curve(1.0), one, epsilon = $epsilon);
1363 assert_relative_eq!(zero.ease_out_curve(-1.0), zero, epsilon = $epsilon);
1364 assert_relative_eq!(one.ease_out_curve(-1.0), one, epsilon = $epsilon);
1365
1366 assert_relative_eq!(zero.ease_in_out_curve(1.0), zero, epsilon = $epsilon);
1367 assert_relative_eq!(one.ease_in_out_curve(1.0), one, epsilon = $epsilon);
1368 assert_relative_eq!(zero.ease_in_out_curve(-1.0), zero, epsilon = $epsilon);
1369 assert_relative_eq!(one.ease_in_out_curve(-1.0), one, epsilon = $epsilon);
1370 }
1371 }
1372 };
1373 }
1374
1375 macro_rules! generate_mirror_symmetry_tests {
1377 ($type:ty, $epsilon:expr) => {
1378 paste! {
1379 #[test]
1380 fn [<mirror_symmetry_ $type>]() {
1381 let points = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
1382 let one: $type = 1.0.into();
1383 for &t in &points {
1384 let t_val: $type = t.into();
1385 let one_minus_t: $type = (1.0 - t).into();
1386
1387 assert_relative_eq!(t_val.ease_out_quad(), one - one_minus_t.ease_in_quad(), epsilon = $epsilon);
1388 assert_relative_eq!(t_val.ease_out_cubic(), one - one_minus_t.ease_in_cubic(), epsilon = $epsilon);
1389 assert_relative_eq!(t_val.ease_out_quart(), one - one_minus_t.ease_in_quart(), epsilon = $epsilon);
1390 assert_relative_eq!(t_val.ease_out_quint(), one - one_minus_t.ease_in_quint(), epsilon = $epsilon);
1391 assert_relative_eq!(t_val.ease_out_sine(), one - one_minus_t.ease_in_sine(), epsilon = $epsilon);
1392 assert_relative_eq!(t_val.ease_out_circ(), one - one_minus_t.ease_in_circ(), epsilon = $epsilon);
1393 assert_relative_eq!(t_val.ease_out_back(), one - one_minus_t.ease_in_back(), epsilon = $epsilon);
1394 assert_relative_eq!(t_val.ease_out_bounce(), one - one_minus_t.ease_in_bounce(), epsilon = $epsilon);
1395 assert_relative_eq!(t_val.ease_out_expo(), one - one_minus_t.ease_in_expo(), epsilon = $epsilon);
1396 assert_relative_eq!(t_val.ease_out_elastic(), one - one_minus_t.ease_in_elastic(), epsilon = $epsilon);
1397 assert_relative_eq!(t_val.ease_out_curve(1.0), one - one_minus_t.ease_in_curve(1.0), epsilon = $epsilon);
1398 }
1399 }
1400 }
1401 };
1402 }
1403
1404 macro_rules! generate_in_out_symmetry_tests {
1406 ($type:ty, $epsilon:expr) => {
1407 paste! {
1408 #[test]
1409 fn [<in_out_symmetry_ $type>]() {
1410 let points = [0.1, 0.2, 0.3, 0.4, 0.5];
1411 let one: $type = 1.0.into();
1412 for &t in &points {
1413 let t_val: $type = t.into();
1414 let one_minus_t: $type = (1.0 - t).into();
1415
1416 assert_relative_eq!(t_val.ease_in_out_quad(), one - one_minus_t.ease_in_out_quad(), epsilon = $epsilon);
1417 assert_relative_eq!(t_val.ease_in_out_cubic(), one - one_minus_t.ease_in_out_cubic(), epsilon = $epsilon);
1418 assert_relative_eq!(t_val.ease_in_out_quart(), one - one_minus_t.ease_in_out_quart(), epsilon = $epsilon);
1419 assert_relative_eq!(t_val.ease_in_out_quint(), one - one_minus_t.ease_in_out_quint(), epsilon = $epsilon);
1420 assert_relative_eq!(t_val.ease_in_out_sine(), one - one_minus_t.ease_in_out_sine(), epsilon = $epsilon);
1421 assert_relative_eq!(t_val.ease_in_out_circ(), one - one_minus_t.ease_in_out_circ(), epsilon = $epsilon);
1422 assert_relative_eq!(t_val.ease_in_out_back(), one - one_minus_t.ease_in_out_back(), epsilon = $epsilon);
1423 assert_relative_eq!(t_val.ease_in_out_bounce(), one - one_minus_t.ease_in_out_bounce(), epsilon = $epsilon);
1424 assert_relative_eq!(t_val.ease_in_out_expo(), one - one_minus_t.ease_in_out_expo(), epsilon = $epsilon);
1425 assert_relative_eq!(t_val.ease_in_out_elastic(), one - one_minus_t.ease_in_out_elastic(), epsilon = $epsilon);
1426 assert_relative_eq!(t_val.ease_in_out_curve(1.0), one - one_minus_t.ease_in_out_curve(1.0), epsilon = $epsilon);
1427 }
1428 }
1429 }
1430 };
1431 }
1432
1433 generate_boundary_tests!(f32, 1e-6);
1435 generate_mirror_symmetry_tests!(f32, 1e-6);
1436 generate_in_out_symmetry_tests!(f32, 1e-6);
1437
1438 generate_boundary_tests!(f64, 1e-7);
1440 generate_mirror_symmetry_tests!(f64, 1e-7);
1441 generate_in_out_symmetry_tests!(f64, 1e-7);
1442 }
1443
1444 #[cfg(feature = "nightly")]
1445 #[test]
1446 fn test_mixed_arguments() {
1447 let arg: f32x4 = Simd::splat(0.5);
1448 {
1449 let curve = 1.0f32;
1450 arg.ease_in_out_curve(curve);
1451 }
1452
1453 {
1454 let curve = f32x4::splat(1.0);
1455 arg.ease_in_out_curve(curve);
1456 }
1457 }
1458}
1459
1460#[cfg(test)]
1461mod reference_value_tests {
1462 use super::EasingArgument;
1463 use approx::assert_relative_eq;
1464
1465 macro_rules! generate_reference_tests {
1466 ($func:ident, $vals:expr) => {
1467 #[test]
1468 fn $func() {
1469 let inputs = [0.2f32, 0.4, 0.5, 0.6, 0.8];
1470 #[allow(clippy::approx_constant)]
1471 let expected = $vals;
1472 for (&input, &exp) in inputs.iter().zip(expected.iter()) {
1473 assert_relative_eq!(input.$func(), exp, epsilon = 1e-6);
1474 }
1475 }
1476 };
1477 ($func:ident, $param:expr, $vals:expr) => {
1478 #[test]
1479 fn $func() {
1480 let inputs = [0.2f32, 0.4, 0.5, 0.6, 0.8];
1481 #[allow(clippy::approx_constant)]
1482 let expected = $vals;
1483 for (&input, &exp) in inputs.iter().zip(expected.iter()) {
1484 assert_relative_eq!(input.$func($param), exp, epsilon = 1e-6);
1485 }
1486 }
1487 };
1488 }
1489
1490 generate_reference_tests!(
1491 ease_in_quad,
1492 [0.040000, 0.160000, 0.250000, 0.360000, 0.640000]
1493 );
1494 generate_reference_tests!(
1495 ease_out_quad,
1496 [0.360000, 0.640000, 0.750000, 0.840000, 0.960000]
1497 );
1498 generate_reference_tests!(
1499 ease_in_out_quad,
1500 [0.080000, 0.320000, 0.500000, 0.680000, 0.920000]
1501 );
1502 generate_reference_tests!(
1503 ease_in_cubic,
1504 [0.008000, 0.064000, 0.125000, 0.216000, 0.512000]
1505 );
1506 generate_reference_tests!(
1507 ease_out_cubic,
1508 [0.488000, 0.784000, 0.875000, 0.936000, 0.992000]
1509 );
1510 generate_reference_tests!(
1511 ease_in_out_cubic,
1512 [0.032000, 0.256000, 0.500000, 0.744000, 0.968000]
1513 );
1514 generate_reference_tests!(
1515 ease_in_quart,
1516 [0.001600, 0.025600, 0.062500, 0.129600, 0.409600]
1517 );
1518 generate_reference_tests!(
1519 ease_out_quart,
1520 [0.590400, 0.870400, 0.937500, 0.974400, 0.998400]
1521 );
1522 generate_reference_tests!(
1523 ease_in_out_quart,
1524 [0.012800, 0.204800, 0.500000, 0.795200, 0.987200]
1525 );
1526 generate_reference_tests!(
1527 ease_in_quint,
1528 [0.000320, 0.010240, 0.031250, 0.077760, 0.327680]
1529 );
1530 generate_reference_tests!(
1531 ease_out_quint,
1532 [0.672320, 0.922240, 0.968750, 0.989760, 0.999680]
1533 );
1534 generate_reference_tests!(
1535 ease_in_out_quint,
1536 [0.005120, 0.163840, 0.500000, 0.836160, 0.994880]
1537 );
1538 generate_reference_tests!(
1539 ease_in_sine,
1540 [0.048943, 0.190983, 0.292893, 0.412215, 0.690983]
1541 );
1542 generate_reference_tests!(
1543 ease_out_sine,
1544 [0.309017, 0.587785, 0.707107, 0.809017, 0.951057]
1545 );
1546 generate_reference_tests!(
1547 ease_in_out_sine,
1548 [0.095491, 0.345492, 0.500000, 0.654509, 0.904509]
1549 );
1550 generate_reference_tests!(
1551 ease_in_circ,
1552 [0.020204, 0.083485, 0.133975, 0.200000, 0.400000]
1553 );
1554 generate_reference_tests!(
1555 ease_out_circ,
1556 [0.600000, 0.800000, 0.866025, 0.916515, 0.979796]
1557 );
1558 generate_reference_tests!(
1559 ease_in_out_circ,
1560 [0.041742, 0.200000, 0.500000, 0.800000, 0.958258]
1561 );
1562 generate_reference_tests!(
1563 ease_in_back,
1564 [-0.046451, -0.099352, -0.087698, -0.029028, 0.294198]
1565 );
1566 generate_reference_tests!(
1567 ease_out_back,
1568 [0.705802, 1.029027, 1.087698, 1.099352, 1.046_45]
1569 );
1570 generate_reference_tests!(
1571 ease_in_out_back,
1572 [-0.092556, 0.089926, 0.500000, 0.910074, 1.092556]
1573 );
1574 generate_reference_tests!(
1575 ease_in_bounce,
1576 [0.060000, 0.227500, 0.234375, 0.090000, 0.697500]
1577 );
1578 generate_reference_tests!(
1579 ease_out_bounce,
1580 [0.302500, 0.910000, 0.765625, 0.772500, 0.940000]
1581 );
1582 generate_reference_tests!(
1583 ease_in_out_bounce,
1584 [0.113750, 0.348750, 0.500000, 0.651250, 0.886250]
1585 );
1586 generate_reference_tests!(
1587 ease_in_expo,
1588 [0.003906, 0.015625, 0.031250, 0.062500, 0.250000]
1589 );
1590 generate_reference_tests!(
1591 ease_out_expo,
1592 [0.750000, 0.937500, 0.968750, 0.984375, 0.996094]
1593 );
1594 generate_reference_tests!(
1595 ease_in_out_expo,
1596 [0.007812, 0.125000, 0.500000, 0.875000, 0.992188]
1597 );
1598 generate_reference_tests!(
1599 ease_in_elastic,
1600 [-0.001953, 0.015625, -0.015625, -0.031250, -0.125000]
1601 );
1602 generate_reference_tests!(
1603 ease_out_elastic,
1604 [1.125, 1.031_25, 1.015625, 0.984375, 1.001953]
1605 );
1606 generate_reference_tests!(
1607 ease_in_out_elastic,
1608 [-0.003906, -0.117462, 0.500000, 1.117462, 1.003906]
1609 );
1610 generate_reference_tests!(
1611 ease_in_curve,
1612 1.0,
1613 [0.128851, 0.286231, 0.377541, 0.478454, 0.713236]
1614 );
1615 generate_reference_tests!(
1616 ease_out_curve,
1617 1.0,
1618 [0.286764, 0.521546, 0.622459, 0.713769, 0.871149]
1619 );
1620 generate_reference_tests!(
1621 ease_in_out_curve,
1622 1.0,
1623 [0.143115, 0.356618, 0.500000, 0.643382, 0.856885]
1624 );
1625}