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}