From fa4ec059f77ab30082649f08487fa9820bfdb1e6 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 23 Jun 2024 17:09:06 -0400 Subject: [PATCH] Calculate the normal of the transformed sphere --- ray-tracer/src/bin/projectile.rs | 2 +- ray-tracer/src/ppm.rs | 4 +- ray-tracer/src/types/matrix.rs | 17 ++++++++- ray-tracer/src/types/point.rs | 11 +++++- ray-tracer/src/types/sphere.rs | 64 +++++++++++++++++++++++++++++++- ray-tracer/src/types/tuple.rs | 1 - 6 files changed, 90 insertions(+), 9 deletions(-) diff --git a/ray-tracer/src/bin/projectile.rs b/ray-tracer/src/bin/projectile.rs index 8f71c28..f71553d 100644 --- a/ray-tracer/src/bin/projectile.rs +++ b/ray-tracer/src/bin/projectile.rs @@ -46,7 +46,7 @@ fn main() { } } - let ppm = PPM::from(canvas); + let ppm = PPM::from(&canvas); let mut file = File::create("projectile.ppm").unwrap(); let _ = file.write(ppm.as_bytes()); diff --git a/ray-tracer/src/ppm.rs b/ray-tracer/src/ppm.rs index 575d43a..b1e1b71 100644 --- a/ray-tracer/src/ppm.rs +++ b/ray-tracer/src/ppm.rs @@ -91,7 +91,7 @@ mod tests { 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n\ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n"; - let ppm = PPM::from(c); + let ppm = PPM::from(&c); assert_eq!(*ppm, expected); } @@ -112,7 +112,7 @@ mod tests { 255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n\ 153 255 204 153 255 204 153 255 204 153 255 204 153\n"; - let ppm = PPM::from(c); + let ppm = PPM::from(&c); assert_eq!(*ppm, expected); } } diff --git a/ray-tracer/src/types/matrix.rs b/ray-tracer/src/types/matrix.rs index 226a42c..0e9bc36 100644 --- a/ray-tracer/src/types/matrix.rs +++ b/ray-tracer/src/types/matrix.rs @@ -235,10 +235,25 @@ impl std::ops::Mul for Matrix { } } +impl std::ops::Mul<&Point> for &Matrix { + type Output = Point; + fn mul(self, rside: &Point) -> Point { + let t: Tuple = **rside; + Point::from(self * t) + } +} + impl std::ops::Mul for &Matrix { type Output = Point; fn mul(self, rside: Point) -> Point { - Point::from(self * *rside) + self * &rside + } +} + +impl std::ops::Mul<&Point> for Matrix { + type Output = Point; + fn mul(self, rside: &Point) -> Point { + &self * rside } } diff --git a/ray-tracer/src/types/point.rs b/ray-tracer/src/types/point.rs index c8a0d98..cc8b5d2 100644 --- a/ray-tracer/src/types/point.rs +++ b/ray-tracer/src/types/point.rs @@ -51,10 +51,17 @@ impl std::ops::Add for Point { } } -impl std::ops::Sub for Point { +impl std::ops::Sub<&Point> 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 = Vector; fn sub(self, r: Point) -> Self::Output { - Vector::from(self.0 - r.0) + &self - &r } } diff --git a/ray-tracer/src/types/sphere.rs b/ray-tracer/src/types/sphere.rs index 07b1afe..3e9dc44 100644 --- a/ray-tracer/src/types/sphere.rs +++ b/ray-tracer/src/types/sphere.rs @@ -1,4 +1,4 @@ -use crate::types::{Matrix, Point}; +use crate::types::{Matrix, Point, Vector}; #[derive(Debug, PartialEq)] pub struct Sphere { @@ -14,6 +14,15 @@ impl Sphere { pub fn set_transformation(&mut self, m: Matrix) { self.transformation = m } + + pub fn normal_at(&self, world_point: &Point) -> Vector { + let inverted_transform = self.transformation.inverse(); + let object_point = &inverted_transform * world_point; + let object_normal = object_point - Point::new(0., 0., 0.); + let mut world_normal = inverted_transform.transpose() * *object_normal; + world_normal.3 = 0.; + Vector::from(world_normal).normalize() + } } impl Default for Sphere { @@ -28,7 +37,7 @@ impl Default for Sphere { #[cfg(test)] mod test { use super::*; - use crate::transforms::translation; + use crate::transforms::{rotation_z, scaling, translation}; #[test] fn sphere_has_default_transformation() { @@ -43,4 +52,55 @@ mod test { s.set_transformation(t.clone()); assert_eq!(*s.transformation(), t); } + + #[test] + fn normal_of_sphere_on_x() { + let s = Sphere::default(); + let n = s.normal_at(&Point::new(1., 0., 0.)); + assert_eq!(n, Vector::new(1., 0., 0.)); + } + + #[test] + fn normal_of_sphere_on_y() { + let s = Sphere::default(); + let n = s.normal_at(&Point::new(0., 1., 0.)); + assert_eq!(n, Vector::new(0., 1., 0.)); + } + + #[test] + fn normal_of_sphere_on_z() { + let s = Sphere::default(); + let n = s.normal_at(&Point::new(0., 0., 1.)); + assert_eq!(n, Vector::new(0., 0., 1.)); + } + + #[test] + fn normal_of_sphere_on_nonaxial() { + let s = Sphere::default(); + let n = s.normal_at(&Point::new(3_f64.sqrt() / 3., 3_f64.sqrt() / 3., 3_f64.sqrt() / 3.)); + assert_eq!(n, Vector::new(3_f64.sqrt() / 3., 3_f64.sqrt() / 3., 3_f64.sqrt() / 3.)); + } + + #[test] + fn normal_is_normalized() { + let s = Sphere::default(); + let n = s.normal_at(&Point::new(3_f64.sqrt() / 3., 3_f64.sqrt() / 3., 3_f64.sqrt() / 3.)); + assert_eq!(n.normalize(), n); + } + + #[test] + fn compute_normal_of_translated_sphere() { + let mut s = Sphere::default(); + s.set_transformation(translation(0., 1., 0.)); + let n = s.normal_at(&Point::new(0., 1.70711, -0.70711)); + assert_eq!(n, Vector::new(0., 0.70711, -0.70711)); + } + + #[test] + fn compute_normal_of_transformed_sphere() { + let mut s = Sphere::default(); + s.set_transformation(scaling(1., 0.5, 1.) * rotation_z(std::f64::consts::PI / 5.)); + let n = s.normal_at(&Point::new(0., 2_f64.sqrt() / 2., -2_f64.sqrt() / 2.)); + assert_eq!(n, Vector::new(0., 0.97014, -0.24254)); + } } diff --git a/ray-tracer/src/types/tuple.rs b/ray-tracer/src/types/tuple.rs index 1082d14..af394e7 100644 --- a/ray-tracer/src/types/tuple.rs +++ b/ray-tracer/src/types/tuple.rs @@ -108,4 +108,3 @@ impl std::ops::Div for Tuple { &self / scalar } } -