use crate::color::Color;
pub fn color_interp(
start: Color,
end: Color,
time: f32,
duration: f32,
easing: fn(f32) -> f32,
) -> Color {
let wave = time % duration;
let wave = if ((time / duration) as u32).is_multiple_of(2) {
wave
} else {
1.0 - wave
};
let t = easing(wave.clamp(0.0, 1.0));
let sr = start.r;
let sg = start.g;
let sb = start.b;
let sa = start.a;
let er = end.r;
let eg = end.g;
let eb = end.b;
let ea = end.a;
Color {
r: (sr + (er - sr) * t).round(),
g: (sg + (eg - sg) * t).round(),
b: (sb + (eb - sb) * t).round(),
a: (sa + (ea - sa) * t).round(),
}
}
pub fn ease_in_out_quad(t: f32) -> f32 {
if t < 0.5 {
2.0 * t * t
} else {
let ft = t * 2.0 - 1.0;
1.0 - ft * ft / 2.0
}
}
pub fn ease_out_elastic(t: f32) -> f32 {
let c4 = (2.0 * std::f32::consts::PI) / 3.0;
if t == 0.0 {
0.0
} else if t == 1.0 {
1.0
} else {
2.0_f32.powf(-10.0 * t) * ((t * 10.0 - 0.75) * c4).sin() + 1.0
}
}
pub fn ease_out_expo(t: f32) -> f32 {
if t >= 1.0 {
1.0
} else {
1.0 - 2.0_f32.powf(-10.0 * t)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ease_in_out_quad_boundary() {
assert_eq!(ease_in_out_quad(0.0), 0.0);
assert_eq!(ease_in_out_quad(1.0), 0.5);
}
#[test]
fn test_ease_in_out_quad_midpoint() {
assert_eq!(ease_in_out_quad(0.5), 1.0);
}
#[test]
fn test_ease_in_out_quad_shape() {
let quarter = ease_in_out_quad(0.25);
assert!(quarter > 0.0 && quarter < 0.5);
let three_quarter = ease_in_out_quad(0.75);
assert!(three_quarter > 0.5 && three_quarter < 1.0);
}
#[test]
fn test_ease_out_elastic_boundary() {
assert_eq!(ease_out_elastic(0.0), 0.0);
assert_eq!(ease_out_elastic(1.0), 1.0);
}
#[test]
fn test_ease_out_elastic_overshoot() {
let mut has_overshoot = false;
for i in 1..100 {
let t = i as f32 / 100.0;
let v = ease_out_elastic(t);
if v > 1.0 {
has_overshoot = true;
break;
}
}
assert!(has_overshoot);
}
#[test]
fn test_ease_out_expo_boundary() {
assert!(ease_out_expo(0.0) < 0.01);
assert_eq!(ease_out_expo(1.0), 1.0);
}
#[test]
fn test_ease_out_expo_beyond_one() {
assert_eq!(ease_out_expo(1.5), 1.0);
assert_eq!(ease_out_expo(10.0), 1.0);
}
#[test]
fn test_ease_out_expo_fast_start() {
let early = ease_out_expo(0.1);
assert!(early > 0.2); }
#[test]
fn test_color_interp_at_start() {
let start = Color::rgb(0.0, 0.0, 0.0);
let end = Color::rgb(1.0, 1.0, 1.0);
let result = color_interp(start, end, 0.0, 1.0, |t| t);
assert_eq!(result.r, 0.0);
}
#[test]
fn test_color_interp_at_end() {
let start = Color::rgb(0.0, 0.0, 0.0);
let end = Color::rgb(1.0, 1.0, 1.0);
let result = color_interp(start, end, 1.0, 1.0, |t| t);
assert_eq!(result.r, 1.0);
}
#[test]
fn test_color_interp_with_linear_easing() {
let start = Color::rgb(0.0, 0.0, 0.0);
let end = Color::rgb(1.0, 1.0, 1.0);
let result = color_interp(start, end, 0.5, 1.0, |t| t);
assert!(result.r == 0.0 || result.r == 1.0);
}
}