sable_core/math/
vec4.rs

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