mini_math/
vector.rs

1use crate::NearlyEqual;
2
3/// A vector in 2D space.
4#[derive(Copy, Clone, Debug, PartialEq)]
5#[repr(C)]
6pub struct Vector2 {
7    pub x: f32,
8    pub y: f32,
9}
10
11/// A vector in 3D space.
12#[derive(Copy, Clone, Debug, PartialEq)]
13#[repr(C)]
14pub struct Vector3 {
15    pub x: f32,
16    pub y: f32,
17    pub z: f32,
18}
19
20/// A point in 3D space.
21#[derive(Copy, Clone, Debug, PartialEq)]
22#[repr(C)]
23pub struct Point {
24    pub x: f32,
25    pub y: f32,
26    pub z: f32,
27}
28
29/// A homogeneous vector in 3D space.
30#[derive(Copy, Clone, Debug, PartialEq)]
31#[repr(C)]
32pub struct Vector4 {
33    pub x: f32,
34    pub y: f32,
35    pub z: f32,
36    pub w: f32,
37}
38
39macro_rules! implement_operator {
40    // Binary operator
41    (impl $Op:ident<$S:ident> for $T:ident {
42        fn $op:ident($x:ident, $s:ident) -> $Output:ty $body:block
43    }) => {
44        impl std::ops::$Op<$S> for $T {
45            type Output = $Output;
46
47            fn $op($x, $s: $S) -> Self::Output $body
48        }
49    };
50    // Binary assignment operator
51    (impl $Op:ident<$S:ident> for $T:ident {
52        fn $op:ident(&mut $x:ident, $s:ident) $body:block
53    }) => {
54        impl std::ops::$Op<$S> for $T {
55            fn $op(&mut $x, $s: $S) $body
56        }
57    };
58}
59
60macro_rules! implement_vector {
61    ($VectorT:ident { $($field:ident),+ }) => {
62        impl $VectorT {
63            /// Construct new a vector from individual coordinates
64            pub const fn new($($field: f32),+) -> Self {
65                Self { $($field),+ }
66            }
67
68            /// Construct new a vector where each coordinate is the same
69            pub const fn from_scalar(s: f32) -> Self {
70                Self { $($field: s),+ }
71            }
72
73            /// The additive identity
74            pub const fn zero() -> Self {
75                Self { $($field: 0.0),+ }
76            }
77
78            /// The multiplicative identity
79            pub const fn one() -> Self {
80                Self { $($field: 1.0),+ }
81            }
82
83            /// Compute the dot product between this vector and another
84            pub fn dot(&self, rhs: Self) -> f32 {
85                [$(self.$field * rhs.$field),+].iter().sum()
86            }
87
88            /// Linear interpolation between this vector and another
89            pub fn lerp(&self, rhs: Self, factor: f32) -> Self {
90                let t = factor.min(1.0).max(0.0);
91                Self::new($(self.$field * (1.0 - t) + rhs.$field * t),+)
92            }
93
94            /// Compute the element-wise minimum of this vector and another
95            pub fn min(&self, rhs: Self) -> Self {
96                Self::new($(self.$field.min(rhs.$field)),+)
97            }
98
99            /// Compute the element-wise maximum of this vector and another
100            pub fn max(&self, rhs: Self) -> Self {
101                Self::new($(self.$field.max(rhs.$field)),+)
102            }
103
104            /// The length of this vector squared. Note that this avoids an expensive square root.
105            pub fn magnitude_squared(&self) -> f32 {
106                self.dot(*self)
107            }
108
109            /// The length of this vector. Note that this involves an expensive square root.
110            pub fn magnitude(&self) -> f32 {
111                self.magnitude_squared().sqrt()
112            }
113
114            /// Normalize this vector to unit length. Note that this involves an expensive square root.
115            pub fn normalized(&self) -> Self {
116                let d = self.magnitude();
117                if d > 0.0 {
118                    let d = 1.0 / d;
119                    *self * d
120                } else {
121                    *self
122                }
123            }
124
125            pub fn as_slice(&self) -> &[f32] {
126                unsafe { std::slice::from_raw_parts(&self.x, std::mem::size_of::<Self>() / std::mem::size_of::<f32>()) }
127            }
128        }
129
130        impl std::ops::Neg for $VectorT {
131            type Output = $VectorT;
132            fn neg(self) -> $VectorT { $VectorT::new($(-self.$field),+) }
133        }
134
135        implement_operator!(impl Add<f32> for $VectorT {
136            fn add(self, t) -> $VectorT { $VectorT::new($(self.$field + t),+) }
137        });
138        implement_operator!(impl Sub<f32> for $VectorT {
139            fn sub(self, t) -> $VectorT { $VectorT::new($(self.$field - t),+) }
140        });
141        implement_operator!(impl Mul<f32> for $VectorT {
142            fn mul(self, t) -> $VectorT { $VectorT::new($(self.$field * t),+) }
143        });
144        implement_operator!(impl Div<f32> for $VectorT {
145            fn div(self, t) -> $VectorT { $VectorT::new($(self.$field / t),+) }
146        });
147
148        implement_operator!(impl AddAssign<f32> for $VectorT {
149            fn add_assign(&mut self, t) { $(self.$field += t);+ }
150        });
151        implement_operator!(impl SubAssign<f32> for $VectorT {
152            fn sub_assign(&mut self, t) { $(self.$field -= t);+ }
153        });
154        implement_operator!(impl MulAssign<f32> for $VectorT {
155            fn mul_assign(&mut self, t) { $(self.$field *= t);+ }
156        });
157        implement_operator!(impl DivAssign<f32> for $VectorT {
158            fn div_assign(&mut self, t) { $(self.$field /= t);+ }
159        });
160
161        implement_operator!(impl Mul<$VectorT> for f32 {
162            fn mul(self, t) -> $VectorT { $VectorT::new($(self * t.$field),+) }
163        });
164        implement_operator!(impl Div<$VectorT> for f32 {
165            fn div(self, t) -> $VectorT { $VectorT::new($(self / t.$field),+) }
166        });
167
168        impl std::ops::Index<usize> for $VectorT {
169            type Output = f32;
170            fn index(&self, i: usize) -> &f32 {
171                [$(&self.$field),+][i]
172            }
173        }
174
175        impl std::ops::IndexMut<usize> for $VectorT {
176            fn index_mut(&mut self, i: usize) -> &mut f32 {
177                [$(&mut self.$field),+][i]
178            }
179        }
180
181        impl NearlyEqual for &$VectorT {
182            fn nearly_equals(self, rhs: Self) -> bool {
183                $(self.$field.nearly_equals(rhs.$field))&&+
184            }
185        }
186    }
187}
188
189implement_vector!(Vector2 { x, y });
190implement_vector!(Vector3 { x, y, z });
191implement_vector!(Point { x, y, z });
192implement_vector!(Vector4 { x, y, z, w });
193
194impl Vector2 {
195    /// Compute a cross product between this vector and another.
196    /// This treats both inputs as 3D vectors with a z-component of zero,
197    /// performs the normal 3D cross product, and returns only the resulting z-component.
198    pub fn cross(&self, rhs: Self) -> f32 {
199        self.x * rhs.y - self.y * rhs.x
200    }
201}
202
203impl Vector3 {
204    /// Compute the cross product between this vector and another.
205    pub fn cross(&self, rhs: Self) -> Self {
206        Self {
207            x: self.y * rhs.z - self.z * rhs.y,
208            y: self.z * rhs.x - self.x * rhs.z,
209            z: self.x * rhs.y - self.y * rhs.x,
210        }
211    }
212}
213
214impl From<Point> for Vector3 {
215    /// Convert a point into a vector
216    fn from(p: Point) -> Self {
217        Vector3 {
218            x: p.x,
219            y: p.y,
220            z: p.z,
221        }
222    }
223}
224
225impl From<Vector4> for Vector3 {
226    /// Convert a point into a vector
227    fn from(v: Vector4) -> Self {
228        Vector3 {
229            x: v.x,
230            y: v.y,
231            z: v.z,
232        }
233    }
234}
235
236impl From<Vector3> for Point {
237    /// Convert a vector into a point
238    fn from(v: Vector3) -> Self {
239        Point {
240            x: v.x,
241            y: v.y,
242            z: v.z,
243        }
244    }
245}
246
247impl From<Vector4> for Point {
248    /// Convert a vector into a point
249    fn from(v: Vector4) -> Self {
250        Point {
251            x: v.x,
252            y: v.y,
253            z: v.z,
254        }
255    }
256}
257
258impl From<Vector3> for Vector4 {
259    /// Convert a point into a vector
260    fn from(v: Vector3) -> Self {
261        Vector4 {
262            x: v.x,
263            y: v.y,
264            z: v.z,
265            w: 0.0,
266        }
267    }
268}
269
270impl From<Point> for Vector4 {
271    /// Convert a point into a vector
272    fn from(p: Point) -> Self {
273        Vector4 {
274            x: p.x,
275            y: p.y,
276            z: p.z,
277            w: 1.0,
278        }
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use crate::*;
285
286    #[test]
287    fn products() {
288        let a = Vector3::new(3.0, -5.0, 4.0);
289        let b = Vector3::new(2.0, 6.0, 5.0);
290
291        assert!(a.dot(b).nearly_equals(-4.0));
292        assert_eq!(a.cross(b), Vector3::new(-49.0, -7.0, 28.0));
293    }
294
295    #[test]
296    fn lerp() {
297        let a = Vector3::new(1.0, 0.0, 0.0);
298        let b = Vector3::new(0.0, 1.0, 0.0);
299
300        assert_eq!(a.lerp(b, 0.75), Vector3::new(0.25, 0.75, 0.0));
301    }
302
303    #[test]
304    fn slice() {
305        let a = Vector3::new(1.0, 2.0, 3.0);
306
307        assert_eq!(a.as_slice(), &[1.0, 2.0, 3.0]);
308    }
309}