use crate::vec2::{Vec2, Meters, Pixels, World, Local, Screen};
use crate::angle::{Degrees, Radians};
use core::ops::{Add, Mul, Sub};
use core::marker::PhantomData;
use crate::math;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Mat2<Unit: Copy = (), Space: Copy = ()> {
pub x_col: Vec2<Unit, Space>, pub y_col: Vec2<Unit, Space>, #[cfg_attr(feature = "serde", serde(skip))]
_unit: PhantomData<Unit>,
#[cfg_attr(feature = "serde", serde(skip))]
_space: PhantomData<Space>,
}
pub type Mat2f32 = Mat2<(),()>;
pub type Mat2Meters = Mat2<Meters,()>;
pub type Mat2Pixels = Mat2<Pixels,()>;
pub type Mat2World = Mat2<(),World>;
pub type Mat2Local = Mat2<(),Local>;
pub type Mat2Screen = Mat2<(),Screen>;
pub type Mat2MetersWorld = Mat2<Meters,World>;
pub type Mat2PixelsScreen = Mat2<Pixels,Screen>;
impl<Unit: Copy, Space: Copy> Mat2<Unit, Space> {
pub const ZERO: Self = Self {
x_col: Vec2::ZERO,
y_col: Vec2::ZERO,
_unit: PhantomData,
_space: PhantomData,
};
pub const IDENTITY: Self = Self {
x_col: Vec2::new(1.0, 0.0),
y_col: Vec2::new(0.0, 1.0),
_unit: PhantomData,
_space: PhantomData,
};
#[inline]
pub const fn new(x_col: Vec2<Unit, Space>, y_col: Vec2<Unit, Space>) -> Self {
Self { x_col, y_col, _unit: PhantomData, _space: PhantomData }
}
#[inline]
pub const fn from_cols_array(data: &[f32; 4]) -> Self {
Self {
x_col: Vec2::new(data[0], data[1]),
y_col: Vec2::new(data[2], data[3]),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub const fn from_rows(r0c0: f32, r0c1: f32, r1c0: f32, r1c1: f32) -> Self {
Self {
x_col: Vec2::new(r0c0, r1c0),
y_col: Vec2::new(r0c1, r1c1),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub fn from_rotation(angle: Radians) -> Self {
let (sin, cos) = math::sin_cos(angle.0);
Self {
x_col: Vec2::new(cos, sin),
y_col: Vec2::new(-sin, cos),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub fn from_rotation_deg(angle: Degrees) -> Self {
Self::from_rotation(angle.to_radians())
}
#[inline]
pub const fn from_scale(scale: Vec2) -> Self {
Self {
x_col: Vec2::new(scale.x, 0.0),
y_col: Vec2::new(0.0, scale.y),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub const fn determinant(&self) -> f32 {
self.x_col.x * self.y_col.y - self.x_col.y * self.y_col.x
}
#[inline]
pub const fn transpose(&self) -> Self {
Self {
x_col: Vec2::new(self.x_col.x, self.y_col.x),
y_col: Vec2::new(self.x_col.y, self.y_col.y),
_unit: PhantomData,
_space: PhantomData,
}
}
pub fn inverse(&self) -> Option<Self> {
let det = self.determinant();
if det.abs() < 1e-7 {
None
} else {
let inv_det = 1.0 / det;
Some(Self {
x_col: Vec2::new(self.y_col.y * inv_det, -self.x_col.y * inv_det),
y_col: Vec2::new(-self.y_col.x * inv_det, self.x_col.x * inv_det),
_unit: PhantomData,
_space: PhantomData,
})
}
}
#[inline]
pub fn try_inverse(&self) -> Option<Self> {
self.inverse()
}
pub const fn col(&self, i: usize) -> Option<Vec2<Unit, Space>> {
match i {
0 => Some(self.x_col),
1 => Some(self.y_col),
_ => None,
}
}
pub fn set_col(&mut self, i: usize, v: Vec2<Unit, Space>) {
match i {
0 => self.x_col = v,
1 => self.y_col = v,
_ => {}
}
}
pub const fn row(&self, i: usize) -> Option<Vec2<Unit, Space>> {
match i {
0 => Some(Vec2::new(self.x_col.x, self.y_col.x)),
1 => Some(Vec2::new(self.x_col.y, self.y_col.y)),
_ => None,
}
}
pub fn set_row(&mut self, i: usize, v: Vec2<Unit, Space>) {
match i {
0 => {
self.x_col.x = v.x;
self.y_col.x = v.y;
}
1 => {
self.x_col.y = v.x;
self.y_col.y = v.y;
}
_ => {}
}
}
pub fn is_orthonormal(&self) -> bool {
let c0 = self.x_col;
let c1 = self.y_col;
(c0.length() - 1.0).abs() < 1e-5
&& (c1.length() - 1.0).abs() < 1e-5
&& (c0.dot(c1)).abs() < 1e-5
}
pub const fn from_shear(shx: f32, shy: f32) -> Self {
Self {
x_col: Vec2::new(1.0, shy),
y_col: Vec2::new(shx, 1.0),
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Add for Mat2<Unit, Space> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
x_col: self.x_col + rhs.x_col,
y_col: self.y_col + rhs.y_col,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Sub for Mat2<Unit, Space> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
x_col: self.x_col - rhs.x_col,
y_col: self.y_col - rhs.y_col,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Mul for Mat2<Unit, Space> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self {
x_col: self * rhs.x_col,
y_col: self * rhs.y_col,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Mul<Vec2<Unit, Space>> for Mat2<Unit, Space> {
type Output = Vec2<Unit, Space>;
#[inline]
fn mul(self, v: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
Vec2::new(
self.x_col.x * v.x + self.y_col.x * v.y,
self.x_col.y * v.x + self.y_col.y * v.y,
)
}
}
impl<Unit: Copy, Space: Copy> Mul<f32> for Mat2<Unit, Space> {
type Output = Self;
#[inline]
fn mul(self, scalar: f32) -> Self::Output {
Self {
x_col: self.x_col * scalar,
y_col: self.y_col * scalar,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Mul<Mat2<Unit, Space>> for f32 {
type Output = Mat2<Unit, Space>;
#[inline]
fn mul(self, matrix: Mat2<Unit, Space>) -> Self::Output {
matrix * self }
}