rtwlib/hittable/
sphere.rs

1//! A [`Hittable`] object, Sphere. Contains the [`Hittable`] trait implementation for Sphere.
2
3use crate::{
4    hittable::{HitRecord, Hittable},
5    material::Material,
6    utils::RangeExtensions,
7    vec3::*,
8};
9use std::{ops::Range, rc::Rc};
10#[derive(Clone)]
11
12/// A `Sphere` is a struct that represents a sphere in 3D space. It has a center, radius, and pointer to a material.
13pub struct Sphere {
14    center: Point3,
15    radius: f64,
16    mat: Rc<dyn Material>,
17}
18impl Sphere {
19    /// Creates a new `Sphere` with the given center, radius, and material.
20    pub fn new(center: Point3, radius: f64, mat: Rc<dyn Material>) -> Self {
21        Sphere {
22            center,
23            radius: f64::max(radius, 0.0),
24            mat,
25        }
26    }
27}
28
29impl Hittable for Sphere {
30    fn hit(&self, r: &crate::ray::Ray, ray_t: Range<f64>, rec: &mut HitRecord) -> bool {
31        //ray sphere interesctions
32        let oc = self.center - r.origin;
33        let a = &r.direction.length_squared();
34        let h = dot(&r.direction, &oc);
35        let c = oc.length_squared() - self.radius * self.radius;
36        let discriminant = h * h - a * c;
37
38        if discriminant < 0.0 {
39            //if it doesnt hit return false
40            return false;
41        }
42
43        let sqrtd = discriminant.sqrt();
44
45        let mut root = (h - sqrtd) / a;
46        if !ray_t.surrounds(root) {
47            // make sure hit is in range
48            root = (h + sqrtd) / a;
49            if !ray_t.surrounds(root) {
50                return false;
51            }
52        }
53
54        //callculates:
55        rec.t = root; //how far along the ray the hit was
56        rec.p = r.at(rec.t); // hit point
57        let outward_normal = (rec.p - self.center) / self.radius; //normals
58        rec.set_face_normal(r, &outward_normal); //more specific normals
59        rec.set_material(Rc::clone(&self.mat));
60
61        return true;
62    }
63    fn as_string(&self) -> String {
64        format!(
65            "[ Sphere ] Radius: {}, Position: ({}x, {}z, {}z), material: {:?}",
66            self.radius, self.center.x, self.center.y, self.center.z, self.mat
67        )
68    }
69    fn as_info_vec(&self) -> Vec<String> {
70        vec![
71            "Sphere".to_string(),
72            self.radius.to_string(),
73            self.center.x.to_string(),
74            self.center.y.to_string(),
75            self.center.z.to_string(),
76            format!("{:?}", self.mat),
77        ]
78    }
79}