1use super::HitRecord;2use super::Hittable;3use crate::math::Interval;4use crate::math::NotNanF64;5use crate::math::Point;67pub struct Sphere {8 center: Point,9 radius: f64,10}1112impl Sphere {13 pub fn new(center: Point, radius: f64) -> Sphere {14 Sphere { center, radius }15 }16}1718impl Hittable for Sphere {19 fn hit(&self, r: &crate::math::Ray, range: &Interval) -> Option<HitRecord> {20 let oc = self.center - r.orig;21 // a is always 1, so we can remove it from all calculations below.22 // let a = r.dir.length_squared();23 let a = r.dir.length_squared();24 let h = r.dir.dot(&oc);25 let c = oc.length_squared() - self.radius * self.radius;2627 let descriminant = h * h - (a * c);28 if descriminant < 0. {29 return None;30 }3132 let sqrtd = descriminant.sqrt();3334 let mut t = (h - sqrtd) / a;35 if !range.surrounds(t) {36 t = h + sqrtd;37 if !range.surrounds(t) {38 return None;39 }40 }4142 let point = r.at(t);43 // FIXME: Should be able to divide by radius, but there seems to be some44 // precision error creeping in somewhere.45 let normal = (point - self.center).normalize();4647 Some(HitRecord::new(r, point, normal, NotNanF64::new(t).unwrap()))48 }49}