1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::num::FpCategory;
7use core::ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub};
8
9#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct Vector<const N: usize> {
23 components: [f64; N],
24}
25
26pub type Vector2 = Vector<2>;
28
29pub type Vector3 = Vector<3>;
31
32pub type Vector4 = Vector<4>;
34
35pub mod vector2 {
37 pub use crate::Vector2;
38}
39
40pub mod vector3 {
42 pub use crate::Vector3;
43}
44
45pub mod vector4 {
47 pub use crate::Vector4;
48}
49
50impl<const N: usize> Vector<N> {
51 pub const ZERO: Self = Self {
53 components: [0.0; N],
54 };
55
56 pub const ONE: Self = Self {
58 components: [1.0; N],
59 };
60
61 #[must_use]
63 pub const fn from_array(components: [f64; N]) -> Self {
64 Self { components }
65 }
66
67 #[must_use]
69 pub const fn as_array(&self) -> &[f64; N] {
70 &self.components
71 }
72
73 #[must_use]
75 pub const fn into_array(self) -> [f64; N] {
76 self.components
77 }
78
79 #[must_use]
81 pub const fn dimension(self) -> usize {
82 self.components.len()
83 }
84
85 #[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 #[must_use]
107 pub fn magnitude_squared(self) -> f64 {
108 self.dot(self)
109 }
110
111 #[must_use]
113 pub fn magnitude(self) -> f64 {
114 self.magnitude_squared().sqrt()
115 }
116
117 #[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 #[must_use]
146 pub fn scale(self, scalar: f64) -> Self {
147 self * scalar
148 }
149
150 #[must_use]
163 pub fn distance(self, other: Self) -> f64 {
164 (other - self).magnitude()
165 }
166
167 #[must_use]
169 pub fn distance_squared(self, other: Self) -> f64 {
170 (other - self).magnitude_squared()
171 }
172
173 #[must_use]
175 pub fn lerp(self, other: Self, t: f64) -> Self {
176 self + (other - self) * t
177 }
178
179 #[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 #[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 #[must_use]
205 pub fn component_min(self, other: Self) -> Self {
206 self.zip_components(other, f64::min)
207 }
208
209 #[must_use]
211 pub fn component_max(self, other: Self) -> Self {
212 self.zip_components(other, f64::max)
213 }
214
215 #[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 #[must_use]
224 pub fn abs(self) -> Self {
225 self.map_components(f64::abs)
226 }
227
228 #[must_use]
230 pub fn is_finite(self) -> bool {
231 self.components.into_iter().all(f64::is_finite)
232 }
233
234 #[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 #[must_use]
244 pub const fn new(x: f64, y: f64) -> Self {
245 Self::from_array([x, y])
246 }
247
248 #[must_use]
250 pub const fn x(self) -> f64 {
251 self.components[0]
252 }
253
254 #[must_use]
256 pub const fn y(self) -> f64 {
257 self.components[1]
258 }
259}
260
261impl Vector<3> {
262 #[must_use]
264 pub const fn new(x: f64, y: f64, z: f64) -> Self {
265 Self::from_array([x, y, z])
266 }
267
268 #[must_use]
270 pub const fn x(self) -> f64 {
271 self.components[0]
272 }
273
274 #[must_use]
276 pub const fn y(self) -> f64 {
277 self.components[1]
278 }
279
280 #[must_use]
282 pub const fn z(self) -> f64 {
283 self.components[2]
284 }
285
286 #[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 #[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 #[must_use]
317 pub const fn x(self) -> f64 {
318 self.components[0]
319 }
320
321 #[must_use]
323 pub const fn y(self) -> f64 {
324 self.components[1]
325 }
326
327 #[must_use]
329 pub const fn z(self) -> f64 {
330 self.components[2]
331 }
332
333 #[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}