1use crate::{
70 curve::{Curve, CurveExt, FunctionCurve, Interval},
71 Dir2, Dir3, Dir3A, Isometry2d, Isometry3d, Quat, Rot2, VectorSpace,
72};
73
74#[cfg(feature = "bevy_reflect")]
75use bevy_reflect::std_traits::ReflectDefault;
76
77use variadics_please::all_tuples_enumerated;
78
79pub trait Ease: Sized {
88 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self>;
96}
97
98impl<V: VectorSpace<Scalar = f32>> Ease for V {
99 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
100 FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t))
101 }
102}
103
104impl Ease for Rot2 {
105 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
106 FunctionCurve::new(Interval::EVERYWHERE, move |t| Rot2::slerp(start, end, t))
107 }
108}
109
110impl Ease for Quat {
111 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
112 let dot = start.dot(end);
113 let end_adjusted = if dot < 0.0 { -end } else { end };
114 let difference = end_adjusted * start.inverse();
115 let (axis, angle) = difference.to_axis_angle();
116 FunctionCurve::new(Interval::EVERYWHERE, move |s| {
117 Quat::from_axis_angle(axis, angle * s) * start
118 })
119 }
120}
121
122impl Ease for Dir2 {
123 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
124 FunctionCurve::new(Interval::EVERYWHERE, move |t| Dir2::slerp(start, end, t))
125 }
126}
127
128impl Ease for Dir3 {
129 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
130 let difference_quat = Quat::from_rotation_arc(start.as_vec3(), end.as_vec3());
131 Quat::interpolating_curve_unbounded(Quat::IDENTITY, difference_quat).map(move |q| q * start)
132 }
133}
134
135impl Ease for Dir3A {
136 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
137 let difference_quat =
138 Quat::from_rotation_arc(start.as_vec3a().into(), end.as_vec3a().into());
139 Quat::interpolating_curve_unbounded(Quat::IDENTITY, difference_quat).map(move |q| q * start)
140 }
141}
142
143impl Ease for Isometry3d {
144 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
145 FunctionCurve::new(Interval::EVERYWHERE, move |t| {
146 Isometry3d {
149 rotation: Quat::interpolating_curve_unbounded(start.rotation, end.rotation)
150 .sample_unchecked(t),
151 translation: crate::Vec3A::interpolating_curve_unbounded(
152 start.translation,
153 end.translation,
154 )
155 .sample_unchecked(t),
156 }
157 })
158 }
159}
160
161impl Ease for Isometry2d {
162 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
163 FunctionCurve::new(Interval::EVERYWHERE, move |t| {
164 Isometry2d {
167 rotation: Rot2::interpolating_curve_unbounded(start.rotation, end.rotation)
168 .sample_unchecked(t),
169 translation: crate::Vec2::interpolating_curve_unbounded(
170 start.translation,
171 end.translation,
172 )
173 .sample_unchecked(t),
174 }
175 })
176 }
177}
178
179macro_rules! impl_ease_tuple {
180 ($(#[$meta:meta])* $(($n:tt, $T:ident)),*) => {
181 $(#[$meta])*
182 impl<$($T: Ease),*> Ease for ($($T,)*) {
183 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
184 let curve_tuple =
185 (
186 $(
187 <$T as Ease>::interpolating_curve_unbounded(start.$n, end.$n),
188 )*
189 );
190
191 FunctionCurve::new(Interval::EVERYWHERE, move |t|
192 (
193 $(
194 curve_tuple.$n.sample_unchecked(t),
195 )*
196 )
197 )
198 }
199 }
200 };
201}
202
203all_tuples_enumerated!(
204 #[doc(fake_variadic)]
205 impl_ease_tuple,
206 1,
207 11,
208 T
209);
210
211#[derive(Clone, Debug)]
297#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
298#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
299pub struct EasingCurve<T> {
300 start: T,
301 end: T,
302 ease_fn: EaseFunction,
303}
304
305impl<T> EasingCurve<T> {
306 pub fn new(start: T, end: T, ease_fn: EaseFunction) -> Self {
313 Self {
314 start,
315 end,
316 ease_fn,
317 }
318 }
319}
320
321impl<T> Curve<T> for EasingCurve<T>
322where
323 T: Ease + Clone,
324{
325 #[inline]
326 fn domain(&self) -> Interval {
327 Interval::UNIT
328 }
329
330 #[inline]
331 fn sample_unchecked(&self, t: f32) -> T {
332 let remapped_t = self.ease_fn.eval(t);
333 T::interpolating_curve_unbounded(self.start.clone(), self.end.clone())
334 .sample_unchecked(remapped_t)
335 }
336}
337
338#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
343#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
344#[cfg_attr(
345 feature = "bevy_reflect",
346 derive(bevy_reflect::Reflect),
347 reflect(Clone, Default, PartialEq)
348)]
349pub enum JumpAt {
350 #[doc = include_str!("../../images/easefunction/StartSteps.svg")]
353 Start,
354 #[doc = include_str!("../../images/easefunction/EndSteps.svg")]
357 #[default]
358 End,
359 #[doc = include_str!("../../images/easefunction/NoneSteps.svg")]
362 None,
363 #[doc = include_str!("../../images/easefunction/BothSteps.svg")]
366 Both,
367}
368
369impl JumpAt {
370 #[inline]
371 pub(crate) fn eval(self, num_steps: usize, t: f32) -> f32 {
372 use crate::ops;
373
374 let (a, b) = match self {
375 JumpAt::Start => (1.0, 0),
376 JumpAt::End => (0.0, 0),
377 JumpAt::None => (0.0, -1),
378 JumpAt::Both => (1.0, 1),
379 };
380
381 let current_step = ops::floor(t * num_steps as f32) + a;
382 let step_size = (num_steps as isize + b).max(1) as f32;
383
384 (current_step / step_size).clamp(0.0, 1.0)
385 }
386}
387
388#[non_exhaustive]
427#[derive(Debug, Copy, Clone, PartialEq)]
428#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
429#[cfg_attr(
430 feature = "bevy_reflect",
431 derive(bevy_reflect::Reflect),
432 reflect(Clone, PartialEq)
433)]
434pub enum EaseFunction {
436 #[doc = include_str!("../../images/easefunction/Linear.svg")]
439 Linear,
440
441 #[doc = include_str!("../../images/easefunction/QuadraticIn.svg")]
449 QuadraticIn,
450 #[doc = include_str!("../../images/easefunction/QuadraticOut.svg")]
458 QuadraticOut,
459 #[doc = include_str!("../../images/easefunction/QuadraticInOut.svg")]
466 QuadraticInOut,
467
468 #[doc = include_str!("../../images/easefunction/CubicIn.svg")]
477 CubicIn,
478 #[doc = include_str!("../../images/easefunction/CubicOut.svg")]
481 CubicOut,
482 #[doc = include_str!("../../images/easefunction/CubicInOut.svg")]
492 CubicInOut,
493
494 #[doc = include_str!("../../images/easefunction/QuarticIn.svg")]
497 QuarticIn,
498 #[doc = include_str!("../../images/easefunction/QuarticOut.svg")]
501 QuarticOut,
502 #[doc = include_str!("../../images/easefunction/QuarticInOut.svg")]
505 QuarticInOut,
506
507 #[doc = include_str!("../../images/easefunction/QuinticIn.svg")]
510 QuinticIn,
511 #[doc = include_str!("../../images/easefunction/QuinticOut.svg")]
514 QuinticOut,
515 #[doc = include_str!("../../images/easefunction/QuinticInOut.svg")]
523 QuinticInOut,
524
525 #[doc = include_str!("../../images/easefunction/SmoothStepIn.svg")]
530 SmoothStepIn,
531 #[doc = include_str!("../../images/easefunction/SmoothStepOut.svg")]
536 SmoothStepOut,
537 #[doc = include_str!("../../images/easefunction/SmoothStep.svg")]
550 SmoothStep,
551
552 #[doc = include_str!("../../images/easefunction/SmootherStepIn.svg")]
557 SmootherStepIn,
558 #[doc = include_str!("../../images/easefunction/SmootherStepOut.svg")]
563 SmootherStepOut,
564 #[doc = include_str!("../../images/easefunction/SmootherStep.svg")]
575 SmootherStep,
576
577 #[doc = include_str!("../../images/easefunction/SineIn.svg")]
580 SineIn,
581 #[doc = include_str!("../../images/easefunction/SineOut.svg")]
584 SineOut,
585 #[doc = include_str!("../../images/easefunction/SineInOut.svg")]
588 SineInOut,
589
590 #[doc = include_str!("../../images/easefunction/CircularIn.svg")]
593 CircularIn,
594 #[doc = include_str!("../../images/easefunction/CircularOut.svg")]
597 CircularOut,
598 #[doc = include_str!("../../images/easefunction/CircularInOut.svg")]
601 CircularInOut,
602
603 #[doc = include_str!("../../images/easefunction/ExponentialIn.svg")]
609 ExponentialIn,
610 #[doc = include_str!("../../images/easefunction/ExponentialOut.svg")]
616 ExponentialOut,
617 #[doc = include_str!("../../images/easefunction/ExponentialInOut.svg")]
620 ExponentialInOut,
621
622 #[doc = include_str!("../../images/easefunction/ElasticIn.svg")]
625 ElasticIn,
626 #[doc = include_str!("../../images/easefunction/ElasticOut.svg")]
629 ElasticOut,
630 #[doc = include_str!("../../images/easefunction/ElasticInOut.svg")]
633 ElasticInOut,
634
635 #[doc = include_str!("../../images/easefunction/BackIn.svg")]
638 BackIn,
639 #[doc = include_str!("../../images/easefunction/BackOut.svg")]
642 BackOut,
643 #[doc = include_str!("../../images/easefunction/BackInOut.svg")]
646 BackInOut,
647
648 #[doc = include_str!("../../images/easefunction/BounceIn.svg")]
651 BounceIn,
652 #[doc = include_str!("../../images/easefunction/BounceOut.svg")]
655 BounceOut,
656 #[doc = include_str!("../../images/easefunction/BounceInOut.svg")]
659 BounceInOut,
660
661 Steps(usize, JumpAt),
664
665 #[doc = include_str!("../../images/easefunction/Elastic.svg")]
668 Elastic(f32),
669}
670
671#[doc = include_str!("../../images/easefunction/Linear.svg")]
674#[derive(Copy, Clone)]
675pub struct LinearCurve;
676
677#[doc = include_str!("../../images/easefunction/QuadraticIn.svg")]
685#[derive(Copy, Clone)]
686pub struct QuadraticInCurve;
687
688#[doc = include_str!("../../images/easefunction/QuadraticOut.svg")]
696#[derive(Copy, Clone)]
697pub struct QuadraticOutCurve;
698
699#[doc = include_str!("../../images/easefunction/QuadraticInOut.svg")]
706#[derive(Copy, Clone)]
707pub struct QuadraticInOutCurve;
708
709#[doc = include_str!("../../images/easefunction/CubicIn.svg")]
718#[derive(Copy, Clone)]
719pub struct CubicInCurve;
720
721#[doc = include_str!("../../images/easefunction/CubicOut.svg")]
724#[derive(Copy, Clone)]
725pub struct CubicOutCurve;
726
727#[doc = include_str!("../../images/easefunction/CubicInOut.svg")]
737#[derive(Copy, Clone)]
738pub struct CubicInOutCurve;
739
740#[doc = include_str!("../../images/easefunction/QuarticIn.svg")]
743#[derive(Copy, Clone)]
744pub struct QuarticInCurve;
745
746#[doc = include_str!("../../images/easefunction/QuarticOut.svg")]
749#[derive(Copy, Clone)]
750pub struct QuarticOutCurve;
751
752#[doc = include_str!("../../images/easefunction/QuarticInOut.svg")]
755#[derive(Copy, Clone)]
756pub struct QuarticInOutCurve;
757
758#[doc = include_str!("../../images/easefunction/QuinticIn.svg")]
761#[derive(Copy, Clone)]
762pub struct QuinticInCurve;
763
764#[doc = include_str!("../../images/easefunction/QuinticOut.svg")]
767#[derive(Copy, Clone)]
768pub struct QuinticOutCurve;
769
770#[doc = include_str!("../../images/easefunction/QuinticInOut.svg")]
778#[derive(Copy, Clone)]
779pub struct QuinticInOutCurve;
780
781#[doc = include_str!("../../images/easefunction/SmoothStepIn.svg")]
786#[derive(Copy, Clone)]
787pub struct SmoothStepInCurve;
788
789#[doc = include_str!("../../images/easefunction/SmoothStepOut.svg")]
794#[derive(Copy, Clone)]
795pub struct SmoothStepOutCurve;
796
797#[doc = include_str!("../../images/easefunction/SmoothStep.svg")]
810#[derive(Copy, Clone)]
811pub struct SmoothStepCurve;
812
813#[doc = include_str!("../../images/easefunction/SmootherStepIn.svg")]
818#[derive(Copy, Clone)]
819pub struct SmootherStepInCurve;
820
821#[doc = include_str!("../../images/easefunction/SmootherStepOut.svg")]
826#[derive(Copy, Clone)]
827pub struct SmootherStepOutCurve;
828
829#[doc = include_str!("../../images/easefunction/SmootherStep.svg")]
840#[derive(Copy, Clone)]
841pub struct SmootherStepCurve;
842
843#[doc = include_str!("../../images/easefunction/SineIn.svg")]
846#[derive(Copy, Clone)]
847pub struct SineInCurve;
848
849#[doc = include_str!("../../images/easefunction/SineOut.svg")]
852#[derive(Copy, Clone)]
853pub struct SineOutCurve;
854
855#[doc = include_str!("../../images/easefunction/SineInOut.svg")]
858#[derive(Copy, Clone)]
859pub struct SineInOutCurve;
860
861#[doc = include_str!("../../images/easefunction/CircularIn.svg")]
864#[derive(Copy, Clone)]
865pub struct CircularInCurve;
866
867#[doc = include_str!("../../images/easefunction/CircularOut.svg")]
870#[derive(Copy, Clone)]
871pub struct CircularOutCurve;
872
873#[doc = include_str!("../../images/easefunction/CircularInOut.svg")]
876#[derive(Copy, Clone)]
877pub struct CircularInOutCurve;
878
879#[doc = include_str!("../../images/easefunction/ExponentialIn.svg")]
885#[derive(Copy, Clone)]
886pub struct ExponentialInCurve;
887
888#[doc = include_str!("../../images/easefunction/ExponentialOut.svg")]
894#[derive(Copy, Clone)]
895pub struct ExponentialOutCurve;
896
897#[doc = include_str!("../../images/easefunction/ExponentialInOut.svg")]
900#[derive(Copy, Clone)]
901pub struct ExponentialInOutCurve;
902
903#[doc = include_str!("../../images/easefunction/ElasticIn.svg")]
906#[derive(Copy, Clone)]
907pub struct ElasticInCurve;
908
909#[doc = include_str!("../../images/easefunction/ElasticOut.svg")]
912#[derive(Copy, Clone)]
913pub struct ElasticOutCurve;
914
915#[doc = include_str!("../../images/easefunction/ElasticInOut.svg")]
918#[derive(Copy, Clone)]
919pub struct ElasticInOutCurve;
920
921#[doc = include_str!("../../images/easefunction/BackIn.svg")]
924#[derive(Copy, Clone)]
925pub struct BackInCurve;
926
927#[doc = include_str!("../../images/easefunction/BackOut.svg")]
930#[derive(Copy, Clone)]
931pub struct BackOutCurve;
932
933#[doc = include_str!("../../images/easefunction/BackInOut.svg")]
936#[derive(Copy, Clone)]
937pub struct BackInOutCurve;
938
939#[doc = include_str!("../../images/easefunction/BounceIn.svg")]
942#[derive(Copy, Clone)]
943pub struct BounceInCurve;
944
945#[doc = include_str!("../../images/easefunction/BounceOut.svg")]
948#[derive(Copy, Clone)]
949pub struct BounceOutCurve;
950
951#[doc = include_str!("../../images/easefunction/BounceInOut.svg")]
954#[derive(Copy, Clone)]
955pub struct BounceInOutCurve;
956
957#[derive(Copy, Clone)]
960pub struct StepsCurve(pub usize, pub JumpAt);
961
962#[doc = include_str!("../../images/easefunction/Elastic.svg")]
965#[derive(Copy, Clone)]
966pub struct ElasticCurve(pub f32);
967
968macro_rules! impl_ease_unit_struct {
970 ($ty: ty, $fn: ident) => {
971 impl Curve<f32> for $ty {
972 #[inline]
973 fn domain(&self) -> Interval {
974 Interval::UNIT
975 }
976
977 #[inline]
978 fn sample_unchecked(&self, t: f32) -> f32 {
979 easing_functions::$fn(t)
980 }
981 }
982 };
983}
984
985impl_ease_unit_struct!(LinearCurve, linear);
986impl_ease_unit_struct!(QuadraticInCurve, quadratic_in);
987impl_ease_unit_struct!(QuadraticOutCurve, quadratic_out);
988impl_ease_unit_struct!(QuadraticInOutCurve, quadratic_in_out);
989impl_ease_unit_struct!(CubicInCurve, cubic_in);
990impl_ease_unit_struct!(CubicOutCurve, cubic_out);
991impl_ease_unit_struct!(CubicInOutCurve, cubic_in_out);
992impl_ease_unit_struct!(QuarticInCurve, quartic_in);
993impl_ease_unit_struct!(QuarticOutCurve, quartic_out);
994impl_ease_unit_struct!(QuarticInOutCurve, quartic_in_out);
995impl_ease_unit_struct!(QuinticInCurve, quintic_in);
996impl_ease_unit_struct!(QuinticOutCurve, quintic_out);
997impl_ease_unit_struct!(QuinticInOutCurve, quintic_in_out);
998impl_ease_unit_struct!(SmoothStepInCurve, smoothstep_in);
999impl_ease_unit_struct!(SmoothStepOutCurve, smoothstep_out);
1000impl_ease_unit_struct!(SmoothStepCurve, smoothstep);
1001impl_ease_unit_struct!(SmootherStepInCurve, smootherstep_in);
1002impl_ease_unit_struct!(SmootherStepOutCurve, smootherstep_out);
1003impl_ease_unit_struct!(SmootherStepCurve, smootherstep);
1004impl_ease_unit_struct!(SineInCurve, sine_in);
1005impl_ease_unit_struct!(SineOutCurve, sine_out);
1006impl_ease_unit_struct!(SineInOutCurve, sine_in_out);
1007impl_ease_unit_struct!(CircularInCurve, circular_in);
1008impl_ease_unit_struct!(CircularOutCurve, circular_out);
1009impl_ease_unit_struct!(CircularInOutCurve, circular_in_out);
1010impl_ease_unit_struct!(ExponentialInCurve, exponential_in);
1011impl_ease_unit_struct!(ExponentialOutCurve, exponential_out);
1012impl_ease_unit_struct!(ExponentialInOutCurve, exponential_in_out);
1013impl_ease_unit_struct!(ElasticInCurve, elastic_in);
1014impl_ease_unit_struct!(ElasticOutCurve, elastic_out);
1015impl_ease_unit_struct!(ElasticInOutCurve, elastic_in_out);
1016impl_ease_unit_struct!(BackInCurve, back_in);
1017impl_ease_unit_struct!(BackOutCurve, back_out);
1018impl_ease_unit_struct!(BackInOutCurve, back_in_out);
1019impl_ease_unit_struct!(BounceInCurve, bounce_in);
1020impl_ease_unit_struct!(BounceOutCurve, bounce_out);
1021impl_ease_unit_struct!(BounceInOutCurve, bounce_in_out);
1022
1023impl Curve<f32> for StepsCurve {
1024 #[inline]
1025 fn domain(&self) -> Interval {
1026 Interval::UNIT
1027 }
1028
1029 #[inline]
1030 fn sample_unchecked(&self, t: f32) -> f32 {
1031 easing_functions::steps(self.0, self.1, t)
1032 }
1033}
1034
1035impl Curve<f32> for ElasticCurve {
1036 #[inline]
1037 fn domain(&self) -> Interval {
1038 Interval::UNIT
1039 }
1040
1041 #[inline]
1042 fn sample_unchecked(&self, t: f32) -> f32 {
1043 easing_functions::elastic(self.0, t)
1044 }
1045}
1046
1047mod easing_functions {
1048 use core::f32::consts::{FRAC_PI_2, FRAC_PI_3, PI};
1049
1050 use crate::{ops, FloatPow};
1051
1052 #[inline]
1053 pub(crate) fn linear(t: f32) -> f32 {
1054 t
1055 }
1056
1057 #[inline]
1058 pub(crate) fn quadratic_in(t: f32) -> f32 {
1059 t.squared()
1060 }
1061 #[inline]
1062 pub(crate) fn quadratic_out(t: f32) -> f32 {
1063 1.0 - (1.0 - t).squared()
1064 }
1065 #[inline]
1066 pub(crate) fn quadratic_in_out(t: f32) -> f32 {
1067 if t < 0.5 {
1068 2.0 * t.squared()
1069 } else {
1070 1.0 - (-2.0 * t + 2.0).squared() / 2.0
1071 }
1072 }
1073
1074 #[inline]
1075 pub(crate) fn cubic_in(t: f32) -> f32 {
1076 t.cubed()
1077 }
1078 #[inline]
1079 pub(crate) fn cubic_out(t: f32) -> f32 {
1080 1.0 - (1.0 - t).cubed()
1081 }
1082 #[inline]
1083 pub(crate) fn cubic_in_out(t: f32) -> f32 {
1084 if t < 0.5 {
1085 4.0 * t.cubed()
1086 } else {
1087 1.0 - (-2.0 * t + 2.0).cubed() / 2.0
1088 }
1089 }
1090
1091 #[inline]
1092 pub(crate) fn quartic_in(t: f32) -> f32 {
1093 t * t * t * t
1094 }
1095 #[inline]
1096 pub(crate) fn quartic_out(t: f32) -> f32 {
1097 1.0 - (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t)
1098 }
1099 #[inline]
1100 pub(crate) fn quartic_in_out(t: f32) -> f32 {
1101 if t < 0.5 {
1102 8.0 * t * t * t * t
1103 } else {
1104 1.0 - (-2.0 * t + 2.0) * (-2.0 * t + 2.0) * (-2.0 * t + 2.0) * (-2.0 * t + 2.0) / 2.0
1105 }
1106 }
1107
1108 #[inline]
1109 pub(crate) fn quintic_in(t: f32) -> f32 {
1110 t * t * t * t * t
1111 }
1112 #[inline]
1113 pub(crate) fn quintic_out(t: f32) -> f32 {
1114 1.0 - (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t)
1115 }
1116 #[inline]
1117 pub(crate) fn quintic_in_out(t: f32) -> f32 {
1118 if t < 0.5 {
1119 16.0 * t * t * t * t * t
1120 } else {
1121 1.0 - (-2.0 * t + 2.0)
1122 * (-2.0 * t + 2.0)
1123 * (-2.0 * t + 2.0)
1124 * (-2.0 * t + 2.0)
1125 * (-2.0 * t + 2.0)
1126 / 2.0
1127 }
1128 }
1129
1130 #[inline]
1131 pub(crate) fn smoothstep_in(t: f32) -> f32 {
1132 ((1.5 - 0.5 * t) * t) * t
1133 }
1134
1135 #[inline]
1136 pub(crate) fn smoothstep_out(t: f32) -> f32 {
1137 (1.5 + (-0.5 * t) * t) * t
1138 }
1139
1140 #[inline]
1141 pub(crate) fn smoothstep(t: f32) -> f32 {
1142 ((3.0 - 2.0 * t) * t) * t
1143 }
1144
1145 #[inline]
1146 pub(crate) fn smootherstep_in(t: f32) -> f32 {
1147 (((2.5 + (-1.875 + 0.375 * t) * t) * t) * t) * t
1148 }
1149
1150 #[inline]
1151 pub(crate) fn smootherstep_out(t: f32) -> f32 {
1152 (1.875 + ((-1.25 + (0.375 * t) * t) * t) * t) * t
1153 }
1154
1155 #[inline]
1156 pub(crate) fn smootherstep(t: f32) -> f32 {
1157 (((10.0 + (-15.0 + 6.0 * t) * t) * t) * t) * t
1158 }
1159
1160 #[inline]
1161 pub(crate) fn sine_in(t: f32) -> f32 {
1162 1.0 - ops::cos(t * FRAC_PI_2)
1163 }
1164 #[inline]
1165 pub(crate) fn sine_out(t: f32) -> f32 {
1166 ops::sin(t * FRAC_PI_2)
1167 }
1168 #[inline]
1169 pub(crate) fn sine_in_out(t: f32) -> f32 {
1170 -(ops::cos(PI * t) - 1.0) / 2.0
1171 }
1172
1173 #[inline]
1174 pub(crate) fn circular_in(t: f32) -> f32 {
1175 1.0 - ops::sqrt(1.0 - t.squared())
1176 }
1177 #[inline]
1178 pub(crate) fn circular_out(t: f32) -> f32 {
1179 ops::sqrt(1.0 - (t - 1.0).squared())
1180 }
1181 #[inline]
1182 pub(crate) fn circular_in_out(t: f32) -> f32 {
1183 if t < 0.5 {
1184 (1.0 - ops::sqrt(1.0 - (2.0 * t).squared())) / 2.0
1185 } else {
1186 (ops::sqrt(1.0 - (-2.0 * t + 2.0).squared()) + 1.0) / 2.0
1187 }
1188 }
1189
1190 #[expect(
1195 clippy::excessive_precision,
1196 reason = "This is deliberately more precise than an f32 will allow, as truncating the value might imply that the value is carefully chosen."
1197 )]
1198 const LOG2_1023: f32 = 9.998590429745328646459226;
1199 #[expect(
1200 clippy::excessive_precision,
1201 reason = "This is deliberately more precise than an f32 will allow, as truncating the value might imply that the value is carefully chosen."
1202 )]
1203 const FRAC_1_1023: f32 = 0.00097751710654936461388074291;
1204 #[inline]
1205 pub(crate) fn exponential_in(t: f32) -> f32 {
1206 ops::exp2(10.0 * t - LOG2_1023) - FRAC_1_1023
1209 }
1210 #[inline]
1211 pub(crate) fn exponential_out(t: f32) -> f32 {
1212 (FRAC_1_1023 + 1.0) - ops::exp2(-10.0 * t - (LOG2_1023 - 10.0))
1213 }
1214 #[inline]
1215 pub(crate) fn exponential_in_out(t: f32) -> f32 {
1216 if t < 0.5 {
1217 ops::exp2(20.0 * t - (LOG2_1023 + 1.0)) - (FRAC_1_1023 / 2.0)
1218 } else {
1219 (FRAC_1_1023 / 2.0 + 1.0) - ops::exp2(-20.0 * t - (LOG2_1023 - 19.0))
1220 }
1221 }
1222
1223 #[inline]
1224 pub(crate) fn back_in(t: f32) -> f32 {
1225 let c = 1.70158;
1226
1227 (c + 1.0) * t.cubed() - c * t.squared()
1228 }
1229 #[inline]
1230 pub(crate) fn back_out(t: f32) -> f32 {
1231 let c = 1.70158;
1232
1233 1.0 + (c + 1.0) * (t - 1.0).cubed() + c * (t - 1.0).squared()
1234 }
1235 #[inline]
1236 pub(crate) fn back_in_out(t: f32) -> f32 {
1237 let c1 = 1.70158;
1238 let c2 = c1 + 1.525;
1239
1240 if t < 0.5 {
1241 (2.0 * t).squared() * ((c2 + 1.0) * 2.0 * t - c2) / 2.0
1242 } else {
1243 ((2.0 * t - 2.0).squared() * ((c2 + 1.0) * (2.0 * t - 2.0) + c2) + 2.0) / 2.0
1244 }
1245 }
1246
1247 #[inline]
1248 pub(crate) fn elastic_in(t: f32) -> f32 {
1249 -ops::powf(2.0, 10.0 * t - 10.0) * ops::sin((t * 10.0 - 10.75) * 2.0 * FRAC_PI_3)
1250 }
1251 #[inline]
1252 pub(crate) fn elastic_out(t: f32) -> f32 {
1253 ops::powf(2.0, -10.0 * t) * ops::sin((t * 10.0 - 0.75) * 2.0 * FRAC_PI_3) + 1.0
1254 }
1255 #[inline]
1256 pub(crate) fn elastic_in_out(t: f32) -> f32 {
1257 let c = (2.0 * PI) / 4.5;
1258
1259 if t < 0.5 {
1260 -ops::powf(2.0, 20.0 * t - 10.0) * ops::sin((t * 20.0 - 11.125) * c) / 2.0
1261 } else {
1262 ops::powf(2.0, -20.0 * t + 10.0) * ops::sin((t * 20.0 - 11.125) * c) / 2.0 + 1.0
1263 }
1264 }
1265
1266 #[inline]
1267 pub(crate) fn bounce_in(t: f32) -> f32 {
1268 1.0 - bounce_out(1.0 - t)
1269 }
1270 #[inline]
1271 pub(crate) fn bounce_out(t: f32) -> f32 {
1272 if t < 4.0 / 11.0 {
1273 (121.0 * t.squared()) / 16.0
1274 } else if t < 8.0 / 11.0 {
1275 (363.0 / 40.0 * t.squared()) - (99.0 / 10.0 * t) + 17.0 / 5.0
1276 } else if t < 9.0 / 10.0 {
1277 (4356.0 / 361.0 * t.squared()) - (35442.0 / 1805.0 * t) + 16061.0 / 1805.0
1278 } else {
1279 (54.0 / 5.0 * t.squared()) - (513.0 / 25.0 * t) + 268.0 / 25.0
1280 }
1281 }
1282 #[inline]
1283 pub(crate) fn bounce_in_out(t: f32) -> f32 {
1284 if t < 0.5 {
1285 (1.0 - bounce_out(1.0 - 2.0 * t)) / 2.0
1286 } else {
1287 (1.0 + bounce_out(2.0 * t - 1.0)) / 2.0
1288 }
1289 }
1290
1291 #[inline]
1292 pub(crate) fn steps(num_steps: usize, jump_at: super::JumpAt, t: f32) -> f32 {
1293 jump_at.eval(num_steps, t)
1294 }
1295
1296 #[inline]
1297 pub(crate) fn elastic(omega: f32, t: f32) -> f32 {
1298 1.0 - (1.0 - t).squared() * (2.0 * ops::sin(omega * t) / omega + ops::cos(omega * t))
1299 }
1300}
1301
1302impl EaseFunction {
1303 fn eval(&self, t: f32) -> f32 {
1304 match self {
1305 EaseFunction::Linear => easing_functions::linear(t),
1306 EaseFunction::QuadraticIn => easing_functions::quadratic_in(t),
1307 EaseFunction::QuadraticOut => easing_functions::quadratic_out(t),
1308 EaseFunction::QuadraticInOut => easing_functions::quadratic_in_out(t),
1309 EaseFunction::CubicIn => easing_functions::cubic_in(t),
1310 EaseFunction::CubicOut => easing_functions::cubic_out(t),
1311 EaseFunction::CubicInOut => easing_functions::cubic_in_out(t),
1312 EaseFunction::QuarticIn => easing_functions::quartic_in(t),
1313 EaseFunction::QuarticOut => easing_functions::quartic_out(t),
1314 EaseFunction::QuarticInOut => easing_functions::quartic_in_out(t),
1315 EaseFunction::QuinticIn => easing_functions::quintic_in(t),
1316 EaseFunction::QuinticOut => easing_functions::quintic_out(t),
1317 EaseFunction::QuinticInOut => easing_functions::quintic_in_out(t),
1318 EaseFunction::SmoothStepIn => easing_functions::smoothstep_in(t),
1319 EaseFunction::SmoothStepOut => easing_functions::smoothstep_out(t),
1320 EaseFunction::SmoothStep => easing_functions::smoothstep(t),
1321 EaseFunction::SmootherStepIn => easing_functions::smootherstep_in(t),
1322 EaseFunction::SmootherStepOut => easing_functions::smootherstep_out(t),
1323 EaseFunction::SmootherStep => easing_functions::smootherstep(t),
1324 EaseFunction::SineIn => easing_functions::sine_in(t),
1325 EaseFunction::SineOut => easing_functions::sine_out(t),
1326 EaseFunction::SineInOut => easing_functions::sine_in_out(t),
1327 EaseFunction::CircularIn => easing_functions::circular_in(t),
1328 EaseFunction::CircularOut => easing_functions::circular_out(t),
1329 EaseFunction::CircularInOut => easing_functions::circular_in_out(t),
1330 EaseFunction::ExponentialIn => easing_functions::exponential_in(t),
1331 EaseFunction::ExponentialOut => easing_functions::exponential_out(t),
1332 EaseFunction::ExponentialInOut => easing_functions::exponential_in_out(t),
1333 EaseFunction::ElasticIn => easing_functions::elastic_in(t),
1334 EaseFunction::ElasticOut => easing_functions::elastic_out(t),
1335 EaseFunction::ElasticInOut => easing_functions::elastic_in_out(t),
1336 EaseFunction::BackIn => easing_functions::back_in(t),
1337 EaseFunction::BackOut => easing_functions::back_out(t),
1338 EaseFunction::BackInOut => easing_functions::back_in_out(t),
1339 EaseFunction::BounceIn => easing_functions::bounce_in(t),
1340 EaseFunction::BounceOut => easing_functions::bounce_out(t),
1341 EaseFunction::BounceInOut => easing_functions::bounce_in_out(t),
1342 EaseFunction::Steps(num_steps, jump_at) => {
1343 easing_functions::steps(*num_steps, *jump_at, t)
1344 }
1345 EaseFunction::Elastic(omega) => easing_functions::elastic(*omega, t),
1346 }
1347 }
1348}
1349
1350impl Curve<f32> for EaseFunction {
1351 #[inline]
1352 fn domain(&self) -> Interval {
1353 Interval::UNIT
1354 }
1355
1356 #[inline]
1357 fn sample_unchecked(&self, t: f32) -> f32 {
1358 self.eval(t)
1359 }
1360}
1361
1362#[cfg(test)]
1363#[cfg(feature = "approx")]
1364mod tests {
1365
1366 use crate::{Vec2, Vec3, Vec3A};
1367 use approx::assert_abs_diff_eq;
1368
1369 use super::*;
1370 const MONOTONIC_IN_OUT_INOUT: &[[EaseFunction; 3]] = {
1371 use EaseFunction::*;
1372 &[
1373 [QuadraticIn, QuadraticOut, QuadraticInOut],
1374 [CubicIn, CubicOut, CubicInOut],
1375 [QuarticIn, QuarticOut, QuarticInOut],
1376 [QuinticIn, QuinticOut, QuinticInOut],
1377 [SmoothStepIn, SmoothStepOut, SmoothStep],
1378 [SmootherStepIn, SmootherStepOut, SmootherStep],
1379 [SineIn, SineOut, SineInOut],
1380 [CircularIn, CircularOut, CircularInOut],
1381 [ExponentialIn, ExponentialOut, ExponentialInOut],
1382 ]
1383 };
1384
1385 const TOLERANCE: f32 = 1.0e-6;
1388 const _: () = const {
1389 assert!(1.0 - TOLERANCE != 1.0);
1390 };
1391
1392 #[test]
1393 fn ease_functions_zero_to_one() {
1394 for ef in MONOTONIC_IN_OUT_INOUT.iter().flatten() {
1395 let start = ef.eval(0.0);
1396 assert!(
1397 (0.0..=TOLERANCE).contains(&start),
1398 "EaseFunction.{ef:?}(0) was {start:?}",
1399 );
1400
1401 let finish = ef.eval(1.0);
1402 assert!(
1403 (1.0 - TOLERANCE..=1.0).contains(&finish),
1404 "EaseFunction.{ef:?}(1) was {start:?}",
1405 );
1406 }
1407 }
1408
1409 #[test]
1410 fn ease_function_inout_deciles() {
1411 for [ef_in, ef_out, ef_inout] in MONOTONIC_IN_OUT_INOUT {
1413 for x in [0.1, 0.2, 0.3, 0.4] {
1414 let y = ef_inout.eval(x);
1415 assert!(y < x, "EaseFunction.{ef_inout:?}({x:?}) was {y:?}");
1416
1417 let iny = ef_in.eval(2.0 * x) / 2.0;
1418 assert!(
1419 (y - TOLERANCE..y + TOLERANCE).contains(&iny),
1420 "EaseFunction.{ef_inout:?}({x:?}) was {y:?}, but \
1421 EaseFunction.{ef_in:?}(2 * {x:?}) / 2 was {iny:?}",
1422 );
1423 }
1424
1425 for x in [0.6, 0.7, 0.8, 0.9] {
1426 let y = ef_inout.eval(x);
1427 assert!(y > x, "EaseFunction.{ef_inout:?}({x:?}) was {y:?}");
1428
1429 let outy = ef_out.eval(2.0 * x - 1.0) / 2.0 + 0.5;
1430 assert!(
1431 (y - TOLERANCE..y + TOLERANCE).contains(&outy),
1432 "EaseFunction.{ef_inout:?}({x:?}) was {y:?}, but \
1433 EaseFunction.{ef_out:?}(2 * {x:?} - 1) / 2 + ½ was {outy:?}",
1434 );
1435 }
1436 }
1437 }
1438
1439 #[test]
1440 fn ease_function_midpoints() {
1441 for [ef_in, ef_out, ef_inout] in MONOTONIC_IN_OUT_INOUT {
1442 let mid = ef_in.eval(0.5);
1443 assert!(
1444 mid < 0.5 - TOLERANCE,
1445 "EaseFunction.{ef_in:?}(½) was {mid:?}",
1446 );
1447
1448 let mid = ef_out.eval(0.5);
1449 assert!(
1450 mid > 0.5 + TOLERANCE,
1451 "EaseFunction.{ef_out:?}(½) was {mid:?}",
1452 );
1453
1454 let mid = ef_inout.eval(0.5);
1455 assert!(
1456 (0.5 - TOLERANCE..=0.5 + TOLERANCE).contains(&mid),
1457 "EaseFunction.{ef_inout:?}(½) was {mid:?}",
1458 );
1459 }
1460 }
1461
1462 #[test]
1463 fn ease_quats() {
1464 let quat_start = Quat::from_axis_angle(Vec3::Z, 0.0);
1465 let quat_end = Quat::from_axis_angle(Vec3::Z, 90.0_f32.to_radians());
1466
1467 let quat_curve = Quat::interpolating_curve_unbounded(quat_start, quat_end);
1468
1469 assert_abs_diff_eq!(
1470 quat_curve.sample(0.0).unwrap(),
1471 Quat::from_axis_angle(Vec3::Z, 0.0)
1472 );
1473 {
1474 let (before_mid_axis, before_mid_angle) =
1475 quat_curve.sample(0.25).unwrap().to_axis_angle();
1476 assert_abs_diff_eq!(before_mid_axis, Vec3::Z);
1477 assert_abs_diff_eq!(before_mid_angle, 22.5_f32.to_radians());
1478 }
1479 {
1480 let (mid_axis, mid_angle) = quat_curve.sample(0.5).unwrap().to_axis_angle();
1481 assert_abs_diff_eq!(mid_axis, Vec3::Z);
1482 assert_abs_diff_eq!(mid_angle, 45.0_f32.to_radians());
1483 }
1484 {
1485 let (after_mid_axis, after_mid_angle) =
1486 quat_curve.sample(0.75).unwrap().to_axis_angle();
1487 assert_abs_diff_eq!(after_mid_axis, Vec3::Z);
1488 assert_abs_diff_eq!(after_mid_angle, 67.5_f32.to_radians());
1489 }
1490 assert_abs_diff_eq!(
1491 quat_curve.sample(1.0).unwrap(),
1492 Quat::from_axis_angle(Vec3::Z, 90.0_f32.to_radians())
1493 );
1494 }
1495
1496 #[test]
1497 fn ease_isometries_2d() {
1498 let angle = 90.0;
1499 let iso_2d_start = Isometry2d::new(Vec2::ZERO, Rot2::degrees(0.0));
1500 let iso_2d_end = Isometry2d::new(Vec2::ONE, Rot2::degrees(angle));
1501
1502 let iso_2d_curve = Isometry2d::interpolating_curve_unbounded(iso_2d_start, iso_2d_end);
1503
1504 [-1.0, 0.0, 0.5, 1.0, 2.0].into_iter().for_each(|t| {
1505 assert_abs_diff_eq!(
1506 iso_2d_curve.sample(t).unwrap(),
1507 Isometry2d::new(Vec2::ONE * t, Rot2::degrees(angle * t))
1508 );
1509 });
1510 }
1511
1512 #[test]
1513 fn ease_isometries_3d() {
1514 let angle = 90.0_f32.to_radians();
1515 let iso_3d_start = Isometry3d::new(Vec3A::ZERO, Quat::from_axis_angle(Vec3::Z, 0.0));
1516 let iso_3d_end = Isometry3d::new(Vec3A::ONE, Quat::from_axis_angle(Vec3::Z, angle));
1517
1518 let iso_3d_curve = Isometry3d::interpolating_curve_unbounded(iso_3d_start, iso_3d_end);
1519
1520 [-1.0, 0.0, 0.5, 1.0, 2.0].into_iter().for_each(|t| {
1521 assert_abs_diff_eq!(
1522 iso_3d_curve.sample(t).unwrap(),
1523 Isometry3d::new(Vec3A::ONE * t, Quat::from_axis_angle(Vec3::Z, angle * t))
1524 );
1525 });
1526 }
1527
1528 #[test]
1529 fn jump_at_start() {
1530 let jump_at = JumpAt::Start;
1531 let num_steps = 4;
1532
1533 [
1534 (0.0, 0.25),
1535 (0.249, 0.25),
1536 (0.25, 0.5),
1537 (0.499, 0.5),
1538 (0.5, 0.75),
1539 (0.749, 0.75),
1540 (0.75, 1.0),
1541 (1.0, 1.0),
1542 ]
1543 .into_iter()
1544 .for_each(|(t, expected)| {
1545 assert_abs_diff_eq!(jump_at.eval(num_steps, t), expected);
1546 });
1547 }
1548
1549 #[test]
1550 fn jump_at_end() {
1551 let jump_at = JumpAt::End;
1552 let num_steps = 4;
1553
1554 [
1555 (0.0, 0.0),
1556 (0.249, 0.0),
1557 (0.25, 0.25),
1558 (0.499, 0.25),
1559 (0.5, 0.5),
1560 (0.749, 0.5),
1561 (0.75, 0.75),
1562 (0.999, 0.75),
1563 (1.0, 1.0),
1564 ]
1565 .into_iter()
1566 .for_each(|(t, expected)| {
1567 assert_abs_diff_eq!(jump_at.eval(num_steps, t), expected);
1568 });
1569 }
1570
1571 #[test]
1572 fn jump_at_none() {
1573 let jump_at = JumpAt::None;
1574 let num_steps = 5;
1575
1576 [
1577 (0.0, 0.0),
1578 (0.199, 0.0),
1579 (0.2, 0.25),
1580 (0.399, 0.25),
1581 (0.4, 0.5),
1582 (0.599, 0.5),
1583 (0.6, 0.75),
1584 (0.799, 0.75),
1585 (0.8, 1.0),
1586 (0.999, 1.0),
1587 (1.0, 1.0),
1588 ]
1589 .into_iter()
1590 .for_each(|(t, expected)| {
1591 assert_abs_diff_eq!(jump_at.eval(num_steps, t), expected);
1592 });
1593 }
1594
1595 #[test]
1596 fn jump_at_both() {
1597 let jump_at = JumpAt::Both;
1598 let num_steps = 4;
1599
1600 [
1601 (0.0, 0.2),
1602 (0.249, 0.2),
1603 (0.25, 0.4),
1604 (0.499, 0.4),
1605 (0.5, 0.6),
1606 (0.749, 0.6),
1607 (0.75, 0.8),
1608 (0.999, 0.8),
1609 (1.0, 1.0),
1610 ]
1611 .into_iter()
1612 .for_each(|(t, expected)| {
1613 assert_abs_diff_eq!(jump_at.eval(num_steps, t), expected);
1614 });
1615 }
1616
1617 #[test]
1618 fn ease_function_curve() {
1619 let f0 = SmoothStepCurve;
1623 let f1 = EaseFunction::SmoothStep;
1624 let f2 = EasingCurve::new(0.0, 1.0, EaseFunction::SmoothStep);
1625
1626 assert_eq!(f0.domain(), f1.domain());
1627 assert_eq!(f0.domain(), f2.domain());
1628
1629 [
1630 -1.0,
1631 -f32::MIN_POSITIVE,
1632 0.0,
1633 0.5,
1634 1.0,
1635 1.0 + f32::EPSILON,
1636 2.0,
1637 ]
1638 .into_iter()
1639 .for_each(|t| {
1640 assert_eq!(f0.sample(t), f1.sample(t));
1641 assert_eq!(f0.sample(t), f2.sample(t));
1642
1643 assert_eq!(f0.sample_clamped(t), f1.sample_clamped(t));
1644 assert_eq!(f0.sample_clamped(t), f2.sample_clamped(t));
1645 });
1646 }
1647
1648 #[test]
1649 fn unit_structs_match_function() {
1650 fn test(f1: impl Curve<f32>, f2: impl Curve<f32>, t: f32) {
1654 assert_eq!(f1.sample(t), f2.sample(t));
1655 }
1656
1657 for t in [-1.0, 0.0, 0.25, 0.5, 0.75, 1.0, 2.0] {
1658 test(LinearCurve, EaseFunction::Linear, t);
1659 test(QuadraticInCurve, EaseFunction::QuadraticIn, t);
1660 test(QuadraticOutCurve, EaseFunction::QuadraticOut, t);
1661 test(QuadraticInOutCurve, EaseFunction::QuadraticInOut, t);
1662 test(CubicInCurve, EaseFunction::CubicIn, t);
1663 test(CubicOutCurve, EaseFunction::CubicOut, t);
1664 test(CubicInOutCurve, EaseFunction::CubicInOut, t);
1665 test(QuarticInCurve, EaseFunction::QuarticIn, t);
1666 test(QuarticOutCurve, EaseFunction::QuarticOut, t);
1667 test(QuarticInOutCurve, EaseFunction::QuarticInOut, t);
1668 test(QuinticInCurve, EaseFunction::QuinticIn, t);
1669 test(QuinticOutCurve, EaseFunction::QuinticOut, t);
1670 test(QuinticInOutCurve, EaseFunction::QuinticInOut, t);
1671 test(SmoothStepInCurve, EaseFunction::SmoothStepIn, t);
1672 test(SmoothStepOutCurve, EaseFunction::SmoothStepOut, t);
1673 test(SmoothStepCurve, EaseFunction::SmoothStep, t);
1674 test(SmootherStepInCurve, EaseFunction::SmootherStepIn, t);
1675 test(SmootherStepOutCurve, EaseFunction::SmootherStepOut, t);
1676 test(SmootherStepCurve, EaseFunction::SmootherStep, t);
1677 test(SineInCurve, EaseFunction::SineIn, t);
1678 test(SineOutCurve, EaseFunction::SineOut, t);
1679 test(SineInOutCurve, EaseFunction::SineInOut, t);
1680 test(CircularInCurve, EaseFunction::CircularIn, t);
1681 test(CircularOutCurve, EaseFunction::CircularOut, t);
1682 test(CircularInOutCurve, EaseFunction::CircularInOut, t);
1683 test(ExponentialInCurve, EaseFunction::ExponentialIn, t);
1684 test(ExponentialOutCurve, EaseFunction::ExponentialOut, t);
1685 test(ExponentialInOutCurve, EaseFunction::ExponentialInOut, t);
1686 test(ElasticInCurve, EaseFunction::ElasticIn, t);
1687 test(ElasticOutCurve, EaseFunction::ElasticOut, t);
1688 test(ElasticInOutCurve, EaseFunction::ElasticInOut, t);
1689 test(BackInCurve, EaseFunction::BackIn, t);
1690 test(BackOutCurve, EaseFunction::BackOut, t);
1691 test(BackInOutCurve, EaseFunction::BackInOut, t);
1692 test(BounceInCurve, EaseFunction::BounceIn, t);
1693 test(BounceOutCurve, EaseFunction::BounceOut, t);
1694 test(BounceInOutCurve, EaseFunction::BounceInOut, t);
1695
1696 test(
1697 StepsCurve(4, JumpAt::Start),
1698 EaseFunction::Steps(4, JumpAt::Start),
1699 t,
1700 );
1701
1702 test(ElasticCurve(50.0), EaseFunction::Elastic(50.0), t);
1703 }
1704 }
1705}