use std::ops::Index; use crate::{rays::Ray, spheres::Sphere, tuples::Tuple}; #[derive(Debug, Clone, PartialEq)] pub struct Intersection<'i> { pub t: f32, pub object: &'i Sphere, } impl<'i> Intersection<'i> { /// Create new `Intersection` at the given `t` that hits the given `object`. /// /// # Examples /// ``` /// use rtchallenge::{intersections::Intersection, spheres::Sphere}; /// /// // An intersection ecapsulates t and object. /// let s = Sphere::default(); /// let i = Intersection::new(3.5, &s); /// assert_eq!(i.t, 3.5); /// assert_eq!(i.object, &s); /// ``` pub fn new(t: f32, object: &Sphere) -> Intersection { Intersection { t, object } } } /// Aggregates `Intersection`s. /// /// # Examples /// ``` /// use rtchallenge::{ /// intersections::{Intersection, Intersections}, /// rays::Ray, /// spheres::{intersect, Sphere}, /// tuples::Tuple, /// }; /// /// let s = Sphere::default(); /// let i1 = Intersection::new(1., &s); /// let i2 = Intersection::new(2., &s); /// let xs = Intersections::new(vec![i1, i2]); /// assert_eq!(xs.len(), 2); /// assert_eq!(xs[0].t, 1.); /// assert_eq!(xs[1].t, 2.); /// /// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.)); /// let xs = intersect(&s, &r); /// assert_eq!(xs.len(), 2); /// assert_eq!(xs[0].object, &s); /// assert_eq!(xs[1].object, &s); /// ``` #[derive(Debug, Default, PartialEq)] pub struct Intersections<'i>(Vec>); impl<'i> Intersections<'i> { pub fn new(xs: Vec>) -> Intersections { Intersections(xs) } pub fn len(&self) -> usize { self.0.len() } /// Finds nearest hit for this collection of intersections. /// /// # Examples /// ``` /// use rtchallenge::{ /// intersections::{Intersection, Intersections}, /// rays::Ray, /// spheres::{intersect, Sphere}, /// tuples::Tuple, /// }; /// /// // The hit, when all intersections have positive t. /// let s = Sphere::default(); /// let i1 = Intersection::new(1., &s); /// let i2 = Intersection::new(2., &s); /// let xs = Intersections::new(vec![i2, i1.clone()]); /// let i = xs.hit(); /// assert_eq!(i, Some(&i1)); /// /// // The hit, when some intersections have negative t. /// let s = Sphere::default(); /// let i1 = Intersection::new(-1., &s); /// let i2 = Intersection::new(1., &s); /// let xs = Intersections::new(vec![i2.clone(), i1]); /// let i = xs.hit(); /// assert_eq!(i, Some(&i2)); /// /// // The hit, when all intersections have negative t. /// let s = Sphere::default(); /// let i1 = Intersection::new(-2., &s); /// let i2 = Intersection::new(-1., &s); /// let xs = Intersections::new(vec![i2, i1]); /// let i = xs.hit(); /// assert_eq!(i, None); /// /// // The hit is always the lowest nonnegative intersection. /// let s = Sphere::default(); /// let i1 = Intersection::new(5., &s); /// let i2 = Intersection::new(7., &s); /// let i3 = Intersection::new(-3., &s); /// let i4 = Intersection::new(2., &s); /// let xs = Intersections::new(vec![i1, i2, i3, i4.clone()]); /// let i = xs.hit(); /// assert_eq!(i, Some(&i4)); /// ``` pub fn hit(&self) -> Option<&Intersection> { self.0.iter().filter(|i| i.t > 0.).min_by(|i1, i2| { i1.t.partial_cmp(&i2.t) .expect("an intersection has a t value that is NaN") }) } } impl<'i> IntoIterator for Intersections<'i> { type Item = Intersection<'i>; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl<'i> Index for Intersections<'i> { type Output = Intersection<'i>; fn index(&self, idx: usize) -> &Self::Output { &self.0[idx] } } pub struct PrecomputedData<'i> { pub t: f32, pub object: &'i Sphere, pub point: Tuple, pub eyev: Tuple, pub normalv: Tuple, } /// Precomputes data common to all intersections. /// /// # Examples /// ``` /// use rtchallenge::{ /// intersections::{prepare_computations, Intersection, Intersections}, /// rays::Ray, /// spheres::{intersect, Sphere}, /// tuples::Tuple, /// }; /// /// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.)); /// let shape = Sphere::default(); /// let i = Intersection::new(4., &shape); /// let comps = prepare_computations(&i, &r); /// assert_eq!(comps.t, i.t); /// assert_eq!(comps.object, i.object); /// assert_eq!(comps.point, Tuple::point(0., 0., -1.)); /// assert_eq!(comps.eyev, Tuple::vector(0., 0., -1.)); /// assert_eq!(comps.normalv, Tuple::vector(0., 0., -1.)); /// ``` pub fn prepare_computations<'i>(i: &'i Intersection, r: &Ray) -> PrecomputedData<'i> { let point = r.position(i.t); PrecomputedData { t: i.t, object: i.object, point, eyev: -r.direction, normalv: i.object.normal_at(point), } }