sable_core/math/
vec3.rs

1//! 3D vector type.
2
3use super::{EPSILON, Float, Vec2, approx_eq, lerp};
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6/// A 3D vector.
7#[derive(Debug, Clone, Copy, PartialEq, Default)]
8#[repr(C)]
9pub struct Vec3 {
10    /// X component.
11    pub x: Float,
12    /// Y component.
13    pub y: Float,
14    /// Z component.
15    pub z: Float,
16}
17
18impl Vec3 {
19    /// Zero vector (0, 0, 0).
20    pub const ZERO: Self = Self {
21        x: 0.0,
22        y: 0.0,
23        z: 0.0,
24    };
25
26    /// One vector (1, 1, 1).
27    pub const ONE: Self = Self {
28        x: 1.0,
29        y: 1.0,
30        z: 1.0,
31    };
32
33    /// Unit X vector (1, 0, 0).
34    pub const X: Self = Self {
35        x: 1.0,
36        y: 0.0,
37        z: 0.0,
38    };
39
40    /// Unit Y vector (0, 1, 0).
41    pub const Y: Self = Self {
42        x: 0.0,
43        y: 1.0,
44        z: 0.0,
45    };
46
47    /// Unit Z vector (0, 0, 1).
48    pub const Z: Self = Self {
49        x: 0.0,
50        y: 0.0,
51        z: 1.0,
52    };
53
54    /// Negative X vector (-1, 0, 0).
55    pub const NEG_X: Self = Self {
56        x: -1.0,
57        y: 0.0,
58        z: 0.0,
59    };
60
61    /// Negative Y vector (0, -1, 0).
62    pub const NEG_Y: Self = Self {
63        x: 0.0,
64        y: -1.0,
65        z: 0.0,
66    };
67
68    /// Negative Z vector (0, 0, -1).
69    pub const NEG_Z: Self = Self {
70        x: 0.0,
71        y: 0.0,
72        z: -1.0,
73    };
74
75    /// Creates a new vector.
76    #[inline]
77    #[must_use]
78    pub const fn new(x: Float, y: Float, z: Float) -> Self {
79        Self { x, y, z }
80    }
81
82    /// Creates a vector with all components set to the same value.
83    #[inline]
84    #[must_use]
85    pub const fn splat(value: Float) -> Self {
86        Self {
87            x: value,
88            y: value,
89            z: value,
90        }
91    }
92
93    /// Creates a vector from an array.
94    #[inline]
95    #[must_use]
96    pub const fn from_array(arr: [Float; 3]) -> Self {
97        Self {
98            x: arr[0],
99            y: arr[1],
100            z: arr[2],
101        }
102    }
103
104    /// Converts the vector to an array.
105    #[inline]
106    #[must_use]
107    pub const fn to_array(self) -> [Float; 3] {
108        [self.x, self.y, self.z]
109    }
110
111    /// Extends to Vec3 from Vec2 with a z component.
112    #[inline]
113    #[must_use]
114    pub const fn from_vec2(v: Vec2, z: Float) -> Self {
115        Self { x: v.x, y: v.y, z }
116    }
117
118    /// Truncates to Vec2, discarding the z component.
119    #[inline]
120    #[must_use]
121    pub const fn xy(self) -> Vec2 {
122        Vec2 {
123            x: self.x,
124            y: self.y,
125        }
126    }
127
128    /// Returns the xz components as Vec2.
129    #[inline]
130    #[must_use]
131    pub const fn xz(self) -> Vec2 {
132        Vec2 {
133            x: self.x,
134            y: self.z,
135        }
136    }
137
138    /// Returns the yz components as Vec2.
139    #[inline]
140    #[must_use]
141    pub const fn yz(self) -> Vec2 {
142        Vec2 {
143            x: self.y,
144            y: self.z,
145        }
146    }
147
148    /// Computes the dot product.
149    #[inline]
150    #[must_use]
151    pub fn dot(self, other: Self) -> Float {
152        self.x * other.x + self.y * other.y + self.z * other.z
153    }
154
155    /// Computes the cross product.
156    #[inline]
157    #[must_use]
158    pub fn cross(self, other: Self) -> Self {
159        Self {
160            x: self.y * other.z - self.z * other.y,
161            y: self.z * other.x - self.x * other.z,
162            z: self.x * other.y - self.y * other.x,
163        }
164    }
165
166    /// Computes the squared length.
167    #[inline]
168    #[must_use]
169    pub fn length_squared(self) -> Float {
170        self.dot(self)
171    }
172
173    /// Computes the length.
174    #[inline]
175    #[must_use]
176    pub fn length(self) -> Float {
177        self.length_squared().sqrt()
178    }
179
180    /// Returns a normalized vector.
181    #[inline]
182    #[must_use]
183    pub fn normalize(self) -> Self {
184        let len = self.length();
185        if len > EPSILON {
186            self / len
187        } else {
188            Self::ZERO
189        }
190    }
191
192    /// Returns a normalized vector, or `None` if length is near zero.
193    #[inline]
194    #[must_use]
195    pub fn try_normalize(self) -> Option<Self> {
196        let len = self.length();
197        if len > EPSILON {
198            Some(self / len)
199        } else {
200            None
201        }
202    }
203
204    /// Computes the distance to another vector.
205    #[inline]
206    #[must_use]
207    pub fn distance(self, other: Self) -> Float {
208        (self - other).length()
209    }
210
211    /// Computes the squared distance to another vector.
212    #[inline]
213    #[must_use]
214    pub fn distance_squared(self, other: Self) -> Float {
215        (self - other).length_squared()
216    }
217
218    /// Linear interpolation between two vectors.
219    #[inline]
220    #[must_use]
221    pub fn lerp(self, other: Self, t: Float) -> Self {
222        Self {
223            x: lerp(self.x, other.x, t),
224            y: lerp(self.y, other.y, t),
225            z: lerp(self.z, other.z, t),
226        }
227    }
228
229    /// Component-wise minimum.
230    #[inline]
231    #[must_use]
232    pub fn min(self, other: Self) -> Self {
233        Self {
234            x: self.x.min(other.x),
235            y: self.y.min(other.y),
236            z: self.z.min(other.z),
237        }
238    }
239
240    /// Component-wise maximum.
241    #[inline]
242    #[must_use]
243    pub fn max(self, other: Self) -> Self {
244        Self {
245            x: self.x.max(other.x),
246            y: self.y.max(other.y),
247            z: self.z.max(other.z),
248        }
249    }
250
251    /// Component-wise absolute value.
252    #[inline]
253    #[must_use]
254    pub fn abs(self) -> Self {
255        Self {
256            x: self.x.abs(),
257            y: self.y.abs(),
258            z: self.z.abs(),
259        }
260    }
261
262    /// Computes the angle in radians between two vectors.
263    #[inline]
264    #[must_use]
265    pub fn angle_between(self, other: Self) -> Float {
266        let denom = (self.length_squared() * other.length_squared()).sqrt();
267        if denom < EPSILON {
268            0.0
269        } else {
270            (self.dot(other) / denom).clamp(-1.0, 1.0).acos()
271        }
272    }
273
274    /// Checks if this vector is approximately equal to another.
275    #[inline]
276    #[must_use]
277    pub fn approx_eq(self, other: Self) -> bool {
278        approx_eq(self.x, other.x) && approx_eq(self.y, other.y) && approx_eq(self.z, other.z)
279    }
280
281    /// Reflects this vector off a surface with the given normal.
282    #[inline]
283    #[must_use]
284    pub fn reflect(self, normal: Self) -> Self {
285        self - normal * (2.0 * self.dot(normal))
286    }
287
288    /// Projects this vector onto another.
289    #[inline]
290    #[must_use]
291    pub fn project(self, onto: Self) -> Self {
292        let denom = onto.length_squared();
293        if denom < EPSILON {
294            Self::ZERO
295        } else {
296            onto * (self.dot(onto) / denom)
297        }
298    }
299
300    /// Returns the component-wise floor.
301    #[inline]
302    #[must_use]
303    pub fn floor(self) -> Self {
304        Self {
305            x: self.x.floor(),
306            y: self.y.floor(),
307            z: self.z.floor(),
308        }
309    }
310
311    /// Returns the component-wise ceil.
312    #[inline]
313    #[must_use]
314    pub fn ceil(self) -> Self {
315        Self {
316            x: self.x.ceil(),
317            y: self.y.ceil(),
318            z: self.z.ceil(),
319        }
320    }
321}
322
323impl Add for Vec3 {
324    type Output = Self;
325
326    #[inline]
327    fn add(self, other: Self) -> Self {
328        Self {
329            x: self.x + other.x,
330            y: self.y + other.y,
331            z: self.z + other.z,
332        }
333    }
334}
335
336impl AddAssign for Vec3 {
337    #[inline]
338    fn add_assign(&mut self, other: Self) {
339        self.x += other.x;
340        self.y += other.y;
341        self.z += other.z;
342    }
343}
344
345impl Sub for Vec3 {
346    type Output = Self;
347
348    #[inline]
349    fn sub(self, other: Self) -> Self {
350        Self {
351            x: self.x - other.x,
352            y: self.y - other.y,
353            z: self.z - other.z,
354        }
355    }
356}
357
358impl SubAssign for Vec3 {
359    #[inline]
360    fn sub_assign(&mut self, other: Self) {
361        self.x -= other.x;
362        self.y -= other.y;
363        self.z -= other.z;
364    }
365}
366
367impl Mul<Float> for Vec3 {
368    type Output = Self;
369
370    #[inline]
371    fn mul(self, scalar: Float) -> Self {
372        Self {
373            x: self.x * scalar,
374            y: self.y * scalar,
375            z: self.z * scalar,
376        }
377    }
378}
379
380impl Mul<Vec3> for Float {
381    type Output = Vec3;
382
383    #[inline]
384    fn mul(self, vec: Vec3) -> Vec3 {
385        vec * self
386    }
387}
388
389impl MulAssign<Float> for Vec3 {
390    #[inline]
391    fn mul_assign(&mut self, scalar: Float) {
392        self.x *= scalar;
393        self.y *= scalar;
394        self.z *= scalar;
395    }
396}
397
398impl Mul for Vec3 {
399    type Output = Self;
400
401    #[inline]
402    fn mul(self, other: Self) -> Self {
403        Self {
404            x: self.x * other.x,
405            y: self.y * other.y,
406            z: self.z * other.z,
407        }
408    }
409}
410
411impl Div<Float> for Vec3 {
412    type Output = Self;
413
414    #[inline]
415    fn div(self, scalar: Float) -> Self {
416        Self {
417            x: self.x / scalar,
418            y: self.y / scalar,
419            z: self.z / scalar,
420        }
421    }
422}
423
424impl DivAssign<Float> for Vec3 {
425    #[inline]
426    fn div_assign(&mut self, scalar: Float) {
427        self.x /= scalar;
428        self.y /= scalar;
429        self.z /= scalar;
430    }
431}
432
433impl Neg for Vec3 {
434    type Output = Self;
435
436    #[inline]
437    fn neg(self) -> Self {
438        Self {
439            x: -self.x,
440            y: -self.y,
441            z: -self.z,
442        }
443    }
444}
445
446impl From<[Float; 3]> for Vec3 {
447    #[inline]
448    fn from(arr: [Float; 3]) -> Self {
449        Self::from_array(arr)
450    }
451}
452
453impl From<Vec3> for [Float; 3] {
454    #[inline]
455    fn from(v: Vec3) -> Self {
456        v.to_array()
457    }
458}
459
460impl From<(Float, Float, Float)> for Vec3 {
461    #[inline]
462    fn from((x, y, z): (Float, Float, Float)) -> Self {
463        Self { x, y, z }
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470
471    #[test]
472    fn test_new() {
473        let v = Vec3::new(1.0, 2.0, 3.0);
474        assert_eq!(v.x, 1.0);
475        assert_eq!(v.y, 2.0);
476        assert_eq!(v.z, 3.0);
477    }
478
479    #[test]
480    fn test_constants() {
481        assert!(Vec3::ZERO.approx_eq(Vec3::new(0.0, 0.0, 0.0)));
482        assert!(Vec3::ONE.approx_eq(Vec3::new(1.0, 1.0, 1.0)));
483        assert!(Vec3::X.approx_eq(Vec3::new(1.0, 0.0, 0.0)));
484        assert!(Vec3::Y.approx_eq(Vec3::new(0.0, 1.0, 0.0)));
485        assert!(Vec3::Z.approx_eq(Vec3::new(0.0, 0.0, 1.0)));
486    }
487
488    #[test]
489    fn test_splat() {
490        let v = Vec3::splat(5.0);
491        assert!(v.approx_eq(Vec3::new(5.0, 5.0, 5.0)));
492    }
493
494    #[test]
495    fn test_from_vec2() {
496        let v2 = Vec2::new(1.0, 2.0);
497        let v3 = Vec3::from_vec2(v2, 3.0);
498        assert!(v3.approx_eq(Vec3::new(1.0, 2.0, 3.0)));
499    }
500
501    #[test]
502    fn test_swizzle() {
503        let v = Vec3::new(1.0, 2.0, 3.0);
504        assert!(v.xy().approx_eq(Vec2::new(1.0, 2.0)));
505        assert!(v.xz().approx_eq(Vec2::new(1.0, 3.0)));
506        assert!(v.yz().approx_eq(Vec2::new(2.0, 3.0)));
507    }
508
509    #[test]
510    fn test_dot() {
511        let a = Vec3::new(1.0, 2.0, 3.0);
512        let b = Vec3::new(4.0, 5.0, 6.0);
513        assert!(approx_eq(a.dot(b), 32.0)); // 1*4 + 2*5 + 3*6 = 32
514    }
515
516    #[test]
517    fn test_cross() {
518        let a = Vec3::X;
519        let b = Vec3::Y;
520        assert!(a.cross(b).approx_eq(Vec3::Z));
521        assert!(b.cross(a).approx_eq(Vec3::NEG_Z));
522    }
523
524    #[test]
525    fn test_length() {
526        let v = Vec3::new(2.0, 3.0, 6.0);
527        assert!(approx_eq(v.length_squared(), 49.0));
528        assert!(approx_eq(v.length(), 7.0));
529    }
530
531    #[test]
532    fn test_normalize() {
533        let v = Vec3::new(0.0, 3.0, 4.0);
534        let n = v.normalize();
535        assert!(approx_eq(n.length(), 1.0));
536        assert!(n.approx_eq(Vec3::new(0.0, 0.6, 0.8)));
537    }
538
539    #[test]
540    fn test_normalize_zero() {
541        let v = Vec3::ZERO;
542        assert!(v.normalize().approx_eq(Vec3::ZERO));
543        assert!(v.try_normalize().is_none());
544    }
545
546    #[test]
547    fn test_distance() {
548        let a = Vec3::ZERO;
549        let b = Vec3::new(2.0, 3.0, 6.0);
550        assert!(approx_eq(a.distance(b), 7.0));
551    }
552
553    #[test]
554    fn test_lerp() {
555        let a = Vec3::ZERO;
556        let b = Vec3::new(10.0, 20.0, 30.0);
557        assert!(a.lerp(b, 0.0).approx_eq(a));
558        assert!(a.lerp(b, 1.0).approx_eq(b));
559        assert!(a.lerp(b, 0.5).approx_eq(Vec3::new(5.0, 10.0, 15.0)));
560    }
561
562    #[test]
563    fn test_min_max() {
564        let a = Vec3::new(1.0, 4.0, 2.0);
565        let b = Vec3::new(3.0, 2.0, 5.0);
566        assert!(a.min(b).approx_eq(Vec3::new(1.0, 2.0, 2.0)));
567        assert!(a.max(b).approx_eq(Vec3::new(3.0, 4.0, 5.0)));
568    }
569
570    #[test]
571    fn test_reflect() {
572        let v = Vec3::new(1.0, -1.0, 0.0);
573        let n = Vec3::Y;
574        let r = v.reflect(n);
575        assert!(r.approx_eq(Vec3::new(1.0, 1.0, 0.0)));
576    }
577
578    #[test]
579    fn test_project() {
580        let v = Vec3::new(3.0, 4.0, 0.0);
581        let onto = Vec3::X;
582        assert!(v.project(onto).approx_eq(Vec3::new(3.0, 0.0, 0.0)));
583    }
584
585    #[test]
586    fn test_angle_between() {
587        let a = Vec3::X;
588        let b = Vec3::Y;
589        assert!(approx_eq(
590            a.angle_between(b),
591            std::f32::consts::FRAC_PI_2 as Float
592        ));
593    }
594
595    #[test]
596    fn test_add() {
597        let a = Vec3::new(1.0, 2.0, 3.0);
598        let b = Vec3::new(4.0, 5.0, 6.0);
599        assert!((a + b).approx_eq(Vec3::new(5.0, 7.0, 9.0)));
600    }
601
602    #[test]
603    fn test_sub() {
604        let a = Vec3::new(4.0, 5.0, 6.0);
605        let b = Vec3::new(1.0, 2.0, 3.0);
606        assert!((a - b).approx_eq(Vec3::new(3.0, 3.0, 3.0)));
607    }
608
609    #[test]
610    fn test_mul_scalar() {
611        let v = Vec3::new(1.0, 2.0, 3.0);
612        assert!((v * 2.0).approx_eq(Vec3::new(2.0, 4.0, 6.0)));
613        assert!((2.0 * v).approx_eq(Vec3::new(2.0, 4.0, 6.0)));
614    }
615
616    #[test]
617    fn test_div() {
618        let v = Vec3::new(2.0, 4.0, 6.0);
619        assert!((v / 2.0).approx_eq(Vec3::new(1.0, 2.0, 3.0)));
620    }
621
622    #[test]
623    fn test_neg() {
624        let v = Vec3::new(1.0, -2.0, 3.0);
625        assert!((-v).approx_eq(Vec3::new(-1.0, 2.0, -3.0)));
626    }
627
628    #[test]
629    fn test_floor_ceil() {
630        let v = Vec3::new(1.5, -2.3, 3.7);
631        assert!(v.floor().approx_eq(Vec3::new(1.0, -3.0, 3.0)));
632        assert!(v.ceil().approx_eq(Vec3::new(2.0, -2.0, 4.0)));
633    }
634}