blob: 49c3db6f9f606db48f5078fd94acc683e598257c [file] [log] [blame]
use crate::{
Affine2, Affine3A, DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2,
Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4,
};
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
macro_rules! impl_approx_as_ref {
($prim:ident, $type:ty) => {
impl AbsDiffEq for $type {
type Epsilon = <$prim as AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
$prim::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
}
}
impl RelativeEq for $type {
fn default_max_relative() -> Self::Epsilon {
$prim::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.as_ref()
.relative_eq(other.as_ref(), epsilon, max_relative)
}
}
impl UlpsEq for $type {
fn default_max_ulps() -> u32 {
$prim::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
}
}
};
}
macro_rules! impl_approx_xzy_axes {
($prim:ident, $type:ty) => {
impl AbsDiffEq for $type {
type Epsilon = <$prim as AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
$prim::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon)
&& AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon)
&& AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon)
}
}
impl RelativeEq for $type {
fn default_max_relative() -> Self::Epsilon {
$prim::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative)
&& RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative)
&& RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative)
}
}
impl UlpsEq for $type {
fn default_max_ulps() -> u32 {
$prim::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps)
&& UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps)
&& UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps)
}
}
};
}
macro_rules! impl_approx_xzyw_axes {
($prim:ident, $type:ty) => {
impl AbsDiffEq for $type {
type Epsilon = <$prim as AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
$prim::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon)
&& AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon)
&& AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon)
&& AbsDiffEq::abs_diff_eq(&self.w_axis, &other.w_axis, epsilon)
}
}
impl RelativeEq for $type {
fn default_max_relative() -> Self::Epsilon {
$prim::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative)
&& RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative)
&& RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative)
&& RelativeEq::relative_eq(&self.w_axis, &other.w_axis, epsilon, max_relative)
}
}
impl UlpsEq for $type {
fn default_max_ulps() -> u32 {
$prim::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps)
&& UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps)
&& UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps)
&& UlpsEq::ulps_eq(&self.w_axis, &other.w_axis, epsilon, max_ulps)
}
}
};
}
impl_approx_as_ref!(f32, Mat2);
impl_approx_as_ref!(f32, Mat3);
impl_approx_as_ref!(f32, Mat4);
impl_approx_as_ref!(f32, Quat);
impl_approx_as_ref!(f32, Vec2);
impl_approx_as_ref!(f32, Vec3);
impl_approx_as_ref!(f32, Vec4);
impl_approx_as_ref!(f32, Vec3A);
impl_approx_xzy_axes!(f32, Affine2);
impl_approx_xzyw_axes!(f32, Affine3A);
impl_approx_xzy_axes!(f32, Mat3A);
impl_approx_xzy_axes!(f64, DAffine2);
impl_approx_xzyw_axes!(f64, DAffine3);
impl_approx_as_ref!(f64, DMat2);
impl_approx_as_ref!(f64, DMat3);
impl_approx_as_ref!(f64, DMat4);
impl_approx_as_ref!(f64, DQuat);
impl_approx_as_ref!(f64, DVec2);
impl_approx_as_ref!(f64, DVec3);
impl_approx_as_ref!(f64, DVec4);
#[cfg(test)]
mod test {
use crate::*;
use approx::*;
macro_rules! impl_approx_test {
($prim:ident, $type:ident, $ones:expr) => {
let one_eps = $ones * $type::default_epsilon();
let two_eps = one_eps + one_eps;
let one_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 1);
let four_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 16);
approx::assert_abs_diff_eq!($ones, $ones);
approx::assert_abs_diff_eq!($ones, $ones + one_eps);
approx::assert_abs_diff_eq!($ones, $ones - one_eps);
approx::assert_abs_diff_ne!($ones, $ones + two_eps);
approx::assert_abs_diff_ne!($ones, $ones - two_eps);
approx::assert_relative_eq!($ones, $ones);
approx::assert_relative_ne!($ones, $ones - $ones);
// defaults to 4 ulps and I have no idea how to pass other parameters to this macro :)
approx::assert_ulps_eq!($ones, one_ulp);
approx::assert_ulps_ne!($ones, four_ulp);
};
($prim:ident, $type:ident) => {
impl_approx_test!($prim, $type, $type::ONE)
};
}
#[test]
fn test_approx() {
const ONESF32: [f32; 16] = [1.0; 16];
impl_approx_test!(f32, Vec2);
impl_approx_test!(f32, Vec3);
impl_approx_test!(f32, Vec3A);
impl_approx_test!(f32, Vec4);
impl_approx_test!(f32, Quat, Quat::from_slice(&ONESF32));
impl_approx_test!(f32, Mat2, Mat2::from_cols_slice(&ONESF32));
impl_approx_test!(f32, Mat3, Mat3::from_cols_slice(&ONESF32));
impl_approx_test!(f32, Mat3A, Mat3A::from_cols_slice(&ONESF32));
impl_approx_test!(f32, Mat4, Mat4::from_cols_slice(&ONESF32));
const ONESF64: [f64; 16] = [1.0; 16];
impl_approx_test!(f64, DVec2);
impl_approx_test!(f64, DVec3);
impl_approx_test!(f64, DVec4);
impl_approx_test!(f64, DQuat, DQuat::from_slice(&ONESF64));
impl_approx_test!(f64, DMat2, DMat2::from_cols_slice(&ONESF64));
impl_approx_test!(f64, DMat3, DMat3::from_cols_slice(&ONESF64));
impl_approx_test!(f64, DMat4, DMat4::from_cols_slice(&ONESF64));
}
}