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!();