1use nalgebra::SVector;
5use std::ops::{Add, Sub};
6
7#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct Point<const D: usize>(pub SVector<f64, D>);
10
11impl<const D: usize> Point<D> {
12 pub fn origin() -> Self {
14 Self(SVector::zeros())
15 }
16
17 pub fn new(coords: [f64; D]) -> Self {
19 Self(SVector::from(coords))
20 }
21
22 #[inline]
24 pub fn distance(&self, other: &Self) -> f64 {
25 (self.0 - other.0).norm()
26 }
27
28 #[inline]
30 pub fn distance_squared(&self, other: &Self) -> f64 {
31 (self.0 - other.0).norm_squared()
32 }
33
34 #[inline]
36 pub fn lerp(&self, other: &Self, t: f64) -> Self {
37 Self(self.0 * (1.0 - t) + other.0 * t)
38 }
39
40 #[inline]
42 pub fn coord(&self, i: usize) -> f64 {
43 self.0[i]
44 }
45
46 #[inline]
48 pub fn coord_mut(&mut self, i: usize) -> &mut f64 {
49 &mut self.0[i]
50 }
51
52 #[inline]
54 pub fn to_vector(&self) -> SVector<f64, D> {
55 self.0
56 }
57}
58
59impl<const D: usize> std::ops::Index<usize> for Point<D> {
60 type Output = f64;
61
62 #[inline]
63 fn index(&self, index: usize) -> &Self::Output {
64 &self.0[index]
65 }
66}
67
68impl<const D: usize> std::ops::IndexMut<usize> for Point<D> {
69 #[inline]
70 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
71 &mut self.0[index]
72 }
73}
74
75impl<const D: usize> Add<SVector<f64, D>> for Point<D> {
76 type Output = Point<D>;
77 #[inline]
78 fn add(self, rhs: SVector<f64, D>) -> Point<D> {
79 Point(self.0 + rhs)
80 }
81}
82
83impl<const D: usize> Sub for Point<D> {
84 type Output = SVector<f64, D>;
85 #[inline]
86 fn sub(self, rhs: Point<D>) -> SVector<f64, D> {
87 self.0 - rhs.0
88 }
89}
90
91impl<const D: usize> Default for Point<D> {
92 fn default() -> Self {
93 Self::origin()
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn origin_is_zero() {
103 let p = Point::<4>::origin();
104 for i in 0..4 {
105 assert_eq!(p.coord(i), 0.0);
106 }
107 }
108
109 #[test]
110 fn distance_2d() {
111 let a = Point::new([0.0, 0.0]);
112 let b = Point::new([3.0, 4.0]);
113 assert!((a.distance(&b) - 5.0).abs() < 1e-12);
114 }
115
116 #[test]
117 fn distance_4d() {
118 let a = Point::new([1.0, 2.0, 3.0, 4.0]);
119 let b = Point::new([5.0, 6.0, 7.0, 8.0]);
120 assert!((a.distance(&b) - 8.0).abs() < 1e-12);
121 }
122
123 #[test]
124 fn lerp_midpoint() {
125 let a = Point::new([0.0, 0.0, 0.0]);
126 let b = Point::new([10.0, 20.0, 30.0]);
127 let mid = a.lerp(&b, 0.5);
128 assert!((mid.coord(0) - 5.0).abs() < 1e-12);
129 assert!((mid.coord(1) - 10.0).abs() < 1e-12);
130 }
131
132 #[test]
133 fn subtraction() {
134 let a = Point::new([1.0, 2.0]);
135 let b = Point::new([4.0, 6.0]);
136 let v = b - a;
137 assert!((v[0] - 3.0).abs() < 1e-12);
138 assert!((v[1] - 4.0).abs() < 1e-12);
139 }
140
141 #[test]
142 fn is_copy() {
143 let a = Point::new([1.0, 2.0]);
144 let b = a;
145 assert_eq!(a, b);
146 }
147}