rinia 0.0.9

Portable scalar abstractions for game and simulation math.
Documentation
use approx::{AbsDiffEq, RelativeEq, UlpsEq};

use crate::{Quat, QuatScalar, ops::Negate};

#[inline]
fn canonicalize_other_sign<T>(lhs: &Quat<T>, rhs: &Quat<T>) -> Quat<T>
where
    T: QuatScalar,
{
    let dot = lhs.dot(*rhs);
    if dot < T::ZERO { rhs.negate() } else { *rhs }
}

#[inline]
fn components_eq<T, F>(lhs: &Quat<T>, rhs: &Quat<T>, mut cmp: F) -> bool
where
    T: QuatScalar,
    F: FnMut(&T, &T) -> bool,
{
    let rhs = canonicalize_other_sign(lhs, rhs);
    cmp(&lhs.x, &rhs.x) && cmp(&lhs.y, &rhs.y) && cmp(&lhs.z, &rhs.z) && cmp(&lhs.w, &rhs.w)
}

impl<T> AbsDiffEq for Quat<T>
where
    T: QuatScalar + AbsDiffEq<Epsilon = T>,
{
    type Epsilon = T;

    #[inline]
    fn default_epsilon() -> Self::Epsilon {
        T::default_epsilon()
    }

    #[inline]
    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
        components_eq(self, other, |a, b| T::abs_diff_eq(a, b, epsilon))
    }
}

impl<T> RelativeEq for Quat<T>
where
    T: QuatScalar + RelativeEq<Epsilon = T>,
{
    #[inline]
    fn default_max_relative() -> Self::Epsilon {
        T::default_max_relative()
    }

    #[inline]
    fn relative_eq(
        &self,
        other: &Self,
        epsilon: Self::Epsilon,
        max_relative: Self::Epsilon,
    ) -> bool {
        components_eq(self, other, |a, b| T::relative_eq(a, b, epsilon, max_relative))
    }
}

impl<T> UlpsEq for Quat<T>
where
    T: QuatScalar + UlpsEq<Epsilon = T>,
{
    #[inline]
    fn default_max_ulps() -> u32 {
        T::default_max_ulps()
    }

    #[inline]
    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        components_eq(self, other, |a, b| T::ulps_eq(a, b, epsilon, max_ulps))
    }
}