pub type EasingFn = fn(f64) -> f64;
#[inline]
pub fn linear(t: f64) -> f64 {
t
}
#[inline]
pub fn ease_in_quad(t: f64) -> f64 {
t * t
}
#[inline]
pub fn ease_out_quad(t: f64) -> f64 {
t * (2.0 - t)
}
#[inline]
pub fn ease_in_out_quad(t: f64) -> f64 {
if t < 0.5 {
2.0 * t * t
} else {
-1.0 + (4.0 - 2.0 * t) * t
}
}
#[inline]
pub fn ease_in_cubic(t: f64) -> f64 {
t * t * t
}
#[inline]
pub fn ease_out_cubic(t: f64) -> f64 {
let t1 = t - 1.0;
t1 * t1 * t1 + 1.0
}
#[inline]
pub fn ease_in_out_cubic(t: f64) -> f64 {
if t < 0.5 {
4.0 * t * t * t
} else {
(t - 1.0) * (2.0 * t - 2.0) * (2.0 * t - 2.0) + 1.0
}
}
#[inline]
pub fn ease_in_sine(t: f64) -> f64 {
1.0 - (t * std::f64::consts::FRAC_PI_2).cos()
}
#[inline]
pub fn ease_out_sine(t: f64) -> f64 {
(t * std::f64::consts::FRAC_PI_2).sin()
}
#[inline]
pub fn ease_in_out_sine(t: f64) -> f64 {
-(((std::f64::consts::PI * t).cos() - 1.0) / 2.0)
}
pub fn bezier(x1: f64, y1: f64, x2: f64, y2: f64) -> impl Fn(f64) -> f64 {
move |t: f64| {
if t <= 0.0 {
return 0.0;
}
if t >= 1.0 {
return 1.0;
}
let cx = 3.0 * x1;
let bx = 3.0 * (x2 - x1) - cx;
let ax = 1.0 - cx - bx;
let cy = 3.0 * y1;
let by = 3.0 * (y2 - y1) - cy;
let ay = 1.0 - cy - by;
let poly_x = |u: f64| ((ax * u + bx) * u + cx) * u;
let poly_x_deriv = |u: f64| (3.0 * ax * u + 2.0 * bx) * u + cx;
let poly_y = |u: f64| ((ay * u + by) * u + cy) * u;
let mut u = t;
for _ in 0..8 {
let x_err = poly_x(u) - t;
let d = poly_x_deriv(u);
if d.abs() < 1e-12 {
break;
}
u -= x_err / d;
}
poly_y(u)
}
}
pub fn ease() -> impl Fn(f64) -> f64 {
bezier(0.25, 0.1, 0.25, 1.0)
}
pub fn ease_in() -> impl Fn(f64) -> f64 {
bezier(0.42, 0.0, 1.0, 1.0)
}
pub fn ease_out() -> impl Fn(f64) -> f64 {
bezier(0.0, 0.0, 0.58, 1.0)
}
pub fn ease_in_out() -> impl Fn(f64) -> f64 {
bezier(0.42, 0.0, 0.58, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn linear_identity() {
assert!((linear(0.5) - 0.5).abs() < 1e-10);
}
#[test]
fn bezier_endpoints() {
let f = bezier(0.25, 0.1, 0.25, 1.0);
assert!((f(0.0)).abs() < 1e-10);
assert!((f(1.0) - 1.0).abs() < 1e-10);
}
#[test]
fn bezier_midpoint_reasonable() {
let f = ease_out();
assert!(f(0.5) > 0.5);
}
}