#[derive(Clone, Copy, Debug, Default)]
pub enum Easing {
#[default]
Linear,
EaseIn,
EaseOut,
EaseInOut,
EaseInQuad,
EaseOutQuad,
EaseInOutQuad,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
EaseInQuart,
EaseOutQuart,
EaseInOutQuart,
CubicBezier(f32, f32, f32, f32),
}
impl Easing {
pub fn apply(&self, t: f32) -> f32 {
match self {
Easing::Linear => t,
Easing::EaseIn => t * t * t,
Easing::EaseOut => 1.0 - (1.0 - t).powi(3),
Easing::EaseInOut => {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
}
}
Easing::EaseInQuad => t * t,
Easing::EaseOutQuad => 1.0 - (1.0 - t) * (1.0 - t),
Easing::EaseInOutQuad => {
if t < 0.5 {
2.0 * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(2) / 2.0
}
}
Easing::EaseInCubic => t * t * t,
Easing::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
Easing::EaseInOutCubic => {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
}
}
Easing::EaseInQuart => t * t * t * t,
Easing::EaseOutQuart => 1.0 - (1.0 - t).powi(4),
Easing::EaseInOutQuart => {
if t < 0.5 {
8.0 * t * t * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(4) / 2.0
}
}
Easing::CubicBezier(x1, y1, x2, y2) => cubic_bezier_ease(t, *x1, *y1, *x2, *y2),
}
}
}
fn cubic_bezier_ease(t: f32, x1: f32, y1: f32, x2: f32, y2: f32) -> f32 {
if t <= 0.0 {
return 0.0;
}
if t >= 1.0 {
return 1.0;
}
let x = t as f64;
let x1 = x1 as f64;
let y1 = y1 as f64;
let x2 = x2 as f64;
let y2 = y2 as f64;
let mut p = x; for _ in 0..8 {
let err = bezier_sample(p, x1, x2) - x;
if err.abs() < 1e-7 {
return bezier_sample(p, y1, y2) as f32;
}
let slope = bezier_slope(p, x1, x2);
if slope.abs() < 1e-7 {
break; }
p -= err / slope;
}
let mut lo = 0.0_f64;
let mut hi = 1.0_f64;
p = x;
for _ in 0..20 {
let val = bezier_sample(p, x1, x2);
if (val - x).abs() < 1e-7 {
break;
}
if val < x {
lo = p;
} else {
hi = p;
}
p = (lo + hi) * 0.5;
}
bezier_sample(p, y1, y2) as f32
}
#[inline]
fn bezier_sample(t: f64, p1: f64, p2: f64) -> f64 {
let a = 1.0 - 3.0 * p2 + 3.0 * p1;
let b = 3.0 * p2 - 6.0 * p1;
let c = 3.0 * p1;
((a * t + b) * t + c) * t
}
#[inline]
fn bezier_slope(t: f64, p1: f64, p2: f64) -> f64 {
let a = 1.0 - 3.0 * p2 + 3.0 * p1;
let b = 3.0 * p2 - 6.0 * p1;
let c = 3.0 * p1;
(3.0 * a * t + 2.0 * b) * t + c
}