From a93a3f09da55ca569eeded49c0e6629ac1800582 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Fri, 15 Jul 2022 00:39:14 -0700 Subject: [PATCH] RMath: implement cross product and bring back some unit tests. --- sub_crates/rmath/src/lib.rs | 23 ++-- sub_crates/rmath/src/normal.rs | 207 ++++++++++++++++++-------------- sub_crates/rmath/src/point.rs | 106 +++++++---------- sub_crates/rmath/src/vector.rs | 209 +++++++++++++++++++-------------- sub_crates/rmath/src/wide4.rs | 31 +++-- sub_crates/rmath/src/xform.rs | 18 +-- 6 files changed, 324 insertions(+), 270 deletions(-) diff --git a/sub_crates/rmath/src/lib.rs b/sub_crates/rmath/src/lib.rs index fee0aca..e382266 100644 --- a/sub_crates/rmath/src/lib.rs +++ b/sub_crates/rmath/src/lib.rs @@ -29,15 +29,22 @@ pub fn dot_fast(a: T, b: T) -> f32 { a.dot_fast(b) } -// /// Trait for calculating cross products. -// pub trait CrossProduct { -// fn cross(self, other: Self) -> Self; -// } +/// Trait for calculating cross products. +pub trait CrossProduct { + fn cross(self, other: Self) -> Self; -// #[inline] -// pub fn cross(a: T, b: T) -> T { -// a.cross(b) -// } + fn cross_fast(self, other: Self) -> Self; +} + +#[inline(always)] +pub fn cross(a: T, b: T) -> T { + a.cross(b) +} + +#[inline(always)] +pub fn cross_fast(a: T, b: T) -> T { + a.cross_fast(b) +} //------------------------------------------------------------- diff --git a/sub_crates/rmath/src/normal.rs b/sub_crates/rmath/src/normal.rs index b4c6508..ab73bf8 100644 --- a/sub_crates/rmath/src/normal.rs +++ b/sub_crates/rmath/src/normal.rs @@ -1,11 +1,12 @@ #![allow(dead_code)] +use std::cmp::PartialEq; use std::ops::{Add, Div, Mul, Neg, Sub}; use crate::wide4::Float4; use crate::xform::XformFull; -use crate::DotProduct; use crate::Vector; +use crate::{CrossProduct, DotProduct}; /// A surface normal in 3D space. #[derive(Debug, Copy, Clone)] @@ -138,6 +139,13 @@ impl Neg for Normal { } } +impl PartialEq for Normal { + #[inline(always)] + fn eq(&self, rhs: &Self) -> bool { + self.0.a() == rhs.0.a() && self.0.b() == rhs.0.b() && self.0.c() == rhs.0.c() + } +} + impl DotProduct for Normal { #[inline(always)] fn dot(self, other: Self) -> f32 { @@ -150,109 +158,132 @@ impl DotProduct for Normal { } } -// impl CrossProduct for Normal { -// #[inline] -// fn cross(self, other: Normal) -> Normal { -// Normal { -// co: self.co.cross(other.co), -// } -// } -// } +impl CrossProduct for Normal { + #[inline(always)] + fn cross(self, other: Self) -> Self { + Self(Float4::cross_3(self.0, other.0)) + } + + #[inline(always)] + fn cross_fast(self, other: Self) -> Self { + Self(Float4::cross_3_fast(self.0, other.0)) + } +} //------------------------------------------------------------- -// #[cfg(test)] -// mod tests { -// use super::super::{CrossProduct, DotProduct, Transform}; -// use super::*; -// use approx::assert_ulps_eq; +#[cfg(test)] +mod tests { + use super::*; + use crate::{CrossProduct, DotProduct, Xform}; -// #[test] -// fn add() { -// let v1 = Normal::new(1.0, 2.0, 3.0); -// let v2 = Normal::new(1.5, 4.5, 2.5); -// let v3 = Normal::new(2.5, 6.5, 5.5); + #[test] + fn add() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = Normal::new(1.5, 4.5, 2.5); + let v3 = Normal::new(2.5, 6.5, 5.5); -// assert_eq!(v3, v1 + v2); -// } + assert_eq!(v3, v1 + v2); + } -// #[test] -// fn sub() { -// let v1 = Normal::new(1.0, 2.0, 3.0); -// let v2 = Normal::new(1.5, 4.5, 2.5); -// let v3 = Normal::new(-0.5, -2.5, 0.5); + #[test] + fn sub() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = Normal::new(1.5, 4.5, 2.5); + let v3 = Normal::new(-0.5, -2.5, 0.5); -// assert_eq!(v3, v1 - v2); -// } + assert_eq!(v3, v1 - v2); + } -// #[test] -// fn mul_scalar() { -// let v1 = Normal::new(1.0, 2.0, 3.0); -// let v2 = 2.0; -// let v3 = Normal::new(2.0, 4.0, 6.0); + #[test] + fn mul_scalar() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = 2.0; + let v3 = Normal::new(2.0, 4.0, 6.0); -// assert_eq!(v3, v1 * v2); -// } + assert_eq!(v3, v1 * v2); + } -// #[test] -// fn mul_matrix_1() { -// let n = Normal::new(1.0, 2.5, 4.0); -// let m = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// let nm = n * m; -// let nm2 = Normal::new(-4.0625, 1.78125, -0.03125); -// for i in 0..3 { -// assert_ulps_eq!(nm.co[i], nm2.co[i], max_ulps = 4); -// } -// } + #[test] + fn xform() { + let n = Normal::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); -// #[test] -// fn div() { -// let v1 = Normal::new(1.0, 2.0, 3.0); -// let v2 = 2.0; -// let v3 = Normal::new(0.5, 1.0, 1.5); + assert_eq!(n.xform(&m), Normal::new(-4.0625, 1.78125, -0.03125)); + assert_eq!(n.xform(&m).xform_inv(&m), n); + } -// assert_eq!(v3, v1 / v2); -// } + #[test] + fn xform_fast() { + let n = Normal::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); -// #[test] -// fn length() { -// let n = Normal::new(1.0, 2.0, 3.0); -// assert!((n.length() - 3.7416573867739413).abs() < 0.000001); -// } + assert_eq!(n.xform_fast(&m), Normal::new(-4.0625, 1.78125, -0.03125)); + assert_eq!(n.xform_fast(&m).xform_inv_fast(&m), n); + } -// #[test] -// fn length2() { -// let n = Normal::new(1.0, 2.0, 3.0); -// assert_eq!(n.length2(), 14.0); -// } + #[test] + fn div() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = 2.0; + let v3 = Normal::new(0.5, 1.0, 1.5); -// #[test] -// fn normalized() { -// let n1 = Normal::new(1.0, 2.0, 3.0); -// let n2 = Normal::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732); -// let n3 = n1.normalized(); -// assert!((n3.x() - n2.x()).abs() < 0.000001); -// assert!((n3.y() - n2.y()).abs() < 0.000001); -// assert!((n3.z() - n2.z()).abs() < 0.000001); -// } + assert_eq!(v3, v1 / v2); + } -// #[test] -// fn dot_test() { -// let v1 = Normal::new(1.0, 2.0, 3.0); -// let v2 = Normal::new(1.5, 4.5, 2.5); -// let v3 = 18.0f32; + #[test] + fn length() { + let n = Normal::new(1.0, 2.0, 3.0); + assert!((n.length() - 3.7416573867739413).abs() < 0.000001); + } -// assert_eq!(v3, v1.dot(v2)); -// } + #[test] + fn length2() { + let n = Normal::new(1.0, 2.0, 3.0); + assert_eq!(n.length2(), 14.0); + } -// #[test] -// fn cross_test() { -// let v1 = Normal::new(1.0, 0.0, 0.0); -// let v2 = Normal::new(0.0, 1.0, 0.0); -// let v3 = Normal::new(0.0, 0.0, 1.0); + #[test] + fn normalized() { + let n1 = Normal::new(1.0, 2.0, 3.0); + let n2 = Normal::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732); + let n3 = n1.normalized(); + assert!((n3.x() - n2.x()).abs() < 0.000001); + assert!((n3.y() - n2.y()).abs() < 0.000001); + assert!((n3.z() - n2.z()).abs() < 0.000001); + } -// assert_eq!(v3, v1.cross(v2)); -// } -// } + #[test] + fn dot() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = Normal::new(1.5, 4.5, 2.5); + + assert_eq!(v1.dot(v2), 18.0); + } + + #[test] + fn dot_fast() { + let v1 = Normal::new(1.0, 2.0, 3.0); + let v2 = Normal::new(1.5, 4.5, 2.5); + + assert_eq!(v1.dot_fast(v2), 18.0); + } + + #[test] + fn cross() { + let v1 = Normal::new(1.0, 0.0, 0.0); + let v2 = Normal::new(0.0, 1.0, 0.0); + + assert_eq!(v1.cross(v2), Normal::new(0.0, 0.0, 1.0)); + } + + #[test] + fn cross_fast() { + let v1 = Normal::new(1.0, 0.0, 0.0); + let v2 = Normal::new(0.0, 1.0, 0.0); + + assert_eq!(v1.cross_fast(v2), Normal::new(0.0, 0.0, 1.0)); + } +} diff --git a/sub_crates/rmath/src/point.rs b/sub_crates/rmath/src/point.rs index 9b94811..b43a575 100644 --- a/sub_crates/rmath/src/point.rs +++ b/sub_crates/rmath/src/point.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +use std::cmp::PartialEq; use std::ops::{Add, Sub}; use crate::vector::Vector; @@ -111,76 +112,53 @@ impl Sub for Point { } } -// impl Mul for Point { -// type Output = Self; - -// #[inline] -// fn mul(self, other: Transform) -> Self { -// Self { -// co: other.0.transform_point3a(self.0), -// } -// } -// } +impl PartialEq for Point { + #[inline(always)] + fn eq(&self, rhs: &Self) -> bool { + self.0.a() == rhs.0.a() && self.0.b() == rhs.0.b() && self.0.c() == rhs.0.c() + } +} //------------------------------------------------------------- -// #[cfg(test)] -// mod tests { -// use super::super::{Transform, Vector}; -// use super::*; +#[cfg(test)] +mod tests { + use super::*; + use crate::{Vector, Xform}; -// #[test] -// fn add() { -// let p1 = Point::new(1.0, 2.0, 3.0); -// let v1 = Vector::new(1.5, 4.5, 2.5); -// let p2 = Point::new(2.5, 6.5, 5.5); + #[test] + fn add() { + let p1 = Point::new(1.0, 2.0, 3.0); + let v1 = Vector::new(1.5, 4.5, 2.5); + let p2 = Point::new(2.5, 6.5, 5.5); -// assert_eq!(p2, p1 + v1); -// } + assert_eq!(p2, p1 + v1); + } -// #[test] -// fn sub() { -// let p1 = Point::new(1.0, 2.0, 3.0); -// let p2 = Point::new(1.5, 4.5, 2.5); -// let v1 = Vector::new(-0.5, -2.5, 0.5); + #[test] + fn sub() { + let p1 = Point::new(1.0, 2.0, 3.0); + let p2 = Point::new(1.5, 4.5, 2.5); + let v1 = Vector::new(-0.5, -2.5, 0.5); -// assert_eq!(v1, p1 - p2); -// } + assert_eq!(v1, p1 - p2); + } -// #[test] -// fn mul_matrix_1() { -// let p = Point::new(1.0, 2.5, 4.0); -// let m = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// let pm = Point::new(15.5, 54.0, 70.0); -// assert_eq!(p * m, pm); -// } + #[test] + fn xform() { + let p = Point::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); + assert_eq!(p.xform(&m), Point::new(15.5, 54.0, 70.0)); + assert_eq!(p.xform(&m).xform_inv(&m), p); + } -// #[test] -// fn mul_matrix_2() { -// let p = Point::new(1.0, 2.5, 4.0); -// let m = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// let pm = Point::new(15.5, 54.0, 70.0); -// assert_eq!(p * m, pm); -// } - -// #[test] -// fn mul_matrix_3() { -// // Make sure matrix multiplication composes the way one would expect -// let p = Point::new(1.0, 2.5, 4.0); -// let m1 = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// let m2 = -// Transform::new_from_values(4.0, 1.0, 2.0, 3.5, 3.0, 6.0, 5.0, 2.0, 2.0, 2.0, 4.0, 12.0); -// println!("{:?}", m1 * m2); - -// let pmm1 = p * (m1 * m2); -// let pmm2 = (p * m1) * m2; - -// assert!((pmm1 - pmm2).length2() <= 0.00001); // Assert pmm1 and pmm2 are roughly equal -// } -// } + #[test] + fn xform_fast() { + let p = Point::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); + assert_eq!(p.xform_fast(&m), Point::new(15.5, 54.0, 70.0)); + assert_eq!(p.xform_fast(&m).xform_inv_fast(&m), p); + } +} diff --git a/sub_crates/rmath/src/vector.rs b/sub_crates/rmath/src/vector.rs index 04beb1d..4ff2a97 100644 --- a/sub_crates/rmath/src/vector.rs +++ b/sub_crates/rmath/src/vector.rs @@ -1,12 +1,13 @@ #![allow(dead_code)] +use std::cmp::PartialEq; use std::ops::{Add, Div, Mul, Neg, Sub}; use crate::normal::Normal; use crate::point::Point; use crate::wide4::Float4; use crate::xform::XformFull; -use crate::DotProduct; +use crate::{CrossProduct, DotProduct}; /// A direction vector in 3D space. #[derive(Debug, Copy, Clone)] @@ -144,6 +145,13 @@ impl Neg for Vector { } } +impl PartialEq for Vector { + #[inline(always)] + fn eq(&self, rhs: &Self) -> bool { + self.0.a() == rhs.0.a() && self.0.b() == rhs.0.b() && self.0.c() == rhs.0.c() + } +} + impl DotProduct for Vector { #[inline(always)] fn dot(self, other: Self) -> f32 { @@ -156,113 +164,132 @@ impl DotProduct for Vector { } } -// impl CrossProduct for Vector { -// #[inline] -// fn cross(self, other: Self) -> Self { -// Self { -// co: self.co.cross(other.co), -// } -// } -// } +impl CrossProduct for Vector { + #[inline(always)] + fn cross(self, other: Self) -> Self { + Self(Float4::cross_3(self.0, other.0)) + } + + #[inline(always)] + fn cross_fast(self, other: Self) -> Self { + Self(Float4::cross_3_fast(self.0, other.0)) + } +} //------------------------------------------------------------- -// #[cfg(test)] -// mod tests { -// use super::super::{CrossProduct, DotProduct, Transform}; -// use super::*; +#[cfg(test)] +mod tests { + use super::*; + use crate::{CrossProduct, DotProduct, Xform}; -// #[test] -// fn add() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = Vector::new(1.5, 4.5, 2.5); -// let v3 = Vector::new(2.5, 6.5, 5.5); + #[test] + fn add() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = Vector::new(1.5, 4.5, 2.5); + let v3 = Vector::new(2.5, 6.5, 5.5); -// assert_eq!(v3, v1 + v2); -// } + assert_eq!(v3, v1 + v2); + } -// #[test] -// fn sub() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = Vector::new(1.5, 4.5, 2.5); -// let v3 = Vector::new(-0.5, -2.5, 0.5); + #[test] + fn sub() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = Vector::new(1.5, 4.5, 2.5); + let v3 = Vector::new(-0.5, -2.5, 0.5); -// assert_eq!(v3, v1 - v2); -// } + assert_eq!(v3, v1 - v2); + } -// #[test] -// fn mul_scalar() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = 2.0; -// let v3 = Vector::new(2.0, 4.0, 6.0); + #[test] + fn mul_scalar() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = 2.0; + let v3 = Vector::new(2.0, 4.0, 6.0); -// assert_eq!(v3, v1 * v2); -// } + assert_eq!(v3, v1 * v2); + } -// #[test] -// fn mul_matrix_1() { -// let v = Vector::new(1.0, 2.5, 4.0); -// let m = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0)); -// } + #[test] + fn xform() { + let v = Vector::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); -// #[test] -// fn mul_matrix_2() { -// let v = Vector::new(1.0, 2.5, 4.0); -// let m = Transform::new_from_values( -// 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, -// ); -// assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0)); -// } + assert_eq!(v.xform(&m), Vector::new(14.0, 46.0, 58.0)); + assert_eq!(v.xform(&m).xform_inv(&m), v); + } -// #[test] -// fn div() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = 2.0; -// let v3 = Vector::new(0.5, 1.0, 1.5); + #[test] + fn xform_fast() { + let v = Vector::new(1.0, 2.5, 4.0); + let m = + Xform::new(1.0, 3.0, 9.0, 2.0, 6.0, 2.0, 2.0, 7.0, 11.0, 1.5, 8.0, 12.0).into_full(); -// assert_eq!(v3, v1 / v2); -// } + assert_eq!(v.xform_fast(&m), Vector::new(14.0, 46.0, 58.0)); + assert_eq!(v.xform_fast(&m).xform_inv_fast(&m), v); + } -// #[test] -// fn length() { -// let v = Vector::new(1.0, 2.0, 3.0); -// assert!((v.length() - 3.7416573867739413).abs() < 0.000001); -// } + #[test] + fn div() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = 2.0; + let v3 = Vector::new(0.5, 1.0, 1.5); -// #[test] -// fn length2() { -// let v = Vector::new(1.0, 2.0, 3.0); -// assert_eq!(v.length2(), 14.0); -// } + assert_eq!(v3, v1 / v2); + } -// #[test] -// fn normalized() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = Vector::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732); -// let v3 = v1.normalized(); -// assert!((v3.x() - v2.x()).abs() < 0.000001); -// assert!((v3.y() - v2.y()).abs() < 0.000001); -// assert!((v3.z() - v2.z()).abs() < 0.000001); -// } + #[test] + fn length() { + let v = Vector::new(1.0, 2.0, 3.0); + assert!((v.length() - 3.7416573867739413).abs() < 0.000001); + } -// #[test] -// fn dot_test() { -// let v1 = Vector::new(1.0, 2.0, 3.0); -// let v2 = Vector::new(1.5, 4.5, 2.5); -// let v3 = 18.0f32; + #[test] + fn length2() { + let v = Vector::new(1.0, 2.0, 3.0); + assert_eq!(v.length2(), 14.0); + } -// assert_eq!(v3, v1.dot(v2)); -// } + #[test] + fn normalized() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = Vector::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732); + let v3 = v1.normalized(); + assert!((v3.x() - v2.x()).abs() < 0.000001); + assert!((v3.y() - v2.y()).abs() < 0.000001); + assert!((v3.z() - v2.z()).abs() < 0.000001); + } -// #[test] -// fn cross_test() { -// let v1 = Vector::new(1.0, 0.0, 0.0); -// let v2 = Vector::new(0.0, 1.0, 0.0); -// let v3 = Vector::new(0.0, 0.0, 1.0); + #[test] + fn dot() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = Vector::new(1.5, 4.5, 2.5); -// assert_eq!(v3, v1.cross(v2)); -// } -// } + assert_eq!(v1.dot(v2), 18.0); + } + + #[test] + fn dot_fast() { + let v1 = Vector::new(1.0, 2.0, 3.0); + let v2 = Vector::new(1.5, 4.5, 2.5); + + assert_eq!(v1.dot_fast(v2), 18.0); + } + + #[test] + fn cross() { + let v1 = Vector::new(1.0, 0.0, 0.0); + let v2 = Vector::new(0.0, 1.0, 0.0); + + assert_eq!(v1.cross(v2), Vector::new(0.0, 0.0, 1.0)); + } + + #[test] + fn cross_fast() { + let v1 = Vector::new(1.0, 0.0, 0.0); + let v2 = Vector::new(0.0, 1.0, 0.0); + + assert_eq!(v1.cross_fast(v2), Vector::new(0.0, 0.0, 1.0)); + } +} diff --git a/sub_crates/rmath/src/wide4.rs b/sub_crates/rmath/src/wide4.rs index 5962109..24e29ce 100644 --- a/sub_crates/rmath/src/wide4.rs +++ b/sub_crates/rmath/src/wide4.rs @@ -1,6 +1,6 @@ use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign}; -use approx::relative_eq; +use approx::ulps_eq; use crate::{difference_of_products, two_prod, two_sum}; @@ -335,6 +335,20 @@ impl Float4 { c.a() + c.b() + c.c() } + /// 3D cross product (only uses the first 3 components). + #[inline(always)] + pub fn cross_3(a: Self, b: Self) -> Self { + difference_of_products(a.bcad(), b.cabd(), a.cabd(), b.bcad()) + } + + /// 3D cross product (only uses the first 3 components). + /// + /// Faster but less precise version. + #[inline(always)] + pub fn cross_3_fast(a: Self, b: Self) -> Self { + (a.bcad() * b.cabd()) - (a.cabd() * b.bcad()) + } + #[inline(always)] pub fn transpose_3x3(m: [Self; 3]) -> [Self; 3] { [ @@ -504,10 +518,7 @@ impl Float4 { let (s2, s2_err) = two_sum(c, s1); let err2 = c_err + (err1 + s2_err); - let (s3, s3_err) = two_sum(t, s2); - let err3 = err2 + s3_err; - - s3 + err3 + s2 + err2 } /// Transforms a 3d point by an affine transform, except it applies @@ -530,12 +541,12 @@ impl Float4 { /// /// Each corresponding element cannot have a relative error exceeding /// `epsilon`. - pub(crate) fn aprx_eq(a: Self, b: Self, epsilon: f32) -> bool { + pub(crate) fn aprx_eq(a: Self, b: Self, max_ulps: u32) -> bool { let mut eq = true; - eq &= relative_eq!(a.a(), b.a(), epsilon = epsilon); - eq &= relative_eq!(a.b(), b.b(), epsilon = epsilon); - eq &= relative_eq!(a.c(), b.c(), epsilon = epsilon); - eq &= relative_eq!(a.d(), b.d(), epsilon = epsilon); + eq &= ulps_eq!(a.a(), b.a(), max_ulps = max_ulps); + eq &= ulps_eq!(a.b(), b.b(), max_ulps = max_ulps); + eq &= ulps_eq!(a.c(), b.c(), max_ulps = max_ulps); + eq &= ulps_eq!(a.d(), b.d(), max_ulps = max_ulps); eq } } diff --git a/sub_crates/rmath/src/xform.rs b/sub_crates/rmath/src/xform.rs index bd071d4..bece65e 100644 --- a/sub_crates/rmath/src/xform.rs +++ b/sub_crates/rmath/src/xform.rs @@ -14,9 +14,9 @@ pub struct Xform { } impl Xform { - /// Creates a new affine transform the specified values: + /// Creates a new affine transform with the specified values: /// - /// ``` + /// ```text /// a d g j /// b e h k /// c f i l @@ -78,19 +78,19 @@ impl Xform { /// Returns whether the matrices are approximately equal to each other. /// Each corresponding element in the matrices cannot have a relative /// error exceeding epsilon. - pub(crate) fn aprx_eq(&self, other: Xform, epsilon: f32) -> bool { + pub(crate) fn aprx_eq(&self, other: Xform, max_ulps: u32) -> bool { let mut eq = true; - eq &= Float4::aprx_eq(self.m[0], other.m[0], epsilon); - eq &= Float4::aprx_eq(self.m[1], other.m[1], epsilon); - eq &= Float4::aprx_eq(self.m[2], other.m[2], epsilon); - eq &= Float4::aprx_eq(self.t, other.t, epsilon); + eq &= Float4::aprx_eq(self.m[0], other.m[0], max_ulps); + eq &= Float4::aprx_eq(self.m[1], other.m[1], max_ulps); + eq &= Float4::aprx_eq(self.m[2], other.m[2], max_ulps); + eq &= Float4::aprx_eq(self.t, other.t, max_ulps); eq } /// Computes a "full" version of the transform, which can do both /// forward and inverse transforms. #[inline] - pub fn compute_full(self) -> XformFull { + pub fn into_full(self) -> XformFull { XformFull { m: self.m, m_inv: Float4::invert_3x3(self.m).unwrap_or([ @@ -104,7 +104,7 @@ impl Xform { /// Faster but less precise version of `compute_full()`. #[inline] - pub fn compute_full_fast(self) -> XformFull { + pub fn into_full_fast(self) -> XformFull { XformFull { m: self.m, m_inv: Float4::invert_3x3_fast(self.m).unwrap_or([