devela/num/geom/linear/vector/array/methods.rs
1// devela::num::geom::linear::vector::array::methods
2//
3//! impl methods for Vector
4//
5
6#[allow(unused_imports)]
7#[cfg(all(not(feature = "std"), _float··))]
8use crate::ExtFloat;
9use crate::Vector;
10use crate::{concat as cc, stringify as fy};
11#[cfg(_int··)]
12use crate::{unwrap, Int};
13
14/* common methods */
15
16impl<T, const D: usize> Vector<T, D> {
17 /// Returns a new `Vector` from the given `coords` array.
18 pub const fn new(coords: [T; D]) -> Self {
19 Self { coords }
20 }
21}
22
23/* compile-time ops for primitives */
24
25/// helper for implementing methods on `Vector`.
26///
27/// $t: the inner integer primitive type
28/// $cap: the capability feature that enables the given implementation. E.g "_int_i8".
29/// $cmp: the optional feature that enables the given implementation. E.g "_cmp_i8".
30macro_rules! impl_vector {
31 () => {
32 impl_vector![sint
33 i8:"_int_i8":"_cmp_i8",
34 i16:"_int_i16":"_cmp_i16",
35 i32:"_int_i32":"_cmp_i32",
36 i64:"_int_i64":"_cmp_i64",
37 i128:"_int_i128":"_cmp_i128",
38 isize:"_int_isize":"_cmp_isize"
39 ];
40 impl_vector![uint
41 u8:"_int_u8":"_cmp_u8",
42 u16:"_int_u16":"_cmp_u16",
43 u32:"_int_u32":"_cmp_u32",
44 u64:"_int_u64":"_cmp_u64",
45 u128:"_int_u128":"_cmp_u128",
46 usize:"_int_usize" // no _cmp_usize
47 ];
48 impl_vector![float
49 f32:"_float_f32":"_cmp_f32",
50 f64:"_float_f64":"_cmp_f64"
51 ];
52 };
53
54 // integers common methods
55 (int $($t:ty : $cap:literal $(: $cmp:literal)? ),+) => {
56 $( impl_vector![@int $t:$cap $(:$cmp)? ]; )+
57 };
58 (@int $t:ty : $cap:literal $(: $cmp:literal)? ) => {
59 #[doc = cc!("# Methods for vectors represented using `", fy!($t), "`.")]
60 #[cfg(feature = $cap )]
61 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
62 impl<const D: usize> Vector<$t, D> {
63 /// A `Vector` with all ones.
64 pub const ONE: Self = Self::new([1; D]);
65
66 /// A `Vector` with all zeros.
67 pub const ZERO: Self = Self::new([0; D]);
68
69 /* ops with vector */
70
71 /// Returns the normalized vector, using the given vector `magnitude`.
72 ///
73 /// $$
74 /// \bm{n} = \widehat{\bm{a}} = \frac{1}{d}\thinspace\bm{a} =
75 /// \frac{\bm{a}}{|\bm{a}|}
76 /// $$
77 pub const fn c_normalize_with(self, magnitude: $t) -> Self {
78 let mut normalized = [0; D];
79 let mut i = 0;
80 while i < D {
81 normalized[i] = self.coords[i] / magnitude;
82 i += 1;
83 }
84 Vector { coords: normalized }
85 }
86
87 /// Calculates the magnitude of the vector (squared).
88 ///
89 /// This is faster than calculating the magnitude,
90 /// which is useful for comparisons.
91 ///
92 /// # Formula
93 /// $$ \large |\vec{V}|^2 = V_0^2 + ... + V_n^2 $$
94 pub const fn c_magnitude_sq(self) -> $t { self.c_dot(self) }
95
96 /// Adds two vectors together, in compile-time.
97 pub const fn c_add(self, other: Self) -> Self {
98 let mut result = [0; D];
99 let mut i = 0;
100 while i < D {
101 result[i] = self.coords[i] + other.coords[i];
102 i += 1;
103 }
104 Vector::new(result)
105 }
106
107 /// Subtracts another vector from this vector, in compile-time.
108 pub const fn c_sub(self, other: Self) -> Self {
109 let mut result = [0; D];
110 let mut i = 0;
111 while i < D {
112 result[i] = self.coords[i] - other.coords[i];
113 i += 1;
114 }
115 Vector::new(result)
116 }
117
118 /// Computes the dot product of two vectors, in compile-time.
119 pub const fn c_dot(self, other: Self) -> $t {
120 let mut result = 0;
121 let mut i = 0;
122 while i < D {
123 result += self.coords[i] * other.coords[i];
124 i += 1;
125 }
126 result
127 }
128
129 /* ops with scalar */
130
131 /// Multiplies each element of the vector by a scalar, in compile-time.
132 pub const fn c_scalar_mul(self, scalar: $t) -> Self {
133 let mut result = [0; D];
134 let mut i = 0;
135 while i < D {
136 result[i] = self.coords[i] * scalar;
137 i += 1;
138 }
139 Vector::new(result)
140 }
141
142 /// Divides each element of the vector by a scalar, in compile-time.
143 pub const fn c_scalar_div(self, scalar: $t) -> Self {
144 let mut result = [0; D];
145 let mut i = 0;
146 while i < D {
147 result[i] = self.coords[i] / scalar;
148 i += 1;
149 }
150 Vector::new(result)
151 }
152 }
153
154 #[doc = cc!("# Methods for 3d vectors represented using `", fy!($t), "`.")]
155 impl Vector<$t, 3> {
156 /// Computes the cross product of two vectors.
157 ///
158 /// That is the vector orthogonal to both vectors.
159 ///
160 /// Also known as the *exterior product* or the *vector product*.
161 ///
162 /// It is only defined for 3-dimensional vectors, and it is not
163 /// commutative: $\vec{a}\times\vec{b} = -(\vec{b}\times\vec{a})$.
164 ///
165 /// # Formula
166 /// $$
167 /// \bm{a} \times \bm{b} =
168 /// \begin{bmatrix} a_x \cr a_y \cr a_z \end{bmatrix} \times
169 /// \begin{bmatrix} b_x \cr b_y \cr b_z \end{bmatrix} =
170 /// \begin{bmatrix}
171 /// a_y b_z - a_z b_y \cr
172 /// a_z b_x - a_x b_z \cr
173 /// a_x b_y - a_y b_x
174 /// \end{bmatrix}
175 /// $$
176 pub const fn c_cross(self, other: Self) -> Self {
177 let cross_product = [
178 self.coords[1] * other.coords[2] - self.coords[2] * other.coords[1], // i
179 self.coords[2] * other.coords[0] - self.coords[0] * other.coords[2], // j
180 self.coords[0] * other.coords[1] - self.coords[1] * other.coords[0], // k
181 ];
182 Vector::new(cross_product)
183 }
184 }
185 };
186
187 // signed integers specific methods
188 (sint $($t:ty : $cap:literal $(: $cmp:literal)? ),+) => {
189 $( impl_vector![@sint $t:$cap $(:$cmp)? ]; )+
190 };
191 (@sint $t:ty : $cap:literal $(: $cmp:literal)? ) => {
192 impl_vector![int $t:$cap $(:$cmp)? ];
193
194 #[doc = cc!("# Methods for vectors represented using `", fy!($t), "`, signed.")]
195 #[cfg(feature = $cap )]
196 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
197 impl<const D: usize> Vector<$t, D> {
198 /// A `Vector` with all negative ones.
199 pub const NEG_ONE: Self = Self::new([-1; D]);
200
201 /// Calculates the floored magnitude of the vector.
202 ///
203 /// It could underestimate the true magnitude.
204 $(
205 /// # Features
206 #[doc = cc!("This will only be *const* if the ", fy!($cmp), " feature is enabled.")]
207 #[cfg(feature = $cmp)]
208 )? // $cmp
209 pub const fn c_magnitude_floor(self) -> $t {
210 unwrap![ok Int(self.c_dot(self).abs()).sqrt_floor()].0
211 }
212 $( // $cmp
213 #[cfg(not(feature = $cmp))] #[allow(missing_docs)]
214 pub fn c_magnitude_floor(self) -> $t {
215 unwrap![ok Int(self.c_dot(self).abs()).sqrt_floor()].0
216 }
217 )?
218
219 /// Calculates the ceiled magnitude of the vector.
220 ///
221 /// It could overestimate the true magnitude.
222 $(
223 /// # Features
224 #[doc = cc!("This will only be *const* if the ", fy!($cmp), " feature is enabled.")]
225 #[cfg(feature = $cmp)]
226 )? // $cmp
227 pub const fn c_magnitude_ceil(self) -> $t {
228 unwrap![ok Int(self.c_dot(self).abs()).sqrt_ceil()].0
229 }
230 $( // $cmp
231 #[cfg(not(feature = $cmp))] #[allow(missing_docs)]
232 pub fn c_magnitude_ceil(self) -> $t {
233 unwrap![ok Int(self.c_dot(self).abs()).sqrt_ceil()].0
234 }
235 )?
236
237 /// Calculates the rounded magnitude of the vector.
238 /// # Panics
239 /// Can panic if we reach a `i128` value close to its maximum during operations.
240 pub const fn c_magnitude_round(self) -> $t {
241 unwrap![ok Int(self.c_dot(self).abs()).sqrt_round()].0
242 }
243 }
244 };
245
246 // unsigned integers specific methods
247 (uint $($t:ty : $cap:literal $(: $cmp:literal)? ),+) => {
248 $( impl_vector![@uint $t:$cap $(:$cmp)? ]; )+
249 };
250 (@uint $t:ty : $cap:literal $(: $cmp:literal)? ) => {
251 impl_vector![int $t:$cap $(:$cmp)? ];
252
253 #[doc = cc!("# Methods for vectors represented using `", fy!($t), "`, unsigned.")]
254 #[cfg(feature = $cap )]
255 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
256 impl<const D: usize> Vector<$t, D> {
257 /// Calculates the floored magnitude of the vector.
258 ///
259 /// It could underestimate the true magnitude.
260 $(
261 /// # Features
262 #[doc = cc!("This will only be *const* if the ", fy!($cmp), " feature is enabled.")]
263 #[cfg(feature = $cmp)]
264 )? // $cmp
265 pub const fn c_magnitude_floor(self) -> $t {
266 Int(self.c_dot(self)).sqrt_floor().0
267 }
268 $( // $cmp
269 #[cfg(not(feature = $cmp))] #[allow(missing_docs)]
270 pub fn c_magnitude_floor(self) -> $t {
271 Int(self.c_dot(self)).sqrt_floor().0
272 }
273 )?
274
275 /// Calculates the ceiled magnitude of the vector.
276 ///
277 /// It could overestimate the true magnitude.
278 $(
279 /// # Features
280 #[doc = cc!("This will only be *const* if the ", fy!($cmp), " feature is enabled.")]
281 #[cfg(feature = $cmp)]
282 )? // $cmp
283 pub const fn c_magnitude_ceil(self) -> $t {
284 Int(self.c_dot(self)).sqrt_ceil().0
285 }
286 $( // $cmp
287 #[cfg(not(feature = $cmp))] #[allow(missing_docs)]
288 pub fn c_magnitude_ceil(self) -> $t {
289 Int(self.c_dot(self)).sqrt_ceil().0
290 }
291 )?
292
293 /// Calculates the rounded magnitude of the vector.
294 /// # Panics
295 /// Can panic if we reach a `u128` value close to its maximum during operations.
296 pub const fn c_magnitude_round(self) -> $t {
297 unwrap![ok Int(self.c_dot(self)).sqrt_round()].0
298 }
299 }
300 };
301
302 // $f: the inner floating-point primitive type
303 (float $($f:ty : $cap:literal $(: $cmp:literal)? ),+) => {
304 $( impl_vector![@float $f:$cap $(:$cmp)? ]; )+
305 };
306 (@float $f:ty : $cap:literal $(: $cmp:literal)? ) => {
307 #[doc = cc!("# Methods for vectors represented using `", fy!($f), "`.")]
308 #[cfg(feature = $cap )]
309 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
310 impl<const D: usize> Vector<$f, D> {
311 /// A `Vector` with all ones.
312 pub const ONE: Self = Self::new([1.0; D]);
313 /// A `Vector` with all zeros.
314 pub const ZERO: Self = Self::new([0.0; D]);
315 /// A `Vector` with all negative ones.
316 pub const NEG_ONE: Self = Self::new([-1.0; D]);
317
318 /// Returns the normalized vector, as a *unit vector*.
319 ///
320 /// $$
321 /// \bm{n} = \widehat{\bm{a}} = \frac{1}{d}\thinspace\bm{a} =
322 /// \frac{\bm{a}}{|\bm{a}|}
323 /// $$
324 pub fn normalize(&self) -> Self {
325 let mag = self.magnitude();
326 let mut normalized = [0.0; D];
327 for i in 0..D {
328 normalized[i] = self.coords[i] / mag;
329 }
330 Vector { coords: normalized }
331 }
332
333 /// Calculates the magnitude of the vector.
334 ///
335 /// # Formula
336 /// $$ \large |\vec{V}| = \sqrt{V_0^2 + ... + V_n^2} $$
337 pub fn magnitude(self) -> $f { self.dot(self).sqrt() }
338
339 /// Calculates the squared magnitude of the vector.
340 ///
341 /// This is faster than calculating the magnitude,
342 /// which is useful for comparisons.
343 ///
344 /// # Formula
345 /// $$ \large |\vec{V}|^2 = V_0^2 + ... + V_n^2 $$
346 pub fn magnitude_sq(self) -> $f { self.dot(self) }
347
348 /// Adds two vectors together.
349 #[allow(clippy::should_implement_trait)]
350 pub fn add(self, other: Self) -> Self {
351 let mut result = [0.0; D];
352 let mut i = 0;
353 while i < D {
354 result[i] = self.coords[i] + other.coords[i];
355 i += 1;
356 }
357 Vector::new(result)
358 }
359
360 /// Subtracts another vector from this vector.
361 #[allow(clippy::should_implement_trait)]
362 pub fn sub(self, other: Self) -> Self {
363 let mut result = [0.0; D];
364 let mut i = 0;
365 while i < D {
366 result[i] = self.coords[i] - other.coords[i];
367 i += 1;
368 }
369 Vector::new(result)
370 }
371
372 /// Computes the dot product of two vectors.
373 ///
374 /// That is the magnitude of one vector in the direction of another.
375 ///
376 /// Also known as the *inner produc* or the *scalar product*.
377 ///
378 /// # Formula
379 /// $$
380 /// \large \vec{a}\cdot\vec{b} =
381 /// \begin{bmatrix} a_0 \cr ... \cr a_n \end{bmatrix} \cdot
382 /// \begin{bmatrix} b_0 \cr ... \cr b_n \end{bmatrix} =
383 /// a_0 b_0 + ... + a_n b_n
384 /// $$
385 pub fn dot(self, other: Self) -> $f {
386 let mut result = 0.0;
387 let mut i = 0;
388 while i < D {
389 result += self.coords[i] * other.coords[i];
390 i += 1;
391 }
392 result
393 }
394 }
395
396 #[doc = cc!("# Methods for 3d vectors represented using `", fy!($f), "`.")]
397 impl Vector<$f, 3> {
398 /// Computes the cross product of two vectors.
399 ///
400 /// That is the vector orthogonal to both vectors.
401 ///
402 /// Also known as the *exterior product* or the *vector product*.
403 ///
404 /// It is only defined for 3-dimensional vectors, and it is not
405 /// commutative: $\vec{a}\times\vec{b} = -(\vec{b}\times\vec{a})$.
406 ///
407 /// # Formula
408 /// $$
409 /// \bm{a} \times \bm{b} =
410 /// \begin{bmatrix} a_x \cr a_y \cr a_z \end{bmatrix} \times
411 /// \begin{bmatrix} b_x \cr b_y \cr b_z \end{bmatrix} =
412 /// \begin{bmatrix}
413 /// a_y b_z - a_z b_y \cr
414 /// a_z b_x - a_x b_z \cr
415 /// a_x b_y - a_y b_x
416 /// \end{bmatrix}
417 /// $$
418 pub fn cross(self, other: Self) -> Self {
419 let cross_product = [
420 self.coords[1] * other.coords[2] - self.coords[2] * other.coords[1], // i
421 self.coords[2] * other.coords[0] - self.coords[0] * other.coords[2], // j
422 self.coords[0] * other.coords[1] - self.coords[1] * other.coords[0], // k
423 ];
424 Vector::new(cross_product)
425 }
426 }
427 };
428}
429impl_vector!();