From 2a38ca38e16476564a558efbf49dce8765ab9529 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 9 Jun 2024 14:18:46 -0400 Subject: [PATCH] Extract the types into separate files Not for any reason other than clarity. The number of operations that are declared for each type is making it difficult to find operations and difficult to keep the order consistent. --- ray-tracer/src/bin/projectile.rs | 8 +- ray-tracer/src/lib.rs | 1 - ray-tracer/src/types.rs | 407 ------------------------------- ray-tracer/src/types/mod.rs | 194 +++++++++++++++ ray-tracer/src/types/point.rs | 60 +++++ ray-tracer/src/types/tuple.rs | 83 +++++++ ray-tracer/src/types/vector.rs | 80 ++++++ 7 files changed, 424 insertions(+), 409 deletions(-) delete mode 100644 ray-tracer/src/types.rs create mode 100644 ray-tracer/src/types/mod.rs create mode 100644 ray-tracer/src/types/point.rs create mode 100644 ray-tracer/src/types/tuple.rs create mode 100644 ray-tracer/src/types/vector.rs diff --git a/ray-tracer/src/bin/projectile.rs b/ray-tracer/src/bin/projectile.rs index 3cb5c6f..895be40 100644 --- a/ray-tracer/src/bin/projectile.rs +++ b/ray-tracer/src/bin/projectile.rs @@ -33,5 +33,11 @@ fn main() { p = tick(&e, &p); } - println!("distance travelled: [{}] {} {} {}", (p.position - start.position).magnitude(), p.position.x, p.position.y, p.position.z); + println!( + "distance travelled: [{}] {} {} {}", + (p.position - start.position).magnitude(), + p.position.x, + p.position.y, + p.position.z + ); } diff --git a/ray-tracer/src/lib.rs b/ray-tracer/src/lib.rs index 97fe92f..cd40856 100644 --- a/ray-tracer/src/lib.rs +++ b/ray-tracer/src/lib.rs @@ -1,2 +1 @@ pub mod types; - diff --git a/ray-tracer/src/types.rs b/ray-tracer/src/types.rs deleted file mode 100644 index 04e260e..0000000 --- a/ray-tracer/src/types.rs +++ /dev/null @@ -1,407 +0,0 @@ -const EPSILON: f64 = 0.00001; - -fn eq_f64(l: f64, r: f64) -> bool { - (l - r).abs() < EPSILON -} - -#[derive(Debug, Clone, Copy)] -pub struct Tuple { - pub x: f64, - pub y: f64, - pub z: f64, - pub w: f64, // Used for very low-level math. w = 1.0 indicates a point, w = 0.0 indicates a vector. - // Theoretically the type system should make this redundant, so operations on points - // and vectors can always assert the correct value. -} - -impl Tuple { - fn dot(&self, r: &Tuple) -> f64 { - self.x * r.x + self.y * r.y + self.z * r.z + self.w * r.w - } -} - -impl PartialEq for Tuple { - fn eq(&self, r: &Tuple) -> bool { - eq_f64(self.x, r.x) && eq_f64(self.y, r.y) && eq_f64(self.z, r.z) && eq_f64(self.w, r.w) - } -} - -impl std::ops::Add for Tuple { - type Output = Tuple; - fn add(self, r: Tuple) -> Self::Output { - return Self::Output { - x: self.x + r.x, - y: self.y + r.y, - z: self.z + r.z, - w: self.w + r.w, - }; - } -} - -impl std::ops::Sub for Tuple { - type Output = Tuple; - fn sub(self, r: Tuple) -> Self::Output { - return Self::Output { - x: self.x - r.x, - y: self.y - r.y, - z: self.z - r.z, - w: self.w - r.w, - }; - } -} - -impl std::ops::Neg for Tuple { - type Output = Tuple; - fn neg(self) -> Self::Output { - return Self::Output { - x: -self.x, - y: -self.y, - z: -self.z, - w: -self.w, - }; - } -} - -impl std::ops::Mul for Tuple { - type Output = Tuple; - fn mul(self, scalar: f64) -> Self::Output { - return Self::Output { - x: self.x * scalar, - y: self.y * scalar, - z: self.z * scalar, - w: self.w * scalar, - }; - } -} - -impl std::ops::Div for Tuple { - type Output = Tuple; - fn div(self, scalar: f64) -> Self::Output { - return Self::Output { - x: self.x / scalar, - y: self.y / scalar, - z: self.z / scalar, - w: self.w / scalar, - }; - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Point(Tuple); - -impl Point { - pub fn new(x: f64, y: f64, z: f64) -> Self { - Self(Tuple { x, y, z, w: 1.0 }) - } -} - -impl std::ops::Deref for Point { - type Target = Tuple; - fn deref(&self) -> &Tuple { - &self.0 - } -} - -impl From for Point { - fn from(tuple: Tuple) -> Self { - assert_eq!(tuple.w, 1.0); - Self(tuple) - } -} - -impl Default for Point { - fn default() -> Self { - Self::new(0., 0., 0.) - } -} - -impl std::ops::Add for Point { - type Output = Point; - fn add(self, r: Vector) -> Self::Output { - Point::from(self.0 + r.0) - } -} - -impl std::ops::Sub for Point { - type Output = Vector; - fn sub(self, r: Point) -> Self::Output { - Vector::from(self.0 - r.0) - } -} - -impl std::ops::Sub for Point { - type Output = Point; - fn sub(self, r: Vector) -> Self::Output { - Point::from(self.0 - r.0) - } -} - -impl std::ops::Neg for Point { - type Output = Point; - fn neg(self) -> Self::Output { - let mut t = -self.0; - t.w = 1.; - Point::from(t) - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Vector(Tuple); - -impl Vector { - pub fn new(x: f64, y: f64, z: f64) -> Self { - Self(Tuple { x, y, z, w: 0.0 }) - } - - pub fn magnitude(&self) -> f64 { - (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() - } - - pub fn normalize(&self) -> Self { - let mag = self.magnitude(); - Self::new(self.x / mag, self.y / mag, self.z / mag) - } - - pub fn dot(&self, r: &Vector) -> f64 { - self.0.dot(r) - } - - pub fn cross(&self, r: &Vector) -> Self { - let x = self.y * r.z - self.z * r.y; - let y = self.z * r.x - self.x * r.z; - let z = self.x * r.y - self.y * r.x; - Self::new(x, y, z) - } -} - -impl std::ops::Deref for Vector { - type Target = Tuple; - fn deref(&self) -> &Tuple { - &self.0 - } -} - -impl Default for Vector { - fn default() -> Self { - Self::new(0., 0., 0.) - } -} - -impl From for Vector { - fn from(tuple: Tuple) -> Self { - assert_eq!(tuple.w, 0.0); - Self(tuple) - } -} - -impl std::ops::Add for Vector { - type Output = Vector; - fn add(self, r: Self) -> Self { - Vector::from(self.0 + r.0) - } -} - -impl std::ops::Sub for Vector { - type Output = Vector; - fn sub(self, r: Self) -> Self { - Vector::from(self.0 - r.0) - } -} - -impl std::ops::Mul for Vector { - type Output = Vector; - fn mul(self, r: f64) -> Self { - Vector::from(self.0 * r) - } -} - - -impl std::ops::Neg for Vector { - type Output = Vector; - fn neg(self) -> Self::Output { - let mut t = -self.0; - t.w = 0.; - Vector::from(t) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn eq_f64_compares_values() { - assert!(eq_f64(1.0, 1.0)); - assert!(eq_f64(0.9994, 0.9994)); - assert!(eq_f64(0.9999994, 0.9999995)); - assert!(!eq_f64(0.9995, 0.9994)); - } - - #[test] - fn add_two_tuples() { - let a = Tuple { - x: 3., - y: -2., - z: 5., - w: 1., - }; - let b = Tuple { - x: -2., - y: 3., - z: 1., - w: 0., - }; - assert_eq!( - a + b, - Tuple { - x: 1., - y: 1., - z: 6., - w: 1. - } - ); - } - - #[test] - fn subtracts_two_tuples() { - let a = Tuple { - x: 3., - y: 2., - z: 1., - w: 1., - }; - let b = Tuple { - x: 5., - y: 6., - z: 7., - w: 1., - }; - assert_eq!( - a - b, - Tuple { - x: -2., - y: -4., - z: -6., - w: 0. - } - ); - } - - #[test] - fn adds_point_and_vector() { - let a = Point::new(3., -2., 5.); - let b = Vector::new(-2., 3., 1.); - assert_eq!(a + b, Point::new(1., 1., 6.,)); - } - - #[test] - fn subtracts_two_points() { - let a = Point::new(3., 2., 1.); - let b = Point::new(5., 6., 7.); - assert_eq!(a - b, Vector::new(-2., -4., -6.,)); - } - - #[test] - fn subtracts_vector_from_point() { - let a = Point::new(3., 2., 1.); - let b = Vector::new(5., 6., 7.); - assert_eq!(a - b, Point::new(-2., -4., -6.)); - } - - #[test] - fn subtracts_two_vectors() { - let a = Vector::new(3., 2., 1.); - let b = Vector::new(5., 6., 7.); - assert_eq!(a - b, Vector::new(-2., -4., -6.)); - } - - #[test] - fn it_negates_primitives() { - assert_eq!( - -Tuple { - x: 1., - y: 2., - z: 3., - w: 4. - }, - Tuple { - x: -1., - y: -2., - z: -3., - w: -4. - }, - ); - } - - #[test] - fn multiply_tuple_by_scalar() { - assert_eq!( - Tuple { - x: 1., - y: -2., - z: 3., - w: -4. - } * 3.5, - Tuple { - x: 3.5, - y: -7., - z: 10.5, - w: -14. - } - ); - } - - #[test] - fn divide_tuple_by_scalar() { - assert_eq!( - Tuple { - x: 1., - y: -2., - z: 3., - w: -4. - } / 2., - Tuple { - x: 0.5, - y: -1., - z: 1.5, - w: -2. - } - ); - } - - #[test] - fn magnitude_of_vector() { - assert_eq!(Vector::new(1., 0., 0.).magnitude(), 1.); - assert_eq!(Vector::new(0., 1., 0.).magnitude(), 1.); - assert_eq!(Vector::new(0., 0., 1.).magnitude(), 1.); - assert_eq!(Vector::new(1., 2., 3.).magnitude(), 14_f64.sqrt()); - assert_eq!(Vector::new(-1., -2., -3.).magnitude(), 14_f64.sqrt()); - } - - #[test] - fn normalize_vector() { - assert_eq!(Vector::new(4., 0., 0.).normalize(), Vector::new(1., 0., 0.)); - assert_eq!( - Vector::new(1., 2., 3.).normalize(), - Vector::new(0.26726, 0.53452, 0.80178) - ); - assert_eq!(Vector::new(1., 2., 3.).normalize().magnitude(), 1.); - } - - #[test] - fn dot_product() { - assert_eq!(Vector::new(1., 2., 3.).dot(&Vector::new(2., 3., 4.)), 20.); - } - - #[test] - fn cross_product() { - assert_eq!( - Vector::new(1., 2., 3.).cross(&Vector::new(2., 3., 4.)), - Vector::new(-1., 2., -1.) - ); - assert_eq!( - Vector::new(2., 3., 4.).cross(&Vector::new(1., 2., 3.)), - Vector::new(1., -2., 1.) - ); - } -} diff --git a/ray-tracer/src/types/mod.rs b/ray-tracer/src/types/mod.rs new file mode 100644 index 0000000..a89d667 --- /dev/null +++ b/ray-tracer/src/types/mod.rs @@ -0,0 +1,194 @@ +mod point; +mod tuple; +mod vector; + +pub use point::Point; +pub use tuple::Tuple; +pub use vector::Vector; + +const EPSILON: f64 = 0.00001; + +fn eq_f64(l: f64, r: f64) -> bool { + (l - r).abs() < EPSILON +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn eq_f64_compares_values() { + assert!(eq_f64(1.0, 1.0)); + assert!(eq_f64(0.9994, 0.9994)); + assert!(eq_f64(0.9999994, 0.9999995)); + assert!(!eq_f64(0.9995, 0.9994)); + } + + #[test] + fn add_two_tuples() { + let a = Tuple { + x: 3., + y: -2., + z: 5., + w: 1., + }; + let b = Tuple { + x: -2., + y: 3., + z: 1., + w: 0., + }; + assert_eq!( + a + b, + Tuple { + x: 1., + y: 1., + z: 6., + w: 1. + } + ); + } + + #[test] + fn subtracts_two_tuples() { + let a = Tuple { + x: 3., + y: 2., + z: 1., + w: 1., + }; + let b = Tuple { + x: 5., + y: 6., + z: 7., + w: 1., + }; + assert_eq!( + a - b, + Tuple { + x: -2., + y: -4., + z: -6., + w: 0. + } + ); + } + + #[test] + fn adds_point_and_vector() { + let a = Point::new(3., -2., 5.); + let b = Vector::new(-2., 3., 1.); + assert_eq!(a + b, Point::new(1., 1., 6.,)); + } + + #[test] + fn subtracts_two_points() { + let a = Point::new(3., 2., 1.); + let b = Point::new(5., 6., 7.); + assert_eq!(a - b, Vector::new(-2., -4., -6.,)); + } + + #[test] + fn subtracts_vector_from_point() { + let a = Point::new(3., 2., 1.); + let b = Vector::new(5., 6., 7.); + assert_eq!(a - b, Point::new(-2., -4., -6.)); + } + + #[test] + fn subtracts_two_vectors() { + let a = Vector::new(3., 2., 1.); + let b = Vector::new(5., 6., 7.); + assert_eq!(a - b, Vector::new(-2., -4., -6.)); + } + + #[test] + fn it_negates_primitives() { + assert_eq!( + -Tuple { + x: 1., + y: 2., + z: 3., + w: 4. + }, + Tuple { + x: -1., + y: -2., + z: -3., + w: -4. + }, + ); + } + + #[test] + fn multiply_tuple_by_scalar() { + assert_eq!( + Tuple { + x: 1., + y: -2., + z: 3., + w: -4. + } * 3.5, + Tuple { + x: 3.5, + y: -7., + z: 10.5, + w: -14. + } + ); + } + + #[test] + fn divide_tuple_by_scalar() { + assert_eq!( + Tuple { + x: 1., + y: -2., + z: 3., + w: -4. + } / 2., + Tuple { + x: 0.5, + y: -1., + z: 1.5, + w: -2. + } + ); + } + + #[test] + fn magnitude_of_vector() { + assert_eq!(Vector::new(1., 0., 0.).magnitude(), 1.); + assert_eq!(Vector::new(0., 1., 0.).magnitude(), 1.); + assert_eq!(Vector::new(0., 0., 1.).magnitude(), 1.); + assert_eq!(Vector::new(1., 2., 3.).magnitude(), 14_f64.sqrt()); + assert_eq!(Vector::new(-1., -2., -3.).magnitude(), 14_f64.sqrt()); + } + + #[test] + fn normalize_vector() { + assert_eq!(Vector::new(4., 0., 0.).normalize(), Vector::new(1., 0., 0.)); + assert_eq!( + Vector::new(1., 2., 3.).normalize(), + Vector::new(0.26726, 0.53452, 0.80178) + ); + assert_eq!(Vector::new(1., 2., 3.).normalize().magnitude(), 1.); + } + + #[test] + fn dot_product() { + assert_eq!(Vector::new(1., 2., 3.).dot(&Vector::new(2., 3., 4.)), 20.); + } + + #[test] + fn cross_product() { + assert_eq!( + Vector::new(1., 2., 3.).cross(&Vector::new(2., 3., 4.)), + Vector::new(-1., 2., -1.) + ); + assert_eq!( + Vector::new(2., 3., 4.).cross(&Vector::new(1., 2., 3.)), + Vector::new(1., -2., 1.) + ); + } +} diff --git a/ray-tracer/src/types/point.rs b/ray-tracer/src/types/point.rs new file mode 100644 index 0000000..7c0c141 --- /dev/null +++ b/ray-tracer/src/types/point.rs @@ -0,0 +1,60 @@ +use crate::types::{Tuple, Vector}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Point(Tuple); + +impl Point { + pub fn new(x: f64, y: f64, z: f64) -> Self { + Self(Tuple { x, y, z, w: 1.0 }) + } +} + +impl Default for Point { + fn default() -> Self { + Self::new(0., 0., 0.) + } +} + +impl From for Point { + fn from(tuple: Tuple) -> Self { + assert_eq!(tuple.w, 1.0); + Self(tuple) + } +} + +impl std::ops::Deref for Point { + type Target = Tuple; + fn deref(&self) -> &Tuple { + &self.0 + } +} + +impl std::ops::Add for Point { + type Output = Point; + fn add(self, r: Vector) -> Self::Output { + Point::from(self.0 + *r) + } +} + +impl std::ops::Sub for Point { + type Output = Vector; + fn sub(self, r: Point) -> Self::Output { + Vector::from(self.0 - r.0) + } +} + +impl std::ops::Sub for Point { + type Output = Point; + fn sub(self, r: Vector) -> Self::Output { + Point::from(self.0 - *r) + } +} + +impl std::ops::Neg for Point { + type Output = Point; + fn neg(self) -> Self::Output { + let mut t = -self.0; + t.w = 1.; + Point::from(t) + } +} diff --git a/ray-tracer/src/types/tuple.rs b/ray-tracer/src/types/tuple.rs new file mode 100644 index 0000000..7ea6d6b --- /dev/null +++ b/ray-tracer/src/types/tuple.rs @@ -0,0 +1,83 @@ +use crate::types::eq_f64; + +#[derive(Debug, Clone, Copy)] +pub struct Tuple { + pub x: f64, + pub y: f64, + pub z: f64, + pub w: f64, // Used for very low-level math. w = 1.0 indicates a point, w = 0.0 indicates a vector. + // Theoretically the type system should make this redundant, so operations on points + // and vectors can always assert the correct value. +} + +impl Tuple { + pub fn dot(&self, r: &Tuple) -> f64 { + self.x * r.x + self.y * r.y + self.z * r.z + self.w * r.w + } +} + +impl PartialEq for Tuple { + fn eq(&self, r: &Tuple) -> bool { + eq_f64(self.x, r.x) && eq_f64(self.y, r.y) && eq_f64(self.z, r.z) && eq_f64(self.w, r.w) + } +} + +impl std::ops::Add for Tuple { + type Output = Tuple; + fn add(self, r: Tuple) -> Self::Output { + return Self::Output { + x: self.x + r.x, + y: self.y + r.y, + z: self.z + r.z, + w: self.w + r.w, + }; + } +} + +impl std::ops::Sub for Tuple { + type Output = Tuple; + fn sub(self, r: Tuple) -> Self::Output { + return Self::Output { + x: self.x - r.x, + y: self.y - r.y, + z: self.z - r.z, + w: self.w - r.w, + }; + } +} + +impl std::ops::Neg for Tuple { + type Output = Tuple; + fn neg(self) -> Self::Output { + return Self::Output { + x: -self.x, + y: -self.y, + z: -self.z, + w: -self.w, + }; + } +} + +impl std::ops::Mul for Tuple { + type Output = Tuple; + fn mul(self, scalar: f64) -> Self::Output { + return Self::Output { + x: self.x * scalar, + y: self.y * scalar, + z: self.z * scalar, + w: self.w * scalar, + }; + } +} + +impl std::ops::Div for Tuple { + type Output = Tuple; + fn div(self, scalar: f64) -> Self::Output { + return Self::Output { + x: self.x / scalar, + y: self.y / scalar, + z: self.z / scalar, + w: self.w / scalar, + }; + } +} diff --git a/ray-tracer/src/types/vector.rs b/ray-tracer/src/types/vector.rs new file mode 100644 index 0000000..f2aa4c8 --- /dev/null +++ b/ray-tracer/src/types/vector.rs @@ -0,0 +1,80 @@ +use crate::types::Tuple; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Vector(Tuple); + +impl Vector { + pub fn new(x: f64, y: f64, z: f64) -> Self { + Self(Tuple { x, y, z, w: 0.0 }) + } + + pub fn magnitude(&self) -> f64 { + (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() + } + + pub fn normalize(&self) -> Self { + let mag = self.magnitude(); + Self::new(self.x / mag, self.y / mag, self.z / mag) + } + + pub fn dot(&self, r: &Vector) -> f64 { + self.0.dot(r) + } + + pub fn cross(&self, r: &Vector) -> Self { + let x = self.y * r.z - self.z * r.y; + let y = self.z * r.x - self.x * r.z; + let z = self.x * r.y - self.y * r.x; + Self::new(x, y, z) + } +} + +impl Default for Vector { + fn default() -> Self { + Self::new(0., 0., 0.) + } +} + +impl From for Vector { + fn from(tuple: Tuple) -> Self { + assert_eq!(tuple.w, 0.0); + Self(tuple) + } +} + +impl std::ops::Deref for Vector { + type Target = Tuple; + fn deref(&self) -> &Tuple { + &self.0 + } +} + +impl std::ops::Add for Vector { + type Output = Vector; + fn add(self, r: Self) -> Self { + Vector::from(self.0 + r.0) + } +} + +impl std::ops::Sub for Vector { + type Output = Vector; + fn sub(self, r: Self) -> Self { + Vector::from(self.0 - r.0) + } +} + +impl std::ops::Neg for Vector { + type Output = Vector; + fn neg(self) -> Self::Output { + let mut t = -self.0; + t.w = 0.; + Vector::from(t) + } +} + +impl std::ops::Mul for Vector { + type Output = Vector; + fn mul(self, r: f64) -> Self { + Vector::from(self.0 * r) + } +}