#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use num_traits::Float as _;
pub trait Interpolate: Sized {
fn lerp(&self, other: &Self, t: f32) -> Self;
}
pub trait Animatable: Interpolate + Clone + 'static {}
impl<T: Interpolate + Clone + 'static> Animatable for T {}
pub trait Update {
fn update(&mut self, dt: f32) -> bool;
}
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
impl<T: Update + ?Sized> Update for Box<T> {
fn update(&mut self, dt: f32) -> bool {
(**self).update(dt)
}
}
impl Interpolate for f32 {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
self + (other - self) * t
}
}
impl Interpolate for f64 {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
self + (other - self) * (t as f64)
}
}
impl Interpolate for [f32; 2] {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
[self[0].lerp(&other[0], t), self[1].lerp(&other[1], t)]
}
}
impl Interpolate for [f32; 3] {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
[
self[0].lerp(&other[0], t),
self[1].lerp(&other[1], t),
self[2].lerp(&other[2], t),
]
}
}
impl Interpolate for [f32; 4] {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
[
self[0].lerp(&other[0], t),
self[1].lerp(&other[1], t),
self[2].lerp(&other[2], t),
self[3].lerp(&other[3], t),
]
}
}
impl Interpolate for i32 {
#[inline]
fn lerp(&self, other: &Self, t: f32) -> Self {
(*self as f32).lerp(&(*other as f32), t).round() as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f32_lerp_midpoint() {
let a = 0.0_f32;
let b = 10.0_f32;
assert!((a.lerp(&b, 0.5) - 5.0).abs() < 1e-6);
}
#[test]
fn f32_lerp_endpoints() {
let a = 3.0_f32;
let b = 7.0_f32;
assert!((a.lerp(&b, 0.0) - 3.0).abs() < 1e-6);
assert!((a.lerp(&b, 1.0) - 7.0).abs() < 1e-6);
}
#[test]
fn vec2_lerp() {
let a = [0.0_f32, 0.0];
let b = [4.0_f32, 8.0];
let mid = a.lerp(&b, 0.5);
assert!((mid[0] - 2.0).abs() < 1e-6);
assert!((mid[1] - 4.0).abs() < 1e-6);
}
#[test]
fn rgba_lerp_alpha() {
let transparent = [1.0_f32, 0.0, 0.0, 0.0];
let opaque = [1.0_f32, 0.0, 0.0, 1.0];
let half = transparent.lerp(&opaque, 0.5);
assert!((half[3] - 0.5).abs() < 1e-6);
}
#[test]
fn i32_lerp_rounds() {
assert_eq!(0_i32.lerp(&10, 0.34), 3);
assert_eq!(0_i32.lerp(&10, 0.35), 4); }
#[test]
fn animatable_is_auto_impl() {
fn needs_animatable<T: Animatable>(_: T) {}
needs_animatable(1.0_f32);
needs_animatable([0.0_f32; 4]);
}
}