use iced::animation::Easing;
pub fn progress(
elapsed_ms: f32,
duration_ms: f32,
delay_ms: f32,
easing: Easing,
bezier: Option<[f32; 4]>,
) -> (f32, bool) {
let effective = (elapsed_ms - delay_ms).max(0.0);
if elapsed_ms < delay_ms {
return (0.0, false);
}
let t = (effective / duration_ms).min(1.0);
let eased_t = match bezier {
Some([x1, y1, x2, y2]) => cubic_bezier(t, x1, y1, x2, y2),
None => easing.value(t),
};
(eased_t, t >= 1.0)
}
pub fn advance(
from: f32,
to: f32,
elapsed_ms: f32,
duration_ms: f32,
delay_ms: f32,
easing: Easing,
bezier: Option<[f32; 4]>,
) -> (f32, bool) {
let (eased_t, finished) = progress(elapsed_ms, duration_ms, delay_ms, easing, bezier);
if finished {
(to, true)
} else {
let value = from + (to - from) * eased_t;
(value, false)
}
}
fn cubic_bezier(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 mut s = t; for _ in 0..8 {
let x = bezier_eval(s, x1, x2);
let dx = bezier_derivative(s, x1, x2);
if (x - t).abs() < 1.0e-6 || dx.abs() < 1.0e-6 {
break;
}
s -= (x - t) / dx;
s = s.clamp(0.0, 1.0);
}
bezier_eval(s, y1, y2)
}
fn bezier_eval(s: f32, p1: f32, p2: f32) -> f32 {
let s2 = s * s;
let s3 = s2 * s;
let inv = 1.0 - s;
3.0 * inv * inv * s * p1 + 3.0 * inv * s2 * p2 + s3
}
fn bezier_derivative(s: f32, p1: f32, p2: f32) -> f32 {
let inv = 1.0 - s;
3.0 * inv * inv * p1 + 6.0 * inv * s * (p2 - p1) + 3.0 * s * s * (1.0 - p2)
}