use crate::{ intersections::Intersection, matrices::Matrix4x4, rays::Ray, tuples::{dot, Tuple}, }; #[derive(Debug, PartialEq)] /// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0. pub struct Sphere { pub transform: Matrix4x4, } impl Default for Sphere { /// # Examples /// ``` /// use rtchallenge::{matrices::Matrix4x4, spheres::Sphere}; /// /// // A sphere's default transform is the identity matrix. /// let s = Sphere::default(); /// assert_eq!(s.transform, Matrix4x4::identity()); /// /// // It can be changed by directly setting the transform member. /// let mut s = Sphere::default(); /// let t = Matrix4x4::translation(2., 3., 4.); /// s.transform = t.clone(); /// assert_eq!(s.transform, t); /// ``` fn default() -> Sphere { Sphere { transform: Matrix4x4::identity(), } } } /// Intersect a ray with a sphere. /// /// # Examples /// ``` /// use rtchallenge::{ /// intersections::Intersection, /// rays::Ray, /// spheres::{intersect, Sphere}, /// tuples::Tuple, /// }; /// /// // A ray intersects a sphere in two points. /// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.)); /// let s = Sphere::default(); /// let xs = intersect(&s, &r); /// assert_eq!( /// xs, /// vec![Intersection::new(4., &s), Intersection::new(6., &s)] /// ); /// /// // A ray intersects a sphere at a tangent. /// let r = Ray::new(Tuple::point(0., 2., -5.), Tuple::vector(0., 0., 1.)); /// let s = Sphere::default(); /// let xs = intersect(&s, &r); /// assert_eq!(xs, vec![]); /// /// // A ray originates inside a sphere. /// let r = Ray::new(Tuple::point(0., 0., 0.), Tuple::vector(0., 0., 1.)); /// let s = Sphere::default(); /// let xs = intersect(&s, &r); /// assert_eq!( /// xs, /// vec![Intersection::new(-1., &s), Intersection::new(1., &s)] /// ); /// /// // A sphere is behind a ray. /// let r = Ray::new(Tuple::point(0., 0., 5.), Tuple::vector(0., 0., 1.)); /// let s = Sphere::default(); /// let xs = intersect(&s, &r); /// assert_eq!( /// xs, /// vec![Intersection::new(-6., &s), Intersection::new(-4., &s)] /// ); /// ``` pub fn intersect<'s>(sphere: &'s Sphere, ray: &Ray) -> Vec> { let sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.); let a = dot(ray.direction, ray.direction); let b = 2. * dot(ray.direction, sphere_to_ray); let c = dot(sphere_to_ray, sphere_to_ray) - 1.; let discriminant = b * b - 4. * a * c; if discriminant < 0. { vec![] } else { vec![ Intersection::new((-b - discriminant.sqrt()) / (2. * a), &sphere), Intersection::new((-b + discriminant.sqrt()) / (2. * a), &sphere), ] } }