lina/mat/linear.rs
1use std::{array, fmt, ops, marker::PhantomData};
2use bytemuck::{Pod, Zeroable};
3
4use crate::{Point, Scalar, Vector, Float, cross, HcMatrix, HcPoint, Space, WorldSpace, dot, Dir};
5
6
7/// A linear transformatin matrix with `C` columns and `R` rows. Represents a
8/// linear transformation on cartesian coordinates from `Src` to `Dst`.
9///
10/// This type does not implement `ops::Index[Mut]`. Instead, there are two main
11/// ways to access elements. Use whatever you prefer, but keep in mind that
12/// code is read more often than it is written, so the first option is likely
13/// better as it avoids ambiguity.
14/// - [`Self::row`] and [`Self::col`]: `matrix.row(2).col(0)`
15/// - [`Self::elem`]: `matrix.elem(2, 0)`
16///
17///
18/// # Matrices as transformations
19///
20/// Matrices in computer graphics are usually used to represent and carry out
21/// *transformations*. In its simplest form, a matrix can represent a
22/// *linear* transformation, which includes rotation and scaling, but *not*
23/// translation. This is what this type represents.
24///
25/// To represent non-linear transformations (translations & projections), one
26/// needs to use [homogeneous coordinates][hc-wiki]. To represent such a
27/// transformation, use [`HcMatrix`].
28///
29/// To learn more about this whole topic, I strongly recommend watching
30/// [3blue1brown's series "Essence of Linear Algebra"][3b1b-lina], in particular
31/// [Chapter 3: Linear transformations and matrices][3b1b-transform].
32///
33///
34/// ## Transforming a point or vector
35///
36/// Mathematically, to apply a transformation to a vector/point, you multiply
37/// the matrix with the vector/point: `matrix * vec`. The relevant operator is
38/// defined, so you can just do that in Rust as well. Alternatively, you can
39/// use [`Matrix::transform`], which does exactly the same.
40///
41///
42/// ## Combining transformations
43///
44/// Oftentimes you want to apply multiple transformations to a set of points or
45/// vectors. You can save processing time by combining all transformation
46/// matrices into a single matrix. That's the beauty and convenience of
47/// representing transformations as matrix: it's always possible to combine all
48/// of them into a single matrix.
49///
50/// Mathematically, this composition is *matrix multiplication*: `A * B` results
51/// in a matrix that represents the combined transformation of *first* `B`,
52/// *then* `A`. Matrix multiplication is not commutative, i.e. the order of
53/// operands matters. And it's also in an non-intuitive order, with the
54/// rightmost transformation being applied first. For that reason, you can also
55/// use [`Matrix::and_then`] instead of the overloaded operator `*`. Use what
56/// you think is easier to read.
57///
58///
59/// [3b1b-lina]: https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab
60/// [3b1b-transform]: https://www.youtube.com/watch?v=kYB8IZa5AuE
61/// [hc-wiki]: https://en.wikipedia.org/wiki/Homogeneous_coordinates#Use_in_computer_graphics_and_computer_vision
62///
63///
64/// ## `fmt::Debug` output
65///
66/// Setting the alternative flag `#` for debug output is recommended to have a
67/// properly formatted matrix. In order to avoid ambiguity when using
68/// single-line mode, a `row<i>` is prefixed to each row.
69///
70/// ```
71/// use lina::Mat3;
72///
73/// let m = <Mat3<_>>::from_rows([
74/// [1, 2, 3],
75/// [4, 5, 6],
76/// [7, 8, 9],
77/// ]);
78///
79/// // Formatting without `#` alternate flag (one line)
80/// assert_eq!(
81/// format!("{m:?}"),
82/// "Matrix [row0 [1, 2, 3], row1 [4, 5, 6], row2 [7, 8, 9]]",
83/// );
84///
85/// // Formatting with `#` alternate flag (multi line)
86/// assert_eq!(format!("{m:#?}"), concat!(
87/// "Matrix [\n",
88/// " [1, 2, 3],\n",
89/// " [4, 5, 6],\n",
90/// " [7, 8, 9],\n",
91/// "]",
92/// ));
93/// ```
94#[repr(transparent)]
95pub struct Matrix<
96 T: Scalar,
97 const C: usize,
98 const R: usize,
99 Src: Space = WorldSpace,
100 Dst: Space = WorldSpace,
101>(pub(super) [[T; R]; C], PhantomData<(Src, Dst)>);
102
103pub(super) type MatrixStorage<T, const C: usize, const R: usize> = [[T; R]; C];
104
105/// A 3×3 linear transformation matrix.
106pub type Mat3<T, Src = WorldSpace, Dst = WorldSpace> = Matrix<T, 3, 3, Src, Dst>;
107/// A 2×2 linear transformation matrix.
108pub type Mat2<T, Src = WorldSpace, Dst = WorldSpace> = Matrix<T, 2, 2, Src, Dst>;
109
110/// A 3×3 linear transformation matrix with `f32` elements.
111pub type Mat3f<Src = WorldSpace, Dst = WorldSpace> = Mat3<f32, Src, Dst>;
112
113/// A 2×2 linear transformation matrix with `f32` elements.
114pub type Mat2f<Src = WorldSpace, Dst = WorldSpace> = Mat2<f32, Src, Dst>;
115
116
117impl<T: Scalar, const C: usize, const R: usize, Src: Space, Dst: Space> Matrix<T, C, R, Src, Dst> {
118 fn new_impl(data: [[T; R]; C]) -> Self {
119 Self(data, PhantomData)
120 }
121
122 /// Returns a matrix with all elements being zero.
123 pub fn zero() -> Self {
124 Self::new_impl([[T::zero(); R]; C])
125 }
126
127 /// Returns a matrix with the specified rows. This is opposite of the memory
128 /// layout (which is column-major), but using this constructor usually
129 /// leads to easier-to-read code as the element order matches code order.
130 ///
131 /// ```
132 /// use lina::{Matrix, vec3};
133 ///
134 /// let m = <Matrix<_, 3, 2>>::from_rows([
135 /// [1, 2, 3],
136 /// [4, 5, 6],
137 /// ]);
138 ///
139 ///
140 /// assert_eq!(m.row(0).to_array(), [1, 2, 3]);
141 /// assert_eq!(m.row(1).to_array(), [4, 5, 6]);
142 /// ```
143 pub fn from_rows<V>(rows: [V; R]) -> Self
144 where
145 V: Into<[T; C]>,
146 {
147 let mut out = Self::zero();
148 for (i, row) in IntoIterator::into_iter(rows).enumerate() {
149 out.set_row(i, row);
150 }
151 out
152 }
153
154 /// Returns a matrix with the specified columns. This matches the memory
155 /// layout but is usually more difficult to read in code. Consider using
156 /// [`Matrix::from_rows`] instead.
157 ///
158 /// ```
159 /// use lina::{Matrix, vec2};
160 ///
161 /// let m = <Matrix<_, 2, 3>>::from_cols([
162 /// [1, 2, 3],
163 /// [4, 5, 6],
164 /// ]);
165 ///
166 ///
167 /// assert_eq!(m.row(0).to_array(), [1, 4]);
168 /// assert_eq!(m.row(1).to_array(), [2, 5]);
169 /// assert_eq!(m.row(2).to_array(), [3, 6]);
170 /// ```
171 pub fn from_cols<V>(cols: [V; C]) -> Self
172 where
173 V: Into<[T; R]>,
174 {
175 Self::new_impl(cols.map(|v| v.into()))
176 }
177
178 /// Returns the column with index `idx`.
179 pub fn col(&self, index: usize) -> Col<'_, T, C, R> {
180 Col {
181 matrix: &self.0,
182 index,
183 }
184 }
185
186 /// Sets the column with index `idx` to the given values.
187 ///
188 /// ```
189 /// use lina::{Mat3, vec3};
190 ///
191 /// let mut mat = <Mat3<_>>::identity();
192 /// mat.set_col(1, vec3(2, 4, 6));
193 ///
194 /// assert_eq!(mat, Mat3::from_rows([
195 /// [1, 2, 0],
196 /// [0, 4, 0],
197 /// [0, 6, 1],
198 /// ]));
199 /// ```
200 pub fn set_col(&mut self, idx: usize, v: impl Into<[T; R]>) {
201 self.0[idx] = v.into().into();
202 }
203
204 /// Returns the row with index `idx`.
205 pub fn row(&self, index: usize) -> Row<'_, T, C, R> {
206 Row {
207 matrix: &self.0,
208 index,
209 }
210 }
211
212 /// Sets the row with index `idx` to the given values.
213 ///
214 /// ```
215 /// use lina::{Mat3, vec3};
216 ///
217 /// let mut mat = <Mat3<_>>::identity();
218 /// mat.set_row(1, vec3(2, 4, 6));
219 ///
220 /// assert_eq!(mat, Mat3::from_rows([
221 /// [1, 0, 0],
222 /// [2, 4, 6],
223 /// [0, 0, 1],
224 /// ]));
225 /// ```
226 pub fn set_row(&mut self, idx: usize, v: impl Into<[T; C]>) {
227 let v = v.into();
228 for i in 0..C {
229 self.0[i][idx] = v[i];
230 }
231 }
232
233 /// Returns the element at the given (row, column). Panics if either index
234 /// is out of bounds.
235 ///
236 /// ```
237 /// use lina::{Mat3, vec3};
238 ///
239 /// let mat = <Mat3<_>>::from_rows([
240 /// [1, 0, 8],
241 /// [0, 9, 0],
242 /// [0, 7, 1],
243 /// ]);
244 ///
245 /// assert_eq!(mat.elem(1, 1), 9);
246 /// assert_eq!(mat.elem(0, 2), 8);
247 /// assert_eq!(mat.elem(2, 1), 7);
248 ///
249 /// ```
250 pub fn elem(&self, row: usize, col: usize) -> T {
251 self.0[col][row]
252 }
253
254 /// Overwrites the element in the given (row, column) with the given value.
255 /// Panics if `row` or `col` is out of bounds.
256 ///
257 /// ```
258 /// use lina::{Mat3, vec3};
259 ///
260 /// let mut mat = <Mat3<_>>::identity();
261 /// mat.set_elem(1, 1, 9);
262 /// mat.set_elem(0, 2, 8);
263 /// mat.set_elem(2, 1, 7);
264 ///
265 /// assert_eq!(mat, Mat3::from_rows([
266 /// [1, 0, 8],
267 /// [0, 9, 0],
268 /// [0, 7, 1],
269 /// ]));
270 /// ```
271 pub fn set_elem(&mut self, row: usize, col: usize, value: T) {
272 self.0[col][row] = value;
273 }
274
275 /// Returns an iterator over all entries of this matrix, in column-major order.
276 ///
277 /// ```
278 /// let m = <lina::Matrix<_, 2, 3>>::from_rows([
279 /// [1, 2],
280 /// [3, 4],
281 /// [5, 6],
282 /// ]);
283 /// let mut it = m.iter();
284 ///
285 /// assert_eq!(it.next(), Some(1));
286 /// assert_eq!(it.next(), Some(3));
287 /// assert_eq!(it.next(), Some(5));
288 /// assert_eq!(it.next(), Some(2));
289 /// assert_eq!(it.next(), Some(4));
290 /// assert_eq!(it.next(), Some(6));
291 /// assert_eq!(it.next(), None);
292 /// ```
293 pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
294 self.0.iter().flat_map(|col| col).copied()
295 }
296
297 /// Returns the transposed version of this matrix (swapping rows and
298 /// columns). You have to specify the source and target spaces of the
299 /// returned matrix manually as it's likely different from `self`, but
300 /// cannot be inferred.
301 ///
302 /// ```
303 /// use lina::{Matrix, WorldSpace};
304 ///
305 /// let m = <Matrix<_, 3, 2>>::from_rows([
306 /// [1, 2, 3],
307 /// [4, 5, 6],
308 /// ]);
309 /// let t = m.transposed::<WorldSpace, WorldSpace>();
310 ///
311 /// assert_eq!(t, Matrix::from_rows([
312 /// [1, 4],
313 /// [2, 5],
314 /// [3, 6],
315 /// ]));
316 /// ```
317 #[must_use = "does not transpose in-place"]
318 pub fn transposed<NewSrc: Space, NewDst: Space>(&self) -> Matrix<T, R, C, NewSrc, NewDst> {
319 Matrix::from_rows(self.0)
320 }
321
322 /// Reinterprets this matrix to be a transformation into the space `New`.
323 pub fn with_target_space<New: Space>(self) -> Matrix<T, C, R, Src, New> {
324 Matrix::new_impl(self.0)
325 }
326
327 /// Reinterprets this matrix to be a transformation from the space `New`.
328 pub fn with_source_space<New: Space>(self) -> Matrix<T, C, R, New, Dst> {
329 Matrix::new_impl(self.0)
330 }
331
332 /// Reinterprets this matrix to be a transformation from the space `NewSrc`
333 /// into the space `NewDst`.
334 pub fn with_spaces<NewSrc: Space, NewDst: Space>(self) -> Matrix<T, C, R, NewSrc, NewDst> {
335 Matrix::new_impl(self.0)
336 }
337
338 /// Transforms the given point or vector with this matrix (numerically just
339 /// a simple matrix-vector-multiplication).
340 ///
341 /// This function accepts `C`-dimensional [`Point`]s, [`Vector`]s and
342 /// [`HcPoint`]s. For the latter, only its `coords` part are transformed
343 /// with the `weight` untouched. To be clear: as this matrix represents a
344 /// linear transformation in cartesian coordinates, no perspective divide
345 /// is performed. Use [`HcMatrix`] if you want to represent non-linear
346 /// transformations.
347 ///
348 /// Instead of using this function, you can also use the `*` operator
349 /// overload, if you prefer. It does exactly the same.
350 pub fn transform<'a, X>(&'a self, x: X) -> <&'a Self as ops::Mul<X>>::Output
351 where
352 &'a Self: ops::Mul<X>,
353 {
354 self * x
355 }
356
357 /// Combines the transformations of two matrices into a single
358 /// transformation matrix. First the transformation of `self` is applied,
359 /// then the one of `second`. In the language of math, this is just matrix
360 /// multiplication: `second * self`. You can also use the overloaded `*`
361 /// operator instead, but this method exists for those who find the matrix
362 /// multiplication order unintuitive and this method call easier to read.
363 ///
364 /// ```
365 /// use lina::Mat2;
366 ///
367 /// // Rotation by 90° counter clock wise.
368 /// let rotate = <Mat2<_>>::from_rows([
369 /// [0, -1],
370 /// [1, 0],
371 /// ]);
372 ///
373 /// // Scale x-axis by 2, y-axis by 3.
374 /// let scale = <Mat2<_>>::from_rows([
375 /// [2, 0],
376 /// [0, 3],
377 /// ]);
378 ///
379 /// assert_eq!(rotate.and_then(scale), Mat2::from_rows([
380 /// [0, -2],
381 /// [3, 0],
382 /// ]));
383 /// assert_eq!(scale.and_then(rotate), Mat2::from_rows([
384 /// [0, -3],
385 /// [2, 0],
386 /// ]));
387 /// ```
388 pub fn and_then<const R2: usize, Dst2: Space>(
389 self,
390 second: Matrix<T, R, R2, Dst, Dst2>,
391 ) -> Matrix<T, C, R2, Src, Dst2> {
392 second * self
393 }
394
395 /// Returns the homogeneous version of this matrix by first adding one row
396 /// and one column, all filled with 0s, except the element in the bottom
397 /// right corner, which is a 1.
398 ///
399 /// ```
400 /// use lina::{Mat2, HcMat2};
401 ///
402 /// let linear = <Mat2<_>>::from_rows([
403 /// [1, 2],
404 /// [3, 4],
405 /// ]);
406 ///
407 /// assert_eq!(linear.to_homogeneous(), <HcMat2<_>>::from_rows([
408 /// [1, 2, 0],
409 /// [3, 4, 0],
410 /// [0, 0, 1],
411 /// ]));
412 /// ```
413 pub fn to_homogeneous(&self) -> HcMatrix<T, C, R, Src, Dst> {
414 HcMatrix::from_parts(*self, Vector::zero(), Vector::zero(), T::one())
415 }
416
417 /// Applies the given function to each element and returns the resulting new
418 /// matrix.
419 ///
420 /// ```
421 /// use lina::{Mat2, vec2};
422 ///
423 /// let mat = <Mat2<_>>::identity().map(|e: i32| e + 1);
424 ///
425 /// assert_eq!(mat, Mat2::from_rows([
426 /// [2, 1],
427 /// [1, 2],
428 /// ]));
429 /// ```
430 pub fn map<U: Scalar, F: FnMut(T) -> U>(&self, mut f: F) -> Matrix<U, C, R, Src, Dst> {
431 Matrix::new_impl(self.0.map(|col| col.map(&mut f)))
432 }
433
434 /// Pairs up the same elements from `self` and `other`, applies the given
435 /// function to each and returns the resulting matrix. Useful for
436 /// element-wise operations.
437 ///
438 /// ```
439 /// use lina::{Mat3f, vec3};
440 ///
441 /// let a = <Mat3f>::from_rows([
442 /// [1.0, 2.0, 3.0],
443 /// [4.0, 5.0, 6.0],
444 /// [7.0, 8.0, 9.0],
445 /// ]);
446 /// let b = Mat3f::identity();
447 /// let c = a.zip_map(&b, |elem_a, elem_b| elem_a * elem_b); // element-wise multiplication
448 ///
449 /// assert_eq!(c, Mat3f::from_rows([
450 /// [1.0, 0.0, 0.0],
451 /// [0.0, 5.0, 0.0],
452 /// [0.0, 0.0, 9.0],
453 /// ]));
454 /// ```
455 pub fn zip_map<U, O, F>(
456 &self,
457 other: &Matrix<U, C, R, Src, Dst>,
458 mut f: F,
459 ) -> Matrix<O, C, R, Src, Dst>
460 where
461 U: Scalar,
462 O: Scalar,
463 F: FnMut(T, U) -> O,
464 {
465 Matrix::new_impl(array::from_fn(|i| array::from_fn(|j| f(self.0[i][j], other.0[i][j]))))
466 }
467
468 /// Returns a byte slice of this matrix, representing the raw column-major
469 /// data. Useful to pass to graphics APIs.
470 pub fn as_bytes(&self) -> &[u8] {
471 bytemuck::bytes_of(self)
472 }
473}
474
475impl<T: Scalar, const N: usize, Src: Space, Dst: Space> Matrix<T, N, N, Src, Dst> {
476 /// Returns the identity matrix with all elements 0, except the diagonal
477 /// which is all 1.
478 ///
479 /// ```
480 /// use lina::{Mat3f, vec3};
481 ///
482 /// let mat = <Mat3f>::identity();
483 /// assert_eq!(mat, Mat3f::from_rows([
484 /// [1.0, 0.0, 0.0],
485 /// [0.0, 1.0, 0.0],
486 /// [0.0, 0.0, 1.0],
487 /// ]));
488 /// ```
489 pub fn identity() -> Self {
490 Self::from_diagonal([T::one(); N])
491 }
492
493 /// Returns a matrix with the given diagonal and all other elements set to 0.
494 ///
495 /// ```
496 /// use lina::Mat3;
497 ///
498 /// let mat = <Mat3<_>>::from_diagonal([1, 2, 3]);
499 ///
500 /// assert_eq!(mat, Mat3::from_rows([
501 /// [1, 0, 0],
502 /// [0, 2, 0],
503 /// [0, 0, 3],
504 /// ]));
505 /// ```
506 pub fn from_diagonal(v: impl Into<[T; N]>) -> Self {
507 let mut m = Self::zero();
508 m.set_diagonal(v);
509 m
510 }
511
512 /// Returns the diagonal of this matrix.
513 ///
514 /// ```
515 /// use lina::Mat3;
516 ///
517 /// let mat = <Mat3<_>>::from_rows([
518 /// [1, 2, 3],
519 /// [4, 5, 6],
520 /// [7, 8, 9],
521 /// ]);
522 /// assert_eq!(mat.diagonal(), [1, 5, 9]);
523 /// ```
524 pub fn diagonal(&self) -> [T; N] {
525 array::from_fn(|i| self.0[i][i])
526 }
527
528 /// Sets the diagonal to the given values.
529 ///
530 /// ```
531 /// use lina::Mat3;
532 ///
533 /// let mut mat = <Mat3<_>>::from_rows([
534 /// [1, 2, 3],
535 /// [4, 5, 6],
536 /// [7, 8, 9],
537 /// ]);
538 /// mat.set_diagonal([2, 1, 0]);
539 ///
540 /// assert_eq!(mat, Mat3::from_rows([
541 /// [2, 2, 3],
542 /// [4, 1, 6],
543 /// [7, 8, 0],
544 /// ]));
545 /// ```
546 pub fn set_diagonal(&mut self, v: impl Into<[T; N]>) {
547 let v = v.into();
548 for i in 0..N {
549 self.0[i][i] = v[i];
550 }
551 }
552
553 /// Checks whether this matrix is *symmetric*, i.e. whether transposing
554 /// does *not* change the matrix.
555 ///
556 /// ```
557 /// use lina::{Mat3f, Mat2};
558 ///
559 /// assert!(<Mat3f>::identity().is_symmetric());
560 /// assert!(!<Mat2<_>>::from_rows([[1, 2], [3, 4]]).is_symmetric());
561 /// ```
562 pub fn is_symmetric(&self) -> bool {
563 for c in 1..N {
564 for r in 0..c {
565 if self.0[c][r] != self.0[r][c] {
566 return false;
567 }
568 }
569 }
570
571 true
572 }
573
574 /// Returns the *trace* of the matrix, i.e. the sum of all elements on the
575 /// diagonal.
576 ///
577 /// ```
578 /// use lina::{Mat2, Mat3f};
579 ///
580 /// assert_eq!(<Mat3f>::identity().trace(), 3.0);
581 /// assert_eq!(<Mat2<_>>::from_rows([[1, 2], [3, 4]]).trace(), 5);
582 /// ```
583 pub fn trace(&self) -> T {
584 self.diagonal().as_ref().iter().fold(T::zero(), |a, b| a + *b)
585 }
586}
587
588impl<T: Float, Src: Space, Dst: Space> Matrix<T, 1, 1, Src, Dst> {
589 #[doc = include_str!("determinant_docs.md")]
590 pub fn determinant(&self) -> T {
591 self.0[0][0]
592 }
593
594 #[doc = include_str!("inverted_docs.md")]
595 pub fn inverted(&self) -> Option<Matrix<T, 1, 1, Dst, Src>> {
596 let det = self.determinant();
597 if det.is_zero() {
598 return None;
599 }
600
601 Some(Matrix::identity() / det)
602 }
603}
604
605impl<T: Float, Src: Space, Dst: Space> Matrix<T, 2, 2, Src, Dst> {
606 #[doc = include_str!("determinant_docs.md")]
607 pub fn determinant(&self) -> T {
608 self.0[0][0] * self.0[1][1] - self.0[0][1] * self.0[1][0]
609 }
610
611 #[doc = include_str!("inverted_docs.md")]
612 pub fn inverted(&self) -> Option<Matrix<T, 2, 2, Dst, Src>> {
613 let det = self.determinant();
614 if det.is_zero() {
615 return None;
616 }
617
618 let m = Self::from_rows([
619 [ self.row(1).col(1), -self.row(0).col(1)],
620 [-self.row(1).col(0), self.row(0).col(0)],
621 ]);
622 Some(m.with_spaces() / det)
623 }
624}
625
626impl<T: Float, Src: Space, Dst: Space> Matrix<T, 3, 3, Src, Dst> {
627 #[doc = include_str!("determinant_docs.md")]
628 pub fn determinant(&self) -> T {
629 T::zero()
630 + self.0[0][0] * (self.0[1][1] * self.0[2][2] - self.0[2][1] * self.0[1][2])
631 + self.0[1][0] * (self.0[2][1] * self.0[0][2] - self.0[0][1] * self.0[2][2])
632 + self.0[2][0] * (self.0[0][1] * self.0[1][2] - self.0[1][1] * self.0[0][2])
633 }
634
635 #[doc = include_str!("inverted_docs.md")]
636 pub fn inverted(&self) -> Option<Matrix<T, 3, 3, Dst, Src>> {
637 let det = self.determinant();
638 if det.is_zero() {
639 return None;
640 }
641
642 let calc_row = |col_a, col_b| cross::<_, WorldSpace>(
643 self.col(col_a).to_vec(),
644 self.col(col_b).to_vec()
645 );
646
647 let m = Self::from_rows([
648 calc_row(1, 2),
649 calc_row(2, 0),
650 calc_row(0, 1),
651 ]);
652 Some(m.with_spaces() / det)
653 }
654}
655
656impl<T: Float, Src: Space, Dst: Space> Matrix<T, 4, 4, Src, Dst> {
657 #[doc = include_str!("determinant_docs.md")]
658 pub fn determinant(&self) -> T {
659 super::inv4::det(&self.0)
660 }
661
662 #[doc = include_str!("inverted_docs.md")]
663 pub fn inverted(&self) -> Option<Matrix<T, 4, 4, Dst, Src>> {
664 super::inv4::inv(&self.0).map(Matrix::new_impl)
665 }
666}
667
668
669// =============================================================================================
670// ===== Non-mathematical trait impls
671// =============================================================================================
672
673/// The inner array implements `Zeroable` and `Matrix` is just a newtype wrapper
674/// around that array with `repr(transparent)`.
675unsafe impl<
676 T: Scalar + Zeroable,
677 const C: usize,
678 const R: usize,
679 Src: Space,
680 Dst: Space,
681> Zeroable for Matrix<T, C, R, Src, Dst> {}
682
683/// The struct is marked as `repr(transparent)` so is guaranteed to have the
684/// same layout as `[[T; R]; C]`. And `bytemuck` itself has an impl for arrays
685/// where `T: Pod`.
686unsafe impl<
687 T: Scalar + Pod,
688 const C: usize,
689 const R: usize,
690 Src: Space,
691 Dst: Space,
692> Pod for Matrix<T, C, R, Src, Dst> {}
693
694impl<
695 T: Scalar,
696 const C: usize,
697 const R: usize,
698 Src: Space,
699 Dst: Space,
700> fmt::Debug for Matrix<T, C, R, Src, Dst> {
701 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702 write!(f, "Matrix ")?;
703 super::debug_matrix_impl(f, C, R, |r, c| self.elem(r, c))
704 }
705}
706
707
708// =============================================================================================
709// ===== Mathematical trait impls
710// =============================================================================================
711
712super::shared_trait_impls!(Matrix);
713super::impl_scalar_mul!(Matrix => f32, f64, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
714
715
716// =============================================================================================
717// ===== Matrix * matrix multiplication (composition)
718// =============================================================================================
719
720/// `matrix * matrix` multiplication. You can also use [`Matrix::and_then`].
721impl<
722 T: Scalar,
723 const C: usize,
724 const R: usize,
725 const S: usize,
726 Src: Space,
727 Mid: Space,
728 Dst: Space,
729> ops::Mul<Matrix<T, C, S, Src, Mid>> for Matrix<T, S, R, Mid, Dst> {
730 type Output = Matrix<T, C, R, Src, Dst>;
731 fn mul(self, rhs: Matrix<T, C, S, Src, Mid>) -> Self::Output {
732 // This is the straight-forward n³ algorithm. Using more sophisticated
733 // algorithms with sub cubic runtime is not worth it for small
734 // matrices. However, this can certainly be micro-optimized. In
735 // particular, using SSE seems like a good idea, but "requires" all
736 // columns to be properly aligned in memory.
737 //
738 // TODO: try to optimize
739 let mut out = Self::Output::zero();
740 for c in 0..C {
741 for r in 0..R {
742 for s in 0..S {
743 out.0[c][r] += self.0[s][r] * rhs.0[c][s];
744 }
745 }
746 }
747 out
748 }
749}
750
751
752// =============================================================================================
753// ===== Matrix * vector multiplication (transformations)
754// =============================================================================================
755
756/// See [`Matrix::transform`].
757impl<
758 T: Scalar,
759 const C: usize,
760 const R: usize,
761 Src: Space,
762 Dst: Space,
763> ops::Mul<Vector<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
764 type Output = Vector<T, R, Dst>;
765 fn mul(self, rhs: Vector<T, C, Src>) -> Self::Output {
766 array::from_fn(|row| dot(self.row(row), rhs)).into()
767 }
768}
769
770/// See [`Matrix::transform`].
771impl<
772 T: Scalar,
773 const C: usize,
774 const R: usize,
775 Src: Space,
776 Dst: Space,
777> ops::Mul<Dir<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
778 type Output = Vector<T, R, Dst>;
779 fn mul(self, rhs: Dir<T, C, Src>) -> Self::Output {
780 array::from_fn(|row| dot(self.row(row), rhs.to_unit_vec())).into()
781 }
782}
783
784/// See [`Matrix::transform`].
785impl<
786 T: Scalar,
787 const C: usize,
788 const R: usize,
789 Src: Space,
790 Dst: Space,
791> ops::Mul<Point<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
792 type Output = Point<T, R, Dst>;
793 fn mul(self, rhs: Point<T, C, Src>) -> Self::Output {
794 (self * rhs.to_vec()).to_point()
795 }
796}
797
798/// See [`Matrix::transform`].
799impl<
800 T: Scalar,
801 const C: usize,
802 const R: usize,
803 Src: Space,
804 Dst: Space,
805> ops::Mul<HcPoint<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
806 type Output = HcPoint<T, R, Dst>;
807 fn mul(self, rhs: HcPoint<T, C, Src>) -> Self::Output {
808 HcPoint::new(self * Vector::from(rhs.coords), rhs.weight)
809 }
810}
811
812
813// =============================================================================================
814// ===== `Row` and `Col` proxies
815// =============================================================================================
816
817/// Proxy type representing one row of a matrix.
818#[derive(Clone, Copy)]
819pub struct Row<'a, T: Scalar, const C: usize, const R: usize> {
820 matrix: &'a MatrixStorage<T, C, R>,
821 index: usize,
822}
823
824impl<'a, T: Scalar, const C: usize, const R: usize> Row<'a, T, C, R> {
825 /// Indexes into this row with the given column index, returning the element.
826 pub fn col(self, col: usize) -> T {
827 self.matrix[col][self.index]
828 }
829
830 /// Returns this row as array.
831 pub fn to_array(self) -> [T; C] {
832 self.into()
833 }
834
835 /// Returns this row as vector.
836 pub fn to_vec<S: Space>(self) -> Vector<T, C, S> {
837 self.into()
838 }
839
840 /// Returns this row as point.
841 pub fn to_point<S: Space>(self) -> Point<T, C, S> {
842 self.into()
843 }
844}
845
846/// Proxy type representing one column of a matrix.
847#[derive(Clone, Copy)]
848pub struct Col<'a, T: Scalar, const C: usize, const R: usize> {
849 matrix: &'a MatrixStorage<T, C, R>,
850 index: usize,
851}
852
853impl<'a, T: Scalar, const C: usize, const R: usize> Col<'a, T, C, R> {
854 /// Indexes into this column with the given row index, returning the element.
855 pub fn row(self, row: usize) -> T {
856 self.matrix[self.index][row]
857 }
858
859 /// Returns this column as array.
860 pub fn to_array(self) -> [T; R] {
861 self.into()
862 }
863
864 /// Returns this column as vector.
865 pub fn to_vec<S: Space>(self) -> Vector<T, R, S> {
866 self.into()
867 }
868
869 /// Returns this column as point.
870 pub fn to_point<S: Space>(self) -> Point<T, R, S> {
871 self.into()
872 }
873}
874
875impl<'a, T: Scalar, const C: usize, const R: usize> From<Row<'a, T, C, R>> for [T; C] {
876 fn from(src: Row<'a, T, C, R>) -> Self {
877 array::from_fn(|i| src.matrix[i][src.index])
878 }
879}
880impl<
881 'a,
882 T: Scalar,
883 const C: usize,
884 const R: usize,
885 S: Space,
886> From<Row<'a, T, C, R>> for Vector<T, C, S> {
887 fn from(src: Row<'a, T, C, R>) -> Self {
888 src.to_array().into()
889 }
890}
891impl<
892 'a,
893 T: Scalar,
894 const C: usize,
895 const R: usize,
896 S: Space,
897> From<Row<'a, T, C, R>> for Point<T, C, S> {
898 fn from(src: Row<'a, T, C, R>) -> Self {
899 src.to_array().into()
900 }
901}
902impl<'a, T: Scalar, const C: usize, const R: usize> fmt::Debug for Row<'a, T, C, R> {
903 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
904 crate::util::debug_list_one_line(&self.to_array(), f)
905 }
906}
907
908impl<'a, T: Scalar, const C: usize, const R: usize> From<Col<'a, T, C, R>> for [T; R] {
909 fn from(src: Col<'a, T, C, R>) -> Self {
910 src.matrix[src.index]
911 }
912}
913impl<
914 'a,
915 T: Scalar,
916 const C: usize,
917 const R: usize,
918 S: Space,
919> From<Col<'a, T, C, R>> for Vector<T, R, S> {
920 fn from(src: Col<'a, T, C, R>) -> Self {
921 src.to_array().into()
922 }
923}
924impl<
925 'a,
926 T: Scalar,
927 const C: usize,
928 const R: usize,
929 S: Space,
930> From<Col<'a, T, C, R>> for Point<T, R, S> {
931 fn from(src: Col<'a, T, C, R>) -> Self {
932 src.to_array().into()
933 }
934}
935impl<'a, T: Scalar, const C: usize, const R: usize> fmt::Debug for Col<'a, T, C, R> {
936 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
937 crate::util::debug_list_one_line(&self.to_array(), f)
938
939 }
940}