truster/tuple.rs
1//! A 3D tuple which can represent points and vectors.
2//! The coordinates are floating point numbers. Support for generics may be added in the future.
3//!
4//! # Examples
5//!
6//! You can create points and vectors with [Tuple::point] and [Tuple::vector] respectively:
7//! ```
8//! # use truster::tuple::Tuple;
9//! let p = Tuple::point(1.0, 4.2, -3.7);
10//! assert!(p.is_point());
11//! assert!(!p.is_vector());
12//! let v = Tuple::vector(1.0, 4.2, -3.7);
13//! assert!(!v.is_point());
14//! assert!(v.is_vector());
15//! ```
16//!
17//! Points and vectors are not the same:
18//! ```
19//! # use truster::tuple::Tuple;
20//! let p = Tuple::point(1.0, 4.2, -3.7);
21//! let v = Tuple::vector(1.0, 4.2, -3.7);
22//! assert_ne!(p, v);
23//! ```
24//!
25//! Points represent a position in 3D space. Vectors represent a displacement or movement.
26//!
27//!
28//! Individual coordinates can be accessed with their respective methods:
29//! ```
30//! # use truster::tuple::Tuple;
31//! let p = Tuple::point(1.0, 4.2, -3.7);
32//! assert_eq!(p.x(), 1.0);
33//! assert_eq!(p.y(), 4.2);
34//! assert_eq!(p.z(), -3.7);
35//! ```
36//!
37//! ... or with indexing:
38//! ```
39//! # use truster::tuple::Tuple;
40//! let p = Tuple::point(1.0, 4.2, -3.7);
41//! assert_eq!(p[0], 1.0);
42//! assert_eq!(p[1], 4.2);
43//! assert_eq!(p[2], -3.7);
44//! ```
45//!
46//! ## Arithmetic
47//!
48//! Tuples support all common arithmetic operations. However, be careful, as for example points
49//! can't be added to points. You have to add vectors to points to get another point. This library
50//! won't check this for you, because of simplicity and for performance reasons. You should make
51//! sure you handle everything correctly to avoid bugs. All operations which support operator
52//! overloading support mutable assignment. The available operations are:
53//!
54//! - Addition (p+v -> v, v+p -> p, v+v -> v)
55//! ```
56//! # use truster::tuple::Tuple;
57//! let mut p = Tuple::point(3.0, -2.0, 5.0);
58//! let v = Tuple::vector(-2.0, 3.0, 1.0);
59//! assert_eq!(p + v, Tuple::point(1.0, 1.0, 6.0));
60//! p += v;
61//! assert_eq!(p, Tuple::point(1.0, 1.0, 6.0));
62//! ```
63//!
64//! - Subtraction (p-p -> v, p-v -> p, v-v -> v)
65//! ```
66//! # use truster::tuple::Tuple;
67//! let p1 = Tuple::point(3.0, 2.0, 1.0);
68//! let p2 = Tuple::point(5.0, 6.0, 7.0);
69//! assert_eq!(p1 - p2, Tuple::vector(-2.0, -4.0, -6.0));
70//!
71//! let v1 = Tuple::vector(5.0, 6.0, 7.0);
72//! assert_eq!(p1 - v1, Tuple::point(-2.0, -4.0, -6.0));
73//!
74//! let v2 = Tuple::vector(3.0, 2.0, 1.0);
75//! assert_eq!(v2 - v1, Tuple::vector(-2.0, -4.0, -6.0));
76//! ```
77//!
78//! - Negation (-v -> v)
79//! ```
80//! # use truster::tuple::Tuple;
81//! let v = Tuple::vector(1.0, -2.0, 3.0);
82//! assert_eq!(-v, Tuple::vector(-1.0, 2.0, -3.0));
83//! ```
84//!
85//! - Scalar multiplication (v*f -> v)
86//! ```
87//! # use truster::tuple::Tuple;
88//! let v = Tuple::vector(1.0, -2.0, 3.0);
89//! assert_eq!(v * 3.5, Tuple::vector(3.5, -7.0, 10.5));
90//! assert_eq!(v * 0.5, Tuple::vector(0.5, -1.0, 1.5));
91//! ```
92//!
93//! - Scalar division (v/f -> v)
94//! ```
95//! # use truster::tuple::Tuple;
96//! let v = Tuple::vector(1.0, -2.0, 3.0);
97//! assert_eq!(v / 2.0, Tuple::vector(0.5, -1.0, 1.5));
98//! ```
99//!
100//! - Dot product (v⋅v -> f)
101//! ```
102//! # use truster::tuple::Tuple;
103//! let v1 = Tuple::vector(1.0, 2.0, 3.0);
104//! let v2 = Tuple::vector(2.0, 3.0, 4.0);
105//! assert_eq!(v1.dot(v2), 20.0);
106//! ```
107//!
108//! - Cross product (v×v -> v)
109//! ```
110//! # use truster::tuple::Tuple;
111//! let v1 = Tuple::vector(1.0, 2.0, 3.0);
112//! let v2 = Tuple::vector(2.0, 3.0, 4.0);
113//! assert_eq!(v1.cross(v2), Tuple::vector(-1.0, 2.0, -1.0));
114//! assert_eq!(v2.cross(v1), Tuple::vector(1.0, -2.0, 1.0));
115//! ```
116//!
117//! - Reflection (v.reflect(v) -> v)
118//! ```
119//! # use truster::tuple::Tuple;
120//! let v = Tuple::vector(1.0, -1.0, 0.0);
121//! let n = Tuple::vector(0.0, 1.0, 0.0);
122//! let r = v.reflect(n);
123//! assert_eq!(r, Tuple::vector(1.0, 1.0, 0.0));
124//! ```
125//!
126//! ## Normalization
127//!
128//! When working with vectors (so not points), you can take the norm of vectors and normalize them.
129//! You can also ask the square of the norm. This is faster than the norm itself, because no square
130//! root has to be taken.
131//!
132//! ```
133//! # use truster::tuple::Tuple;
134//!
135//! let sqrt14 = (14.0 as f64).sqrt();
136//! let mut v = Tuple::vector(1.0, 2.0, 3.0);
137//! assert_eq!(v.norm_squared(), 14.0);
138//! assert_eq!(v.norm(), sqrt14);
139//! assert_eq!(v.normalized(), Tuple::vector(1.0 / sqrt14, 2.0 / sqrt14, 3.0 / sqrt14));
140//!
141//! v.normalize();
142//! assert_eq!(v, Tuple::vector(1.0 / sqrt14, 2.0 / sqrt14, 3.0 / sqrt14));
143//! ```
144
145use std::fmt::Display;
146use std::ops::{
147 Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
148};
149
150/// Tuple represents a 3D tuple. See the module's documentation for more information.
151#[derive(Debug, PartialEq, Clone, Copy, Default)]
152pub struct Tuple {
153 x: f64,
154 y: f64,
155 z: f64,
156 w: f64,
157}
158
159impl Tuple {
160 /// Returns a new tuple with the given components. You should use [Tuple::point] and
161 /// [Tuple::vector] instead.
162 pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
163 Self { x, y, z, w }
164 }
165
166 /// Returns a new point with the given coordinates.
167 pub fn point(x: f64, y: f64, z: f64) -> Self {
168 Self::new(x, y, z, 1.0)
169 }
170
171 /// Returns a new vector with the given coordinates.
172 pub fn vector(x: f64, y: f64, z: f64) -> Self {
173 Self::new(x, y, z, 0.0)
174 }
175
176 /// Returns `self`s x coordinate.
177 pub fn x(&self) -> f64 {
178 self.x
179 }
180
181 /// Returns `self`s y coordinate.
182 pub fn y(&self) -> f64 {
183 self.y
184 }
185
186 /// Returns `self`s z coordinate.
187 pub fn z(&self) -> f64 {
188 self.z
189 }
190
191 /// Returns `self`s w coordinate.
192 pub fn w(&self) -> f64 {
193 self.w
194 }
195
196 /// Returns true if `self` represents a point, false otherwise.
197 pub fn is_point(&self) -> bool {
198 self.w == 1.0
199 }
200
201 /// Returns true if `self` represents a vector, false otherwise.
202 pub fn is_vector(&self) -> bool {
203 self.w == 0.0
204 }
205
206 /// Returns the dot product between `self` and `other`. See the module's documentation for
207 /// examples. Only works for vectors, not points.
208 pub fn dot(self, other: Self) -> f64 {
209 self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
210 }
211
212 /// Returns the cross product between `self` and `other`. See the module's documentation for
213 /// examples. Only works for vectors, not points.
214 pub fn cross(self, other: Self) -> Self {
215 Self::vector(
216 self.y * other.z - self.z * other.y,
217 self.z * other.x - self.x * other.z,
218 self.x * other.y - self.y * other.x,
219 )
220 }
221
222 /// Returns the square of the euclidean norm of `self`. See the module's documentation for
223 /// examples. Only works for vectors, not points.
224 pub fn norm_squared(self) -> f64 {
225 self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
226 }
227
228 /// Returns the norm of `self`. See the module's documentation for examples. Only works for
229 /// vectors, not points.
230 pub fn norm(self) -> f64 {
231 self.norm_squared().sqrt()
232 }
233
234 /// Returns a vector in the same direction as `self`, but with euclidean norm of one. See the
235 /// module's documentation for examples. Only works for vectors, not points.
236 pub fn normalized(self) -> Self {
237 self / self.norm()
238 }
239
240 /// Changes `self` to have a euclidean norm of one, while keeping its direction. See the
241 /// module's documentation for examples. Only works for vectors, not points.
242 pub fn normalize(&mut self) {
243 *self /= self.norm();
244 }
245
246 /// Reflects `self` along `normal`
247 pub fn reflect(self, normal: Self) -> Self {
248 self - normal * 2.0 * self.dot(normal)
249 }
250}
251
252impl Display for Tuple {
253 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
254 if self.is_point() {
255 write!(f, "P({}, {}, {})", self.x, self.y, self.z)
256 } else if self.is_vector() {
257 write!(f, "V[{} {} {}]", self.x, self.y, self.z)
258 } else {
259 write!(f, "[{} {} {} ({})]", self.x, self.y, self.z, self.w)
260 }
261 }
262}
263
264impl Add for Tuple {
265 type Output = Self;
266
267 fn add(self, rhs: Self) -> Self::Output {
268 Self::Output::new(
269 self.x + rhs.x,
270 self.y + rhs.y,
271 self.z + rhs.z,
272 self.w + rhs.w,
273 )
274 }
275}
276
277impl AddAssign for Tuple {
278 fn add_assign(&mut self, rhs: Self) {
279 self.x += rhs.x;
280 self.y += rhs.y;
281 self.z += rhs.z;
282 self.w += rhs.w;
283 }
284}
285
286impl Sub for Tuple {
287 type Output = Self;
288
289 fn sub(self, rhs: Self) -> Self::Output {
290 Self::Output::new(
291 self.x - rhs.x,
292 self.y - rhs.y,
293 self.z - rhs.z,
294 self.w - rhs.w,
295 )
296 }
297}
298
299impl SubAssign for Tuple {
300 fn sub_assign(&mut self, rhs: Self) {
301 self.x -= rhs.x;
302 self.y -= rhs.y;
303 self.z -= rhs.z;
304 self.w -= rhs.w;
305 }
306}
307
308impl Neg for Tuple {
309 type Output = Self;
310 fn neg(self) -> Self::Output {
311 Self::Output::new(-self.x, -self.y, -self.z, -self.w)
312 }
313}
314
315impl Mul<f64> for Tuple {
316 type Output = Self;
317
318 fn mul(self, rhs: f64) -> Self::Output {
319 Self::Output::new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
320 }
321}
322
323impl MulAssign<f64> for Tuple {
324 fn mul_assign(&mut self, rhs: f64) {
325 self.x *= rhs;
326 self.y *= rhs;
327 self.z *= rhs;
328 self.w *= rhs;
329 }
330}
331
332impl Div<f64> for Tuple {
333 type Output = Self;
334
335 fn div(self, rhs: f64) -> Self::Output {
336 Self::Output::new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
337 }
338}
339
340impl DivAssign<f64> for Tuple {
341 fn div_assign(&mut self, rhs: f64) {
342 self.x /= rhs;
343 self.y /= rhs;
344 self.z /= rhs;
345 self.w /= rhs;
346 }
347}
348
349impl Index<usize> for Tuple {
350 type Output = f64;
351 fn index(&self, index: usize) -> &Self::Output {
352 match index {
353 0 => &self.x,
354 1 => &self.y,
355 2 => &self.z,
356 _ => panic!("Index out of bounds for tuple, got {}", index),
357 }
358 }
359}
360
361impl IndexMut<usize> for Tuple {
362 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
363 match index {
364 0 => &mut self.x,
365 1 => &mut self.y,
366 2 => &mut self.z,
367 _ => panic!("Index out of bounds for tuple, got {}", index),
368 }
369 }
370}