use crate::core::EasingFunction;
pub fn custom(f: fn(f64) -> f64) -> EasingFunction {
EasingFunction::Custom(Box::new(f))
}
pub fn bezier(x1: f64, y1: f64, x2: f64, y2: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| cubic_bezier(t, x1, y1, x2, y2)))
}
fn cubic_bezier(t: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
if x1 == y1 && x2 == y2 {
return t;
}
let sample_curve_x = |t: f64| -> f64 {
((1.0 - 3.0 * x2 + 3.0 * x1) * t * t + (3.0 * x2 - 6.0 * x1) * t + 3.0 * x1) * t
};
let sample_curve_y = |t: f64| -> f64 {
((1.0 - 3.0 * y2 + 3.0 * y1) * t * t + (3.0 * y2 - 6.0 * y1) * t + 3.0 * y1) * t
};
let sample_curve_derivative_x = |t: f64| -> f64 {
(3.0 * (1.0 - 3.0 * x2 + 3.0 * x1) * t + 2.0 * (3.0 * x2 - 6.0 * x1)) * t + 3.0 * x1
};
let solve_curve_x = |x: f64| -> f64 {
let mut t2 = x;
for _ in 0..8 {
let x2 = sample_curve_x(t2) - x;
let d2 = sample_curve_derivative_x(t2);
if x2.abs() < 1e-7 {
break;
}
if d2.abs() < 1e-7 {
break;
}
t2 -= x2 / d2;
}
t2
};
sample_curve_y(solve_curve_x(t))
}
pub fn steps(n: u32, start: bool) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| {
if t <= 0.0 {
return 0.0;
}
if t >= 1.0 {
return 1.0;
}
let step = if start {
(t * n as f64).ceil() / n as f64
} else {
(t * n as f64).floor() / n as f64
};
step.clamp(0.0, 1.0)
}))
}
pub fn power(p: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| t.powf(p)))
}
pub fn elastic(amplitude: f64, period: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| {
if t == 0.0 || t == 1.0 {
t
} else {
let s = period / (2.0 * std::f64::consts::PI) * 1.0_f64.asin();
amplitude
* 2.0_f64.powf(-10.0 * t)
* ((t - s) * 2.0 * std::f64::consts::PI / period).sin()
+ 1.0
}
}))
}
pub fn bounce(amplitude: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| {
if t < 1.0 / 2.75 {
amplitude * t * t
} else if t < 2.0 / 2.75 {
let t = t - 1.5 / 2.75;
amplitude * t * t + 0.75
} else if t < 2.5 / 2.75 {
let t = t - 2.25 / 2.75;
amplitude * t * t + 0.9375
} else {
let t = t - 2.625 / 2.75;
amplitude * t * t + 0.984375
}
}))
}
pub fn overshoot(tension: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| {
if t == 0.0 {
0.0
} else if t == 1.0 {
1.0
} else {
let s = tension + 1.0;
s * t * t * t - tension * t * t
}
}))
}
pub fn anticipate(tension: f64) -> EasingFunction {
EasingFunction::Custom(Box::new(move |t| {
let s = tension * t * t * (2.0 * t - tension - 2.0);
if s.abs() > 1.0 {
1.0
} else if s < 0.0 {
0.0
} else {
s
}
}))
}