1use crate::eigen2::{DSymmetricEigen2, SymmetricEigen2};
4use crate::eigen3::{DSymmetricEigen3, SymmetricEigen3, SymmetricEigen3A};
5use crate::svd2::{DSvd2, Svd2};
6use crate::svd3::{DSvd3, Svd3, Svd3A};
7
8pub trait MatExt: Sized + Copy {
16 type Scalar;
18 type Vector;
20 type SymmetricEigen;
22 type Svd;
24
25 fn abs(&self) -> Self;
27
28 fn try_inverse(&self) -> Option<Self>;
30
31 fn swap_cols(&mut self, a: usize, b: usize);
33
34 fn swap_rows(&mut self, a: usize, b: usize);
36
37 fn symmetric_eigen(&self) -> Self::SymmetricEigen;
41
42 fn symmetric_eigenvalues(&self) -> Self::Vector;
46
47 fn svd(&self) -> Self::Svd;
49}
50
51macro_rules! impl_mat2_ext {
53 ($Mat2:ty, $Vec2:ty, $Real:ty, $SymmetricEigen2:ty, $Svd2:ty) => {
54 impl MatExt for $Mat2 {
55 type Scalar = $Real;
56 type Vector = $Vec2;
57 type SymmetricEigen = $SymmetricEigen2;
58 type Svd = $Svd2;
59
60 #[inline]
61 fn abs(&self) -> Self {
62 Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
63 }
64
65 #[inline]
66 fn try_inverse(&self) -> Option<Self> {
67 let det = self.determinant();
68 if det.abs() < <$Real>::EPSILON {
69 None
70 } else {
71 Some(self.inverse())
72 }
73 }
74
75 #[inline]
76 fn swap_cols(&mut self, a: usize, b: usize) {
77 assert!(a < 2, "column index {a} is out of bounds");
78 assert!(b < 2, "column index {b} is out of bounds");
79
80 let ca = self.col(a);
81 let cb = self.col(b);
82 *self.col_mut(a) = cb;
83 *self.col_mut(b) = ca;
84 }
85
86 #[inline]
87 fn swap_rows(&mut self, a: usize, b: usize) {
88 assert!(a < 2, "row index {a} is out of bounds");
89 assert!(b < 2, "row index {b} is out of bounds");
90 self.x_axis.as_mut().swap(a, b);
91 self.y_axis.as_mut().swap(a, b);
92 }
93
94 #[inline]
95 fn symmetric_eigen(&self) -> Self::SymmetricEigen {
96 <$SymmetricEigen2>::new(*self)
97 }
98
99 #[inline]
100 fn symmetric_eigenvalues(&self) -> Self::Vector {
101 <$SymmetricEigen2>::eigenvalues(*self)
102 }
103
104 #[inline]
105 fn svd(&self) -> Self::Svd {
106 <$Svd2>::from_matrix(*self)
107 }
108 }
109 };
110}
111
112macro_rules! impl_mat3_ext {
114 ($Mat3:ty, $Vec3:ty, $Real:ty, $SymmetricEigen3:ty, $Svd3:ty) => {
115 impl MatExt for $Mat3 {
116 type Scalar = $Real;
117 type Vector = $Vec3;
118 type SymmetricEigen = $SymmetricEigen3;
119 type Svd = $Svd3;
120
121 #[inline]
122 fn abs(&self) -> Self {
123 Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
124 }
125
126 #[inline]
127 fn try_inverse(&self) -> Option<Self> {
128 let det = self.determinant();
129 if det.abs() < <$Real>::EPSILON {
130 None
131 } else {
132 Some(self.inverse())
133 }
134 }
135
136 fn swap_cols(&mut self, a: usize, b: usize) {
137 assert!(a < 3, "column index {a} is out of bounds");
138 assert!(b < 3, "column index {b} is out of bounds");
139
140 let ca = self.col(a);
141 let cb = self.col(b);
142 *self.col_mut(a) = cb;
143 *self.col_mut(b) = ca;
144 }
145
146 fn swap_rows(&mut self, a: usize, b: usize) {
147 assert!(a < 3, "row index {a} is out of bounds");
148 assert!(b < 3, "row index {b} is out of bounds");
149 self.x_axis.as_mut().swap(a, b);
150 self.y_axis.as_mut().swap(a, b);
151 self.z_axis.as_mut().swap(a, b);
152 }
153
154 fn symmetric_eigen(&self) -> Self::SymmetricEigen {
155 <$SymmetricEigen3>::new(*self)
156 }
157
158 fn symmetric_eigenvalues(&self) -> Self::Vector {
159 <$SymmetricEigen3>::eigenvalues(*self)
160 }
161
162 #[inline]
163 fn svd(&self) -> Self::Svd {
164 <$Svd3>::from_matrix(*self)
165 }
166 }
167 };
168}
169
170impl_mat2_ext!(glam::Mat2, glam::Vec2, f32, SymmetricEigen2, Svd2);
171impl_mat2_ext!(glam::DMat2, glam::DVec2, f64, DSymmetricEigen2, DSvd2);
172impl_mat3_ext!(glam::Mat3, glam::Vec3, f32, SymmetricEigen3, Svd3);
173impl_mat3_ext!(glam::Mat3A, glam::Vec3A, f32, SymmetricEigen3A, Svd3A);
174impl_mat3_ext!(glam::DMat3, glam::DVec3, f64, DSymmetricEigen3, DSvd3);
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_mat2_abs() {
182 let m = glam::Mat2::from_cols(glam::Vec2::new(-1.0, 2.0), glam::Vec2::new(3.0, -4.0));
183 let abs_m = m.abs();
184 assert_eq!(abs_m.x_axis, glam::Vec2::new(1.0, 2.0));
185 assert_eq!(abs_m.y_axis, glam::Vec2::new(3.0, 4.0));
186 }
187
188 #[test]
189 fn test_mat2_try_inverse() {
190 let m = glam::Mat2::from_cols(glam::Vec2::new(1.0, 2.0), glam::Vec2::new(3.0, 4.0));
191 let inv = m.try_inverse();
192 assert!(inv.is_some());
193
194 let singular = glam::Mat2::from_cols(glam::Vec2::new(1.0, 2.0), glam::Vec2::new(2.0, 4.0));
196 let no_inv = singular.try_inverse();
197 assert!(no_inv.is_none());
198 }
199
200 #[test]
201 fn test_mat3_abs() {
202 let m = glam::Mat3::from_cols(
203 glam::Vec3::new(-1.0, 2.0, -3.0),
204 glam::Vec3::new(4.0, -5.0, 6.0),
205 glam::Vec3::new(-7.0, 8.0, -9.0),
206 );
207 let abs_m = m.abs();
208 assert_eq!(abs_m.x_axis, glam::Vec3::new(1.0, 2.0, 3.0));
209 assert_eq!(abs_m.y_axis, glam::Vec3::new(4.0, 5.0, 6.0));
210 assert_eq!(abs_m.z_axis, glam::Vec3::new(7.0, 8.0, 9.0));
211 }
212
213 #[test]
214 fn test_mat3_swap_cols() {
215 let mut m = glam::Mat3::from_cols(
216 glam::Vec3::new(1.0, 2.0, 3.0),
217 glam::Vec3::new(4.0, 5.0, 6.0),
218 glam::Vec3::new(7.0, 8.0, 9.0),
219 );
220 m.swap_cols(0, 2);
221 assert_eq!(m.x_axis, glam::Vec3::new(7.0, 8.0, 9.0));
222 assert_eq!(m.z_axis, glam::Vec3::new(1.0, 2.0, 3.0));
223 }
224
225 #[test]
226 fn test_dmat2_abs() {
227 let m = glam::DMat2::from_cols(glam::DVec2::new(-1.0, 2.0), glam::DVec2::new(3.0, -4.0));
228 let abs_m = m.abs();
229 assert_eq!(abs_m.x_axis, glam::DVec2::new(1.0, 2.0));
230 assert_eq!(abs_m.y_axis, glam::DVec2::new(3.0, 4.0));
231 }
232
233 #[test]
234 fn test_mat3_symmetric_eigen() {
235 use approx::assert_relative_eq;
236 let m =
237 glam::Mat3::from_cols_array_2d(&[[2.0, 0.0, 0.0], [0.0, 3.0, 0.0], [0.0, 0.0, 5.0]]);
238 let eigenvalues = m.symmetric_eigenvalues();
239 assert_relative_eq!(eigenvalues.x, 2.0, epsilon = 0.001);
241 assert_relative_eq!(eigenvalues.y, 3.0, epsilon = 0.001);
242 assert_relative_eq!(eigenvalues.z, 5.0, epsilon = 0.001);
243 }
244}