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) + } +}