Calculate the normal of the transformed sphere

This commit is contained in:
Savanni D'Gerinel 2024-06-23 17:09:06 -04:00
parent 4f47d65ba5
commit fa4ec059f7
6 changed files with 90 additions and 9 deletions

View File

@ -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 mut file = File::create("projectile.ppm").unwrap();
let _ = file.write(ppm.as_bytes()); let _ = file.write(ppm.as_bytes());

View File

@ -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 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"; 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); 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\ 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"; 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); assert_eq!(*ppm, expected);
} }
} }

View File

@ -235,10 +235,25 @@ impl std::ops::Mul<Tuple> 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<Point> for &Matrix { impl std::ops::Mul<Point> for &Matrix {
type Output = Point; type Output = Point;
fn mul(self, rside: Point) -> 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
} }
} }

View File

@ -51,10 +51,17 @@ impl std::ops::Add<Vector> 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<Point> for Point {
type Output = Vector; type Output = Vector;
fn sub(self, r: Point) -> Self::Output { fn sub(self, r: Point) -> Self::Output {
Vector::from(self.0 - r.0) &self - &r
} }
} }

View File

@ -1,4 +1,4 @@
use crate::types::{Matrix, Point}; use crate::types::{Matrix, Point, Vector};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Sphere { pub struct Sphere {
@ -14,6 +14,15 @@ impl Sphere {
pub fn set_transformation(&mut self, m: Matrix) { pub fn set_transformation(&mut self, m: Matrix) {
self.transformation = m 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 { impl Default for Sphere {
@ -28,7 +37,7 @@ impl Default for Sphere {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::transforms::translation; use crate::transforms::{rotation_z, scaling, translation};
#[test] #[test]
fn sphere_has_default_transformation() { fn sphere_has_default_transformation() {
@ -43,4 +52,55 @@ mod test {
s.set_transformation(t.clone()); s.set_transformation(t.clone());
assert_eq!(*s.transformation(), t); 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));
}
} }

View File

@ -108,4 +108,3 @@ impl std::ops::Div<f64> for Tuple {
&self / scalar &self / scalar
} }
} }