use crate::{trace, utils::eq_ignore_case};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Interpolation {
Linear,
CubicEaseInOut,
Cosine,
Exponential,
}
impl std::str::FromStr for Interpolation {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
s if eq_ignore_case(s, "linear") => Ok(Self::Linear),
s if eq_ignore_case(s, "cubic") | eq_ignore_case(s, "cubiceaseinout") => {
Ok(Self::CubicEaseInOut)
},
s if eq_ignore_case(s, "cosine") => Ok(Self::Cosine),
s if eq_ignore_case(s, "exponential") => Ok(Self::Exponential),
_ => Err(format!(
"invalid value for interpolation: {s}. Valid values are: linear, cubic, cubiceaseinout, cosine, exponential"
)),
}
}
}
#[must_use]
pub fn interpolate_linear(start: u16, end: u16, factor: f64) -> u16 {
trace!("interpolate_linear({start}, {end}, {factor})");
if end < start {
f64::from(start - end)
.mul_add(1.0 - factor, f64::from(end))
.round() as u16
} else {
f64::from(end - start)
.mul_add(factor, f64::from(start))
.round() as u16
}
}
fn interpolate_cubic(start: u16, end: u16, factor: f64) -> u16 {
trace!("interpolate_cubic({start}, {end}, {factor})");
let factor = factor.clamp(0.0, 1.0);
let start_f = f64::from(start);
let end_f = f64::from(end);
let t = factor;
let smooth_t = (3.0 * t).mul_add(t, -(2.0 * t * t * t));
let result = smooth_t.mul_add(end_f - start_f, start_f);
result.round().max(0.0).min(f64::from(u16::MAX)) as u16
}
fn interpolate_cosine(start: u16, end: u16, factor: f64) -> u16 {
trace!("interpolate_cosine({start}, {end}, {factor})");
let t = (1.0 - (factor.clamp(0.0, 1.0) * std::f64::consts::PI).cos()) / 2.0;
interpolate_linear(start, end, t)
}
fn interpolate_exponential(start: u16, end: u16, factor: f64) -> u16 {
trace!("interpolate_exponential({start}, {end}, {factor})");
let t = factor.clamp(0.0, 1.0);
let t = if t < 0.5 {
0.5 * (2.0 * t).powi(3)
} else {
0.5f64.mul_add(1.0 - (2.0 * (1.0 - t)).powi(3), 0.5)
};
interpolate_linear(start, end, t)
}
pub fn interpolate_value(start: u16, end: u16, factor: f64, interpolation: Interpolation) -> u16 {
trace!("interpolate_value({start}, {end}, {factor}, {interpolation:?})");
match interpolation {
Interpolation::Linear => interpolate_linear(start, end, factor),
Interpolation::CubicEaseInOut => interpolate_cubic(start, end, factor),
Interpolation::Cosine => interpolate_cosine(start, end, factor),
Interpolation::Exponential => interpolate_exponential(start, end, factor),
}
}