use std::{array, fmt, ops, marker::PhantomData};
use bytemuck::{Pod, Zeroable};
use crate::{Point, Scalar, Vector, Float, cross, HcMatrix, HcPoint, Space, WorldSpace, dot, Dir};
#[repr(transparent)]
pub struct Matrix<
T: Scalar,
const C: usize,
const R: usize,
Src: Space = WorldSpace,
Dst: Space = WorldSpace,
>(pub(super) [[T; R]; C], PhantomData<(Src, Dst)>);
pub(super) type MatrixStorage<T, const C: usize, const R: usize> = [[T; R]; C];
pub type Mat3<T, Src = WorldSpace, Dst = WorldSpace> = Matrix<T, 3, 3, Src, Dst>;
pub type Mat2<T, Src = WorldSpace, Dst = WorldSpace> = Matrix<T, 2, 2, Src, Dst>;
pub type Mat3f<Src = WorldSpace, Dst = WorldSpace> = Mat3<f32, Src, Dst>;
pub type Mat2f<Src = WorldSpace, Dst = WorldSpace> = Mat2<f32, Src, Dst>;
impl<T: Scalar, const C: usize, const R: usize, Src: Space, Dst: Space> Matrix<T, C, R, Src, Dst> {
fn new_impl(data: [[T; R]; C]) -> Self {
Self(data, PhantomData)
}
pub fn zero() -> Self {
Self::new_impl([[T::zero(); R]; C])
}
pub fn from_rows<V>(rows: [V; R]) -> Self
where
V: Into<[T; C]>,
{
let mut out = Self::zero();
for (i, row) in IntoIterator::into_iter(rows).enumerate() {
out.set_row(i, row);
}
out
}
pub fn from_cols<V>(cols: [V; C]) -> Self
where
V: Into<[T; R]>,
{
Self::new_impl(cols.map(|v| v.into()))
}
pub fn col(&self, index: usize) -> Col<'_, T, C, R> {
Col {
matrix: &self.0,
index,
}
}
pub fn set_col(&mut self, idx: usize, v: impl Into<[T; R]>) {
self.0[idx] = v.into().into();
}
pub fn row(&self, index: usize) -> Row<'_, T, C, R> {
Row {
matrix: &self.0,
index,
}
}
pub fn set_row(&mut self, idx: usize, v: impl Into<[T; C]>) {
let v = v.into();
for i in 0..C {
self.0[i][idx] = v[i];
}
}
pub fn elem(&self, row: usize, col: usize) -> T {
self.0[col][row]
}
pub fn set_elem(&mut self, row: usize, col: usize, value: T) {
self.0[col][row] = value;
}
pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
self.0.iter().flat_map(|col| col).copied()
}
#[must_use = "does not transpose in-place"]
pub fn transposed<NewSrc: Space, NewDst: Space>(&self) -> Matrix<T, R, C, NewSrc, NewDst> {
Matrix::from_rows(self.0)
}
pub fn with_target_space<New: Space>(self) -> Matrix<T, C, R, Src, New> {
Matrix::new_impl(self.0)
}
pub fn with_source_space<New: Space>(self) -> Matrix<T, C, R, New, Dst> {
Matrix::new_impl(self.0)
}
pub fn with_spaces<NewSrc: Space, NewDst: Space>(self) -> Matrix<T, C, R, NewSrc, NewDst> {
Matrix::new_impl(self.0)
}
pub fn transform<'a, X>(&'a self, x: X) -> <&'a Self as ops::Mul<X>>::Output
where
&'a Self: ops::Mul<X>,
{
self * x
}
pub fn and_then<const R2: usize, Dst2: Space>(
self,
second: Matrix<T, R, R2, Dst, Dst2>,
) -> Matrix<T, C, R2, Src, Dst2> {
second * self
}
pub fn to_homogeneous(&self) -> HcMatrix<T, C, R, Src, Dst> {
HcMatrix::from_parts(*self, Vector::zero(), Vector::zero(), T::one())
}
pub fn map<U: Scalar, F: FnMut(T) -> U>(&self, mut f: F) -> Matrix<U, C, R, Src, Dst> {
Matrix::new_impl(self.0.map(|col| col.map(&mut f)))
}
pub fn zip_map<U, O, F>(
&self,
other: &Matrix<U, C, R, Src, Dst>,
mut f: F,
) -> Matrix<O, C, R, Src, Dst>
where
U: Scalar,
O: Scalar,
F: FnMut(T, U) -> O,
{
Matrix::new_impl(array::from_fn(|i| array::from_fn(|j| f(self.0[i][j], other.0[i][j]))))
}
pub fn as_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
}
impl<T: Scalar, const N: usize, Src: Space, Dst: Space> Matrix<T, N, N, Src, Dst> {
pub fn identity() -> Self {
Self::from_diagonal([T::one(); N])
}
pub fn from_diagonal(v: impl Into<[T; N]>) -> Self {
let mut m = Self::zero();
m.set_diagonal(v);
m
}
pub fn diagonal(&self) -> [T; N] {
array::from_fn(|i| self.0[i][i])
}
pub fn set_diagonal(&mut self, v: impl Into<[T; N]>) {
let v = v.into();
for i in 0..N {
self.0[i][i] = v[i];
}
}
pub fn is_symmetric(&self) -> bool {
for c in 1..N {
for r in 0..c {
if self.0[c][r] != self.0[r][c] {
return false;
}
}
}
true
}
pub fn trace(&self) -> T {
self.diagonal().as_ref().iter().fold(T::zero(), |a, b| a + *b)
}
}
impl<T: Float, Src: Space, Dst: Space> Matrix<T, 1, 1, Src, Dst> {
#[doc = include_str!("determinant_docs.md")]
pub fn determinant(&self) -> T {
self.0[0][0]
}
#[doc = include_str!("inverted_docs.md")]
pub fn inverted(&self) -> Option<Matrix<T, 1, 1, Dst, Src>> {
let det = self.determinant();
if det.is_zero() {
return None;
}
Some(Matrix::identity() / det)
}
}
impl<T: Float, Src: Space, Dst: Space> Matrix<T, 2, 2, Src, Dst> {
#[doc = include_str!("determinant_docs.md")]
pub fn determinant(&self) -> T {
self.0[0][0] * self.0[1][1] - self.0[0][1] * self.0[1][0]
}
#[doc = include_str!("inverted_docs.md")]
pub fn inverted(&self) -> Option<Matrix<T, 2, 2, Dst, Src>> {
let det = self.determinant();
if det.is_zero() {
return None;
}
let m = Self::from_rows([
[ self.row(1).col(1), -self.row(0).col(1)],
[-self.row(1).col(0), self.row(0).col(0)],
]);
Some(m.with_spaces() / det)
}
}
impl<T: Float, Src: Space, Dst: Space> Matrix<T, 3, 3, Src, Dst> {
#[doc = include_str!("determinant_docs.md")]
pub fn determinant(&self) -> T {
T::zero()
+ self.0[0][0] * (self.0[1][1] * self.0[2][2] - self.0[2][1] * self.0[1][2])
+ self.0[1][0] * (self.0[2][1] * self.0[0][2] - self.0[0][1] * self.0[2][2])
+ self.0[2][0] * (self.0[0][1] * self.0[1][2] - self.0[1][1] * self.0[0][2])
}
#[doc = include_str!("inverted_docs.md")]
pub fn inverted(&self) -> Option<Matrix<T, 3, 3, Dst, Src>> {
let det = self.determinant();
if det.is_zero() {
return None;
}
let calc_row = |col_a, col_b| cross::<_, WorldSpace>(
self.col(col_a).to_vec(),
self.col(col_b).to_vec()
);
let m = Self::from_rows([
calc_row(1, 2),
calc_row(2, 0),
calc_row(0, 1),
]);
Some(m.with_spaces() / det)
}
}
impl<T: Float, Src: Space, Dst: Space> Matrix<T, 4, 4, Src, Dst> {
#[doc = include_str!("determinant_docs.md")]
pub fn determinant(&self) -> T {
super::inv4::det(&self.0)
}
#[doc = include_str!("inverted_docs.md")]
pub fn inverted(&self) -> Option<Matrix<T, 4, 4, Dst, Src>> {
super::inv4::inv(&self.0).map(Matrix::new_impl)
}
}
unsafe impl<
T: Scalar + Zeroable,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> Zeroable for Matrix<T, C, R, Src, Dst> {}
unsafe impl<
T: Scalar + Pod,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> Pod for Matrix<T, C, R, Src, Dst> {}
impl<
T: Scalar,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> fmt::Debug for Matrix<T, C, R, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Matrix ")?;
super::debug_matrix_impl(f, C, R, |r, c| self.elem(r, c))
}
}
super::shared_trait_impls!(Matrix);
super::impl_scalar_mul!(Matrix => f32, f64, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
impl<
T: Scalar,
const C: usize,
const R: usize,
const S: usize,
Src: Space,
Mid: Space,
Dst: Space,
> ops::Mul<Matrix<T, C, S, Src, Mid>> for Matrix<T, S, R, Mid, Dst> {
type Output = Matrix<T, C, R, Src, Dst>;
fn mul(self, rhs: Matrix<T, C, S, Src, Mid>) -> Self::Output {
let mut out = Self::Output::zero();
for c in 0..C {
for r in 0..R {
for s in 0..S {
out.0[c][r] += self.0[s][r] * rhs.0[c][s];
}
}
}
out
}
}
impl<
T: Scalar,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> ops::Mul<Vector<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
type Output = Vector<T, R, Dst>;
fn mul(self, rhs: Vector<T, C, Src>) -> Self::Output {
array::from_fn(|row| dot(self.row(row), rhs)).into()
}
}
impl<
T: Scalar,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> ops::Mul<Dir<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
type Output = Vector<T, R, Dst>;
fn mul(self, rhs: Dir<T, C, Src>) -> Self::Output {
array::from_fn(|row| dot(self.row(row), rhs.to_unit_vec())).into()
}
}
impl<
T: Scalar,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> ops::Mul<Point<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
type Output = Point<T, R, Dst>;
fn mul(self, rhs: Point<T, C, Src>) -> Self::Output {
(self * rhs.to_vec()).to_point()
}
}
impl<
T: Scalar,
const C: usize,
const R: usize,
Src: Space,
Dst: Space,
> ops::Mul<HcPoint<T, C, Src>> for &Matrix<T, C, R, Src, Dst> {
type Output = HcPoint<T, R, Dst>;
fn mul(self, rhs: HcPoint<T, C, Src>) -> Self::Output {
HcPoint::new(self * Vector::from(rhs.coords), rhs.weight)
}
}
#[derive(Clone, Copy)]
pub struct Row<'a, T: Scalar, const C: usize, const R: usize> {
matrix: &'a MatrixStorage<T, C, R>,
index: usize,
}
impl<'a, T: Scalar, const C: usize, const R: usize> Row<'a, T, C, R> {
pub fn col(self, col: usize) -> T {
self.matrix[col][self.index]
}
pub fn to_array(self) -> [T; C] {
self.into()
}
pub fn to_vec<S: Space>(self) -> Vector<T, C, S> {
self.into()
}
pub fn to_point<S: Space>(self) -> Point<T, C, S> {
self.into()
}
}
#[derive(Clone, Copy)]
pub struct Col<'a, T: Scalar, const C: usize, const R: usize> {
matrix: &'a MatrixStorage<T, C, R>,
index: usize,
}
impl<'a, T: Scalar, const C: usize, const R: usize> Col<'a, T, C, R> {
pub fn row(self, row: usize) -> T {
self.matrix[self.index][row]
}
pub fn to_array(self) -> [T; R] {
self.into()
}
pub fn to_vec<S: Space>(self) -> Vector<T, R, S> {
self.into()
}
pub fn to_point<S: Space>(self) -> Point<T, R, S> {
self.into()
}
}
impl<'a, T: Scalar, const C: usize, const R: usize> From<Row<'a, T, C, R>> for [T; C] {
fn from(src: Row<'a, T, C, R>) -> Self {
array::from_fn(|i| src.matrix[i][src.index])
}
}
impl<
'a,
T: Scalar,
const C: usize,
const R: usize,
S: Space,
> From<Row<'a, T, C, R>> for Vector<T, C, S> {
fn from(src: Row<'a, T, C, R>) -> Self {
src.to_array().into()
}
}
impl<
'a,
T: Scalar,
const C: usize,
const R: usize,
S: Space,
> From<Row<'a, T, C, R>> for Point<T, C, S> {
fn from(src: Row<'a, T, C, R>) -> Self {
src.to_array().into()
}
}
impl<'a, T: Scalar, const C: usize, const R: usize> fmt::Debug for Row<'a, T, C, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
crate::util::debug_list_one_line(&self.to_array(), f)
}
}
impl<'a, T: Scalar, const C: usize, const R: usize> From<Col<'a, T, C, R>> for [T; R] {
fn from(src: Col<'a, T, C, R>) -> Self {
src.matrix[src.index]
}
}
impl<
'a,
T: Scalar,
const C: usize,
const R: usize,
S: Space,
> From<Col<'a, T, C, R>> for Vector<T, R, S> {
fn from(src: Col<'a, T, C, R>) -> Self {
src.to_array().into()
}
}
impl<
'a,
T: Scalar,
const C: usize,
const R: usize,
S: Space,
> From<Col<'a, T, C, R>> for Point<T, R, S> {
fn from(src: Col<'a, T, C, R>) -> Self {
src.to_array().into()
}
}
impl<'a, T: Scalar, const C: usize, const R: usize> fmt::Debug for Col<'a, T, C, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
crate::util::debug_list_one_line(&self.to_array(), f)
}
}