diff --git a/rtchallenge/src/spheres.rs b/rtchallenge/src/spheres.rs index d479196..940cbe0 100644 --- a/rtchallenge/src/spheres.rs +++ b/rtchallenge/src/spheres.rs @@ -3,6 +3,7 @@ use crate::{ matrices::Matrix4x4, rays::Ray, tuples::{dot, Tuple}, + EPSILON, }; #[derive(Debug, PartialEq)] @@ -34,6 +35,65 @@ impl Default for Sphere { } } +impl Sphere { + /// Find the normal at the point on the sphere. + /// + /// # Examples + /// ``` + /// use rtchallenge::{matrices::Matrix4x4, spheres::Sphere, tuples::Tuple}; + /// + /// // Normal on X-axis + /// let s = Sphere::default(); + /// let n = s.normal_at(Tuple::point(1., 0., 0.)); + /// assert_eq!(n, Tuple::vector(1., 0., 0.)); + /// + /// // Normal on Y-axis + /// let s = Sphere::default(); + /// let n = s.normal_at(Tuple::point(0., 1., 0.)); + /// assert_eq!(n, Tuple::vector(0., 1., 0.)); + /// + /// // Normal on Z-axis + /// let s = Sphere::default(); + /// let n = s.normal_at(Tuple::point(0., 0., 1.)); + /// assert_eq!(n, Tuple::vector(0., 0., 1.)); + /// + /// // Normal on a sphere at a nonaxial point. + /// let s = Sphere::default(); + /// let n = s.normal_at(Tuple::point( + /// 3_f32.sqrt() / 3., + /// 3_f32.sqrt() / 3., + /// 3_f32.sqrt() / 3., + /// )); + /// assert_eq!( + /// n, + /// Tuple::vector(3_f32.sqrt() / 3., 3_f32.sqrt() / 3., 3_f32.sqrt() / 3.,) + /// ); + /// // Normals returned are normalized. + /// let s = Sphere::default(); + /// let n = s.normal_at(Tuple::point( + /// 3_f32.sqrt() / 3., + /// 3_f32.sqrt() / 3., + /// 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)); + /// ``` + 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() + } +} + /// Intersect a ray with a sphere. /// /// # Examples