Skip to main content

use_vector/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Const-generic vector primitives and operations for `RustUse`.
5
6use core::num::FpCategory;
7use core::ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub};
8
9/// A vector with `N` plain `f64` components.
10///
11/// # Examples
12///
13/// ```
14/// use use_vector::Vector;
15///
16/// let vector = Vector::<3>::from_array([2.0, 3.0, 6.0]);
17///
18/// assert_eq!(vector.dimension(), 3);
19/// assert_eq!(vector.magnitude(), 7.0);
20/// ```
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct Vector<const N: usize> {
23    components: [f64; N],
24}
25
26/// A two-dimensional vector.
27pub type Vector2 = Vector<2>;
28
29/// A three-dimensional vector.
30pub type Vector3 = Vector<3>;
31
32/// A four-dimensional vector.
33pub type Vector4 = Vector<4>;
34
35/// Two-dimensional vector primitives and operations.
36pub mod vector2 {
37    pub use crate::Vector2;
38}
39
40/// Three-dimensional vector primitives and operations.
41pub mod vector3 {
42    pub use crate::Vector3;
43}
44
45/// Four-dimensional vector primitives and operations.
46pub mod vector4 {
47    pub use crate::Vector4;
48}
49
50impl<const N: usize> Vector<N> {
51    /// The zero vector.
52    pub const ZERO: Self = Self {
53        components: [0.0; N],
54    };
55
56    /// A vector with every component set to `1.0`.
57    pub const ONE: Self = Self {
58        components: [1.0; N],
59    };
60
61    /// Creates a vector from its component array.
62    #[must_use]
63    pub const fn from_array(components: [f64; N]) -> Self {
64        Self { components }
65    }
66
67    /// Returns a shared reference to the component array.
68    #[must_use]
69    pub const fn as_array(&self) -> &[f64; N] {
70        &self.components
71    }
72
73    /// Returns the component array.
74    #[must_use]
75    pub const fn into_array(self) -> [f64; N] {
76        self.components
77    }
78
79    /// Returns the vector dimension.
80    #[must_use]
81    pub const fn dimension(self) -> usize {
82        self.components.len()
83    }
84
85    /// Returns the dot product with `other`.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use use_vector::Vector;
91    ///
92    /// let a = Vector::<3>::from_array([1.0, 2.0, 3.0]);
93    /// let b = Vector::<3>::from_array([4.0, 5.0, 6.0]);
94    ///
95    /// assert_eq!(a.dot(b), 32.0);
96    /// ```
97    #[must_use]
98    pub fn dot(self, other: Self) -> f64 {
99        self.components
100            .into_iter()
101            .zip(other.components)
102            .fold(0.0, |sum, (left, right)| left.mul_add(right, sum))
103    }
104
105    /// Returns the squared Euclidean magnitude.
106    #[must_use]
107    pub fn magnitude_squared(self) -> f64 {
108        self.dot(self)
109    }
110
111    /// Returns the Euclidean magnitude.
112    #[must_use]
113    pub fn magnitude(self) -> f64 {
114        self.magnitude_squared().sqrt()
115    }
116
117    /// Returns a normalized vector when the magnitude is finite and non-zero.
118    ///
119    /// Returns `None` for zero vectors or vectors with non-finite magnitude.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use use_vector::Vector2;
125    ///
126    /// let unit = Vector2::new(3.0, 4.0)
127    ///     .normalize()
128    ///     .expect("non-zero finite vector should normalize");
129    ///
130    /// assert!((unit.x() - 0.6).abs() < 1.0e-12);
131    /// assert!((unit.y() - 0.8).abs() < 1.0e-12);
132    /// ```
133    #[must_use]
134    pub fn normalize(self) -> Option<Self> {
135        let magnitude = self.magnitude();
136
137        if !magnitude.is_finite() || matches!(magnitude.classify(), FpCategory::Zero) {
138            return None;
139        }
140
141        Some(self / magnitude)
142    }
143
144    /// Returns the vector scaled by `scalar`.
145    #[must_use]
146    pub fn scale(self, scalar: f64) -> Self {
147        self * scalar
148    }
149
150    /// Returns the Euclidean distance to `other`.
151    ///
152    /// # Examples
153    ///
154    /// ```
155    /// use use_vector::Vector2;
156    ///
157    /// let start = Vector2::ZERO;
158    /// let end = Vector2::new(3.0, 4.0);
159    ///
160    /// assert_eq!(start.distance(end), 5.0);
161    /// ```
162    #[must_use]
163    pub fn distance(self, other: Self) -> f64 {
164        (other - self).magnitude()
165    }
166
167    /// Returns the squared Euclidean distance to `other`.
168    #[must_use]
169    pub fn distance_squared(self, other: Self) -> f64 {
170        (other - self).magnitude_squared()
171    }
172
173    /// Returns the linear interpolation between `self` and `other` for `t`.
174    #[must_use]
175    pub fn lerp(self, other: Self, t: f64) -> Self {
176        self + (other - self) * t
177    }
178
179    /// Maps each component with `mapper`.
180    #[must_use]
181    pub fn map_components(self, mut mapper: impl FnMut(f64) -> f64) -> Self {
182        let mut components = self.components;
183
184        for component in &mut components {
185            *component = mapper(*component);
186        }
187
188        Self::from_array(components)
189    }
190
191    /// Combines components from `self` and `other` with `mapper`.
192    #[must_use]
193    pub fn zip_components(self, other: Self, mut mapper: impl FnMut(f64, f64) -> f64) -> Self {
194        let mut components = [0.0; N];
195
196        for (index, component) in components.iter_mut().enumerate() {
197            *component = mapper(self.components[index], other.components[index]);
198        }
199
200        Self::from_array(components)
201    }
202
203    /// Returns the component-wise minimum with `other`.
204    #[must_use]
205    pub fn component_min(self, other: Self) -> Self {
206        self.zip_components(other, f64::min)
207    }
208
209    /// Returns the component-wise maximum with `other`.
210    #[must_use]
211    pub fn component_max(self, other: Self) -> Self {
212        self.zip_components(other, f64::max)
213    }
214
215    /// Returns each component bounded by the matching `minimum` and `maximum` components.
216    #[must_use]
217    pub fn clamp_components(self, minimum: Self, maximum: Self) -> Self {
218        self.zip_components(minimum, f64::max)
219            .zip_components(maximum, f64::min)
220    }
221
222    /// Returns a vector with the absolute value of each component.
223    #[must_use]
224    pub fn abs(self) -> Self {
225        self.map_components(f64::abs)
226    }
227
228    /// Returns `true` when every component is finite.
229    #[must_use]
230    pub fn is_finite(self) -> bool {
231        self.components.into_iter().all(f64::is_finite)
232    }
233
234    /// Returns `true` when any component is `NaN`.
235    #[must_use]
236    pub fn is_nan(self) -> bool {
237        self.components.into_iter().any(f64::is_nan)
238    }
239}
240
241impl Vector<2> {
242    /// Creates a two-dimensional vector from its components.
243    #[must_use]
244    pub const fn new(x: f64, y: f64) -> Self {
245        Self::from_array([x, y])
246    }
247
248    /// Returns the x component.
249    #[must_use]
250    pub const fn x(self) -> f64 {
251        self.components[0]
252    }
253
254    /// Returns the y component.
255    #[must_use]
256    pub const fn y(self) -> f64 {
257        self.components[1]
258    }
259}
260
261impl Vector<3> {
262    /// Creates a three-dimensional vector from its components.
263    #[must_use]
264    pub const fn new(x: f64, y: f64, z: f64) -> Self {
265        Self::from_array([x, y, z])
266    }
267
268    /// Returns the x component.
269    #[must_use]
270    pub const fn x(self) -> f64 {
271        self.components[0]
272    }
273
274    /// Returns the y component.
275    #[must_use]
276    pub const fn y(self) -> f64 {
277        self.components[1]
278    }
279
280    /// Returns the z component.
281    #[must_use]
282    pub const fn z(self) -> f64 {
283        self.components[2]
284    }
285
286    /// Returns the cross product with `other`.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use use_vector::Vector3;
292    ///
293    /// let x = Vector3::new(1.0, 0.0, 0.0);
294    /// let y = Vector3::new(0.0, 1.0, 0.0);
295    ///
296    /// assert_eq!(x.cross(y), Vector3::new(0.0, 0.0, 1.0));
297    /// ```
298    #[must_use]
299    pub fn cross(self, other: Self) -> Self {
300        Self::new(
301            mul_sub(self.y(), other.z(), self.z(), other.y()),
302            mul_sub(self.z(), other.x(), self.x(), other.z()),
303            mul_sub(self.x(), other.y(), self.y(), other.x()),
304        )
305    }
306}
307
308impl Vector<4> {
309    /// Creates a four-dimensional vector from its components.
310    #[must_use]
311    pub const fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
312        Self::from_array([x, y, z, w])
313    }
314
315    /// Returns the x component.
316    #[must_use]
317    pub const fn x(self) -> f64 {
318        self.components[0]
319    }
320
321    /// Returns the y component.
322    #[must_use]
323    pub const fn y(self) -> f64 {
324        self.components[1]
325    }
326
327    /// Returns the z component.
328    #[must_use]
329    pub const fn z(self) -> f64 {
330        self.components[2]
331    }
332
333    /// Returns the w component.
334    #[must_use]
335    pub const fn w(self) -> f64 {
336        self.components[3]
337    }
338}
339
340impl<const N: usize> Add for Vector<N> {
341    type Output = Self;
342
343    fn add(self, rhs: Self) -> Self::Output {
344        self.zip_components(rhs, |left, right| left + right)
345    }
346}
347
348impl<const N: usize> Sub for Vector<N> {
349    type Output = Self;
350
351    fn sub(self, rhs: Self) -> Self::Output {
352        self.zip_components(rhs, |left, right| left - right)
353    }
354}
355
356impl<const N: usize> Mul<f64> for Vector<N> {
357    type Output = Self;
358
359    fn mul(self, rhs: f64) -> Self::Output {
360        self.map_components(|component| component * rhs)
361    }
362}
363
364impl<const N: usize> Div<f64> for Vector<N> {
365    type Output = Self;
366
367    fn div(self, rhs: f64) -> Self::Output {
368        self.map_components(|component| component / rhs)
369    }
370}
371
372impl<const N: usize> Neg for Vector<N> {
373    type Output = Self;
374
375    fn neg(self) -> Self::Output {
376        self.map_components(|component| -component)
377    }
378}
379
380impl<const N: usize> Index<usize> for Vector<N> {
381    type Output = f64;
382
383    fn index(&self, index: usize) -> &Self::Output {
384        &self.components[index]
385    }
386}
387
388impl<const N: usize> IndexMut<usize> for Vector<N> {
389    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
390        &mut self.components[index]
391    }
392}
393
394impl<const N: usize> AsRef<[f64; N]> for Vector<N> {
395    fn as_ref(&self) -> &[f64; N] {
396        self.as_array()
397    }
398}
399
400impl<const N: usize> From<[f64; N]> for Vector<N> {
401    fn from(components: [f64; N]) -> Self {
402        Self::from_array(components)
403    }
404}
405
406impl<const N: usize> From<Vector<N>> for [f64; N] {
407    fn from(vector: Vector<N>) -> Self {
408        vector.into_array()
409    }
410}
411
412#[inline]
413fn mul_sub(a: f64, b: f64, c: f64, d: f64) -> f64 {
414    a.mul_add(b, -(c * d))
415}
416
417#[cfg(test)]
418mod tests {
419    use super::{Vector, Vector2, Vector3, Vector4};
420
421    fn approx_eq(left: f64, right: f64) -> bool {
422        (left - right).abs() < 1.0e-12
423    }
424
425    fn assert_vector_close<const N: usize>(actual: Vector<N>, expected: [f64; N]) {
426        let actual_components = actual.into_array();
427
428        for (actual_component, expected_component) in actual_components.iter().zip(expected) {
429            assert!(
430                approx_eq(*actual_component, expected_component),
431                "expected {expected:?}, got {actual_components:?}"
432            );
433        }
434    }
435
436    #[test]
437    fn generic_vectors_support_component_workflows() {
438        let mut vector = Vector::<3>::from_array([1.0, -2.0, 3.0]);
439
440        assert_eq!(vector.dimension(), 3);
441        assert_vector_close(Vector::from_array(*vector.as_array()), [1.0, -2.0, 3.0]);
442        assert_vector_close(
443            Vector::from_array(<[f64; 3]>::from(vector)),
444            [1.0, -2.0, 3.0],
445        );
446        assert_vector_close(Vector::<3>::from([1.0, 2.0, 3.0]), [1.0, 2.0, 3.0]);
447        assert!(approx_eq(vector[0], 1.0));
448
449        vector[1] = 5.0;
450
451        assert_vector_close(Vector::from_array(vector.into_array()), [1.0, 5.0, 3.0]);
452        assert_vector_close(Vector::<4>::ZERO, [0.0, 0.0, 0.0, 0.0]);
453        assert_vector_close(Vector::<4>::ONE, [1.0, 1.0, 1.0, 1.0]);
454    }
455
456    #[test]
457    fn generic_vectors_support_arithmetic_and_norms() {
458        let a = Vector::<3>::from_array([1.0, 2.0, 3.0]);
459        let b = Vector::<3>::from_array([4.0, -2.0, 0.5]);
460
461        assert_vector_close(a + b, [5.0, 0.0, 3.5]);
462        assert_vector_close(a - b, [-3.0, 4.0, 2.5]);
463        assert_vector_close(a * 2.0, [2.0, 4.0, 6.0]);
464        assert_vector_close(a / 2.0, [0.5, 1.0, 1.5]);
465        assert_vector_close(-a, [-1.0, -2.0, -3.0]);
466        assert!(approx_eq(a.dot(b), 1.5));
467        assert!(approx_eq(a.magnitude_squared(), 14.0));
468        assert!(approx_eq(a.magnitude(), 14.0_f64.sqrt()));
469        assert_vector_close(Vector::<3>::ZERO.lerp(a, 0.25), [0.25, 0.5, 0.75]);
470        assert!(approx_eq(Vector::<3>::ZERO.distance(a), a.magnitude()));
471        assert!(approx_eq(Vector::<3>::ZERO.distance_squared(a), 14.0));
472    }
473
474    #[test]
475    fn generic_vectors_support_component_helpers() {
476        let a = Vector::<3>::from_array([-1.0, 2.0, 5.0]);
477        let b = Vector::<3>::from_array([3.0, -4.0, 4.0]);
478        let minimum = Vector::<3>::from_array([0.0, 0.0, 0.0]);
479        let maximum = Vector::<3>::from_array([2.0, 3.0, 4.0]);
480
481        assert_vector_close(
482            a.map_components(|component| component * 2.0),
483            [-2.0, 4.0, 10.0],
484        );
485        assert_vector_close(
486            a.zip_components(b, |left, right| left - right),
487            [-4.0, 6.0, 1.0],
488        );
489        assert_vector_close(a.component_min(b), [-1.0, -4.0, 4.0]);
490        assert_vector_close(a.component_max(b), [3.0, 2.0, 5.0]);
491        assert_vector_close(a.clamp_components(minimum, maximum), [0.0, 2.0, 4.0]);
492        assert_vector_close(a.abs(), [1.0, 2.0, 5.0]);
493        assert!(a.is_finite());
494        assert!(!Vector::<3>::from_array([f64::INFINITY, 1.0, 2.0]).is_finite());
495        assert!(Vector::<3>::from_array([f64::NAN, 1.0, 2.0]).is_nan());
496    }
497
498    #[test]
499    fn vector2_alias_supports_specialized_construction_and_accessors() {
500        let constructed = Vector2::new(3.0, 4.0);
501
502        assert!(approx_eq(constructed.x(), 3.0));
503        assert!(approx_eq(constructed.y(), 4.0));
504        assert_vector_close(constructed.scale(0.5), [1.5, 2.0]);
505        assert!(approx_eq(constructed.dot(Vector2::new(-2.0, 1.0)), -2.0));
506        assert!(approx_eq(constructed.magnitude(), 5.0));
507        assert!(Vector2::ZERO.normalize().is_none());
508
509        let Some(normalized) = constructed.normalize() else {
510            panic!("non-zero finite vector should normalize");
511        };
512
513        assert_vector_close(normalized, [0.6, 0.8]);
514    }
515
516    #[test]
517    fn vector3_alias_supports_cross_product() {
518        let constructed = Vector3::new(2.0, 3.0, 6.0);
519
520        assert!(approx_eq(constructed.x(), 2.0));
521        assert!(approx_eq(constructed.y(), 3.0));
522        assert!(approx_eq(constructed.z(), 6.0));
523        assert_vector_close(constructed + Vector3::ONE, [3.0, 4.0, 7.0]);
524        assert_vector_close(
525            Vector3::new(1.0, 0.0, 0.0).cross(Vector3::new(0.0, 1.0, 0.0)),
526            [0.0, 0.0, 1.0],
527        );
528        assert!(approx_eq(constructed.magnitude(), 7.0));
529    }
530
531    #[test]
532    fn vector4_alias_supports_specialized_construction_and_accessors() {
533        let constructed = Vector4::new(2.0, 4.0, 4.0, 4.0);
534
535        assert!(approx_eq(constructed.x(), 2.0));
536        assert!(approx_eq(constructed.y(), 4.0));
537        assert!(approx_eq(constructed.z(), 4.0));
538        assert!(approx_eq(constructed.w(), 4.0));
539        assert_vector_close(constructed + Vector4::ONE, [3.0, 5.0, 5.0, 5.0]);
540        assert!(approx_eq(constructed.magnitude_squared(), 52.0));
541    }
542
543    #[test]
544    fn normalize_rejects_zero_and_non_finite_magnitudes() {
545        assert!(Vector2::ZERO.normalize().is_none());
546        assert!(Vector2::new(f64::INFINITY, 1.0).normalize().is_none());
547        assert!(Vector3::new(f64::NAN, 1.0, 2.0).normalize().is_none());
548        assert!(
549            Vector4::new(f64::MAX, f64::MAX, f64::MAX, f64::MAX)
550                .normalize()
551                .is_none()
552        );
553    }
554}