geop_core_math/vector/
linalg.rs1use crate::{
2 geop_error::{GeopError, GeopResult},
3 scalars::Number,
4 vector::{Vector, Vector2, Vector3, Vector4},
5};
6
7impl<T: Number, const N: usize> Vector<N, T> {
8 pub fn add(&self, other: &Self) -> Self {
9 let mut out = Self::new();
10 for i in 0..self.size() {
11 out[i] = self[i].add(other[i]);
12 }
13 out
14 }
15
16 pub fn sub(&self, other: &Self) -> Self {
17 let mut out = Self::new();
18 for i in 0..self.size() {
19 out[i] = self[i].sub(other[i]);
20 }
21 out
22 }
23
24 pub fn neg(&self) -> Self {
25 let mut out = Self::new();
26 for i in 0..self.size() {
27 out[i] = self[i].neg();
28 }
29 out
30 }
31
32 pub fn prod_scalar(&self, s: T) -> Self {
33 let mut out = Self::new();
34 for i in 0..self.size() {
35 out[i] = self[i].mul(s);
36 }
37 out
38 }
39
40 pub fn prod_dot(&self, other: &Self) -> T {
41 let mut acc = T::ZERO;
42 for i in 0..self.size() {
43 acc = acc.add(self[i].mul(other[i]));
44 }
45 acc
46 }
47
48 pub fn norm_sq(&self) -> T {
49 self.prod_dot(self)
50 }
51
52 pub fn norm(&self) -> T {
53 self.norm_sq().sqrt().expect("Norm cannot be negative")
54 }
55
56 pub fn try_norm(&self) -> GeopResult<T> {
59 self.norm_sq().sqrt()
60 }
61
62 pub fn normalize(&self) -> GeopResult<Self> {
63 let n = self.try_norm()?;
64 if n.could_be_equal(T::ZERO) {
65 return Err(GeopError::new("Cannot normalize zero-length vector"));
66 }
67 Ok(self.prod_scalar(T::ONE.div(n)?))
68 }
69
70 pub fn orthonormal_complement(&self) -> GeopResult<Vec<Self>> {
78 let v = self.normalize()?;
79 let sigma = if v[0].definitely_less(T::ZERO) {
80 T::ONE.neg()
81 } else {
82 T::ONE
83 };
84 let w = v.add(&Self::axis(0).prod_scalar(sigma));
85 let scale = T::from_i64(2).div(w.norm_sq())?;
86 Ok((1..N)
87 .map(|j| Self::axis(j).sub(&w.prod_scalar(scale.mul(w[j]))))
88 .collect())
89 }
90
91 pub fn distance_to_segment(&self, a: &Self, b: &Self) -> T {
93 let ab = b.sub(a);
94 let along = self
95 .sub(a)
96 .prod_dot(&ab)
97 .div(ab.norm_sq())
98 .unwrap_or(T::ZERO);
99 let along = if along.definitely_less(T::ZERO) {
100 T::ZERO
101 } else if along.definitely_greater(T::ONE) {
102 T::ONE
103 } else {
104 along
105 };
106 self.sub(&a.add(&ab.prod_scalar(along))).norm()
107 }
108
109 pub fn could_be_equal(&self, other: &Self) -> bool {
111 (0..self.size()).all(|i| self[i].could_be_equal(other[i]))
112 }
113
114 pub fn union(&self, other: &Self) -> Self {
116 let mut out = Self::new();
117 for i in 0..self.size() {
118 out[i] = self[i].union(other[i]);
119 }
120 out
121 }
122}
123
124impl<const N: usize> Vector<N> {
125 pub fn sharpen(&self) -> Self {
130 let mut out = Self::new();
131 for i in 0..self.size() {
132 out[i] = self[i].sharpen();
133 }
134 out
135 }
136}
137
138impl<T: Number> Vector3<T> {
139 pub fn across(&self) -> GeopResult<[Self; 2]> {
146 let size = |k: usize| self[k].abs();
147 let least = (0..3)
148 .find(|&k| !(0..3).any(|j| size(j).definitely_less(size(k))))
149 .unwrap_or(0);
150 let reference = Self::axis(least);
151 let first = (reference.sub(&self.prod_scalar(self.prod_dot(&reference))))
152 .normalize()
153 .map_err(|e| e.with_context(format!("a direction square to {self:?}")))?;
154 Ok([first, self.prod_cross(&first)])
155 }
156
157 pub fn prod_cross(&self, other: &Self) -> Self {
158 let mut out = Self::new();
159 out[0] = self[1].mul(other[2]).sub(self[2].mul(other[1]));
160 out[1] = self[2].mul(other[0]).sub(self[0].mul(other[2]));
161 out[2] = self[0].mul(other[1]).sub(self[1].mul(other[0]));
162 out
163 }
164
165 pub fn could_be_parallel(&self, other: &Self) -> bool {
168 self.prod_cross(other).norm_sq().could_be_equal(T::ZERO)
169 }
170
171 pub fn rotated_about(&self, axis: &Self, cos: T, sin: T) -> Self {
174 let along = axis.prod_scalar(axis.prod_dot(self).mul(T::ONE.sub(cos)));
175 self.prod_scalar(cos)
176 .add(&axis.prod_cross(self).prod_scalar(sin))
177 .add(&along)
178 }
179
180 pub fn homogeneous(&self, w: T) -> Vector4<T> {
184 if w.is_exactly(T::ONE) {
185 Vector4::from_array([self[0], self[1], self[2], w])
186 } else {
187 Vector4::from_array([self[0].mul(w), self[1].mul(w), self[2].mul(w), w])
188 }
189 }
190
191 pub fn dehomogenized(&self) -> GeopResult<Vector2<T>> {
195 Ok(Vector2::from_array([
196 self[0].div(self[2])?,
197 self[1].div(self[2])?,
198 ]))
199 }
200}
201
202impl<T: Number> Vector4<T> {
203 pub fn dehomogenized(&self) -> GeopResult<Vector3<T>> {
207 Ok(Vector3::from_array([
208 self[0].div(self[3])?,
209 self[1].div(self[3])?,
210 self[2].div(self[3])?,
211 ]))
212 }
213}
214
215impl<T: Number> Vector2<T> {
216 pub fn prod_cross(&self, other: &Self) -> T {
220 self[0].mul(other[1]).sub(self[1].mul(other[0]))
221 }
222
223 pub fn homogeneous(&self, w: T) -> Vector3<T> {
227 if w.is_exactly(T::ONE) {
228 Vector3::from_array([self[0], self[1], w])
229 } else {
230 Vector3::from_array([self[0].mul(w), self[1].mul(w), w])
231 }
232 }
233
234 pub fn perp(&self) -> Self {
237 Self::from_array([self[1].neg(), self[0]])
238 }
239
240 pub fn rotate(&self, c: T, s: T) -> Self {
242 Self::from_array([
243 self[0].mul(c).sub(self[1].mul(s)),
244 self[0].mul(s).add(self[1].mul(c)),
245 ])
246 }
247}