1use crate::{
7 curve::{FunctionCurve, Interval},
8 Curve, Dir2, Dir3, Dir3A, Quat, Rot2, VectorSpace,
9};
10
11pub trait Ease: Sized {
20 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self>;
28}
29
30impl<V: VectorSpace> Ease for V {
31 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
32 FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t))
33 }
34}
35
36impl Ease for Rot2 {
37 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
38 FunctionCurve::new(Interval::EVERYWHERE, move |t| Rot2::slerp(start, end, t))
39 }
40}
41
42impl Ease for Quat {
43 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
44 let dot = start.dot(end);
45 let end_adjusted = if dot < 0.0 { -end } else { end };
46 let difference = end_adjusted * start.inverse();
47 let (axis, angle) = difference.to_axis_angle();
48 FunctionCurve::new(Interval::EVERYWHERE, move |s| {
49 Quat::from_axis_angle(axis, angle * s) * start
50 })
51 }
52}
53
54impl Ease for Dir2 {
55 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
56 FunctionCurve::new(Interval::EVERYWHERE, move |t| Dir2::slerp(start, end, t))
57 }
58}
59
60impl Ease for Dir3 {
61 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
62 let difference_quat = Quat::from_rotation_arc(start.as_vec3(), end.as_vec3());
63 Quat::interpolating_curve_unbounded(Quat::IDENTITY, difference_quat).map(move |q| q * start)
64 }
65}
66
67impl Ease for Dir3A {
68 fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve<Self> {
69 let difference_quat =
70 Quat::from_rotation_arc(start.as_vec3a().into(), end.as_vec3a().into());
71 Quat::interpolating_curve_unbounded(Quat::IDENTITY, difference_quat).map(move |q| q * start)
72 }
73}
74
75#[derive(Clone, Debug)]
86#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
87#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
88pub struct EasingCurve<T> {
89 start: T,
90 end: T,
91 ease_fn: EaseFunction,
92}
93
94impl<T> EasingCurve<T> {
95 pub fn new(start: T, end: T, ease_fn: EaseFunction) -> Self {
102 Self {
103 start,
104 end,
105 ease_fn,
106 }
107 }
108}
109
110impl<T> Curve<T> for EasingCurve<T>
111where
112 T: Ease + Clone,
113{
114 #[inline]
115 fn domain(&self) -> Interval {
116 Interval::UNIT
117 }
118
119 #[inline]
120 fn sample_unchecked(&self, t: f32) -> T {
121 let remapped_t = self.ease_fn.eval(t);
122 T::interpolating_curve_unbounded(self.start.clone(), self.end.clone())
123 .sample_unchecked(remapped_t)
124 }
125}
126
127#[derive(Debug, Copy, Clone, PartialEq)]
131#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
132#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
133pub enum EaseFunction {
134 Linear,
136
137 QuadraticIn,
139 QuadraticOut,
141 QuadraticInOut,
143
144 CubicIn,
146 CubicOut,
148 CubicInOut,
150
151 QuarticIn,
153 QuarticOut,
155 QuarticInOut,
157
158 QuinticIn,
160 QuinticOut,
162 QuinticInOut,
164
165 SineIn,
167 SineOut,
169 SineInOut,
171
172 CircularIn,
174 CircularOut,
176 CircularInOut,
178
179 ExponentialIn,
181 ExponentialOut,
183 ExponentialInOut,
185
186 ElasticIn,
188 ElasticOut,
190 ElasticInOut,
192
193 BackIn,
195 BackOut,
197 BackInOut,
199
200 BounceIn,
202 BounceOut,
204 BounceInOut,
206
207 Steps(usize),
209
210 Elastic(f32),
212}
213
214mod easing_functions {
215 use core::f32::consts::{FRAC_PI_2, FRAC_PI_3, PI};
216
217 use crate::{ops, FloatPow};
218
219 #[inline]
220 pub(crate) fn linear(t: f32) -> f32 {
221 t
222 }
223
224 #[inline]
225 pub(crate) fn quadratic_in(t: f32) -> f32 {
226 t.squared()
227 }
228 #[inline]
229 pub(crate) fn quadratic_out(t: f32) -> f32 {
230 1.0 - (1.0 - t).squared()
231 }
232 #[inline]
233 pub(crate) fn quadratic_in_out(t: f32) -> f32 {
234 if t < 0.5 {
235 2.0 * t.squared()
236 } else {
237 1.0 - (-2.0 * t + 2.0).squared() / 2.0
238 }
239 }
240
241 #[inline]
242 pub(crate) fn cubic_in(t: f32) -> f32 {
243 t.cubed()
244 }
245 #[inline]
246 pub(crate) fn cubic_out(t: f32) -> f32 {
247 1.0 - (1.0 - t).cubed()
248 }
249 #[inline]
250 pub(crate) fn cubic_in_out(t: f32) -> f32 {
251 if t < 0.5 {
252 4.0 * t.cubed()
253 } else {
254 1.0 - (-2.0 * t + 2.0).cubed() / 2.0
255 }
256 }
257
258 #[inline]
259 pub(crate) fn quartic_in(t: f32) -> f32 {
260 t * t * t * t
261 }
262 #[inline]
263 pub(crate) fn quartic_out(t: f32) -> f32 {
264 1.0 - (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t)
265 }
266 #[inline]
267 pub(crate) fn quartic_in_out(t: f32) -> f32 {
268 if t < 0.5 {
269 8.0 * t * t * t * t
270 } else {
271 1.0 - (-2.0 * t + 2.0) * (-2.0 * t + 2.0) * (-2.0 * t + 2.0) * (-2.0 * t + 2.0) / 2.0
272 }
273 }
274
275 #[inline]
276 pub(crate) fn quintic_in(t: f32) -> f32 {
277 t * t * t * t * t
278 }
279 #[inline]
280 pub(crate) fn quintic_out(t: f32) -> f32 {
281 1.0 - (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t) * (1.0 - t)
282 }
283 #[inline]
284 pub(crate) fn quintic_in_out(t: f32) -> f32 {
285 if t < 0.5 {
286 16.0 * t * t * t * t * t
287 } else {
288 1.0 - (-2.0 * t + 2.0)
289 * (-2.0 * t + 2.0)
290 * (-2.0 * t + 2.0)
291 * (-2.0 * t + 2.0)
292 * (-2.0 * t + 2.0)
293 / 2.0
294 }
295 }
296
297 #[inline]
298 pub(crate) fn sine_in(t: f32) -> f32 {
299 1.0 - ops::cos(t * FRAC_PI_2)
300 }
301 #[inline]
302 pub(crate) fn sine_out(t: f32) -> f32 {
303 ops::sin(t * FRAC_PI_2)
304 }
305 #[inline]
306 pub(crate) fn sine_in_out(t: f32) -> f32 {
307 -(ops::cos(PI * t) - 1.0) / 2.0
308 }
309
310 #[inline]
311 pub(crate) fn circular_in(t: f32) -> f32 {
312 1.0 - (1.0 - t.squared()).sqrt()
313 }
314 #[inline]
315 pub(crate) fn circular_out(t: f32) -> f32 {
316 (1.0 - (t - 1.0).squared()).sqrt()
317 }
318 #[inline]
319 pub(crate) fn circular_in_out(t: f32) -> f32 {
320 if t < 0.5 {
321 (1.0 - (1.0 - (2.0 * t).squared()).sqrt()) / 2.0
322 } else {
323 ((1.0 - (-2.0 * t + 2.0).squared()).sqrt() + 1.0) / 2.0
324 }
325 }
326
327 #[inline]
328 pub(crate) fn exponential_in(t: f32) -> f32 {
329 ops::powf(2.0, 10.0 * t - 10.0)
330 }
331 #[inline]
332 pub(crate) fn exponential_out(t: f32) -> f32 {
333 1.0 - ops::powf(2.0, -10.0 * t)
334 }
335 #[inline]
336 pub(crate) fn exponential_in_out(t: f32) -> f32 {
337 if t < 0.5 {
338 ops::powf(2.0, 20.0 * t - 10.0) / 2.0
339 } else {
340 (2.0 - ops::powf(2.0, -20.0 * t + 10.0)) / 2.0
341 }
342 }
343
344 #[inline]
345 pub(crate) fn back_in(t: f32) -> f32 {
346 let c = 1.70158;
347
348 (c + 1.0) * t.cubed() - c * t.squared()
349 }
350 #[inline]
351 pub(crate) fn back_out(t: f32) -> f32 {
352 let c = 1.70158;
353
354 1.0 + (c + 1.0) * (t - 1.0).cubed() + c * (t - 1.0).squared()
355 }
356 #[inline]
357 pub(crate) fn back_in_out(t: f32) -> f32 {
358 let c1 = 1.70158;
359 let c2 = c1 + 1.525;
360
361 if t < 0.5 {
362 (2.0 * t).squared() * ((c2 + 1.0) * 2.0 * t - c2) / 2.0
363 } else {
364 ((2.0 * t - 2.0).squared() * ((c2 + 1.0) * (2.0 * t - 2.0) + c2) + 2.0) / 2.0
365 }
366 }
367
368 #[inline]
369 pub(crate) fn elastic_in(t: f32) -> f32 {
370 -ops::powf(2.0, 10.0 * t - 10.0) * ops::sin((t * 10.0 - 10.75) * 2.0 * FRAC_PI_3)
371 }
372 #[inline]
373 pub(crate) fn elastic_out(t: f32) -> f32 {
374 ops::powf(2.0, -10.0 * t) * ops::sin((t * 10.0 - 0.75) * 2.0 * FRAC_PI_3) + 1.0
375 }
376 #[inline]
377 pub(crate) fn elastic_in_out(t: f32) -> f32 {
378 let c = (2.0 * PI) / 4.5;
379
380 if t < 0.5 {
381 -ops::powf(2.0, 20.0 * t - 10.0) * ops::sin((t * 20.0 - 11.125) * c) / 2.0
382 } else {
383 ops::powf(2.0, -20.0 * t + 10.0) * ops::sin((t * 20.0 - 11.125) * c) / 2.0 + 1.0
384 }
385 }
386
387 #[inline]
388 pub(crate) fn bounce_in(t: f32) -> f32 {
389 1.0 - bounce_out(1.0 - t)
390 }
391 #[inline]
392 pub(crate) fn bounce_out(t: f32) -> f32 {
393 if t < 4.0 / 11.0 {
394 (121.0 * t.squared()) / 16.0
395 } else if t < 8.0 / 11.0 {
396 (363.0 / 40.0 * t.squared()) - (99.0 / 10.0 * t) + 17.0 / 5.0
397 } else if t < 9.0 / 10.0 {
398 (4356.0 / 361.0 * t.squared()) - (35442.0 / 1805.0 * t) + 16061.0 / 1805.0
399 } else {
400 (54.0 / 5.0 * t.squared()) - (513.0 / 25.0 * t) + 268.0 / 25.0
401 }
402 }
403 #[inline]
404 pub(crate) fn bounce_in_out(t: f32) -> f32 {
405 if t < 0.5 {
406 (1.0 - bounce_out(1.0 - 2.0 * t)) / 2.0
407 } else {
408 (1.0 + bounce_out(2.0 * t - 1.0)) / 2.0
409 }
410 }
411
412 #[inline]
413 pub(crate) fn steps(num_steps: usize, t: f32) -> f32 {
414 (t * num_steps as f32).round() / num_steps.max(1) as f32
415 }
416
417 #[inline]
418 pub(crate) fn elastic(omega: f32, t: f32) -> f32 {
419 1.0 - (1.0 - t).squared() * (2.0 * ops::sin(omega * t) / omega + ops::cos(omega * t))
420 }
421}
422
423impl EaseFunction {
424 fn eval(&self, t: f32) -> f32 {
425 match self {
426 EaseFunction::Linear => easing_functions::linear(t),
427 EaseFunction::QuadraticIn => easing_functions::quadratic_in(t),
428 EaseFunction::QuadraticOut => easing_functions::quadratic_out(t),
429 EaseFunction::QuadraticInOut => easing_functions::quadratic_in_out(t),
430 EaseFunction::CubicIn => easing_functions::cubic_in(t),
431 EaseFunction::CubicOut => easing_functions::cubic_out(t),
432 EaseFunction::CubicInOut => easing_functions::cubic_in_out(t),
433 EaseFunction::QuarticIn => easing_functions::quartic_in(t),
434 EaseFunction::QuarticOut => easing_functions::quartic_out(t),
435 EaseFunction::QuarticInOut => easing_functions::quartic_in_out(t),
436 EaseFunction::QuinticIn => easing_functions::quintic_in(t),
437 EaseFunction::QuinticOut => easing_functions::quintic_out(t),
438 EaseFunction::QuinticInOut => easing_functions::quintic_in_out(t),
439 EaseFunction::SineIn => easing_functions::sine_in(t),
440 EaseFunction::SineOut => easing_functions::sine_out(t),
441 EaseFunction::SineInOut => easing_functions::sine_in_out(t),
442 EaseFunction::CircularIn => easing_functions::circular_in(t),
443 EaseFunction::CircularOut => easing_functions::circular_out(t),
444 EaseFunction::CircularInOut => easing_functions::circular_in_out(t),
445 EaseFunction::ExponentialIn => easing_functions::exponential_in(t),
446 EaseFunction::ExponentialOut => easing_functions::exponential_out(t),
447 EaseFunction::ExponentialInOut => easing_functions::exponential_in_out(t),
448 EaseFunction::ElasticIn => easing_functions::elastic_in(t),
449 EaseFunction::ElasticOut => easing_functions::elastic_out(t),
450 EaseFunction::ElasticInOut => easing_functions::elastic_in_out(t),
451 EaseFunction::BackIn => easing_functions::back_in(t),
452 EaseFunction::BackOut => easing_functions::back_out(t),
453 EaseFunction::BackInOut => easing_functions::back_in_out(t),
454 EaseFunction::BounceIn => easing_functions::bounce_in(t),
455 EaseFunction::BounceOut => easing_functions::bounce_out(t),
456 EaseFunction::BounceInOut => easing_functions::bounce_in_out(t),
457 EaseFunction::Steps(num_steps) => easing_functions::steps(*num_steps, t),
458 EaseFunction::Elastic(omega) => easing_functions::elastic(*omega, t),
459 }
460 }
461}