pub fn lerp_double(a: Option<f64>, b: Option<f64>, t: f64) -> Option<f64> {
if a == b || (a.is_some_and(f64::is_nan) && b.is_some_and(f64::is_nan)) {
return a;
}
let a = a.unwrap_or(0.0);
let b = b.unwrap_or(0.0);
debug_assert!(
a.is_finite(),
"Cannot interpolate between finite and non-finite values"
);
debug_assert!(
b.is_finite(),
"Cannot interpolate between finite and non-finite values"
);
debug_assert!(
t.is_finite(),
"t must be finite when interpolating between values"
);
Some(a * (1.0 - t) + b * t)
}
pub(crate) fn lerp_double_non_null(a: f64, b: f64, t: f64) -> f64 {
a * (1.0 - t) + b * t
}
pub fn lerp_int(a: i32, b: i32, t: f64) -> f64 {
f64::from(a) + f64::from(b - a) * t
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interpolating_between_two_values_walks_the_line() {
assert_eq!(lerp_double(Some(0.0), Some(10.0), 0.25), Some(2.5));
assert_eq!(lerp_double(Some(10.0), Some(0.0), 0.25), Some(7.5));
}
#[test]
fn extrapolating_past_the_unit_interval_is_allowed() {
assert_eq!(lerp_double(Some(0.0), Some(10.0), -0.5), Some(-5.0));
assert_eq!(lerp_double(Some(0.0), Some(10.0), 1.5), Some(15.0));
}
#[test]
fn a_null_end_defaults_to_zero_and_two_nulls_stay_null() {
assert_eq!(lerp_double(None, Some(10.0), 0.25), Some(2.5));
assert_eq!(lerp_double(Some(10.0), None, 0.25), Some(7.5));
assert_eq!(lerp_double(None, None, 0.25), None);
}
#[test]
fn two_nans_return_the_first_where_equality_would_say_they_differ() {
assert!(lerp_double(Some(f64::NAN), Some(f64::NAN), 0.25).is_some_and(f64::is_nan));
}
#[test]
fn lerp_int_walks_the_line() {
assert_eq!(lerp_int(100, 900, 0.5), 500.0);
assert_eq!(lerp_int(400, 400, 0.25), 400.0);
}
}