1use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
17
18use crate::MathError;
19
20#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct Vector<const N: usize>(pub [f64; N]);
27
28pub type Vec2 = Vector<2>;
30
31pub type Vec3 = Vector<3>;
33
34impl<const N: usize> Vector<N> {
39 #[must_use]
41 pub fn length_squared(self) -> f64 {
42 let mut sum = 0.0;
43 let mut i = 0;
44 while i < N {
45 sum = self.0[i].mul_add(self.0[i], sum);
46 i += 1;
47 }
48 sum
49 }
50
51 #[must_use]
53 pub fn length(self) -> f64 {
54 self.length_squared().sqrt()
55 }
56
57 pub fn normalize(self) -> Result<Self, MathError> {
65 let len = self.length();
66 if !len.is_finite() || len < f64::MIN_POSITIVE {
67 return Err(MathError::ZeroVector);
68 }
69 let inv = 1.0 / len;
70 Ok(Self(std::array::from_fn(|i| self.0[i] * inv)))
71 }
72}
73
74impl Vector<2> {
79 #[must_use]
81 pub const fn new(x: f64, y: f64) -> Self {
82 Self([x, y])
83 }
84
85 #[must_use]
87 pub const fn x(self) -> f64 {
88 self.0[0]
89 }
90
91 #[must_use]
93 pub const fn y(self) -> f64 {
94 self.0[1]
95 }
96
97 #[must_use]
99 pub fn dot(self, rhs: Self) -> f64 {
100 self.0[0].mul_add(rhs.0[0], self.0[1] * rhs.0[1])
101 }
102}
103
104impl Vector<3> {
109 #[must_use]
111 pub const fn new(x: f64, y: f64, z: f64) -> Self {
112 Self([x, y, z])
113 }
114
115 #[must_use]
117 pub const fn x(self) -> f64 {
118 self.0[0]
119 }
120
121 #[must_use]
123 pub const fn y(self) -> f64 {
124 self.0[1]
125 }
126
127 #[must_use]
129 pub const fn z(self) -> f64 {
130 self.0[2]
131 }
132
133 #[must_use]
135 pub fn dot(self, rhs: Self) -> f64 {
136 self.0[0].mul_add(rhs.0[0], self.0[1].mul_add(rhs.0[1], self.0[2] * rhs.0[2]))
137 }
138
139 #[must_use]
141 pub fn cross(self, rhs: Self) -> Self {
142 Self([
143 self.0[1].mul_add(rhs.0[2], -(self.0[2] * rhs.0[1])),
144 self.0[2].mul_add(rhs.0[0], -(self.0[0] * rhs.0[2])),
145 self.0[0].mul_add(rhs.0[1], -(self.0[1] * rhs.0[0])),
146 ])
147 }
148}
149
150impl<const N: usize> Add for Vector<N> {
155 type Output = Self;
156
157 fn add(self, rhs: Self) -> Self {
158 Self(std::array::from_fn(|i| self.0[i] + rhs.0[i]))
159 }
160}
161
162impl<const N: usize> AddAssign for Vector<N> {
163 fn add_assign(&mut self, rhs: Self) {
164 for i in 0..N {
165 self.0[i] += rhs.0[i];
166 }
167 }
168}
169
170impl<const N: usize> Sub for Vector<N> {
171 type Output = Self;
172
173 fn sub(self, rhs: Self) -> Self {
174 Self(std::array::from_fn(|i| self.0[i] - rhs.0[i]))
175 }
176}
177
178impl<const N: usize> SubAssign for Vector<N> {
179 fn sub_assign(&mut self, rhs: Self) {
180 for i in 0..N {
181 self.0[i] -= rhs.0[i];
182 }
183 }
184}
185
186impl<const N: usize> Mul<f64> for Vector<N> {
188 type Output = Self;
189
190 fn mul(self, s: f64) -> Self {
191 Self(std::array::from_fn(|i| self.0[i] * s))
192 }
193}
194
195impl<const N: usize> Mul<Vector<N>> for f64 {
197 type Output = Vector<N>;
198
199 fn mul(self, rhs: Vector<N>) -> Vector<N> {
200 Vector(std::array::from_fn(|i| self * rhs.0[i]))
201 }
202}
203
204impl<const N: usize> Neg for Vector<N> {
205 type Output = Self;
206
207 fn neg(self) -> Self {
208 Self(std::array::from_fn(|i| -self.0[i]))
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq)]
218pub struct Position<const N: usize>(pub [f64; N]);
219
220pub type Point2 = Position<2>;
222
223pub type Point3 = Position<3>;
225
226impl Position<2> {
231 #[must_use]
233 pub const fn new(x: f64, y: f64) -> Self {
234 Self([x, y])
235 }
236
237 #[must_use]
239 pub const fn x(self) -> f64 {
240 self.0[0]
241 }
242
243 #[must_use]
245 pub const fn y(self) -> f64 {
246 self.0[1]
247 }
248}
249
250impl Position<3> {
255 #[must_use]
257 pub const fn new(x: f64, y: f64, z: f64) -> Self {
258 Self([x, y, z])
259 }
260
261 #[must_use]
263 pub const fn x(self) -> f64 {
264 self.0[0]
265 }
266
267 #[must_use]
269 pub const fn y(self) -> f64 {
270 self.0[1]
271 }
272
273 #[must_use]
275 pub const fn z(self) -> f64 {
276 self.0[2]
277 }
278}
279
280impl<const N: usize> Add<Vector<N>> for Position<N> {
286 type Output = Self;
287
288 fn add(self, rhs: Vector<N>) -> Self {
289 Self(std::array::from_fn(|i| self.0[i] + rhs.0[i]))
290 }
291}
292
293impl<const N: usize> Sub<Vector<N>> for Position<N> {
295 type Output = Self;
296
297 fn sub(self, rhs: Vector<N>) -> Self {
298 Self(std::array::from_fn(|i| self.0[i] - rhs.0[i]))
299 }
300}
301
302impl<const N: usize> Sub for Position<N> {
304 type Output = Vector<N>;
305
306 fn sub(self, rhs: Self) -> Vector<N> {
307 Vector(std::array::from_fn(|i| self.0[i] - rhs.0[i]))
308 }
309}
310
311macro_rules! impl_serde_for_array_newtype {
319 ($ty:ident, $name:expr) => {
320 #[cfg(feature = "serde")]
321 impl<const N: usize> serde::Serialize for $ty<N> {
322 fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
323 use serde::ser::SerializeTuple;
324 let mut tup = ser.serialize_tuple(N)?;
325 for &val in &self.0 {
326 tup.serialize_element(&val)?;
327 }
328 tup.end()
329 }
330 }
331
332 #[cfg(feature = "serde")]
333 impl<'de, const N: usize> serde::Deserialize<'de> for $ty<N> {
334 fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
335 struct ArrayVisitor<const M: usize>;
336
337 impl<'de, const M: usize> serde::de::Visitor<'de> for ArrayVisitor<M> {
338 type Value = [f64; M];
339
340 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 write!(f, "an array of {} floats", M)
342 }
343
344 fn visit_seq<A: serde::de::SeqAccess<'de>>(
345 self,
346 mut seq: A,
347 ) -> Result<Self::Value, A::Error> {
348 let mut arr = [0.0; M];
349 for (i, slot) in arr.iter_mut().enumerate() {
350 *slot = seq
351 .next_element()?
352 .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
353 }
354 Ok(arr)
355 }
356 }
357
358 de.deserialize_tuple(N, ArrayVisitor::<N>).map($ty)
359 }
360 }
361 };
362}
363
364impl_serde_for_array_newtype!(Vector, "Vector");
365impl_serde_for_array_newtype!(Position, "Position");
366
367#[cfg(test)]
372#[allow(clippy::unwrap_used, clippy::expect_used)]
373mod tests {
374 use super::*;
375
376 #[test]
377 fn vec3_dot() {
378 let a = Vec3::new(1.0, 2.0, 3.0);
379 let b = Vec3::new(4.0, 5.0, 6.0);
380 assert!((a.dot(b) - 32.0).abs() < 1e-14);
381 }
382
383 #[test]
384 fn vec3_cross() {
385 let x = Vec3::new(1.0, 0.0, 0.0);
386 let y = Vec3::new(0.0, 1.0, 0.0);
387 let z = x.cross(y);
388 assert!((z.x()).abs() < 1e-14);
389 assert!((z.y()).abs() < 1e-14);
390 assert!((z.z() - 1.0).abs() < 1e-14);
391 }
392
393 #[test]
394 fn vec3_normalize() {
395 let v = Vec3::new(3.0, 4.0, 0.0);
396 let n = v.normalize().expect("non-zero");
397 assert!((n.length() - 1.0).abs() < 1e-14);
398 }
399
400 #[test]
401 fn vec3_zero_normalize_fails() {
402 let v = Vec3::new(0.0, 0.0, 0.0);
403 assert!(v.normalize().is_err());
404 }
405
406 #[test]
407 fn vec3_denormal_normalize_fails() {
408 let v = Vec3::new(1e-320, 0.0, 0.0);
410 assert!(v.normalize().is_err());
411 }
412
413 #[test]
414 fn vec2_dot() {
415 let a = Vec2::new(3.0, 4.0);
416 let b = Vec2::new(1.0, 2.0);
417 assert!((a.dot(b) - 11.0).abs() < 1e-14);
418 }
419
420 #[test]
421 fn point3_sub_gives_vec3() {
422 let a = Point3::new(3.0, 4.0, 5.0);
423 let b = Point3::new(1.0, 1.0, 1.0);
424 let v = a - b;
425 assert!((v.x() - 2.0).abs() < 1e-14);
426 assert!((v.y() - 3.0).abs() < 1e-14);
427 assert!((v.z() - 4.0).abs() < 1e-14);
428 }
429
430 #[test]
431 fn point3_add_vec3() {
432 let p = Point3::new(1.0, 2.0, 3.0);
433 let v = Vec3::new(1.0, 1.0, 1.0);
434 let q = p + v;
435 assert!((q.x() - 2.0).abs() < 1e-14);
436 }
437
438 #[test]
439 fn point3_sub_vec3() {
440 let p = Point3::new(3.0, 4.0, 5.0);
441 let v = Vec3::new(1.0, 1.0, 1.0);
442 let q = p - v;
443 assert!((q.x() - 2.0).abs() < 1e-14);
444 assert!((q.y() - 3.0).abs() < 1e-14);
445 assert!((q.z() - 4.0).abs() < 1e-14);
446 }
447
448 #[test]
449 fn scalar_times_vec3() {
450 let v = Vec3::new(1.0, 2.0, 3.0);
451 let scaled = 2.0 * v;
452 assert!((scaled.x() - 2.0).abs() < 1e-14);
453 assert!((scaled.y() - 4.0).abs() < 1e-14);
454 assert!((scaled.z() - 6.0).abs() < 1e-14);
455 }
456
457 #[test]
458 fn vec3_add_assign() {
459 let mut a = Vec3::new(1.0, 2.0, 3.0);
460 a += Vec3::new(4.0, 5.0, 6.0);
461 assert!((a.x() - 5.0).abs() < 1e-14);
462 assert!((a.y() - 7.0).abs() < 1e-14);
463 assert!((a.z() - 9.0).abs() < 1e-14);
464 }
465
466 #[test]
467 fn vec3_sub_assign() {
468 let mut a = Vec3::new(5.0, 7.0, 9.0);
469 a -= Vec3::new(1.0, 2.0, 3.0);
470 assert!((a.x() - 4.0).abs() < 1e-14);
471 assert!((a.y() - 5.0).abs() < 1e-14);
472 assert!((a.z() - 6.0).abs() < 1e-14);
473 }
474
475 use proptest::prelude::*;
476
477 proptest! {
478 #[test]
479 fn prop_normalize_unit_length(x in -10.0f64..10.0, y in -10.0f64..10.0, z in -10.0f64..10.0) {
480 let v = Vec3::new(x, y, z);
481 if let Ok(n) = v.normalize() {
482 prop_assert!((n.length() - 1.0).abs() < 1e-12, "length = {}", n.length());
483 }
484 }
485
486 #[test]
487 fn prop_cross_anticommutative(
488 ax in -10.0f64..10.0, ay in -10.0f64..10.0, az in -10.0f64..10.0,
489 bx in -10.0f64..10.0, by in -10.0f64..10.0, bz in -10.0f64..10.0,
490 ) {
491 let a = Vec3::new(ax, ay, az);
492 let b = Vec3::new(bx, by, bz);
493 let ab = a.cross(b);
494 let ba = b.cross(a);
495 prop_assert!((ab.x() + ba.x()).abs() < 1e-10);
497 prop_assert!((ab.y() + ba.y()).abs() < 1e-10);
498 prop_assert!((ab.z() + ba.z()).abs() < 1e-10);
499 }
500
501 #[test]
502 fn prop_point_sub_vec_inverse_of_add(
503 px in -100.0f64..100.0, py in -100.0f64..100.0, pz in -100.0f64..100.0,
504 vx in -100.0f64..100.0, vy in -100.0f64..100.0, vz in -100.0f64..100.0,
505 ) {
506 let p = Point3::new(px, py, pz);
507 let v = Vec3::new(vx, vy, vz);
508 let roundtrip = (p + v) - v;
510 prop_assert!((roundtrip.x() - p.x()).abs() < 1e-10);
511 prop_assert!((roundtrip.y() - p.y()).abs() < 1e-10);
512 prop_assert!((roundtrip.z() - p.z()).abs() < 1e-10);
513 }
514 }
515}