1use std::sync::Arc;
2use crate::material::Material;
3use crate::vec3::{Point3, dot};
4use crate::ray::Ray;
5use crate::hittable::{HitRecord, Hittable};
6
7pub struct Sphere {
9 pub center: Point3,
11 pub radius: f64,
13 pub material: Option<Arc<dyn Material>>,
15}
16
17impl Sphere {
18 pub fn new(center: Point3, radius: f64) -> Self {
20 Sphere { center, radius, material: None }
21 }
22
23 pub fn with_material(center: Point3, radius: f64, material: Arc<dyn Material>) -> Self {
25 Sphere { center, radius, material: Some(material) }
26 }
27}
28
29impl Hittable for Sphere {
30 fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
31 let oc = r.origin() - self.center;
32 let a = r.direction().length_squared();
33 let half_b = dot(oc, r.direction());
34 let c = oc.length_squared() - self.radius * self.radius;
35 let discriminant = half_b * half_b - a * c;
36
37 if discriminant < 0.0 {
38 return None;
39 }
40
41 let sqrtd = discriminant.sqrt();
42 let mut root = (-half_b - sqrtd) / a;
43 if root < t_min || root > t_max {
44 root = (-half_b + sqrtd) / a;
45 if root < t_min || root > t_max {
46 return None;
47 }
48 }
49
50 let p = r.at(root);
51 let outward_normal = (p - self.center) / self.radius;
52 let mut rec = HitRecord {
53 t: root,
54 p,
55 normal: outward_normal,
56 front_face: false,
57 mat: self.material.clone(),
58 };
59 rec.set_face_normal(r, outward_normal);
60 Some(rec)
61 }
62}