truster/shape/sphere.rs
1//! Holds the [Sphere] struct;
2
3use std::rc::Rc;
4
5use crate::intersection::Intersection;
6use crate::material::Material;
7use crate::matrix::Matrix;
8use crate::ray::Ray;
9use crate::tuple::Tuple;
10
11use super::Shape;
12
13/// A 3D ellipsoid (spheroid).
14#[derive(Default, Clone)]
15pub struct Sphere {
16 transform: Matrix,
17 transform_inverse: Matrix,
18 material: Material,
19}
20
21impl Sphere {
22 /// Returns a new sphere with radius 1, centered at the origin.
23 /// Use [Sphere::set_transform] to transform it. Give it a material with [Sphere::set_material].
24 pub fn new() -> Self {
25 Self::default()
26 }
27}
28
29impl Shape for Sphere {
30 /// Returns a sorted vector of all distances where `ray` intersects `self`.
31 ///
32 /// # Examples
33 ///
34 /// A ray intersects a sphere at two points.
35 /// ```
36 /// # use truster::shape::{Shape, sphere::Sphere};
37 /// use truster::ray::Ray;
38 /// use truster::tuple::Tuple;
39 ///
40 /// let ray = Ray::new(Tuple::point(0.0, 0.0, -5.0), Tuple::vector(0.0, 0.0, 1.0));
41 /// let sphere = Sphere::new();
42 /// let intersections = sphere.intersect(&ray);
43 /// assert_eq!(intersections.len(), 2);
44 /// assert_eq!(intersections[0].t(), 4.0);
45 /// assert_eq!(intersections[1].t(), 6.0);
46 /// ```
47 ///
48 /// A ray intersects a sphere at a tangent.
49 /// ```
50 /// # use truster::shape::{Shape, sphere::Sphere};
51 /// use truster::ray::Ray;
52 /// use truster::tuple::Tuple;
53 ///
54 /// let ray = Ray::new(Tuple::point(0.0, 1.0, -5.0), Tuple::vector(0.0, 0.0, 1.0));
55 /// let sphere = Sphere::new();
56 /// let intersections = sphere.intersect(&ray);
57 /// assert_eq!(intersections.len(), 2);
58 /// assert_eq!(intersections[0].t(), 5.0);
59 /// assert_eq!(intersections[1].t(), 5.0);
60 /// ```
61 ///
62 /// A ray misses a sphere.
63 /// ```
64 /// # use truster::shape::{Shape, sphere::Sphere};
65 /// use truster::ray::Ray;
66 /// use truster::tuple::Tuple;
67 ///
68 /// let ray = Ray::new(Tuple::point(0.0, 2.0, -5.0), Tuple::vector(0.0, 0.0, 1.0));
69 /// let sphere = Sphere::new();
70 /// let intersections = sphere.intersect(&ray);
71 /// assert_eq!(intersections.len(), 0);
72 /// ```
73 ///
74 /// A ray originates inside a sphere.
75 /// ```
76 /// # use truster::shape::{Shape, sphere::Sphere};
77 /// use truster::ray::Ray;
78 /// use truster::tuple::Tuple;
79 ///
80 /// let ray = Ray::new(Tuple::point(0.0, 0.0, 0.0), Tuple::vector(0.0, 0.0, 1.0));
81 /// let sphere = Sphere::new();
82 /// let intersections = sphere.intersect(&ray);
83 /// assert_eq!(intersections.len(), 2);
84 /// assert_eq!(intersections[0].t(), -1.0);
85 /// assert_eq!(intersections[1].t(), 1.0);
86 /// ```
87 ///
88 /// A ray is behind a sphere.
89 /// ```
90 /// # use truster::shape::{Shape, sphere::Sphere};
91 /// use truster::ray::Ray;
92 /// use truster::tuple::Tuple;
93 ///
94 /// let ray = Ray::new(Tuple::point(0.0, 0.0, 5.0), Tuple::vector(0.0, 0.0, 1.0));
95 /// let sphere = Sphere::new();
96 /// let intersections = sphere.intersect(&ray);
97 /// assert_eq!(intersections.len(), 2);
98 /// assert_eq!(intersections[0].t(), -6.0);
99 /// assert_eq!(intersections[1].t(), -4.0);
100 /// ```
101 fn local_intersect(&self, ray: &Ray) -> Vec<Intersection> {
102 let oc = ray.origin() - Tuple::point(0.0, 0.0, 0.0);
103
104 let a = ray.direction().norm_squared();
105 let b = ray.direction().dot(oc);
106 let c = oc.norm_squared() - 1.0;
107
108 let d = b * b - a * c;
109
110 if d < 0.0 {
111 return Vec::new();
112 }
113
114 let sqrtd = d.sqrt();
115 let t1 = (-b - sqrtd) / a;
116 let t2 = (-b + sqrtd) / a;
117
118 vec![
119 Intersection::new(t1, Rc::new(self.clone())),
120 Intersection::new(t2, Rc::new(self.clone())),
121 ]
122 }
123
124 /// Returns the surface normal of `self` at `point`.
125 ///
126 /// # Examples
127 ///
128 /// The normal on a sphere at a point on the X axis.
129 /// ```
130 /// # use truster::shape::{Shape, sphere::Sphere};
131 /// use truster::tuple::Tuple;
132 ///
133 /// let sphere = Sphere::new();
134 /// let normal = sphere.normal_at(Tuple::point(1.0, 0.0, 0.0));
135 /// assert_eq!(normal, Tuple::vector(1.0, 0.0, 0.0));
136 /// ```
137 ///
138 /// The normal on a sphere at a point on the Y axis.
139 /// ```
140 /// # use truster::shape::{Shape, sphere::Sphere};
141 /// use truster::tuple::Tuple;
142 ///
143 /// let sphere = Sphere::new();
144 /// let normal = sphere.normal_at(Tuple::point(0.0, 1.0, 0.0));
145 /// assert_eq!(normal, Tuple::vector(0.0, 1.0, 0.0));
146 /// ```
147 ///
148 /// The normal on a sphere at a point on the Z axis.
149 /// ```
150 /// # use truster::shape::{Shape, sphere::Sphere};
151 /// use truster::tuple::Tuple;
152 ///
153 /// let sphere = Sphere::new();
154 /// let normal = sphere.normal_at(Tuple::point(0.0, 0.0, 1.0));
155 /// assert_eq!(normal, Tuple::vector(0.0, 0.0, 1.0));
156 /// ```
157 ///
158 /// The normal on a sphere at a point on the non-axial point.
159 /// ```
160 /// # use truster::shape::{Shape, sphere::Sphere};
161 /// use truster::tuple::Tuple;
162 ///
163 /// let sphere = Sphere::new();
164 /// let normal = sphere.normal_at(Tuple::point(
165 /// (3.0 as f64).sqrt() / 3.0,
166 /// (3.0 as f64).sqrt() / 3.0,
167 /// (3.0 as f64).sqrt() / 3.0,
168 /// ));
169 /// assert_eq!(normal, Tuple::vector(
170 /// (3.0 as f64).sqrt() / 3.0,
171 /// (3.0 as f64).sqrt() / 3.0,
172 /// (3.0 as f64).sqrt() / 3.0,
173 /// ));
174 /// ```
175 fn local_normal_at(&self, point: Tuple) -> Tuple {
176 (point - Tuple::point(0.0, 0.0, 0.0)).normalized()
177 }
178
179 /// Sets `self`'s transform to be `transform`.
180 fn set_transform(&mut self, transform: Matrix) {
181 self.transform_inverse = transform.inverse();
182 self.transform = transform;
183 }
184
185 /// Returns `self`'s transform.
186 fn transform(&self) -> &Matrix {
187 &self.transform
188 }
189
190 /// Returns `self`'s transform's inverse.
191 fn transform_inverse(&self) -> &Matrix {
192 &self.transform_inverse
193 }
194
195 /// Returns `self`'s material.
196 fn material(&self) -> &Material {
197 &self.material
198 }
199
200 /// Sets `self`'s material to be `material`.
201 fn set_material(&mut self, material: Material) {
202 self.material = material;
203 }
204}