diff --git a/rtchallenge/src/spheres.rs b/rtchallenge/src/spheres.rs index 940cbe0..ca3e0db 100644 --- a/rtchallenge/src/spheres.rs +++ b/rtchallenge/src/spheres.rs @@ -76,21 +76,27 @@ impl Sphere { /// 3_f32.sqrt() / 3., /// )); /// assert_eq!(n, n.normalize()); - /// ``` - /// ```should_panic - /// use rtchallenge::{spheres::Sphere, tuples::Tuple}; /// - /// // In debug builds points not on the sphere should fail. - /// let s = Sphere::default(); - /// let n = s.normal_at(Tuple::point(0., 0., 0.5)); + /// // Compute the normal on a translated sphere. + /// let mut s = Sphere::default(); + /// s.transform = Matrix4x4::translation(0., 1., 0.); + /// let n = s.normal_at(Tuple::point(0., 1.70711, -0.70711)); + /// assert_eq!(n, Tuple::vector(0., 0.70711, -0.70711)); + + /// // Compute the normal on a transformed sphere. + /// use std::f32::consts::PI; + /// + /// let mut s = Sphere::default(); + /// s.transform = Matrix4x4::scaling(1.,0.5,1.) * Matrix4x4::rotation_z(PI/5.); + /// let n = s.normal_at(Tuple::point(0., 2_f32.sqrt()/2., -2_f32.sqrt()/2.)); + /// assert_eq!(n, Tuple::vector(0., 0.97014, -0.24254)); /// ``` - pub fn normal_at(&self, point: Tuple) -> Tuple { - debug_assert!( - ((point - Tuple::point(0., 0., 0.)).magnitude() - 1.).abs() < EPSILON, - "{} != 1.", - (point - Tuple::point(0., 0., 0.)).magnitude() - ); - (point - Tuple::point(0., 0., 0.)).normalize() + pub fn normal_at(&self, world_point: Tuple) -> Tuple { + let object_point = self.transform.inverse() * world_point; + let object_normal = object_point - Tuple::point(0., 0., 0.); + let mut world_normal = self.transform.inverse().transpose() * object_normal; + world_normal.w = 0.; + world_normal.normalize() } }