use crate::matrices::Mat3;
use crate::utils::epsilon_eq_default;
use crate::vec::{Vec2, Vec3};
use core::f32;
use std::{
cmp::PartialEq,
fmt,
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
};
#[derive(Debug, Clone, Copy)]
pub struct Mat2 {
pub col0: Vec2,
pub col1: Vec2,
}
pub type Mat2Tuple2D = ((f32, f32), (f32, f32));
pub type Mat2Tuple = (f32, f32, f32, f32);
impl Mat2 {
pub fn new(col0: Vec2, col1: Vec2) -> Mat2 {
Mat2 { col0, col1 }
}
pub fn from_rows(r0: Vec2, r1: Vec2) -> Mat2 {
Mat2 {
col0: Vec2::new(r0.x, r1.x),
col1: Vec2::new(r0.y, r1.y),
}
}
pub fn from_angle(radians: f32) -> Mat2 {
let (s, c) = radians.sin_cos();
Mat2::new(Vec2::new(c, s), Vec2::new(-s, c))
}
pub fn from_scale(scale: Vec2) -> Mat2 {
Mat2::new(Vec2::new(scale.x, 0.0), Vec2::new(0.0, scale.y))
}
pub const ZERO: Self = Self {
col0: Vec2::ZERO,
col1: Vec2::ZERO,
};
pub const IDENTITY: Self = Self {
col0: Vec2 { x: 1.0, y: 0.0 },
col1: Vec2 { x: 0.0, y: 1.0 },
};
pub const NAN: Self = Self {
col0: Vec2::NAN,
col1: Vec2::NAN,
};
pub const INFINITY: Self = Self {
col0: Vec2::INFINITY,
col1: Vec2::INFINITY,
};
pub fn transpose(&self) -> Mat2 {
Mat2::new(
Vec2::new(self.col0.x, self.col1.x),
Vec2::new(self.col0.y, self.col1.y),
)
}
pub fn determinant(&self) -> f32 {
self.col0.x * self.col1.y - self.col0.y * self.col1.x
}
pub fn inverse(&self) -> Option<Mat2> {
let det = self.determinant();
if epsilon_eq_default(det.abs(), 0.0) {
return None;
}
let inv_det = 1.0 / det;
let a = self.col0.x;
let b = self.col0.y;
let c = self.col1.x;
let d = self.col1.y;
Some(Mat2::new(
Vec2::new(d, -b) * inv_det,
Vec2::new(-c, a) * inv_det,
))
}
pub fn is_invertible(&self) -> bool {
self.determinant().abs() > 1e-6
}
pub fn to_array_2d_row_major(&self) -> [[f32; 2]; 2] {
[[self.col0.x, self.col1.x], [self.col0.y, self.col1.y]]
}
pub fn to_array_row_major(&self) -> [f32; 4] {
[self.col0.x, self.col0.y, self.col1.x, self.col1.y]
}
pub fn to_array_2d_col_major(&self) -> [[f32; 2]; 2] {
[[self.col0.x, self.col0.y], [self.col1.x, self.col1.y]]
}
pub fn to_array_col_major(&self) -> [f32; 4] {
[self.col0.x, self.col0.y, self.col1.x, self.col1.y]
}
pub fn to_tuple_2d_row_major(&self) -> Mat2Tuple2D {
((self.col0.x, self.col1.x), (self.col0.y, self.col1.y))
}
pub fn to_tuple_row_major(&self) -> Mat2Tuple {
(self.col0.x, self.col0.y, self.col1.x, self.col1.y)
}
pub fn to_tuple_2d_col_major(&self) -> Mat2Tuple2D {
((self.col0.x, self.col0.y), (self.col1.x, self.col1.y))
}
pub fn to_tuple_col_major(&self) -> Mat2Tuple {
(self.col0.x, self.col0.y, self.col1.x, self.col1.y)
}
pub fn from_2d_array(arr: [[f32; 2]; 2]) -> Mat2 {
Mat2::new(
Vec2::new(arr[0][0], arr[0][1]),
Vec2::new(arr[1][0], arr[1][1]),
)
}
pub fn from_array(arr: [f32; 4]) -> Mat2 {
Mat2::new(Vec2::new(arr[0], arr[1]), Vec2::new(arr[2], arr[3]))
}
pub fn from_2d_tuple(t: Mat2Tuple2D) -> Mat2 {
Mat2::new(Vec2::new(t.0 .0, t.0 .1), Vec2::new(t.1 .0, t.1 .1))
}
pub fn from_tuple(t: Mat2Tuple) -> Mat2 {
Mat2::new(Vec2::new(t.0, t.1), Vec2::new(t.2, t.3))
}
pub fn col(&self, index: usize) -> Vec2 {
match index {
0 => self.col0,
1 => self.col1,
_ => panic!("Mat2 column index out of bounds: {}", index),
}
}
pub fn row(&self, index: usize) -> Vec2 {
match index {
0 => Vec2::new(self.col0.x, self.col1.x),
1 => Vec2::new(self.col0.y, self.col1.y),
_ => panic!("Mat2 row index out of bounds: {}", index),
}
}
pub fn to_mat3(&self) -> Mat3 {
Mat3::new(
Vec3::new(self.col0.x, self.col0.y, 0.0),
Vec3::new(self.col1.x, self.col1.y, 0.0),
Vec3::new(0.0, 0.0, 1.0),
)
}
pub fn abs(self) -> Mat2 {
Mat2 {
col0: self.col0.abs(),
col1: self.col1.abs(),
}
}
pub fn signum(self) -> Mat2 {
Mat2 {
col0: self.col0.signum(),
col1: self.col1.signum(),
}
}
pub fn lerp(&self, b: Mat2, t: f32) -> Mat2 {
Mat2::new(self.col0.lerp(b.col0, t), self.col1.lerp(b.col1, t))
}
pub fn lerp_between(a: Mat2, b: Mat2, t: f32) -> Mat2 {
Mat2::new(a.col0.lerp(b.col0, t), a.col1.lerp(b.col1, t))
}
pub fn approx_eq(&self, other: Mat2) -> bool {
self.col0.approx_eq(other.col0) && self.col1.approx_eq(other.col1)
}
pub fn approx_eq_eps(&self, other: Mat2, epsilon: f32) -> bool {
self.col0.approx_eq_eps(other.col0, epsilon) && self.col1.approx_eq_eps(other.col1, epsilon)
}
pub fn is_nan(&self) -> bool {
self.col0.is_nan() || self.col1.is_nan()
}
pub fn is_finite(&self) -> bool {
self.col0.is_finite() && self.col1.is_finite()
}
pub fn adjugate(self) -> Mat2 {
Mat2::new(
Vec2::new(self.col1.y, -self.col0.y),
Vec2::new(-self.col1.x, self.col0.x),
)
}
pub fn extract_scale(&self) -> Vec2 {
Vec2::new(self.col0.length(), self.col1.length())
}
pub fn extract_scale_raw(&self) -> Vec2 {
Vec2::new(self.col0.x, self.col1.y)
}
pub fn extract_rotation(&self) -> f32 {
self.col0.y.atan2(self.col0.x)
}
pub fn orthonormalize(&self) -> Mat2 {
let x = self.col0.normalize();
let y = Vec2::new(-x.y, x.x);
Mat2::new(x, y)
}
pub fn is_orthogonal(&self, epsilon: f32) -> bool {
(self.transpose() * *self).approx_eq_eps(Mat2::IDENTITY, epsilon)
}
pub fn trace(self) -> f32 {
self.col0.x + self.col1.y
}
pub fn decompose(&self) -> (Vec2, f32) {
(self.extract_scale(), self.extract_rotation())
}
pub fn is_lower_triangular(&self, epsilon: f32) -> bool {
self.col0.y.abs() <= epsilon
}
pub fn is_upper_triangular(&self, epsilon: f32) -> bool {
self.col1.x.abs() <= epsilon
}
}
impl Add for Mat2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.col0 + rhs.col0, self.col1 + rhs.col1)
}
}
impl Sub for Mat2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.col0 - rhs.col0, self.col1 - rhs.col1)
}
}
impl Mul for Mat2 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self * rhs.col0, self * rhs.col1)
}
}
impl Mul<Vec2> for Mat2 {
type Output = Vec2;
#[inline]
fn mul(self, rhs: Vec2) -> Self::Output {
self.col0 * rhs.x + self.col1 * rhs.y
}
}
impl Mul<f32> for Mat2 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self::new(self.col0 * rhs, self.col1 * rhs)
}
}
impl Mul<Mat2> for f32 {
type Output = Mat2;
#[inline]
fn mul(self, rhs: Mat2) -> Self::Output {
rhs * self
}
}
impl Div<f32> for Mat2 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self::new(self.col0 / rhs, self.col1 / rhs)
}
}
impl Neg for Mat2 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::new(-self.col0, -self.col1)
}
}
impl AddAssign for Mat2 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.col0 += rhs.col0;
self.col1 += rhs.col1;
}
}
impl SubAssign for Mat2 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.col0 -= rhs.col0;
self.col1 -= rhs.col1;
}
}
impl MulAssign for Mat2 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl MulAssign<f32> for Mat2 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.col0 *= rhs;
self.col1 *= rhs;
}
}
impl DivAssign<f32> for Mat2 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.col0 /= rhs;
self.col1 /= rhs;
}
}
impl Default for Mat2 {
#[inline]
fn default() -> Self {
Self::IDENTITY }
}
impl PartialEq for Mat2 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.col0 == other.col0 && self.col1 == other.col1
}
}
impl Index<usize> for Mat2 {
type Output = Vec2;
#[inline]
fn index(&self, col_index: usize) -> &Self::Output {
match col_index {
0 => &self.col0,
1 => &self.col1,
_ => panic!("Mat2 column index out of bounds: {}", col_index),
}
}
}
impl IndexMut<usize> for Mat2 {
#[inline]
fn index_mut(&mut self, col_index: usize) -> &mut Self::Output {
match col_index {
0 => &mut self.col0,
1 => &mut self.col1,
_ => panic!("Mat2 column index out of bounds: {}", col_index),
}
}
}
impl fmt::Display for Mat2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{:.3}, {:.3}]\n[{:.3}, {:.3}]",
self.col0.x, self.col1.x, self.col0.y, self.col1.y
)
}
}
impl approx::AbsDiffEq for Mat2 {
type Epsilon = f32;
#[inline]
fn default_epsilon() -> f32 {
f32::EPSILON
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
self.col0.abs_diff_eq(&other.col0, epsilon) && self.col1.abs_diff_eq(&other.col1, epsilon)
}
}
impl approx::RelativeEq for Mat2 {
#[inline]
fn default_max_relative() -> f32 {
f32::EPSILON
}
#[inline]
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
self.col0.relative_eq(&other.col0, epsilon, max_relative)
&& self.col1.relative_eq(&other.col1, epsilon, max_relative)
}
}