diff --git a/rtchallenge/Cargo.lock b/rtchallenge/Cargo.lock index 5b8910d..f005268 100644 --- a/rtchallenge/Cargo.lock +++ b/rtchallenge/Cargo.lock @@ -212,15 +212,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "float-cmp" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" -dependencies = [ - "num-traits", -] - [[package]] name = "half" version = "1.7.1" @@ -449,7 +440,6 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion", - "float-cmp", "png", "thiserror", ] diff --git a/rtchallenge/Cargo.toml b/rtchallenge/Cargo.toml index a1dbdfa..4bc9440 100644 --- a/rtchallenge/Cargo.toml +++ b/rtchallenge/Cargo.toml @@ -9,11 +9,10 @@ edition = "2018" [dependencies] anyhow = "1.0.41" criterion = "0.3.4" -float-cmp = "0.8.0" png = "0.16.8" thiserror = "1.0.25" [[bench]] name = "matrices" -harness = false \ No newline at end of file +harness = false diff --git a/rtchallenge/src/matrices.rs b/rtchallenge/src/matrices.rs index 834f261..5843ca9 100644 --- a/rtchallenge/src/matrices.rs +++ b/rtchallenge/src/matrices.rs @@ -1,18 +1,15 @@ use std::fmt; use std::ops::{Index, IndexMut, Mul}; -use float_cmp::{ApproxEq, F32Margin}; +// Implement a PartialEq that does approx_eq internally. +//use float_cmp::{ApproxEq, F32Margin}; use crate::tuples::Tuple; -#[derive(Default, Clone, Copy)] -/// Matrix4x4 represents a 4x4 matrix in row-major form. So, element `m[i][j]` corresponds to mi,j -/// where `i` is the row number and `j` is the column number. -pub struct Matrix4x4 { - m: [[f32; 4]; 4], -} +/// Value considered close enough for PartialEq implementations. +const EPSILON: f32 = 0.00001; -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct Matrix2x2 { m: [[f32; 2]; 2], } @@ -44,8 +41,23 @@ impl Index<(usize, usize)> for Matrix2x2 { &self.m[row][col] } } +impl PartialEq for Matrix2x2 { + fn eq(&self, rhs: &Matrix2x2) -> bool { + let l = self.m; + let r = rhs.m; + for i in 0..2 { + for j in 0..2 { + let d = (l[i][j] - r[i][j]).abs(); + if d > EPSILON { + return false; + } + } + } + true + } +} -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct Matrix3x3 { m: [[f32; 3]; 3], } @@ -139,6 +151,29 @@ impl Index<(usize, usize)> for Matrix3x3 { } } +impl PartialEq for Matrix3x3 { + fn eq(&self, rhs: &Matrix3x3) -> bool { + let l = self.m; + let r = rhs.m; + for i in 0..3 { + for j in 0..3 { + let d = (l[i][j] - r[i][j]).abs(); + if d > EPSILON { + return false; + } + } + } + true + } +} + +#[derive(Copy, Clone, Default)] +/// Matrix4x4 represents a 4x4 matrix in row-major form. So, element `m[i][j]` corresponds to mi,j +/// where `i` is the row number and `j` is the column number. +pub struct Matrix4x4 { + m: [[f32; 4]; 4], +} + impl From<[f32; 16]> for Matrix4x4 { fn from(t: [f32; 16]) -> Self { Matrix4x4 { @@ -152,44 +187,6 @@ impl From<[f32; 16]> for Matrix4x4 { } } -impl<'a> ApproxEq for &'a Matrix4x4 { - type Margin = F32Margin; - - /// Implement float_cmp::ApproxEq for Matrix4x4 - /// - /// # Examples - /// ``` - /// use float_cmp::approx_eq; - /// use rtchallenge::matrices::Matrix4x4; - /// - /// assert!(!approx_eq!( - /// &Matrix4x4, - /// &Matrix4x4::default(), - /// &Matrix4x4::identity(), - /// ulps = 1 - /// )); - /// - /// assert!(approx_eq!( - /// &Matrix4x4, - /// &Matrix4x4::identity(), - /// &Matrix4x4::identity(), - /// ulps = 1 - /// )); - /// ``` - fn approx_eq>(self, m2: Self, margin: T) -> bool { - let m = self; - let margin = margin.into(); - for row in 0..4 { - for col in 0..4 { - if !m[(row, col)].approx_eq(m2[(row, col)], margin) { - return false; - } - } - } - true - } -} - impl Matrix4x4 { /// Create a `Matrix4x4` containing the identity, all zeros with ones along the diagonal. /// # Examples @@ -290,8 +287,6 @@ impl Matrix4x4 { /// ``` /// use std::f32::consts::PI; /// - /// use float_cmp::approx_eq; - /// /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; /// /// // A scaling matrix applied to a point. @@ -303,12 +298,7 @@ impl Matrix4x4 { /// half_quarter * p, /// Tuple::point(0., 2_f32.sqrt() / 2., 2_f32.sqrt() / 2.) /// ); - /// assert!(approx_eq!( - /// Tuple, - /// full_quarter * p, - /// Tuple::point(0., 0., 1.), - /// epsilon = 0.0001 - /// )); + /// assert_eq!(full_quarter * p, Tuple::point(0., 0., 1.),); /// ``` pub fn rotation_x(radians: f32) -> Matrix4x4 { let r = radians; @@ -327,8 +317,6 @@ impl Matrix4x4 { /// ``` /// use std::f32::consts::PI; /// - /// use float_cmp::approx_eq; - /// /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; /// /// // A scaling matrix applied to a point. @@ -340,12 +328,7 @@ impl Matrix4x4 { /// half_quarter * p, /// Tuple::point(2_f32.sqrt() / 2., 0., 2_f32.sqrt() / 2.) /// ); - /// assert!(approx_eq!( - /// Tuple, - /// full_quarter * p, - /// Tuple::point(1., 0., 0.,), - /// epsilon = 0.0001 - /// )); + /// assert_eq!(full_quarter * p, Tuple::point(1., 0., 0.,),); /// ``` pub fn rotation_y(radians: f32) -> Matrix4x4 { let r = radians; @@ -364,8 +347,6 @@ impl Matrix4x4 { /// ``` /// use std::f32::consts::PI; /// - /// use float_cmp::approx_eq; - /// /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; /// /// // A scaling matrix applied to a point. @@ -377,12 +358,7 @@ impl Matrix4x4 { /// half_quarter * p, /// Tuple::point(-2_f32.sqrt() / 2., 2_f32.sqrt() / 2., 0.) /// ); - /// assert!(approx_eq!( - /// Tuple, - /// full_quarter * p, - /// Tuple::point(-1., 0., 0.,), - /// epsilon = 0.0001 - /// )); + /// assert_eq!(full_quarter * p, Tuple::point(-1., 0., 0.,),); /// ``` pub fn rotation_z(radians: f32) -> Matrix4x4 { let r = radians; @@ -624,7 +600,6 @@ impl Matrix4x4 { /// /// # Examples /// ``` - /// use float_cmp::approx_eq; /// use rtchallenge::matrices::Matrix4x4; /// /// let a = Matrix4x4::new( @@ -640,55 +615,49 @@ impl Matrix4x4 { /// assert_eq!(b[(3, 2)], -160. / 532.); /// assert_eq!(a.cofactor(3, 2), 105.); /// assert_eq!(b[(2, 3)], 105. / 532.); - /// assert!(approx_eq!( - /// &Matrix4x4, - /// &b, - /// &Matrix4x4::new( - /// [0.21805, 0.45113, 0.24060, -0.04511], - /// [-0.80827, -1.45677, -0.44361, 0.52068], - /// [-0.07895, -0.22368, -0.05263, 0.19737], - /// [-0.52256, -0.81391, -0.30075, 0.30639], - /// ), - /// epsilon = 0.0001 - /// )); + /// assert_eq!( + /// b, + /// Matrix4x4::new( + /// [0.21804512, 0.45112783, 0.24060151, -0.04511278], + /// [-0.8082707, -1.456767, -0.44360903, 0.5206767], + /// [-0.078947365, -0.2236842, -0.05263158, 0.19736843], + /// [-0.52255636, -0.81390977, -0.30075186, 0.30639097] + /// ) + /// ); /// /// // Second test case - /// assert!(approx_eq!( - /// &Matrix4x4, - /// &Matrix4x4::new( + /// assert_eq!( + /// Matrix4x4::new( /// [8., -5., 9., 2.], /// [7., 5., 6., 1.], /// [-6., 0., 9., 6.], /// [-3., 0., -9., -4.], /// ) /// .inverse(), - /// &Matrix4x4::new( - /// [-0.15385, -0.15385, -0.28205, -0.53846], - /// [-0.07692, 0.12308, 0.02564, 0.03077], - /// [0.35897, 0.35897, 0.43590, 0.92308], - /// [-0.69231, -0.69241, -0.76923, -1.92308], + /// Matrix4x4::new( + /// [-0.15384616, -0.15384616, -0.2820513, -0.53846157], + /// [-0.07692308, 0.12307692, 0.025641026, 0.03076923], + /// [0.35897437, 0.35897437, 0.43589744, 0.9230769], + /// [-0.6923077, -0.6923077, -0.7692308, -1.9230769] /// ), - /// epsilon = 0.0005 - /// )); + /// ); /// /// // Third test case - /// assert!(approx_eq!( - /// &Matrix4x4, - /// &Matrix4x4::new( + /// assert_eq!( + /// Matrix4x4::new( /// [9., 3., 0., 9.], /// [-5., -2., -6., -3.], /// [-4., 9., 6., 4.], /// [-7., 6., 6., 2.], /// ) /// .inverse(), - /// &Matrix4x4::new( - /// [-0.04074, -0.07778, 0.14444, -0.22222], - /// [-0.07778, 0.03333, 0.36667, -0.33333], - /// [-0.02901, -0.14630, -0.10926, 0.12963], - /// [0.17778, 0.06667, -0.26667, 0.33333], + /// Matrix4x4::new( + /// [-0.04074074, -0.07777778, 0.14444445, -0.22222222], + /// [-0.07777778, 0.033333335, 0.36666667, -0.33333334], + /// [-0.029012345, -0.14629629, -0.10925926, 0.12962963], + /// [0.17777778, 0.06666667, -0.26666668, 0.33333334] /// ), - /// epsilon = 0.0001 - /// )); + /// ); /// /// let a = Matrix4x4::new( /// [3., -9., 7., 3.], @@ -703,12 +672,7 @@ impl Matrix4x4 { /// [6., -2., 0., 5.], /// ); /// let c = a * b; - /// assert!(approx_eq!( - /// &Matrix4x4, - /// &(c * b.inverse()), - /// &a, - /// epsilon = 0.0001 - /// )); + /// assert_eq!(c * b.inverse(), a); /// ``` pub fn inverse(&self) -> Matrix4x4 { let m = self; @@ -813,7 +777,7 @@ impl PartialEq for Matrix4x4 { for i in 0..4 { for j in 0..4 { let d = (l[i][j] - r[i][j]).abs(); - if d > f32::EPSILON { + if d > EPSILON { return false; } } diff --git a/rtchallenge/src/tuples.rs b/rtchallenge/src/tuples.rs index efe9c7b..46379de 100644 --- a/rtchallenge/src/tuples.rs +++ b/rtchallenge/src/tuples.rs @@ -1,8 +1,6 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; -use float_cmp::{ApproxEq, F32Margin}; - -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct Tuple { pub x: f32, pub y: f32, @@ -45,40 +43,6 @@ impl Tuple { } } -impl ApproxEq for Tuple { - type Margin = F32Margin; - - /// Implement float_cmp::ApproxEq for Tuple - /// - /// # Examples - /// ``` - /// use float_cmp::approx_eq; - /// use rtchallenge::tuples::Tuple; - /// - /// assert!(approx_eq!( - /// Tuple, - /// Tuple::point(1., 1., 0.), - /// Tuple::point(1., 1., 0.), - /// ulps = 1 - /// )); - /// - /// assert!(approx_eq!( - /// Tuple, - /// Tuple::vector(1., 1., 0.), - /// Tuple::vector(1., 1., 0.), - /// ulps = 1 - /// )); - /// ``` - fn approx_eq>(self, t2: Self, margin: T) -> bool { - let t = self; - let margin = margin.into(); - t.x.approx_eq(t2.x, margin) - && t.y.approx_eq(t2.y, margin) - && t.z.approx_eq(t2.z, margin) - && t.w.approx_eq(t2.w, margin) - } -} - impl Add for Tuple { type Output = Self; fn add(self, other: Self) -> Self { @@ -150,6 +114,14 @@ impl Sub for Tuple { } } +impl PartialEq for Tuple { + fn eq(&self, rhs: &Tuple) -> bool { + ((self.x - rhs.x).abs() < f32::EPSILON) + && ((self.y - rhs.y).abs() < f32::EPSILON) + && ((self.z - rhs.z).abs() < f32::EPSILON) + && ((self.w - rhs.w).abs() < f32::EPSILON) + } +} pub fn dot(a: Tuple, b: Tuple) -> f32 { a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w } @@ -249,8 +221,6 @@ impl Sub for Color { #[cfg(test)] mod tests { - use float_cmp::approx_eq; - use super::{cross, dot, Color, Tuple}; #[test] fn is_point() { @@ -355,12 +325,8 @@ mod tests { } #[test] fn vector_normalize_magnitude() { - assert!(approx_eq!( - f32, - 1., - Tuple::vector(1., 2., 3.).normalize().magnitude(), - ulps = 1 - )); + let len = Tuple::vector(1., 2., 3.).normalize().magnitude(); + assert!((1. - len).abs() < f32::EPSILON); } #[test] fn dot_two_tuples() {