common/
sphere.rs

1use std::sync::Arc;
2use crate::material::Material;
3use crate::vec3::{Point3, dot};
4use crate::ray::Ray;
5use crate::hittable::{HitRecord, Hittable};
6
7/// A sphere defined by a center, a radius, and an optional material.
8pub struct Sphere {
9    /// Center of the sphere.
10    pub center: Point3,
11    /// Radius of the sphere (negative radius creates a hollow sphere).
12    pub radius: f64,
13    /// Material of the sphere (`None` means no material is assigned).
14    pub material: Option<Arc<dyn Material>>,
15}
16
17impl Sphere {
18    /// Creates a sphere without a material (backward-compatible with earlier chapters).
19    pub fn new(center: Point3, radius: f64) -> Self {
20        Sphere { center, radius, material: None }
21    }
22
23    /// Creates a sphere with a material (used from chapter 1.9 onward).
24    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}