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}