1use std::slice;
7
8use cxx::{type_id, ExternType};
9
10#[repr(C)]
18#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
19pub struct QGenericMatrix<const N: usize, const M: usize> {
20 data: [[f32; M]; N],
21}
22
23impl<const N: usize, const M: usize> QGenericMatrix<N, M> {
24 pub const fn new(values: &[[f32; N]; M]) -> Self {
26 let mut data = [[0.0; M]; N];
27 let mut col = 0;
28 while col < N {
29 let mut row = 0;
30 while row < M {
31 data[col][row] = values[row][col];
32 row += 1;
33 }
34 col += 1;
35 }
36 Self { data }
37 }
38
39 pub const fn data(&self) -> &[f32] {
41 unsafe { slice::from_raw_parts(self.data.as_ptr().cast(), N * M) }
43 }
44
45 pub fn data_mut(&mut self) -> &mut [f32] {
47 unsafe { slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), N * M) }
49 }
50
51 pub fn copy_data_to(&self, values: &mut [f32]) {
53 for (col, data) in self.data.iter().enumerate() {
54 for (row, &value) in data.iter().enumerate() {
55 values[row * N + col] = value;
56 }
57 }
58 }
59
60 pub fn fill(&mut self, value: f32) {
62 self.data_mut().fill(value);
63 }
64
65 pub const fn filled(value: f32) -> Self {
67 Self {
68 data: [[value; M]; N],
69 }
70 }
71
72 pub const fn identity() -> Self {
74 let mut data = [[0.0; M]; N];
75 let mut i = 0;
76 let size = if M < N { M } else { N };
77 while i < size {
78 data[i][i] = 1.0;
79 i += 1;
80 }
81 Self { data }
82 }
83
84 pub fn is_identity(&self) -> bool {
86 self == &Self::identity()
87 }
88
89 pub const fn rows(&self) -> [[f32; N]; M] {
91 self.transposed().data
92 }
93
94 pub fn set_to_identity(&mut self) {
96 for (col, data) in self.data.iter_mut().enumerate() {
97 for (row, value) in data.iter_mut().enumerate() {
98 *value = if row == col { 1.0 } else { 0.0 };
99 }
100 }
101 }
102
103 pub const fn transposed(&self) -> QGenericMatrix<M, N> {
105 let mut transposed = [[0.0; N]; M];
106 let mut col = 0;
107 while col < N {
108 let mut row = 0;
109 while row < M {
110 transposed[row][col] = self.data[col][row];
111 row += 1;
112 }
113 col += 1;
114 }
115 QGenericMatrix { data: transposed }
116 }
117}
118
119impl<const N: usize, const M: usize> Default for QGenericMatrix<N, M> {
120 fn default() -> Self {
122 Self::identity()
123 }
124}
125
126impl<const N: usize, const M: usize> std::ops::Index<(usize, usize)> for QGenericMatrix<N, M> {
127 type Output = f32;
128
129 fn index(&self, (row, column): (usize, usize)) -> &Self::Output {
131 &self.data[column][row]
132 }
133}
134
135impl<const N: usize, const M: usize> std::ops::IndexMut<(usize, usize)> for QGenericMatrix<N, M> {
136 fn index_mut(&mut self, (row, column): (usize, usize)) -> &mut Self::Output {
138 &mut self.data[column][row]
139 }
140}
141
142impl<const N: usize, const M: usize> std::ops::AddAssign for QGenericMatrix<N, M> {
143 fn add_assign(&mut self, rhs: Self) {
144 for (lhs, &rhs) in self.data_mut().iter_mut().zip(rhs.data()) {
145 *lhs += rhs;
146 }
147 }
148}
149
150impl<const N: usize, const M: usize> std::ops::Add for QGenericMatrix<N, M> {
151 type Output = Self;
152
153 fn add(mut self, rhs: Self) -> Self::Output {
154 self += rhs;
155 self
156 }
157}
158
159impl<const N: usize, const M: usize> std::ops::SubAssign for QGenericMatrix<N, M> {
160 fn sub_assign(&mut self, rhs: Self) {
161 for (lhs, &rhs) in self.data_mut().iter_mut().zip(rhs.data()) {
162 *lhs -= rhs;
163 }
164 }
165}
166
167impl<const N: usize, const M: usize> std::ops::Sub for QGenericMatrix<N, M> {
168 type Output = Self;
169
170 fn sub(mut self, rhs: Self) -> Self::Output {
171 self -= rhs;
172 self
173 }
174}
175
176impl<const N: usize, const M: usize> std::ops::MulAssign<f32> for QGenericMatrix<N, M> {
177 fn mul_assign(&mut self, rhs: f32) {
178 for value in self.data_mut() {
179 *value *= rhs;
180 }
181 }
182}
183
184impl<const N: usize, const M: usize> std::ops::Mul<f32> for QGenericMatrix<N, M> {
185 type Output = Self;
186
187 fn mul(mut self, rhs: f32) -> Self::Output {
188 self *= rhs;
189 self
190 }
191}
192
193impl<const N: usize, const M: usize> std::ops::DivAssign<f32> for QGenericMatrix<N, M> {
194 fn div_assign(&mut self, rhs: f32) {
195 for value in self.data_mut() {
196 *value /= rhs;
197 }
198 }
199}
200
201impl<const N: usize, const M: usize> std::ops::Div<f32> for QGenericMatrix<N, M> {
202 type Output = Self;
203
204 fn div(mut self, rhs: f32) -> Self::Output {
205 self /= rhs;
206 self
207 }
208}
209
210impl<const N: usize, const M: usize> std::ops::Neg for QGenericMatrix<N, M> {
211 type Output = Self;
212
213 fn neg(mut self) -> Self::Output {
214 for value in self.data_mut() {
215 *value = -*value;
216 }
217 self
218 }
219}
220
221impl<const N: usize, const M: usize> TryFrom<&[f32]> for QGenericMatrix<N, M> {
222 type Error = &'static str;
223
224 fn try_from(values: &[f32]) -> Result<Self, Self::Error> {
226 if values.len() != M * N {
227 return Err("invalid array length");
228 }
229 let mut matrix = [[0.0; M]; N];
230 for (col, data) in matrix.iter_mut().enumerate() {
231 for (row, value) in data.iter_mut().enumerate() {
232 *value = values[row * N + col];
233 }
234 }
235 Ok(Self { data: matrix })
236 }
237}
238
239impl<const N: usize, const M: usize> From<&[[f32; N]; M]> for QGenericMatrix<N, M> {
240 fn from(values: &[[f32; N]; M]) -> Self {
242 Self::new(values)
243 }
244}
245
246impl<const N: usize, const M: usize> From<&QGenericMatrix<N, M>> for [[f32; N]; M] {
247 fn from(value: &QGenericMatrix<N, M>) -> Self {
249 value.rows()
250 }
251}
252
253macro_rules! impl_matrix {
254 ($i:ident, $id:literal, $n:literal, $m:literal) => {
255 pub type $i = QGenericMatrix<$n, $m>;
256 unsafe impl ExternType for $i {
260 type Id = type_id!($id);
261 type Kind = cxx::kind::Trivial;
262 }
263 };
264}
265
266impl_matrix!(QMatrix2x2, "QMatrix2x2", 2, 2);
267impl_matrix!(QMatrix2x3, "QMatrix2x3", 2, 3);
268impl_matrix!(QMatrix2x4, "QMatrix2x4", 2, 4);
269impl_matrix!(QMatrix3x2, "QMatrix3x2", 3, 2);
270impl_matrix!(QMatrix3x3, "QMatrix3x3", 3, 3);
271impl_matrix!(QMatrix3x4, "QMatrix3x4", 3, 4);
272impl_matrix!(QMatrix4x2, "QMatrix4x2", 4, 2);
273impl_matrix!(QMatrix4x3, "QMatrix4x3", 4, 3);
274
275#[cfg(test)]
276mod test {
277 use super::*;
278
279 #[rustfmt::skip]
280 const MATRIX: &QGenericMatrix<4, 2> = &QGenericMatrix::new(&[
281 [5.0, 4.0, 3.0, 2.0],
282 [6.0, 7.0, 8.0, 9.0],
283 ]);
284
285 #[test]
286 fn index() {
287 assert_eq!(MATRIX[(1, 2)], 8.0);
288 }
289
290 #[test]
291 fn data() {
292 assert_eq!(MATRIX.data(), [5.0, 6.0, 4.0, 7.0, 3.0, 8.0, 2.0, 9.0]);
293 }
294
295 #[test]
296 fn copy_data_to() {
297 let mut dest = [0.0; 8];
298 MATRIX.copy_data_to(&mut dest);
299 assert_eq!(dest, [5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0]);
300 }
301
302 #[test]
303 fn fill() {
304 let mut filled = *MATRIX;
305 filled.fill(11.0);
306 assert_eq!(filled.data(), [11.0; 8]);
307 }
308
309 #[test]
310 fn filled() {
311 let filled = QGenericMatrix::<4, 2>::filled(11.0);
312 assert_eq!(filled.data(), [11.0; 8]);
313 }
314
315 #[test]
316 fn identity() {
317 let matrix = QGenericMatrix::<4, 2>::identity();
318 assert_eq!(matrix.rows(), [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0]]);
319 }
320
321 #[test]
322 fn is_identity() {
323 let matrix = QGenericMatrix::new(&[
324 [1.0, 0.0, 0.0, 0.0],
325 [0.0, 1.0, 0.0, 0.0],
326 [0.0, 0.0, 1.0, 0.0],
327 ]);
328 assert!(matrix.is_identity());
329 }
330
331 #[test]
332 fn is_not_identity() {
333 let matrix = QGenericMatrix::new(&[
334 [1.0, 1.0, 0.0, 0.0],
335 [0.0, 1.0, 0.0, 0.0],
336 [0.0, 0.0, 1.0, 0.0],
337 ]);
338 assert!(!matrix.is_identity());
339 }
340
341 #[test]
342 fn rows() {
343 let rows = [[5.0, 4.0, 3.0, 2.0], [6.0, 7.0, 8.0, 9.0]];
344 let matrix = QGenericMatrix::new(&rows);
345 assert_eq!(matrix.rows(), rows);
346 }
347
348 #[test]
349 fn set_to_identity() {
350 let mut matrix = *MATRIX;
351 matrix.set_to_identity();
352 assert_eq!(matrix, QGenericMatrix::identity());
353 }
354
355 #[test]
356 fn transposed() {
357 let rows = MATRIX.transposed().rows();
358 assert_eq!(rows, [[5.0, 6.0], [4.0, 7.0], [3.0, 8.0], [2.0, 9.0]]);
359 }
360
361 #[test]
362 fn try_from_valid() {
363 let matrix =
364 QGenericMatrix::<4, 2>::try_from([5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0].as_slice());
365 assert_eq!(matrix, Ok(*MATRIX));
366 }
367
368 #[test]
369 fn try_from_too_short() {
370 let matrix =
371 QGenericMatrix::<4, 2>::try_from([5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0].as_slice());
372 matrix.expect_err("Expected error, got");
373 }
374
375 #[test]
376 fn try_from_too_long() {
377 let matrix = QGenericMatrix::<4, 2>::try_from(
378 [5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0, 1.0].as_slice(),
379 );
380 matrix.expect_err("Expected error, got");
381 }
382}