use crate::PrimitiveFloat;
#[cfg(feature = "wide")]
mod wide;
#[expect(private_bounds)]
pub trait FloatExt: Sealed {
#[must_use]
fn lerp(self, other: Self, t: Self) -> Self;
#[must_use]
fn move_towards(self, target: Self, max_delta: Self) -> Self;
#[must_use]
fn abs_diff_eq(self, other: Self, max_abs_diff: Self) -> bool;
}
trait Sealed {}
impl<T: PrimitiveFloat> FloatExt for T {
#[inline]
fn lerp(self, other: Self, t: Self) -> Self {
self * (T::ONE - t) + other * t
}
#[inline]
fn move_towards(self, target: Self, max_delta: Self) -> Self {
let delta = target - self;
let delta_abs = delta.abs();
if delta_abs <= max_delta || delta_abs <= Self::as_from(1e-4) {
target
} else {
self + max_delta * delta.signum()
}
}
#[inline]
fn abs_diff_eq(self, other: Self, max_abs_diff: Self) -> bool {
(self - other).abs() <= max_abs_diff
}
}
impl<T: PrimitiveFloat> Sealed for T {}
#[cfg(test)]
mod tests {
use crate::{
FloatExt,
test_utils::{assert_test_eq, for_types, random_iter},
};
#[test]
fn test_lerp() {
for_types!(|T: PrimitiveFloat| {
for [x, y] in
random_iter::<[T; 2]>().filter(|values| values.iter().all(|x| x.is_finite()))
{
assert_test_eq!(x.lerp(y, 0.0), x, 0.0 = -0.0);
assert_test_eq!(x.lerp(y, 0.5), x * 0.5 + y * 0.5, 0.0 = -0.0);
assert_test_eq!(x.lerp(y, 1.0), y, 0.0 = -0.0);
}
});
}
#[test]
fn test_move_towards() {
for_types!(|T: PrimitiveFloat| {
assert!(T::abs_diff_eq(5.0.move_towards(10.0, 2.0), 7.0, 1e-5));
assert!(T::abs_diff_eq(10.0.move_towards(5.0, 2.0), 8.0, 1e-5));
assert!(T::abs_diff_eq((-5.0).move_towards(10.0, 2.0), -3.0, 1e-5));
assert!(T::abs_diff_eq(10.0.move_towards(-5.0, 2.0), 8.0, 1e-5));
assert!(T::abs_diff_eq(5.0.move_towards(10.0, 20.0), 10.0, 1e-5));
assert!(T::abs_diff_eq(10.0.move_towards(5.0, 20.0), 5.0, 1e-5));
assert!(T::abs_diff_eq((-5.0).move_towards(10.0, 20.0), 10.0, 1e-5));
assert!(T::abs_diff_eq(10.0.move_towards(-5.0, 20.0), -5.0, 1e-5));
});
}
#[test]
fn test_abs_diff_eq() {
for_types!(|T: PrimitiveFloat| {
assert!(T::abs_diff_eq(0.0, 0.0, 0.125));
assert!(T::abs_diff_eq(0.0, 0.1, 0.125));
assert!(T::abs_diff_eq(5.0, 4.9, 0.125));
assert!(!T::abs_diff_eq(0.0, 1.0, 0.125));
assert!(!T::abs_diff_eq(0.0, 0.9, 0.125));
assert!(!T::abs_diff_eq(5.0, 3.9, 0.125));
});
}
}