nova_easing/
lib.rs

1// Copyright (C) 2025 Tim Blechmann
2// SPDX-License-Identifier: MIT
3
4#![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
21////////////////////////////////////////////////////////////////////////////////////////////////////
22
23// Marker trait for scalar float types we support.
24trait 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
118/// A trait providing easing functions for smooth interpolation.
119///
120/// Easing functions take a value `t` in the range [0, 1] and return an eased value
121/// in the same range, useful for animations and transitions.
122///
123/// Supported for scalar types (`f32`, `f64`) and SIMD vectors (with `nightly` feature).
124/// See [easings.net](https://easings.net/) for visualizations.
125pub trait EasingArgument: internal::Sealed + Sized + Copy {
126    /// Applies quadratic easing in. Starts slow and accelerates.
127    ///
128    /// See [easings.net](https://easings.net/#easeInQuad) for visualization.
129    #[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    /// Applies quadratic easing out. Starts fast and decelerates.
138    ///
139    /// See [easings.net](https://easings.net/#easeOutQuad) for visualization.
140    #[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    /// Applies quadratic easing in-out. Accelerates then decelerates.
149    ///
150    /// See [easings.net](https://easings.net/#easeInOutQuad) for visualization.
151    #[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    /// Applies cubic easing in. Starts slow and accelerates more gradually.
160    ///
161    /// See [easings.net](https://easings.net/#easeInCubic) for visualization.
162    #[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    /// Applies cubic easing out. Starts fast and decelerates more gradually.
171    ///
172    /// See [easings.net](https://easings.net/#easeOutCubic) for visualization.
173    #[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    /// Applies cubic easing in-out. Accelerates then decelerates more gradually.
182    ///
183    /// See [easings.net](https://easings.net/#easeInOutCubic) for visualization.
184    #[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    /// Applies quartic easing in. Starts very slow and accelerates sharply.
193    ///
194    /// See [easings.net](https://easings.net/#easeInQuart) for visualization.
195    #[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    /// Applies quartic easing out. Starts very fast and decelerates sharply.
204    ///
205    /// See [easings.net](https://easings.net/#easeOutQuart) for visualization.
206    #[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    /// Applies quartic easing in-out. Accelerates sharply then decelerates sharply.
215    ///
216    /// See [easings.net](https://easings.net/#easeInOutQuart) for visualization.
217    #[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    /// Applies quintic easing in. Starts extremely slow and accelerates very sharply.
226    ///
227    /// See [easings.net](https://easings.net/#easeInQuint) for visualization.
228    #[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    /// Applies quintic easing out. Starts extremely fast and decelerates very sharply.
237    ///
238    /// See [easings.net](https://easings.net/#easeOutQuint) for visualization.
239    #[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    /// Applies quintic easing in-out. Accelerates very sharply then decelerates very sharply.
248    ///
249    /// See [easings.net](https://easings.net/#easeInOutQuint) for visualization.
250    #[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    /// Applies back easing in-out. Accelerates with overshoot then decelerates with overshoot.
259    ///
260    /// See [easings.net](https://easings.net/#easeInOutBack) for visualization.
261    #[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    /// Applies bounce easing in. Starts with bounces and settles.
270    ///
271    /// See [easings.net](https://easings.net/#easeInBounce) for visualization.
272    #[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    /// Applies bounce easing out. Ends with bounces.
282    ///
283    /// See [easings.net](https://easings.net/#easeOutBounce) for visualization.
284    #[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    /// Applies bounce easing in-out. Bounces at start and end.
293    ///
294    /// See [easings.net](https://easings.net/#easeInOutBounce) for visualization.
295    #[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    /// Applies exponential easing in. Starts very slow and accelerates exponentially.
304    ///
305    /// See [easings.net](https://easings.net/#easeInExpo) for visualization.
306    #[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    /// Applies exponential easing out. Starts very fast and decelerates exponentially.
315    ///
316    /// See [easings.net](https://easings.net/#easeOutExpo) for visualization.
317    #[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    /// Applies exponential easing in-out. Accelerates exponentially then decelerates exponentially.
326    ///
327    /// See [easings.net](https://easings.net/#easeInOutExpo) for visualization.
328    #[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    /// Applies elastic easing in. Starts with oscillation and settles.
337    ///
338    /// See [easings.net](https://easings.net/#easeInElastic) for visualization.
339    #[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    /// Applies elastic easing out. Ends with oscillation.
348    ///
349    /// See [easings.net](https://easings.net/#easeOutElastic) for visualization.
350    #[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    /// Applies elastic easing in-out. Oscillates at start and end.
359    ///
360    /// See [easings.net](https://easings.net/#easeInOutElastic) for visualization.
361    #[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    /// Applies sine easing in. Starts slow with a smooth curve.
370    ///
371    /// See [easings.net](https://easings.net/#easeInSine) for visualization.
372    #[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    /// Applies sine easing out. Ends slow with a smooth curve.
383    ///
384    /// See [easings.net](https://easings.net/#easeOutSine) for visualization.
385    #[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    /// Applies sine easing in-out. Smooth acceleration and deceleration.
395    ///
396    /// See [easings.net](https://easings.net/#easeInOutSine) for visualization.
397    #[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    /// Applies circular easing in. Starts very slow and accelerates sharply.
408    ///
409    /// See [easings.net](https://easings.net/#easeInCirc) for visualization.
410    #[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    /// Applies circular easing out. Starts very fast and decelerates sharply.
420    ///
421    /// See [easings.net](https://easings.net/#easeOutCirc) for visualization.
422    #[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    /// Applies circular easing in-out. Accelerates sharply then decelerates sharply.
432    ///
433    /// See [easings.net](https://easings.net/#easeInOutCirc) for visualization.
434    #[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    /// Applies back easing in. Starts with a slight overshoot.
443    ///
444    /// See [easings.net](https://easings.net/#easeInBack) for visualization.
445    #[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    /// Applies back easing out. Ends with a slight overshoot.
457    ///
458    /// See [easings.net](https://easings.net/#easeOutBack) for visualization.
459    #[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    /// Applies custom exponential easing in with a curve parameter.
472    ///
473    /// Accelerates from slow to fast using exponential growth controlled by the `curve` parameter.
474    /// - `curve > 0`: Convex curve, steeper acceleration (e.g., `curve = 1.0` for moderate, `curve = 4.0` for sharp).
475    /// - `curve < 0`: Concave curve, gentler acceleration (e.g., `curve = -1.0` for soft, `curve = -4.0` for very gradual).
476    /// - `curve ≈ 0`: Approximates linear easing.
477    ///
478    /// The `curve` parameter can be a scalar or SIMD vector matching the easing argument type.
479    /// Inspired by SuperCollider's `Env` curve parameter for envelope shaping.
480    /// See [SuperCollider Env documentation](https://doc.sccode.org/Classes/Env.html) for more on curve values.
481    #[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    /// Applies custom exponential easing out with a curve parameter.
491    ///
492    /// Decelerates from fast to slow using exponential decay controlled by the `curve` parameter.
493    /// - `curve > 0`: Convex curve, steeper deceleration.
494    /// - `curve < 0`: Concave curve, gentler deceleration.
495    /// - `curve ≈ 0`: Approximates linear easing.
496    ///
497    /// The `curve` parameter can be a scalar or SIMD vector matching the easing argument type.
498    /// Mirrors `ease_in_curve` but in reverse. Inspired by SuperCollider's `Env` curve parameter.
499    /// See [SuperCollider Env documentation](https://doc.sccode.org/Classes/Env.html).
500    #[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    /// Applies custom exponential easing in-out with a curve parameter.
510    ///
511    /// Accelerates then decelerates using exponential transitions controlled by the `curve` parameter.
512    /// - `curve > 0`: Sharper acceleration and deceleration.
513    /// - `curve < 0`: Softer transitions.
514    /// - `curve ≈ 0`: Approximates linear easing.
515    ///
516    /// The `curve` parameter can be a scalar or SIMD vector matching the easing argument type.
517    /// Combines `ease_in_curve` and `ease_out_curve` for smooth bidirectional transitions.
518    /// Inspired by SuperCollider's `Env` curve parameter for envelope shaping.
519    /// See [SuperCollider Env documentation](https://doc.sccode.org/Classes/Env.html).
520    #[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
530////////////////////////////////////////////////////////////////////////////////////////////////////
531
532trait 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
589////////////////////////////////////////////////////////////////////////////////////////////////////
590
591impl<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////////////////////////////////////////////////////////////////////////////////////////////////////
856
857#[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////////////////////////////////////////////////////////////////////////////////////////////////////
1177
1178#[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        // Boundary tests: f(0) == 0 and f(1) == 1 for all functions
1278        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        // Mirror symmetry: ease_out(t) == 1 - ease_in(1 - t)
1376        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        // In-out symmetry: ease_in_out(t) == 1 - ease_in_out(1 - t)
1405        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        // Instantiate for f32
1434        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        // Instantiate for f64
1439        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}