#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Curve {
Linear,
EaseOut,
EaseInOut,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Animation {
duration_ms: u32,
elapsed_ms: u32,
curve: Curve,
}
impl Animation {
pub const fn new(duration_ms: u32, curve: Curve) -> Self {
Self {
duration_ms,
elapsed_ms: 0,
curve,
}
}
pub const fn is_running(&self) -> bool {
self.elapsed_ms < self.duration_ms
}
pub const fn is_finished(&self) -> bool {
!self.is_running()
}
pub fn advance(&mut self, dt_ms: u32) -> bool {
self.elapsed_ms = self.elapsed_ms.saturating_add(dt_ms).min(self.duration_ms);
self.is_running()
}
pub fn progress_permille(&self) -> u16 {
if self.duration_ms == 0 {
return 1000;
}
let linear = (self.elapsed_ms.saturating_mul(1000) / self.duration_ms).min(1000) as u16;
ease(self.curve, linear)
}
}
fn ease(curve: Curve, t: u16) -> u16 {
let t = i64::from(t.min(1000));
match curve {
Curve::Linear => t as u16,
Curve::EaseOut => {
let inv = 1000 - t;
let inv2 = (inv * inv) / 1000;
let inv3 = (inv2 * inv) / 1000;
(1000 - inv3) as u16
}
Curve::EaseInOut => smoothstep(t) as u16,
}
}
fn smoothstep(t: i64) -> i64 {
let t2 = (t * t) / 1000;
let t3 = (t2 * t) / 1000;
((3 * t2) - (2 * t3)).clamp(0, 1000)
}
pub fn lerp_i32(from: i32, to: i32, progress_permille: u16) -> i32 {
let delta = i64::from(to - from);
from + ((delta * i64::from(progress_permille)) / 1000) as i32
}
pub fn lerp_u8(from: u8, to: u8, progress_permille: u16) -> u8 {
let value = lerp_i32(i32::from(from), i32::from(to), progress_permille);
value.clamp(0, 255) as u8
}