Skip to main content

khora_core/math/
vector.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Provides 2D, 3D, and 4D vector types and their associated operations.
16
17use bincode::{Decode, Encode};
18use serde::{Deserialize, Serialize};
19
20use super::EPSILON;
21use std::ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub};
22
23// --- Vec2 ---
24
25/// A 2-dimensional vector with `f32` components.
26#[derive(
27    Debug,
28    Default,
29    Copy,
30    Clone,
31    PartialEq,
32    bytemuck::Pod,
33    bytemuck::Zeroable,
34    Serialize,
35    Deserialize,
36    Encode,
37    Decode,
38)]
39#[repr(C)]
40pub struct Vec2 {
41    /// The x component of the vector.
42    pub x: f32,
43    /// The y component of the vector.
44    pub y: f32,
45}
46
47impl Vec2 {
48    /// A vector with all components set to `0.0`.
49    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
50    /// A vector with all components set to `1.0`.
51    pub const ONE: Self = Self { x: 1.0, y: 1.0 };
52    /// The unit vector pointing along the positive X-axis.
53    pub const X: Self = Self { x: 1.0, y: 0.0 };
54    /// The unit vector pointing along the positive Y-axis.
55    pub const Y: Self = Self { x: 0.0, y: 1.0 };
56
57    /// Creates a new `Vec2` with the specified components.
58    #[inline]
59    pub const fn new(x: f32, y: f32) -> Self {
60        Self { x, y }
61    }
62
63    /// Returns a new vector with the absolute value of each component.
64    #[inline]
65    pub const fn abs(self) -> Self {
66        Self {
67            x: if self.x < 0.0 { -self.x } else { self.x },
68            y: if self.y < 0.0 { -self.y } else { self.y },
69        }
70    }
71
72    /// Calculates the squared length (magnitude) of the vector.
73    /// This is faster than `length()` as it avoids a square root.
74    #[inline]
75    pub fn length_squared(&self) -> f32 {
76        self.dot(*self)
77    }
78
79    /// Calculates the length (magnitude) of the vector.
80    #[inline]
81    pub fn length(&self) -> f32 {
82        self.length_squared().sqrt()
83    }
84
85    /// Returns a normalized version of the vector with a length of 1.
86    /// If the vector's length is near zero, it returns `Vec2::ZERO`.
87    #[inline]
88    pub fn normalize(&self) -> Self {
89        let len_sq = self.length_squared();
90        if len_sq > EPSILON * EPSILON {
91            *self * (1.0 / len_sq.sqrt())
92        } else {
93            Self::ZERO
94        }
95    }
96
97    /// Calculates the dot product of this vector and another.
98    #[inline]
99    pub fn dot(&self, rhs: Self) -> f32 {
100        self.x * rhs.x + self.y * rhs.y
101    }
102
103    /// Performs a linear interpolation between two vectors.
104    /// The interpolation factor `t` is clamped to the `[0.0, 1.0]` range.
105    #[inline]
106    pub fn lerp(start: Self, end: Self, t: f32) -> Self {
107        start + (end - start) * t.clamp(0.0, 1.0)
108    }
109
110    /// Converts the vector to a `[f32; 2]` array.
111    #[inline]
112    pub fn to_array(self) -> [f32; 2] {
113        [self.x, self.y]
114    }
115}
116
117impl From<Vec2> for [f32; 2] {
118    #[inline]
119    fn from(v: Vec2) -> Self {
120        [v.x, v.y]
121    }
122}
123
124impl From<[f32; 2]> for Vec2 {
125    #[inline]
126    fn from(v: [f32; 2]) -> Self {
127        Self::new(v[0], v[1])
128    }
129}
130
131// --- Operator Overloads ---
132
133impl Add for Vec2 {
134    type Output = Self;
135    /// Adds two vectors component-wise.
136    #[inline]
137    fn add(self, rhs: Self) -> Self::Output {
138        Self {
139            x: self.x + rhs.x,
140            y: self.y + rhs.y,
141        }
142    }
143}
144
145impl Sub for Vec2 {
146    type Output = Self;
147    /// Subtracts two vectors component-wise.
148    #[inline]
149    fn sub(self, rhs: Self) -> Self::Output {
150        Self {
151            x: self.x - rhs.x,
152            y: self.y - rhs.y,
153        }
154    }
155}
156
157impl Mul<f32> for Vec2 {
158    type Output = Self;
159    /// Multiplies the vector by a scalar.
160    #[inline]
161    fn mul(self, rhs: f32) -> Self::Output {
162        Self {
163            x: self.x * rhs,
164            y: self.y * rhs,
165        }
166    }
167}
168
169impl Mul<Vec2> for f32 {
170    type Output = Vec2;
171    /// Multiplies a scalar by a vector.
172    #[inline]
173    fn mul(self, rhs: Vec2) -> Self::Output {
174        rhs * self
175    }
176}
177
178impl Mul<Vec2> for Vec2 {
179    type Output = Self;
180    /// Multiplies two vectors component-wise.
181    #[inline]
182    fn mul(self, rhs: Vec2) -> Self::Output {
183        Self {
184            x: self.x * rhs.x,
185            y: self.y * rhs.y,
186        }
187    }
188}
189
190impl Div<f32> for Vec2 {
191    type Output = Self;
192    /// Divides the vector by a scalar.
193    #[inline]
194    fn div(self, rhs: f32) -> Self::Output {
195        let inv_rhs = 1.0 / rhs;
196        Self {
197            x: self.x * inv_rhs,
198            y: self.y * inv_rhs,
199        }
200    }
201}
202
203impl Neg for Vec2 {
204    type Output = Self;
205    /// Negates the vector.
206    #[inline]
207    fn neg(self) -> Self::Output {
208        Self {
209            x: -self.x,
210            y: -self.y,
211        }
212    }
213}
214
215impl Index<usize> for Vec2 {
216    type Output = f32;
217    /// Allows accessing a vector component by index (`v[0]`, `v[1]`).
218    ///
219    /// # Panics
220    /// Panics if `index` is not 0 or 1.
221    #[inline]
222    fn index(&self, index: usize) -> &Self::Output {
223        match index {
224            0 => &self.x,
225            1 => &self.y,
226            _ => panic!("Index out of bounds for Vec2"),
227        }
228    }
229}
230
231impl IndexMut<usize> for Vec2 {
232    /// Allows mutably accessing a vector component by index (`v[0] = ...`).
233    ///
234    /// # Panics
235    /// Panics if `index` is not 0 or 1.
236    #[inline]
237    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
238        match index {
239            0 => &mut self.x,
240            1 => &mut self.y,
241            _ => panic!("Index out of bounds for Vec2"),
242        }
243    }
244}
245
246// --- Vector3D ---
247
248/// A 3-dimensional vector with `f32` components.
249///
250/// `Vec3` is the workhorse for positions, directions, and scales throughout
251/// the engine. It supports the usual arithmetic operators plus `dot`, `cross`,
252/// `length`, and `normalize`.
253///
254/// # Examples
255///
256/// ```rust
257/// use khora_core::math::Vec3;
258///
259/// // Component-wise arithmetic and scalar multiplication.
260/// let a = Vec3::new(1.0, 2.0, 3.0);
261/// let b = Vec3::new(4.0, 5.0, 6.0);
262/// assert_eq!(a + b, Vec3::new(5.0, 7.0, 9.0));
263/// assert_eq!(a * 2.0, Vec3::new(2.0, 4.0, 6.0));
264///
265/// // Dot product and the right-handed cross product of the basis axes.
266/// assert_eq!(Vec3::X.dot(Vec3::Y), 0.0);
267/// assert_eq!(Vec3::X.cross(Vec3::Y), Vec3::Z);
268///
269/// // Normalizing yields a unit-length direction.
270/// let dir = Vec3::new(0.0, 3.0, 0.0).normalize();
271/// assert_eq!(dir, Vec3::Y);
272/// ```
273#[derive(
274    Debug,
275    Clone,
276    Copy,
277    PartialEq,
278    bytemuck::Pod,
279    bytemuck::Zeroable,
280    Serialize,
281    Deserialize,
282    Encode,
283    Decode,
284)]
285#[repr(C)]
286pub struct Vec3 {
287    /// The x component of the vector.
288    pub x: f32,
289    /// The y component of the vector.
290    pub y: f32,
291    /// The z component of the vector.
292    pub z: f32,
293}
294
295impl Vec3 {
296    /// A vector with all components set to `0.0`.
297    pub const ZERO: Self = Self {
298        x: 0.0,
299        y: 0.0,
300        z: 0.0,
301    };
302    /// A vector with all components set to `1.0`.
303    pub const ONE: Self = Self {
304        x: 1.0,
305        y: 1.0,
306        z: 1.0,
307    };
308    /// The unit vector pointing along the positive X-axis.
309    pub const X: Self = Self {
310        x: 1.0,
311        y: 0.0,
312        z: 0.0,
313    };
314    /// The unit vector pointing along the positive Y-axis.
315    pub const Y: Self = Self {
316        x: 0.0,
317        y: 1.0,
318        z: 0.0,
319    };
320    /// The unit vector pointing along the positive Z-axis.
321    pub const Z: Self = Self {
322        x: 0.0,
323        y: 0.0,
324        z: 1.0,
325    };
326    /// The unit vector pointing along the negative X-axis.
327    pub const NEG_X: Self = Self {
328        x: -1.0,
329        y: 0.0,
330        z: 0.0,
331    };
332    /// The unit vector pointing along the negative Y-axis.
333    pub const NEG_Y: Self = Self {
334        x: 0.0,
335        y: -1.0,
336        z: 0.0,
337    };
338    /// The unit vector pointing along the negative Z-axis.
339    pub const NEG_Z: Self = Self {
340        x: 0.0,
341        y: 0.0,
342        z: -1.0,
343    };
344
345    /// Creates a new `Vec3` with the specified components.
346    #[inline]
347    pub const fn new(x: f32, y: f32, z: f32) -> Self {
348        Self { x, y, z }
349    }
350
351    /// Returns a new vector with the absolute value of each component.
352    #[inline]
353    pub const fn abs(self) -> Self {
354        Self {
355            x: if self.x < 0.0 { -self.x } else { self.x },
356            y: if self.y < 0.0 { -self.y } else { self.y },
357            z: if self.z < 0.0 { -self.z } else { self.z },
358        }
359    }
360
361    /// Returns the largest component of the vector (signed).
362    ///
363    /// Useful for major-axis selection in cubemap sampling and bounding-box
364    /// checks. Note that this is not the absolute maximum — for that,
365    /// call `.abs().max_element()`.
366    #[inline]
367    pub fn max_element(self) -> f32 {
368        self.x.max(self.y).max(self.z)
369    }
370
371    /// Returns the smallest component of the vector (signed).
372    #[inline]
373    pub fn min_element(self) -> f32 {
374        self.x.min(self.y).min(self.z)
375    }
376
377    /// Calculates the squared length (magnitude) of the vector.
378    #[inline]
379    pub fn length_squared(&self) -> f32 {
380        self.dot(*self)
381    }
382
383    /// Calculates the length (magnitude) of the vector.
384    #[inline]
385    pub fn length(&self) -> f32 {
386        self.length_squared().sqrt()
387    }
388
389    /// Returns a normalized version of the vector with a length of 1.
390    #[inline]
391    pub fn normalize(&self) -> Self {
392        let len_sq = self.length_squared();
393        if len_sq > EPSILON * EPSILON {
394            // Use squared length to avoid sqrt
395            // Multiply by inverse sqrt for potentially better performance
396            *self * (1.0 / len_sq.sqrt())
397        } else {
398            Self::ZERO
399        }
400    }
401
402    /// Calculates the dot product of this vector and another.
403    #[inline]
404    pub fn dot(&self, other: Self) -> f32 {
405        self.x * other.x + self.y * other.y + self.z * other.z
406    }
407
408    /// Computes the cross product of this vector and another.
409    #[inline]
410    pub fn cross(&self, other: Self) -> Self {
411        Self {
412            x: self.y * other.z - self.z * other.y,
413            y: self.z * other.x - self.x * other.z,
414            z: self.x * other.y - self.y * other.x,
415        }
416    }
417
418    /// Calculates the squared distance between this vector and another.
419    #[inline]
420    pub fn distance_squared(&self, other: Self) -> f32 {
421        let dx = self.x - other.x;
422        let dy = self.y - other.y;
423        let dz = self.z - other.z;
424        dx * dx + dy * dy + dz * dz
425    }
426
427    /// Calculates the distance between this vector and another.
428    #[inline]
429    pub fn distance(&self, other: Self) -> f32 {
430        self.distance_squared(other).sqrt()
431    }
432
433    /// Performs a linear interpolation between two vectors.
434    #[inline]
435    pub fn lerp(start: Self, end: Self, t: f32) -> Self {
436        Self {
437            x: start.x + (end.x - start.x) * t,
438            y: start.y + (end.y - start.y) * t,
439            z: start.z + (end.z - start.z) * t,
440        }
441    }
442
443    /// Retrieves a component of the vector by its index.
444    ///
445    /// # Panics
446    /// Panics if `index` is not 0, 1, or 2.
447    #[inline]
448    pub fn get(&self, index: usize) -> f32 {
449        match index {
450            0 => self.x,
451            1 => self.y,
452            2 => self.z,
453            _ => panic!("Index out of bounds for Vec3"),
454        }
455    }
456}
457
458// --- Operator Overloads ---
459
460impl Default for Vec3 {
461    /// Returns `Vec3::ZERO`.
462    #[inline]
463    fn default() -> Self {
464        Self::ZERO
465    }
466}
467
468impl Add for Vec3 {
469    type Output = Self;
470    /// Adds two vectors component-wise.
471    #[inline]
472    fn add(self, rhs: Self) -> Self::Output {
473        Self {
474            x: self.x + rhs.x,
475            y: self.y + rhs.y,
476            z: self.z + rhs.z,
477        }
478    }
479}
480
481impl Sub for Vec3 {
482    type Output = Self;
483    /// Subtracts two vectors component-wise.
484    #[inline]
485    fn sub(self, rhs: Self) -> Self::Output {
486        Self {
487            x: self.x - rhs.x,
488            y: self.y - rhs.y,
489            z: self.z - rhs.z,
490        }
491    }
492}
493
494impl Mul<f32> for Vec3 {
495    type Output = Self;
496    /// Multiplies the vector by a scalar.
497    #[inline]
498    fn mul(self, rhs: f32) -> Self::Output {
499        Self {
500            x: self.x * rhs,
501            y: self.y * rhs,
502            z: self.z * rhs,
503        }
504    }
505}
506
507impl Mul<Vec3> for f32 {
508    type Output = Vec3;
509    /// Multiplies a scalar by a vector.
510    #[inline]
511    fn mul(self, rhs: Vec3) -> Self::Output {
512        rhs * self
513    }
514}
515
516impl Mul<Vec3> for Vec3 {
517    type Output = Self;
518    /// Multiplies two vectors component-wise.
519    #[inline]
520    fn mul(self, rhs: Self) -> Self::Output {
521        Self {
522            x: self.x * rhs.x,
523            y: self.y * rhs.y,
524            z: self.z * rhs.z,
525        }
526    }
527}
528
529impl Div<f32> for Vec3 {
530    type Output = Self;
531    /// Divides the vector by a scalar.
532    #[inline]
533    fn div(self, rhs: f32) -> Self::Output {
534        let inv_rhs = 1.0 / rhs;
535        Self {
536            x: self.x * inv_rhs,
537            y: self.y * inv_rhs,
538            z: self.z * inv_rhs,
539        }
540    }
541}
542
543impl Neg for Vec3 {
544    type Output = Self;
545    /// Negates the vector.
546    #[inline]
547    fn neg(self) -> Self::Output {
548        Self {
549            x: -self.x,
550            y: -self.y,
551            z: -self.z,
552        }
553    }
554}
555
556impl Index<usize> for Vec3 {
557    type Output = f32;
558    /// Allows accessing a vector component by index.
559    /// # Panics
560    /// Panics if `index` is not 0, 1, or 2.
561    #[inline]
562    fn index(&self, index: usize) -> &Self::Output {
563        match index {
564            0 => &self.x,
565            1 => &self.y,
566            2 => &self.z,
567            _ => panic!("Index out of bounds for Vec3"),
568        }
569    }
570}
571
572impl IndexMut<usize> for Vec3 {
573    /// Allows mutably accessing a vector component by index.
574    /// # Panics
575    /// Panics if `index` is not 0, 1, or 2.
576    #[inline]
577    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
578        match index {
579            0 => &mut self.x,
580            1 => &mut self.y,
581            2 => &mut self.z,
582            _ => panic!("Index out of bounds for Vec3"),
583        }
584    }
585}
586
587// --- Vector4D ---
588
589/// A 4-dimensional vector with `f32` components, often used for homogeneous coordinates.
590///
591/// In 3D graphics, `Vec4` is primarily used to represent points (`w`=1.0) and
592/// vectors (`w`=0.0) in homogeneous space, allowing them to be transformed by a `Mat4`.
593#[derive(
594    Debug,
595    Default,
596    Copy,
597    Clone,
598    PartialEq,
599    bytemuck::Pod,
600    bytemuck::Zeroable,
601    Serialize,
602    Deserialize,
603    Encode,
604    Decode,
605)]
606#[repr(C)]
607pub struct Vec4 {
608    /// The x component of the vector.
609    pub x: f32,
610    /// The y component of the vector.
611    pub y: f32,
612    /// The z component of the vector.
613    pub z: f32,
614    /// The w component, used for homogeneous coordinates.
615    pub w: f32,
616}
617
618impl Vec4 {
619    /// A vector with all components set to `0.0`.
620    pub const ZERO: Self = Self {
621        x: 0.0,
622        y: 0.0,
623        z: 0.0,
624        w: 0.0,
625    };
626    /// A vector with all components set to `1.0`.
627    pub const ONE: Self = Self {
628        x: 1.0,
629        y: 1.0,
630        z: 1.0,
631        w: 1.0,
632    };
633    /// The unit vector pointing along the positive X-axis.
634    pub const X: Self = Self {
635        x: 1.0,
636        y: 0.0,
637        z: 0.0,
638        w: 0.0,
639    };
640    /// The unit vector pointing along the positive Y-axis.
641    pub const Y: Self = Self {
642        x: 0.0,
643        y: 1.0,
644        z: 0.0,
645        w: 0.0,
646    };
647    /// The unit vector pointing along the positive Z-axis.
648    pub const Z: Self = Self {
649        x: 0.0,
650        y: 0.0,
651        z: 1.0,
652        w: 0.0,
653    };
654    /// The unit vector pointing along the positive W-axis.
655    pub const W: Self = Self {
656        x: 0.0,
657        y: 0.0,
658        z: 0.0,
659        w: 1.0,
660    };
661
662    /// Creates a new `Vec4` with the specified components.
663    #[inline]
664    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
665        Self { x, y, z, w }
666    }
667
668    /// Returns a new vector with the absolute value of each component.
669    #[inline]
670    pub const fn abs(self) -> Self {
671        Self {
672            x: if self.x < 0.0 { -self.x } else { self.x },
673            y: if self.y < 0.0 { -self.y } else { self.y },
674            z: if self.z < 0.0 { -self.z } else { self.z },
675            w: if self.w < 0.0 { -self.w } else { self.w },
676        }
677    }
678
679    /// Creates a `Vec4` from a `Vec3` and a `w` component.
680    #[inline]
681    pub fn from_vec3(v: Vec3, w: f32) -> Self {
682        Self::new(v.x, v.y, v.z, w)
683    }
684
685    /// Returns the `[x, y, z]` components of the vector as a `Vec3`, discarding `w`.
686    #[inline]
687    pub fn truncate(&self) -> Vec3 {
688        Vec3::new(self.x, self.y, self.z)
689    }
690
691    /// Calculates the dot product of this vector and another.
692    #[inline]
693    pub fn dot(&self, other: Self) -> f32 {
694        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
695    }
696
697    /// Retrieves a component of the vector by its index.
698    ///
699    /// # Panics
700    /// Panics if `index` is not between 0 and 3.
701    #[inline]
702    pub fn get(&self, index: usize) -> f32 {
703        match index {
704            0 => self.x,
705            1 => self.y,
706            2 => self.z,
707            3 => self.w,
708            _ => panic!("Index out of bounds for Vec4"),
709        }
710    }
711
712    /// Converts the vector to a `[f32; 4]` array.
713    #[inline]
714    pub fn to_array(self) -> [f32; 4] {
715        [self.x, self.y, self.z, self.w]
716    }
717}
718
719impl From<Vec4> for [f32; 4] {
720    #[inline]
721    fn from(v: Vec4) -> Self {
722        [v.x, v.y, v.z, v.w]
723    }
724}
725
726impl From<[f32; 4]> for Vec4 {
727    #[inline]
728    fn from(v: [f32; 4]) -> Self {
729        Self::new(v[0], v[1], v[2], v[3])
730    }
731}
732
733// --- Operator Overloads ---
734
735impl Add for Vec4 {
736    type Output = Self;
737    /// Adds two vectors component-wise.
738    #[inline]
739    fn add(self, rhs: Self) -> Self::Output {
740        Self {
741            x: self.x + rhs.x,
742            y: self.y + rhs.y,
743            z: self.z + rhs.z,
744            w: self.w + rhs.w,
745        }
746    }
747}
748
749impl Sub for Vec4 {
750    type Output = Self;
751    /// Subtracts two vectors component-wise.
752    #[inline]
753    fn sub(self, rhs: Self) -> Self::Output {
754        Self {
755            x: self.x - rhs.x,
756            y: self.y - rhs.y,
757            z: self.z - rhs.z,
758            w: self.w - rhs.w,
759        }
760    }
761}
762
763impl Mul<f32> for Vec4 {
764    type Output = Self;
765    /// Multiplies the vector by a scalar.
766    #[inline]
767    fn mul(self, rhs: f32) -> Self::Output {
768        Self {
769            x: self.x * rhs,
770            y: self.y * rhs,
771            z: self.z * rhs,
772            w: self.w * rhs,
773        }
774    }
775}
776
777impl Mul<Vec4> for f32 {
778    type Output = Vec4;
779    /// Multiplies a scalar by a vector.
780    #[inline]
781    fn mul(self, rhs: Vec4) -> Self::Output {
782        rhs * self
783    }
784}
785
786impl Mul<Vec4> for Vec4 {
787    type Output = Self;
788    /// Multiplies two vectors component-wise.
789    #[inline]
790    fn mul(self, rhs: Self) -> Self::Output {
791        Self {
792            x: self.x * rhs.x,
793            y: self.y * rhs.y,
794            z: self.z * rhs.z,
795            w: self.w * rhs.w,
796        }
797    }
798}
799
800impl Div<f32> for Vec4 {
801    type Output = Self;
802    /// Divides the vector by a scalar.
803    #[inline]
804    fn div(self, rhs: f32) -> Self::Output {
805        let inv_rhs = 1.0 / rhs;
806        Self {
807            x: self.x * inv_rhs,
808            y: self.y * inv_rhs,
809            z: self.z * inv_rhs,
810            w: self.w * inv_rhs,
811        }
812    }
813}
814
815impl Neg for Vec4 {
816    type Output = Self;
817    /// Negates the vector.
818    #[inline]
819    fn neg(self) -> Self::Output {
820        Self {
821            x: -self.x,
822            y: -self.y,
823            z: -self.z,
824            w: -self.w,
825        }
826    }
827}
828
829impl Index<usize> for Vec4 {
830    type Output = f32;
831    /// Allows accessing a vector component by index.
832    /// # Panics
833    /// Panics if `index` is not between 0 and 3.
834    #[inline]
835    fn index(&self, index: usize) -> &Self::Output {
836        match index {
837            0 => &self.x,
838            1 => &self.y,
839            2 => &self.z,
840            3 => &self.w,
841            _ => panic!("Index out of bounds for Vec4"),
842        }
843    }
844}
845
846impl IndexMut<usize> for Vec4 {
847    /// Allows mutably accessing a vector component by index.
848    /// # Panics
849    /// Panics if `index` is not between 0 and 3.
850    #[inline]
851    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
852        match index {
853            0 => &mut self.x,
854            1 => &mut self.y,
855            2 => &mut self.z,
856            3 => &mut self.w,
857            _ => panic!("Index out of bounds for Vec4"),
858        }
859    }
860}
861
862/// --- Tests ---
863#[cfg(test)]
864mod tests {
865    use super::*; // Import Vec3 from the parent module
866    use crate::math::approx_eq;
867
868    fn vec2_approx_eq(a: Vec2, b: Vec2) -> bool {
869        approx_eq(a.x, b.x) && approx_eq(a.y, b.y)
870    }
871
872    fn vec3_approx_eq(a: Vec3, b: Vec3) -> bool {
873        approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z)
874    }
875
876    // Test Vec2
877
878    #[test]
879    fn test_vec2_new() {
880        let v = Vec2::new(1.0, 2.0);
881        assert_eq!(v.x, 1.0);
882        assert_eq!(v.y, 2.0);
883    }
884
885    #[test]
886    fn test_vec2_abs() {
887        let v = Vec2::new(-1.0, 2.0);
888        assert_eq!(v.abs(), Vec2::new(1.0, 2.0));
889    }
890
891    #[test]
892    fn test_vec2_constants() {
893        assert_eq!(Vec2::ZERO, Vec2::new(0.0, 0.0));
894        assert_eq!(Vec2::ONE, Vec2::new(1.0, 1.0));
895        assert_eq!(Vec2::X, Vec2::new(1.0, 0.0));
896        assert_eq!(Vec2::Y, Vec2::new(0.0, 1.0));
897    }
898
899    #[test]
900    fn test_vec2_ops() {
901        let v1 = Vec2::new(1.0, 2.0);
902        let v2 = Vec2::new(3.0, 4.0);
903        assert_eq!(v1 + v2, Vec2::new(4.0, 6.0));
904        assert_eq!(v2 - v1, Vec2::new(2.0, 2.0));
905        assert_eq!(v1 * 2.0, Vec2::new(2.0, 4.0));
906        assert_eq!(3.0 * v1, Vec2::new(3.0, 6.0));
907        assert_eq!(v1 * v2, Vec2::new(3.0, 8.0)); // Component-wise
908        assert_eq!(-v1, Vec2::new(-1.0, -2.0));
909        assert!(vec2_approx_eq(
910            Vec2::new(4.0, 6.0) / 2.0,
911            Vec2::new(2.0, 3.0)
912        ));
913    }
914
915    #[test]
916    fn test_vec2_dot() {
917        let v1 = Vec2::new(1.0, 2.0);
918        let v2 = Vec2::new(3.0, 4.0);
919        assert!(approx_eq(v1.dot(v2), 1.0 * 3.0 + 2.0 * 4.0)); // 3 + 8 = 11
920    }
921
922    #[test]
923    fn test_vec2_length() {
924        let v = Vec2::new(3.0, 4.0);
925        assert!(approx_eq(v.length_squared(), 25.0));
926        assert!(approx_eq(v.length(), 5.0));
927        assert!(approx_eq(Vec2::ZERO.length(), 0.0));
928    }
929
930    #[test]
931    fn test_vec2_normalize() {
932        let v1 = Vec2::new(3.0, 0.0);
933        let norm_v1 = v1.normalize();
934        assert!(vec2_approx_eq(norm_v1, Vec2::X));
935        assert!(approx_eq(norm_v1.length(), 1.0));
936
937        let v_zero = Vec2::ZERO;
938        assert_eq!(v_zero.normalize(), Vec2::ZERO);
939    }
940
941    #[test]
942    fn test_vec2_lerp() {
943        let start = Vec2::new(0.0, 10.0);
944        let end = Vec2::new(10.0, 0.0);
945        assert!(vec2_approx_eq(Vec2::lerp(start, end, 0.0), start));
946        assert!(vec2_approx_eq(Vec2::lerp(start, end, 1.0), end));
947        assert!(vec2_approx_eq(
948            Vec2::lerp(start, end, 0.5),
949            Vec2::new(5.0, 5.0)
950        ));
951        // Test clamping
952        assert!(vec2_approx_eq(Vec2::lerp(start, end, -0.5), start));
953        assert!(vec2_approx_eq(Vec2::lerp(start, end, 1.5), end));
954    }
955
956    #[test]
957    fn test_vec2_index() {
958        let mut v = Vec2::new(5.0, 6.0);
959        assert_eq!(v[0], 5.0);
960        assert_eq!(v[1], 6.0);
961        v[0] = 10.0;
962        assert_eq!(v.x, 10.0);
963    }
964
965    #[test]
966    #[should_panic]
967    fn test_vec2_index_out_of_bounds() {
968        let v = Vec2::new(1.0, 2.0);
969        let _ = v[2]; // Should panic
970    }
971
972    // Test Vec3
973
974    #[test]
975    fn test_new() {
976        let v = Vec3::new(1.0, 2.0, 3.0);
977        assert_eq!(v.x, 1.0);
978        assert_eq!(v.y, 2.0);
979        assert_eq!(v.z, 3.0);
980    }
981
982    #[test]
983    fn test_vec3_abs() {
984        let v = Vec3::new(-1.0, 2.0, -3.0);
985        assert_eq!(v.abs(), Vec3::new(1.0, 2.0, 3.0));
986        assert_eq!(Vec3::ZERO.abs(), Vec3::ZERO);
987    }
988
989    #[test]
990    fn test_constants() {
991        assert_eq!(Vec3::ZERO, Vec3::new(0.0, 0.0, 0.0));
992        assert_eq!(Vec3::ONE, Vec3::new(1.0, 1.0, 1.0));
993        assert_eq!(Vec3::X, Vec3::new(1.0, 0.0, 0.0));
994        assert_eq!(Vec3::Y, Vec3::new(0.0, 1.0, 0.0));
995        assert_eq!(Vec3::Z, Vec3::new(0.0, 0.0, 1.0));
996    }
997
998    #[test]
999    fn test_add() {
1000        let v1 = Vec3::new(1.0, 2.0, 3.0);
1001        let v2 = Vec3::new(4.0, 5.0, 6.0);
1002        assert_eq!(v1 + v2, Vec3::new(5.0, 7.0, 9.0));
1003    }
1004
1005    #[test]
1006    fn test_sub() {
1007        let v1 = Vec3::new(5.0, 7.0, 9.0);
1008        let v2 = Vec3::new(1.0, 2.0, 3.0);
1009        assert_eq!(v1 - v2, Vec3::new(4.0, 5.0, 6.0));
1010    }
1011
1012    #[test]
1013    fn test_scalar_mul() {
1014        let v = Vec3::new(1.0, 2.0, 3.0);
1015        assert_eq!(v * 2.0, Vec3::new(2.0, 4.0, 6.0));
1016        assert_eq!(3.0 * v, Vec3::new(3.0, 6.0, 9.0)); // Test f32 * Vec3
1017    }
1018
1019    #[test]
1020    fn test_component_mul() {
1021        let v1 = Vec3::new(1.0, 2.0, 3.0);
1022        let v2 = Vec3::new(4.0, 5.0, 6.0);
1023        assert_eq!(v1 * v2, Vec3::new(4.0, 10.0, 18.0));
1024    }
1025
1026    #[test]
1027    fn test_scalar_div() {
1028        let v = Vec3::new(2.0, 4.0, 6.0);
1029        assert_eq!(v / 2.0, Vec3::new(1.0, 2.0, 3.0));
1030    }
1031
1032    #[test]
1033    fn test_neg() {
1034        let v = Vec3::new(1.0, -2.0, 3.0);
1035        assert_eq!(-v, Vec3::new(-1.0, 2.0, -3.0));
1036    }
1037
1038    #[test]
1039    fn test_length() {
1040        let v1 = Vec3::new(3.0, 4.0, 0.0);
1041        assert!(approx_eq(v1.length_squared(), 25.0));
1042        assert!(approx_eq(v1.length(), 5.0));
1043
1044        let v2 = Vec3::ZERO;
1045        assert!(approx_eq(v2.length_squared(), 0.0));
1046        assert!(approx_eq(v2.length(), 0.0));
1047    }
1048
1049    #[test]
1050    fn test_dot() {
1051        let v1 = Vec3::new(1.0, 2.0, 3.0);
1052        let v2 = Vec3::new(4.0, -5.0, 6.0);
1053        // 1*4 + 2*(-5) + 3*6 = 4 - 10 + 18 = 12
1054        assert!(approx_eq(v1.dot(v2), 12.0));
1055
1056        // Orthogonal vectors
1057        assert!(approx_eq(Vec3::X.dot(Vec3::Y), 0.0));
1058    }
1059
1060    #[test]
1061    fn test_distance() {
1062        let v1 = Vec3::new(1.0, 2.0, 3.0);
1063        let v2 = Vec3::new(4.0, 5.0, 6.0);
1064        // Distance = sqrt((4-1)^2 + (5-2)^2 + (6-3)^2) = sqrt(9 + 9 + 9) = sqrt(27) = 3*sqrt(3)
1065        assert!(approx_eq(v1.distance(v2), 3.0 * (3.0_f32).sqrt()));
1066    }
1067
1068    #[test]
1069    fn test_cross() {
1070        // Standard basis vectors
1071        assert_eq!(Vec3::X.cross(Vec3::Y), Vec3::Z);
1072        assert_eq!(Vec3::Y.cross(Vec3::Z), Vec3::X);
1073        assert_eq!(Vec3::Z.cross(Vec3::X), Vec3::Y);
1074
1075        // Anti-commutative property
1076        assert_eq!(Vec3::Y.cross(Vec3::X), -Vec3::Z);
1077
1078        // Parallel vectors
1079        assert_eq!(Vec3::X.cross(Vec3::X), Vec3::ZERO);
1080    }
1081
1082    #[test]
1083    fn test_normalize() {
1084        let v1 = Vec3::new(3.0, 0.0, 0.0);
1085        let norm_v1 = v1.normalize();
1086        assert!(vec3_approx_eq(norm_v1, Vec3::X));
1087        assert!(approx_eq(norm_v1.length(), 1.0));
1088
1089        let v2 = Vec3::new(1.0, 1.0, 1.0);
1090        let norm_v2 = v2.normalize();
1091        assert!(approx_eq(norm_v2.length(), 1.0)); // Check length is 1
1092
1093        // Test normalizing zero vector
1094        let v_zero = Vec3::ZERO;
1095        assert_eq!(v_zero.normalize(), Vec3::ZERO);
1096    }
1097
1098    #[test]
1099    fn test_lerp() {
1100        let start = Vec3::new(0.0, 0.0, 0.0);
1101        let end = Vec3::new(10.0, 10.0, 10.0);
1102
1103        assert!(vec3_approx_eq(Vec3::lerp(start, end, 0.0), start));
1104        assert!(vec3_approx_eq(Vec3::lerp(start, end, 1.0), end));
1105        assert!(vec3_approx_eq(
1106            Vec3::lerp(start, end, 0.5),
1107            Vec3::new(5.0, 5.0, 5.0)
1108        ));
1109    }
1110
1111    // Test Vec4
1112
1113    #[test]
1114    fn test_vec4_new() {
1115        let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
1116        assert_eq!(v.x, 1.0);
1117        assert_eq!(v.y, 2.0);
1118        assert_eq!(v.z, 3.0);
1119        assert_eq!(v.w, 4.0);
1120    }
1121
1122    #[test]
1123    fn test_vec4_abs() {
1124        let v = Vec4::new(-1.0, 2.0, -3.0, -0.5);
1125        assert_eq!(v.abs(), Vec4::new(1.0, 2.0, 3.0, 0.5));
1126    }
1127
1128    #[test]
1129    fn test_vec4_from_vec3() {
1130        let v3 = Vec3::new(1.0, 2.0, 3.0);
1131        let v4 = Vec4::from_vec3(v3, 4.0);
1132        assert_eq!(v4, Vec4::new(1.0, 2.0, 3.0, 4.0));
1133    }
1134
1135    #[test]
1136    fn test_vec4_truncate() {
1137        let v4 = Vec4::new(1.0, 2.0, 3.0, 4.0);
1138        let v3 = v4.truncate();
1139        assert_eq!(v3, Vec3::new(1.0, 2.0, 3.0));
1140    }
1141
1142    #[test]
1143    fn vec3_neg_constants_have_unit_length() {
1144        assert!((Vec3::NEG_X.length() - 1.0).abs() < EPSILON);
1145        assert!((Vec3::NEG_Y.length() - 1.0).abs() < EPSILON);
1146        assert!((Vec3::NEG_Z.length() - 1.0).abs() < EPSILON);
1147    }
1148
1149    #[test]
1150    fn vec3_neg_constants_oppose_positive_axes() {
1151        assert_eq!(Vec3::NEG_X, -Vec3::X);
1152        assert_eq!(Vec3::NEG_Y, -Vec3::Y);
1153        assert_eq!(Vec3::NEG_Z, -Vec3::Z);
1154        assert_eq!(Vec3::NEG_X.dot(Vec3::X), -1.0);
1155        assert_eq!(Vec3::NEG_Y.dot(Vec3::Y), -1.0);
1156        assert_eq!(Vec3::NEG_Z.dot(Vec3::Z), -1.0);
1157    }
1158
1159    #[test]
1160    fn vec3_max_element_picks_largest_signed() {
1161        assert_eq!(Vec3::new(1.0, 2.0, 3.0).max_element(), 3.0);
1162        assert_eq!(Vec3::new(-1.0, -2.0, -3.0).max_element(), -1.0);
1163        assert_eq!(Vec3::new(5.0, -10.0, 0.0).max_element(), 5.0);
1164        assert_eq!(Vec3::ZERO.max_element(), 0.0);
1165    }
1166
1167    #[test]
1168    fn vec3_min_element_picks_smallest_signed() {
1169        assert_eq!(Vec3::new(1.0, 2.0, 3.0).min_element(), 1.0);
1170        assert_eq!(Vec3::new(-1.0, -2.0, -3.0).min_element(), -3.0);
1171        assert_eq!(Vec3::new(5.0, -10.0, 0.0).min_element(), -10.0);
1172        assert_eq!(Vec3::ZERO.min_element(), 0.0);
1173    }
1174}