use std::slice;
use cxx::{type_id, ExternType};
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct QGenericMatrix<const N: usize, const M: usize> {
data: [[f32; M]; N],
}
impl<const N: usize, const M: usize> QGenericMatrix<N, M> {
pub const fn new(values: &[[f32; N]; M]) -> Self {
let mut data = [[0.0; M]; N];
let mut col = 0;
while col < N {
let mut row = 0;
while row < M {
data[col][row] = values[row][col];
row += 1;
}
col += 1;
}
Self { data }
}
pub const fn data(&self) -> &[f32] {
unsafe { slice::from_raw_parts(self.data.as_ptr().cast(), N * M) }
}
pub fn data_mut(&mut self) -> &mut [f32] {
unsafe { slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), N * M) }
}
pub fn copy_data_to(&self, values: &mut [f32]) {
for (col, data) in self.data.iter().enumerate() {
for (row, &value) in data.iter().enumerate() {
values[row * N + col] = value;
}
}
}
pub fn fill(&mut self, value: f32) {
self.data_mut().fill(value);
}
pub const fn filled(value: f32) -> Self {
Self {
data: [[value; M]; N],
}
}
pub const fn identity() -> Self {
let mut data = [[0.0; M]; N];
let mut i = 0;
let size = if M < N { M } else { N };
while i < size {
data[i][i] = 1.0;
i += 1;
}
Self { data }
}
pub fn is_identity(&self) -> bool {
self == &Self::identity()
}
pub const fn rows(&self) -> [[f32; N]; M] {
self.transposed().data
}
pub fn set_to_identity(&mut self) {
for (col, data) in self.data.iter_mut().enumerate() {
for (row, value) in data.iter_mut().enumerate() {
*value = if row == col { 1.0 } else { 0.0 };
}
}
}
pub const fn transposed(&self) -> QGenericMatrix<M, N> {
let mut transposed = [[0.0; N]; M];
let mut col = 0;
while col < N {
let mut row = 0;
while row < M {
transposed[row][col] = self.data[col][row];
row += 1;
}
col += 1;
}
QGenericMatrix { data: transposed }
}
}
impl<const N: usize, const M: usize> Default for QGenericMatrix<N, M> {
fn default() -> Self {
Self::identity()
}
}
impl<const N: usize, const M: usize> std::ops::Index<(usize, usize)> for QGenericMatrix<N, M> {
type Output = f32;
fn index(&self, (row, column): (usize, usize)) -> &Self::Output {
&self.data[column][row]
}
}
impl<const N: usize, const M: usize> std::ops::IndexMut<(usize, usize)> for QGenericMatrix<N, M> {
fn index_mut(&mut self, (row, column): (usize, usize)) -> &mut Self::Output {
&mut self.data[column][row]
}
}
impl<const N: usize, const M: usize> std::ops::AddAssign for QGenericMatrix<N, M> {
fn add_assign(&mut self, rhs: Self) {
for (lhs, &rhs) in self.data_mut().iter_mut().zip(rhs.data()) {
*lhs += rhs;
}
}
}
impl<const N: usize, const M: usize> std::ops::Add for QGenericMatrix<N, M> {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}
impl<const N: usize, const M: usize> std::ops::SubAssign for QGenericMatrix<N, M> {
fn sub_assign(&mut self, rhs: Self) {
for (lhs, &rhs) in self.data_mut().iter_mut().zip(rhs.data()) {
*lhs -= rhs;
}
}
}
impl<const N: usize, const M: usize> std::ops::Sub for QGenericMatrix<N, M> {
type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
impl<const N: usize, const M: usize> std::ops::MulAssign<f32> for QGenericMatrix<N, M> {
fn mul_assign(&mut self, rhs: f32) {
for value in self.data_mut() {
*value *= rhs;
}
}
}
impl<const N: usize, const M: usize> std::ops::Mul<f32> for QGenericMatrix<N, M> {
type Output = Self;
fn mul(mut self, rhs: f32) -> Self::Output {
self *= rhs;
self
}
}
impl<const N: usize, const M: usize> std::ops::DivAssign<f32> for QGenericMatrix<N, M> {
fn div_assign(&mut self, rhs: f32) {
for value in self.data_mut() {
*value /= rhs;
}
}
}
impl<const N: usize, const M: usize> std::ops::Div<f32> for QGenericMatrix<N, M> {
type Output = Self;
fn div(mut self, rhs: f32) -> Self::Output {
self /= rhs;
self
}
}
impl<const N: usize, const M: usize> std::ops::Neg for QGenericMatrix<N, M> {
type Output = Self;
fn neg(mut self) -> Self::Output {
for value in self.data_mut() {
*value = -*value;
}
self
}
}
impl<const N: usize, const M: usize> TryFrom<&[f32]> for QGenericMatrix<N, M> {
type Error = &'static str;
fn try_from(values: &[f32]) -> Result<Self, Self::Error> {
if values.len() != M * N {
return Err("invalid array length");
}
let mut matrix = [[0.0; M]; N];
for (col, data) in matrix.iter_mut().enumerate() {
for (row, value) in data.iter_mut().enumerate() {
*value = values[row * N + col];
}
}
Ok(Self { data: matrix })
}
}
impl<const N: usize, const M: usize> From<&[[f32; N]; M]> for QGenericMatrix<N, M> {
fn from(values: &[[f32; N]; M]) -> Self {
Self::new(values)
}
}
impl<const N: usize, const M: usize> From<&QGenericMatrix<N, M>> for [[f32; N]; M] {
fn from(value: &QGenericMatrix<N, M>) -> Self {
value.rows()
}
}
macro_rules! impl_matrix {
($i:ident, $id:literal, $n:literal, $m:literal) => {
pub type $i = QGenericMatrix<$n, $m>;
unsafe impl ExternType for $i {
type Id = type_id!($id);
type Kind = cxx::kind::Trivial;
}
};
}
impl_matrix!(QMatrix2x2, "QMatrix2x2", 2, 2);
impl_matrix!(QMatrix2x3, "QMatrix2x3", 2, 3);
impl_matrix!(QMatrix2x4, "QMatrix2x4", 2, 4);
impl_matrix!(QMatrix3x2, "QMatrix3x2", 3, 2);
impl_matrix!(QMatrix3x3, "QMatrix3x3", 3, 3);
impl_matrix!(QMatrix3x4, "QMatrix3x4", 3, 4);
impl_matrix!(QMatrix4x2, "QMatrix4x2", 4, 2);
impl_matrix!(QMatrix4x3, "QMatrix4x3", 4, 3);
#[cfg(test)]
mod test {
use super::*;
#[rustfmt::skip]
const MATRIX: &QGenericMatrix<4, 2> = &QGenericMatrix::new(&[
[5.0, 4.0, 3.0, 2.0],
[6.0, 7.0, 8.0, 9.0],
]);
#[test]
fn index() {
assert_eq!(MATRIX[(1, 2)], 8.0);
}
#[test]
fn data() {
assert_eq!(MATRIX.data(), [5.0, 6.0, 4.0, 7.0, 3.0, 8.0, 2.0, 9.0]);
}
#[test]
fn copy_data_to() {
let mut dest = [0.0; 8];
MATRIX.copy_data_to(&mut dest);
assert_eq!(dest, [5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0]);
}
#[test]
fn fill() {
let mut filled = *MATRIX;
filled.fill(11.0);
assert_eq!(filled.data(), [11.0; 8]);
}
#[test]
fn filled() {
let filled = QGenericMatrix::<4, 2>::filled(11.0);
assert_eq!(filled.data(), [11.0; 8]);
}
#[test]
fn identity() {
let matrix = QGenericMatrix::<4, 2>::identity();
assert_eq!(matrix.rows(), [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0]]);
}
#[test]
fn is_identity() {
let matrix = QGenericMatrix::new(&[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
]);
assert!(matrix.is_identity());
}
#[test]
fn is_not_identity() {
let matrix = QGenericMatrix::new(&[
[1.0, 1.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
]);
assert!(!matrix.is_identity());
}
#[test]
fn rows() {
let rows = [[5.0, 4.0, 3.0, 2.0], [6.0, 7.0, 8.0, 9.0]];
let matrix = QGenericMatrix::new(&rows);
assert_eq!(matrix.rows(), rows);
}
#[test]
fn set_to_identity() {
let mut matrix = *MATRIX;
matrix.set_to_identity();
assert_eq!(matrix, QGenericMatrix::identity());
}
#[test]
fn transposed() {
let rows = MATRIX.transposed().rows();
assert_eq!(rows, [[5.0, 6.0], [4.0, 7.0], [3.0, 8.0], [2.0, 9.0]]);
}
#[test]
fn try_from_valid() {
let matrix =
QGenericMatrix::<4, 2>::try_from([5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0].as_slice());
assert_eq!(matrix, Ok(*MATRIX));
}
#[test]
fn try_from_too_short() {
let matrix =
QGenericMatrix::<4, 2>::try_from([5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0].as_slice());
matrix.expect_err("Expected error, got");
}
#[test]
fn try_from_too_long() {
let matrix = QGenericMatrix::<4, 2>::try_from(
[5.0, 4.0, 3.0, 2.0, 6.0, 7.0, 8.0, 9.0, 1.0].as_slice(),
);
matrix.expect_err("Expected error, got");
}
}