lars/vector/
vector3.rs

1//! 3D vector math utilities.
2//!
3//! This module provides a simple 3D vector (`Vec3`) type with common operations used in
4//! computer graphics, ray tracing, and physics. Includes vector arithmetic, dot and cross products,
5//! normalization and much more.
6
7use derive_more::{Add, Constructor, Div, Mul, Neg, Sub};
8use std::fmt;
9use std::ops::Mul;
10use crate::Mat3;
11
12/// A 3-dimensional vector type.
13///
14/// Provides common vector operations such as addition, subtraction, scalar and component-wise
15/// multiplication, normalization, dot and cross products.
16///
17/// # Examples
18/// ```
19///
20/// use lars::Vec3;
21/// let a = Vec3::new(1.0, 0.0, 0.0);
22/// let b = Vec3::new(0.0, 1.0, 0.0);
23///
24/// let cross = a.cross(&b); // Vec3 { x: 0.0, y: 0.0, z: 1.0 }
25/// let dot = a.dot(&b); // 0.0
26/// ```
27#[derive(Add, Sub, Mul, Div, Neg, Clone, Copy, Debug, PartialOrd, Constructor)]
28pub struct Vec3 {
29    /// X component of the vector.
30    pub x: f64,
31    /// Y component of the vector.
32    pub y: f64,
33    /// Z component of the vector.
34    pub z: f64,
35}
36
37const EPSILON: f64 = 1e-9;
38
39impl PartialEq for Vec3 {
40    fn eq(&self, other: &Self) -> bool {
41        (self.x - other.x).abs() < EPSILON &&
42        (self.y - other.y).abs() < EPSILON &&
43        (self.z - other.z).abs() < EPSILON
44    }
45}
46
47impl Vec3 {
48    /// A zero Vector (0.0, 0.0, 0.0)
49    pub const ZERO: Vec3 = Vec3 {
50        x: 0.0,
51        y: 0.0,
52        z: 0.0,
53    };
54    /// A one Vector (1.0, 1.0, 1.0)
55    pub const ONE: Vec3 = Vec3 {
56        x: 1.0,
57        y: 1.0,
58        z: 1.0,
59    };
60    /// A Unit Vector in X (1.0, 0.0, 0.0)
61    pub const UNIT_X: Vec3 = Vec3 {
62        x: 1.0,
63        y: 0.0,
64        z: 0.0,
65    };
66    /// A Unit Vector in Y (0.0, 1.0, 0.0)
67    pub const UNIT_Y: Vec3 = Vec3 {
68        x: 0.0,
69        y: 1.0,
70        z: 0.0,
71    };
72    /// A Unit Vector in Z (0.0, 0.0, 1.0)
73    pub const UNIT_Z: Vec3 = Vec3 {
74        x: 0.0,
75        y: 0.0,
76        z: 1.0,
77    };
78
79    /// Returns the **magnitude** (length) of the vector.
80    ///
81    /// # Examples
82    /// ```
83    ///  use lars::Vec3;
84    /// let v = Vec3::new(3.0, 4.0, 0.0);
85    /// assert_eq!(v.mag(), 5.0);
86    /// ```
87    pub fn mag(&self) -> f64 {
88        (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
89    }
90
91    /// Returns the **dot product** between `self` and another [`Vec3`].
92    ///
93    /// # Examples
94    /// ```
95    ///
96    /// use lars::Vec3;
97    /// let a = Vec3::new(1.0, 2.0, 3.0);
98    /// let b = Vec3::new(4.0, -5.0, 6.0);
99    /// assert_eq!(a.dot(&b), 12.0);
100    /// ```
101    pub fn dot(&self, other: &Vec3) -> f64 {
102        (self.x * other.x) + (self.y * other.y) + (self.z * other.z)
103    }
104
105    /// Returns the **cross product** between `self` and another [`Vec3`].
106    ///
107    /// The cross product is perpendicular to both vectors.
108    ///
109    /// # Examples
110    /// ```
111    /// use lars::Vec3;
112    /// let a = Vec3::new(1.0, 0.0, 0.0);
113    /// let b = Vec3::new(0.0, 1.0, 0.0);
114    /// assert_eq!(a.cross(&b), Vec3::new(0.0, 0.0, 1.0));
115    /// ```
116    pub fn cross(&self, other: &Vec3) -> Vec3 {
117        let x = (self.y * other.z) - (self.z * other.y);
118        let y = (self.z * other.x) - (self.x * other.z);
119        let z = (self.x * other.y) - (self.y * other.x);
120        Vec3 { x, y, z }
121    }
122
123    /// Applies a function `f` to each component (`x`, `y`, and `z`) of the vector.
124    ///
125    /// # Examples
126    /// ```
127    ///  use lars::Vec3;
128    /// let v = Vec3::new(1.0, 2.0, 3.0);
129    /// let squared = v.map(|x| x * x);
130    /// assert_eq!(squared, Vec3::new(1.0, 4.0, 9.0));
131    /// ```
132    pub fn map<F>(&self, f: F) -> Vec3
133    where
134        F: Fn(f64) -> f64,
135    {
136        let fx = f(self.x);
137        let fy = f(self.y);
138        let fz = f(self.z);
139        Vec3 {
140            x: fx,
141            y: fy,
142            z: fz,
143        }
144    }
145
146    /// Returns a **normalized** version of the vector (unit length).
147    ///
148    /// Each component is divided by the vector's magnitude.
149    ///
150    /// # Panics
151    /// Panics if the vector has zero magnitude (division by zero).
152    ///
153    /// # Examples
154    /// ```
155    ///  use lars::Vec3;
156    /// let v = Vec3::new(3.0, 0.0, 0.0);
157    /// assert_eq!(v.normalize(), Vec3::new(1.0, 0.0, 0.0));
158    /// ```
159    pub fn normalize(&self) -> Vec3 {
160        let m = self.mag();
161        self.map(|i| i / m)
162    }
163
164    // All functions below this point are variations of the above functions
165
166    /// Returns the **magnitude**  of the vector, squared.
167    ///
168    /// # Examples
169    /// ```
170    ///  use lars::Vec3;
171    /// let v = Vec3::new(3.0, 4.0, 0.0);
172    /// assert_eq!(v.mag_sq(), 25.0);
173    /// ```
174    pub fn mag_sq(&self) -> f64 {
175        self.x * self.x + self.y * self.y + self.z * self.z
176    }
177}
178
179/// Implements scalar multiplication of a vector by a float (`f64`).
180///
181/// This enables `f64 * Vec3` syntax.
182///
183/// # Examples
184/// ```
185///  use lars::Vec3;
186/// let v = Vec3::new(1.0, 2.0, 3.0);
187/// let scaled = 2.0 * v;
188/// assert_eq!(scaled, Vec3::new(2.0, 4.0, 6.0));
189/// ```
190impl Mul<Vec3> for f64 {
191    type Output = Vec3;
192    fn mul(self, vector: Vec3) -> Vec3 {
193        Vec3 {
194            x: self * vector.x,
195            y: self * vector.y,
196            z: self * vector.z,
197        }
198    }
199}
200
201/// displays the vector in the form (X, Y, Z)
202
203impl fmt::Display for Vec3 {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        write!(f, "({}, {}, {})", self.x, self.y, self.z)
206    }
207}
208
209/// Returns (0.0, 0.0, 0.0)
210impl Default for Vec3 {
211    fn default() -> Self {
212        Self::ZERO
213    }
214}
215
216/// Implements **component-wise multiplication** between two [`Vec3`]s.
217///
218/// This is useful for operations such as color blending or per-component scaling.
219///
220/// # Examples
221/// ```
222///  use lars::Vec3;
223/// let a = Vec3::new(1.0, 2.0, 3.0);
224/// let b = Vec3::new(2.0, 0.5, 4.0);
225/// assert_eq!(a * b, Vec3::new(2.0, 1.0, 12.0));
226/// ```
227impl Mul<Vec3> for Vec3 {
228    type Output = Vec3;
229    fn mul(self, vector: Vec3) -> Vec3 {
230        Vec3 {
231            x: self.x * vector.x,
232            y: self.y * vector.y,
233            z: self.z * vector.z,
234        }
235    }
236}
237
238impl Mul<Mat3> for Vec3 {
239    type Output = Vec3;
240
241    fn mul(self, rhs: Mat3) -> Vec3 {
242        Vec3 {
243            x: self.x * rhs.a + self.y * rhs.d + self.z * rhs.g,
244            y: self.x * rhs.b + self.y * rhs.e + self.z * rhs.h,
245            z: self.x * rhs.c + self.y * rhs.f + self.z * rhs.i,
246        }
247    }
248}
249
250/// Represents an RGB color with values between `0.0` and `1.0`.
251/// Will eventually contain support for conversions with the image crate
252///
253/// Alias for [`Vec3`].
254pub type Colour = Vec3;
255
256/// Represents a 3D point in space.
257///
258/// Alias for [`Vec3`].
259pub type Point3D = Vec3;
260impl Point3D {
261    /// Finds the unsigned distance between `self` and another 3D point `Other`.
262    ///
263    /// #examples
264    ///
265    /// ```
266    /// use lars::{Point3D};
267    /// let a = Point3D::new(1.0, 0.0, 0.0);
268    /// let b = Point3D::new(0.0, 0.0, 0.0);
269    /// assert_eq!(a.dist(&b), 1.0)
270    /// ```
271    pub fn dist(&self, other: &Point3D) -> f64 {
272        (*self - *other).mag().abs()
273    }
274    /// Finds the unsigned distance between `self` and another 3D point `Other`, squared
275    ///
276    /// #examples
277    ///
278    /// ```
279    ///
280    /// use lars::Point3D;
281    /// let a = Point3D::new(2.0, 0.0, 0.0);
282    /// let b = Point3D::new(0.0, 0.0, 0.0);
283    /// assert_eq!(a.dist_sq(&b), 4.0)
284    ///
285    /// ```
286    pub fn dist_sq(&self, other: &Point3D) -> f64 {
287        (*self - *other).mag_sq().abs()
288    }
289}
290
291// TESTS
292#[cfg(test)]
293mod tests {
294    use super::*;
295
296    #[test]
297    fn test_add() {
298        let v1 = Vec3::ZERO;
299        let v2 = Vec3::ONE;
300        assert_eq!(v1 + v2, Vec3::ONE);
301    }
302    #[test]
303    fn test_mag() {
304        let v = Vec3::new(1.0, 2.0, 2.0);
305        assert_eq!(v.mag(), 3.0);
306    }
307    #[test]
308    fn test_mag_sq() {
309        let v = Vec3::new(1.0, 2.0, 2.0);
310        assert_eq!(v.mag_sq(), 9.0);
311    }
312    #[test]
313    fn test_dist() {
314        let a = Vec3::new(1.0, 2.0, 3.0);
315        let b = Vec3::new(1.0, 0.0, 3.0);
316        assert_eq!(a.dist(&b), 2.0);
317    }
318    #[test]
319    fn test_dist_sq() {
320        let a = Point3D::new(1.0, 2.0, 3.0);
321        let b = Point3D::new(1.0, 0.0, 3.0);
322        assert_eq!(a.dist_sq(&b), 4.0);
323    }
324    #[test]
325    fn test_dot_product() {
326        let a = Point3D::new(1.0, 2.0, 3.0);
327        let b = Point3D::new(4.0, -5.0, 6.0);
328        assert_eq!(a.dot(&b), 12.0);
329    }
330
331    #[test]
332    fn test_cross_product() {
333        let a = Vec3::new(1.0, 0.0, 0.0);
334        let b = Vec3::new(0.0, 1.0, 0.0);
335        assert_eq!(a.cross(&b), Vec3::new(0.0, 0.0, 1.0));
336    }
337
338    #[test]
339    fn test_normalize() {
340        let v = Vec3::new(3.0, 4.0, 0.0);
341        let n = v.normalize();
342        assert!((n.mag() - 1.0).abs() < 1e-10);
343    }
344
345    #[test]
346    fn test_scalar_mul() {
347        let v = Vec3::new(1.0, 2.0, 3.0);
348        assert_eq!(2.0 * v, Vec3::new(2.0, 4.0, 6.0));
349    }
350
351    #[test]
352    fn test_component_mul() {
353        let a = Vec3::new(2.0, 3.0, 4.0);
354        let b = Vec3::new(1.0, 2.0, 3.0);
355        assert_eq!(a * b, Vec3::new(2.0, 6.0, 12.0));
356    }
357
358    #[test]
359    fn test_default() {
360        let v = Vec3::default();
361        assert_eq!(v, Vec3::ZERO);
362    }
363
364    #[test]
365    fn test_point3d_dist() {
366        let a = Point3D::new(1.0, 2.0, 3.0);
367        let b = Point3D::new(4.0, 6.0, 3.0);
368        assert_eq!(a.dist(&b), 5.0);
369    }
370
371    #[test]
372    fn test_point3d_dist_sq() {
373        let a = Point3D::new(1.0, 2.0, 3.0);
374        let b = Point3D::new(4.0, 6.0, 3.0);
375        assert_eq!(a.dist_sq(&b), 25.0);
376    }
377
378    #[test]
379    fn test_mul_mat3() {
380        let v = Vec3::new(1.0, 2.0, 3.0);
381        let m = Mat3::new(
382            1.0, 0.0, 0.0,
383            0.0, 1.0, 0.0,
384            0.0, 0.0, 1.0
385        );
386        assert_eq!(v * m, v);
387    }
388}