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