From 245b02b443a88f4d55d5ed93cc12d70063f9316f Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Mon, 5 Jul 2021 18:36:43 -0700 Subject: [PATCH] matrices: implement Matrix4x4:rotation_[xyz] --- rtchallenge/src/matrices.rs | 111 ++++++++++++++++++++++++++++++++++++ rtchallenge/src/tuples.rs | 36 ++++++++++++ 2 files changed, 147 insertions(+) diff --git a/rtchallenge/src/matrices.rs b/rtchallenge/src/matrices.rs index 294667a..834f261 100644 --- a/rtchallenge/src/matrices.rs +++ b/rtchallenge/src/matrices.rs @@ -283,6 +283,117 @@ impl Matrix4x4 { ) } + /// Creates a 4x4 matrix representing a rotation around the x-axis. + /// + /// # Examples + /// + /// ``` + /// use std::f32::consts::PI; + /// + /// use float_cmp::approx_eq; + /// + /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; + /// + /// // A scaling matrix applied to a point. + /// let p = Tuple::point(0., 1., 0.); + /// let half_quarter = Matrix4x4::rotation_x(PI / 4.); + /// let full_quarter = Matrix4x4::rotation_x(PI / 2.); + /// + /// assert_eq!( + /// 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 + /// )); + /// ``` + pub fn rotation_x(radians: f32) -> Matrix4x4 { + let r = radians; + Matrix4x4::new( + [1., 0., 0., 0.], + [0., r.cos(), -r.sin(), 0.], + [0., r.sin(), r.cos(), 0.], + [0., 0., 0., 1.], + ) + } + + /// Creates a 4x4 matrix representing a rotation around the y-axis. + /// + /// # Examples + /// + /// ``` + /// use std::f32::consts::PI; + /// + /// use float_cmp::approx_eq; + /// + /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; + /// + /// // A scaling matrix applied to a point. + /// let p = Tuple::point(0., 0., 1.); + /// let half_quarter = Matrix4x4::rotation_y(PI / 4.); + /// let full_quarter = Matrix4x4::rotation_y(PI / 2.); + /// + /// assert_eq!( + /// 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 + /// )); + /// ``` + pub fn rotation_y(radians: f32) -> Matrix4x4 { + let r = radians; + Matrix4x4::new( + [r.cos(), 0., r.sin(), 0.], + [0., 1., 0., 0.], + [-r.sin(), 0., r.cos(), 0.], + [0., 0., 0., 1.], + ) + } + + /// Creates a 4x4 matrix representing a rotation around the z-axis. + /// + /// # Examples + /// + /// ``` + /// use std::f32::consts::PI; + /// + /// use float_cmp::approx_eq; + /// + /// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple}; + /// + /// // A scaling matrix applied to a point. + /// let p = Tuple::point(0., 1., 0.); + /// let half_quarter = Matrix4x4::rotation_z(PI / 4.); + /// let full_quarter = Matrix4x4::rotation_z(PI / 2.); + /// + /// assert_eq!( + /// 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 + /// )); + /// ``` + pub fn rotation_z(radians: f32) -> Matrix4x4 { + let r = radians; + Matrix4x4::new( + [r.cos(), -r.sin(), 0., 0.], + [r.sin(), r.cos(), 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.], + ) + } + /// Transpose self, returning a new matrix that has been reflected across the diagonal. /// # Examples /// diff --git a/rtchallenge/src/tuples.rs b/rtchallenge/src/tuples.rs index 6308789..efe9c7b 100644 --- a/rtchallenge/src/tuples.rs +++ b/rtchallenge/src/tuples.rs @@ -1,5 +1,7 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; +use float_cmp::{ApproxEq, F32Margin}; + #[derive(Debug, PartialEq, Copy, Clone)] pub struct Tuple { pub x: f32, @@ -43,6 +45,40 @@ 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 {