use crate::mat4::Mat4;
use crate::utils::epsilon_eq_default;
use crate::vec::Vec2;
use crate::vec::Vec3;
use crate::vec::Vec4;
use std::{
cmp::PartialEq,
fmt,
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
};
#[derive(Debug, Clone, Copy)]
pub struct Mat3 {
pub col0: Vec3,
pub col1: Vec3,
pub col2: Vec3,
}
pub type Mat3Tuple2D = ((f32, f32, f32), (f32, f32, f32), (f32, f32, f32));
pub type Mat3Tuple = (f32, f32, f32, f32, f32, f32, f32, f32, f32);
impl Mat3 {
pub fn new(col0: Vec3, col1: Vec3, col2: Vec3) -> Mat3 {
Mat3 { col0, col1, col2 }
}
pub fn from_rows(r0: Vec3, r1: Vec3, r2: Vec3) -> Mat3 {
Mat3::new(
Vec3::new(r0.x, r1.x, r2.x),
Vec3::new(r0.y, r1.y, r2.y),
Vec3::new(r0.z, r1.z, r2.z),
)
}
pub fn from_angle_axis(radians: f32, axis: Vec3) -> Self {
let axis = axis.normalize();
let (s, c) = radians.sin_cos();
let c1 = 1.0 - c;
Self::new(
Vec3::new(
axis.x * axis.x * c1 + c,
axis.x * axis.y * c1 + axis.z * s,
axis.x * axis.z * c1 - axis.y * s,
),
Vec3::new(
axis.y * axis.x * c1 - axis.z * s,
axis.y * axis.y * c1 + c,
axis.y * axis.z * c1 + axis.x * s,
),
Vec3::new(
axis.z * axis.x * c1 + axis.y * s,
axis.z * axis.y * c1 - axis.x * s,
axis.z * axis.z * c1 + c,
),
)
}
pub fn from_angle_z(radians: f32) -> Self {
let (s, c) = radians.sin_cos();
Self::new(
Vec3::new(c, s, 0.0),
Vec3::new(-s, c, 0.0),
Vec3::new(0.0, 0.0, 1.0),
)
}
pub fn from_shear(shear: Vec2) -> Self {
Self::new(
Vec3::new(1.0, shear.y, 0.0),
Vec3::new(shear.x, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
)
}
pub fn from_trs(translation: Vec2, angle: f32, scale: Vec2, pivot: Vec2) -> Self {
Self::from_translation(translation)
* Self::from_translation(pivot)
* Self::from_angle_z(angle)
* Self::from_scale(scale)
* Self::from_translation(-pivot)
}
pub fn from_translation(translation: Vec2) -> Mat3 {
Mat3::new(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(translation.x, translation.y, 1.0),
)
}
pub fn from_scale(scale: Vec2) -> Mat3 {
Mat3::new(
Vec3::new(scale.x, 0.0, 0.0),
Vec3::new(0.0, scale.y, 0.0),
Vec3::new(0.0, 0.0, 1.0),
)
}
pub const ZERO: Self = Self {
col0: Vec3::ZERO,
col1: Vec3::ZERO,
col2: Vec3::ZERO,
};
pub const IDENTITY: Self = Self {
col0: Vec3 {
x: 1.0,
y: 0.0,
z: 0.0,
},
col1: Vec3 {
x: 0.0,
y: 1.0,
z: 0.0,
},
col2: Vec3 {
x: 0.0,
y: 0.0,
z: 1.0,
},
};
pub const NAN: Self = Self {
col0: Vec3::NAN,
col1: Vec3::NAN,
col2: Vec3::NAN,
};
pub const INFINITY: Self = Self {
col0: Vec3::INFINITY,
col1: Vec3::INFINITY,
col2: Vec3::INFINITY,
};
pub fn transpose(&self) -> Self {
Self::new(
Vec3::new(self.col0.x, self.col1.x, self.col2.x),
Vec3::new(self.col0.y, self.col1.y, self.col2.y),
Vec3::new(self.col0.z, self.col1.z, self.col2.z),
)
}
pub fn determinant(&self) -> f32 {
self.col0.x * (self.col1.y * self.col2.z - self.col2.y * self.col1.z)
- self.col1.x * (self.col0.y * self.col2.z - self.col2.y * self.col0.z)
+ self.col2.x * (self.col0.y * self.col1.z - self.col1.y * self.col0.z)
}
pub fn inverse(&self) -> Option<Self> {
let det = self.determinant();
if det.abs() <= 1e-6 {
return None;
}
Some(self.adjugate() * (1.0 / det))
}
pub fn inverse_or_identity(&self) -> Mat3 {
self.inverse().unwrap_or(Mat3::IDENTITY)
}
pub fn row(&self, row_idx: usize) -> Vec3 {
match row_idx {
0 => Vec3::new(self.col0.x, self.col1.x, self.col2.x),
1 => Vec3::new(self.col0.y, self.col1.y, self.col2.y),
2 => Vec3::new(self.col0.z, self.col1.z, self.col2.z),
_ => panic!("Mat3 row index out of bounds: {}", row_idx),
}
}
pub fn look_at(eye: Vec2, target: Vec2) -> Self {
let fwd = (target - eye).normalize_or_zero();
let right = Vec2::new(fwd.y, -fwd.x); Self::from_rows(
Vec3::new(right.x, right.y, -eye.dot(right)),
Vec3::new(fwd.x, fwd.y, -eye.dot(fwd)),
Vec3::new(0.0, 0.0, 1.0),
)
.transpose()
}
pub fn ortho(left: f32, right: f32, bottom: f32, top: f32) -> Self {
let rml = 1.0 / (right - left);
let tmb = 1.0 / (top - bottom);
Self::new(
Vec3::new(2.0 * rml, 0.0, 0.0),
Vec3::new(0.0, 2.0 * tmb, 0.0),
Vec3::new(-(right + left) * rml, -(top + bottom) * tmb, 1.0),
)
}
pub fn orthonormalize(&self) -> Mat3 {
let mut m = *self;
m.col0 = m.col0.normalize();
let y_rej = m.col1.reject(m.col0);
m.col1 = y_rej.normalize_or_zero();
let z_rej_xy = m.col2.reject(m.col0).reject(m.col1);
m.col2 = z_rej_xy.normalize_or_zero();
m
}
pub fn rect_transform(from: (Vec2, Vec2), to: (Vec2, Vec2)) -> Mat3 {
let from_size = from.1 - from.0;
let to_size = to.1 - to.0;
let scale = to_size / from_size;
Mat3::from_translation(to.0) * Mat3::from_scale(scale) * Mat3::from_translation(-from.0)
}
pub fn fit_rect(container: Vec2, content: Vec2) -> Mat3 {
let scale_ratio = (container.x / content.x).min(container.y / content.y);
let scaled_content = content * scale_ratio;
let offset = (container - scaled_content) * 0.5;
Mat3::from_translation(offset) * Mat3::from_scale(Vec2::new(scale_ratio, scale_ratio))
}
pub fn abs(self) -> Mat3 {
Mat3::new(self.col0.abs(), self.col1.abs(), self.col2.abs())
}
pub fn signum(self) -> Mat3 {
Mat3::new(self.col0.signum(), self.col1.signum(), self.col2.signum())
}
pub fn adjugate(&self) -> Mat3 {
let m = self;
Mat3::new(
Vec3::new(
m.col1.y * m.col2.z - m.col1.z * m.col2.y, m.col0.z * m.col2.y - m.col0.y * m.col2.z, m.col0.y * m.col1.z - m.col0.z * m.col1.y, ),
Vec3::new(
m.col1.z * m.col2.x - m.col1.x * m.col2.z, m.col0.x * m.col2.z - m.col0.z * m.col2.x, m.col0.z * m.col1.x - m.col0.x * m.col1.z, ),
Vec3::new(
m.col1.x * m.col2.y - m.col1.y * m.col2.x, m.col0.y * m.col2.x - m.col0.x * m.col2.y, m.col0.x * m.col1.y - m.col0.y * m.col1.x, ),
)
}
pub fn get_scale(&self) -> Vec2 {
Vec2::new(
Vec2::new(self.col0.x, self.col0.y).length(),
Vec2::new(self.col1.x, self.col1.y).length(),
)
}
pub fn get_rotation(&self) -> f32 {
let norm_x = Vec2::new(self.col0.x, self.col0.y).normalize();
norm_x.y.atan2(norm_x.x)
}
pub fn get_translation(&self) -> Vec2 {
Vec2::new(self.col2.x, self.col2.y)
}
pub fn decompose(&self) -> (Vec2, f32, Vec2) {
(
self.get_translation(),
self.get_rotation(),
self.get_scale(),
)
}
pub fn transform_point(&self, point: Vec2) -> Vec2 {
let v = *self * (Vec3::new(point.x, point.y, 1.0));
Vec2::new(v.x, v.y)
}
pub fn transform_vector(&self, vector: Vec2) -> Vec2 {
let v = *self * (Vec3::new(vector.x, vector.y, 0.0));
Vec2::new(v.x, v.y)
}
pub fn transform_aabb(&self, min: Vec2, max: Vec2) -> (Vec2, Vec2) {
let corners = [
self.transform_point(min),
self.transform_point(Vec2::new(min.x, max.y)),
self.transform_point(Vec2::new(max.x, min.y)),
self.transform_point(max),
];
let mut new_min = corners[0];
let mut new_max = corners[0];
for corner in corners.iter().skip(1) {
new_min = new_min.min(*corner);
new_max = new_max.max(*corner);
}
(new_min, new_max)
}
pub fn apply_translation(&mut self, translation: Vec2) {
*self = Mat3::from_translation(translation) * *self
}
pub fn apply_rotation(&mut self, angle_rad: f32) {
*self = Mat3::from_angle_z(angle_rad) * *self
}
pub fn with_translation(self, translation: Vec2) -> Mat3 {
Mat3::from_translation(translation) * self
}
pub fn with_rotation(self, angle_rad: f32) -> Mat3 {
Mat3::from_angle_z(angle_rad) * self
}
pub fn lerp(&self, b: Mat3, t: f32) -> Mat3 {
Mat3::new(
self.col0.lerp(b.col0, t),
self.col1.lerp(b.col1, t),
self.col2.lerp(b.col2, t),
)
}
pub fn lerp_between(a: Mat3, b: Mat3, t: f32) -> Mat3 {
Mat3::new(
a.col0.lerp(b.col0, t),
a.col1.lerp(b.col1, t),
a.col2.lerp(b.col2, t),
)
}
pub fn get_diagonal(&self) -> Vec2 {
Vec2::new(self.col0.x, self.col1.y)
}
pub fn trace(&self) -> f32 {
self.col0.x + self.col1.y + self.col2.z
}
pub fn is_invertible(&self) -> bool {
let det = self.determinant();
det.abs() > f32::EPSILON && det.is_finite()
}
pub fn is_identity(&self) -> bool {
*self == Mat3::IDENTITY
}
pub fn is_affine(&self) -> bool {
epsilon_eq_default(self.col0.z, 0.0)
&& epsilon_eq_default(self.col1.z, 0.0)
&& epsilon_eq_default(self.col2.z, 1.0)
}
pub fn has_mirroring(&self) -> bool {
self.determinant() < 0.0
}
pub fn approx_eq(&self, other: Mat3) -> bool {
self.col0.approx_eq(other.col0)
&& self.col1.approx_eq(other.col1)
&& self.col2.approx_eq(other.col2)
}
pub fn approx_eq_eps(&self, other: Mat3, epsilon: f32) -> bool {
self.col0.approx_eq_eps(other.col0, epsilon)
&& self.col1.approx_eq_eps(other.col1, epsilon)
&& self.col2.approx_eq_eps(other.col2, epsilon)
}
pub fn is_nan(&self) -> bool {
self.col0.is_nan() || self.col1.is_nan() || self.col2.is_nan()
}
pub fn is_finite(&self) -> bool {
self.col0.is_finite() && self.col1.is_finite() && self.col2.is_finite()
}
pub fn to_mat4_affine(&self) -> Mat4 {
Mat4::new(
Vec4::new(self.col0.x, self.col0.y, 0.0, 0.0),
Vec4::new(self.col1.x, self.col1.y, 0.0, 0.0),
Vec4::new(0.0, 0.0, 1.0, 0.0),
Vec4::new(self.col2.x, self.col2.y, 0.0, 1.0),
)
}
pub fn to_array_2d_row_major(&self) -> [[f32; 3]; 3] {
[
[self.col0.x, self.col1.x, self.col2.x], [self.col0.y, self.col1.y, self.col2.y], [self.col0.z, self.col1.z, self.col2.z], ]
}
pub fn to_array_row_major(&self) -> [f32; 9] {
[
self.col0.x,
self.col1.x,
self.col2.x, self.col0.y,
self.col1.y,
self.col2.y, self.col0.z,
self.col1.z,
self.col2.z, ]
}
pub fn to_array_2d_col_major(&self) -> [[f32; 3]; 3] {
[
[self.col0.x, self.col0.y, self.col0.z], [self.col1.x, self.col1.y, self.col1.z], [self.col2.x, self.col2.y, self.col2.z], ]
}
pub fn to_array_col_major(&self) -> [f32; 9] {
[
self.col0.x,
self.col0.y,
self.col0.z, self.col1.x,
self.col1.y,
self.col1.z, self.col2.x,
self.col2.y,
self.col2.z, ]
}
pub fn to_tuple_2d_row_major(&self) -> Mat3Tuple2D {
(
(self.col0.x, self.col1.x, self.col2.x), (self.col0.y, self.col1.y, self.col2.y), (self.col0.z, self.col1.z, self.col2.z), )
}
pub fn to_tuple_row_major(&self) -> Mat3Tuple {
(
self.col0.x,
self.col1.x,
self.col2.x, self.col0.y,
self.col1.y,
self.col2.y, self.col0.z,
self.col1.z,
self.col2.z, )
}
pub fn to_tuple_2d_col_major(&self) -> Mat3Tuple2D {
(
(self.col0.x, self.col0.y, self.col0.z), (self.col1.x, self.col1.y, self.col1.z), (self.col2.x, self.col2.y, self.col2.z), )
}
pub fn to_tuple_col_major(&self) -> Mat3Tuple {
(
self.col0.x,
self.col0.y,
self.col0.z, self.col1.x,
self.col1.y,
self.col1.z, self.col2.x,
self.col2.y,
self.col2.z, )
}
pub fn from_2d_array(arr: [[f32; 3]; 3]) -> Mat3 {
Mat3::new(
Vec3::new(arr[0][0], arr[1][0], arr[2][0]), Vec3::new(arr[0][1], arr[1][1], arr[2][1]), Vec3::new(arr[0][2], arr[1][2], arr[2][2]), )
}
pub fn from_array(arr: [f32; 9]) -> Mat3 {
Mat3::new(
Vec3::new(arr[0], arr[3], arr[6]), Vec3::new(arr[1], arr[4], arr[7]), Vec3::new(arr[2], arr[5], arr[8]), )
}
pub fn from_2d_tuple(t: Mat3Tuple2D) -> Mat3 {
Mat3::new(
Vec3::new(t.0 .0, t.1 .0, t.2 .0), Vec3::new(t.0 .1, t.1 .1, t.2 .1), Vec3::new(t.0 .2, t.1 .2, t.2 .2), )
}
pub fn from_tuple(t: Mat3Tuple) -> Mat3 {
Mat3::new(
Vec3::new(t.0, t.3, t.6), Vec3::new(t.1, t.4, t.7), Vec3::new(t.2, t.5, t.8), )
}
}
impl Add for Mat3 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::new(
self.col0 + rhs.col0,
self.col1 + rhs.col1,
self.col2 + rhs.col2,
)
}
}
impl Sub for Mat3 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self::new(
self.col0 - rhs.col0,
self.col1 - rhs.col1,
self.col2 - rhs.col2,
)
}
}
impl Mul for Mat3 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self * rhs.col0, self * rhs.col1, self * rhs.col2)
}
}
impl Mul<Vec3> for Mat3 {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Self::Output {
self.col0 * rhs.x + self.col1 * rhs.y + self.col2 * rhs.z
}
}
impl Mul<f32> for Mat3 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self::new(self.col0 * rhs, self.col1 * rhs, self.col2 * rhs)
}
}
impl Mul<Mat3> for f32 {
type Output = Mat3;
#[inline]
fn mul(self, rhs: Mat3) -> Self::Output {
rhs * self
}
}
impl Div<f32> for Mat3 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self::new(self.col0 / rhs, self.col1 / rhs, self.col2 / rhs)
}
}
impl Neg for Mat3 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::new(-self.col0, -self.col1, -self.col2)
}
}
impl AddAssign for Mat3 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.col0 += rhs.col0;
self.col1 += rhs.col1;
self.col2 += rhs.col2;
}
}
impl SubAssign for Mat3 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.col0 -= rhs.col0;
self.col1 -= rhs.col1;
self.col2 -= rhs.col2;
}
}
impl MulAssign for Mat3 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl MulAssign<f32> for Mat3 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.col0 *= rhs;
self.col1 *= rhs;
self.col2 *= rhs;
}
}
impl DivAssign<f32> for Mat3 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.col0 /= rhs;
self.col1 /= rhs;
self.col2 /= rhs;
}
}
impl Default for Mat3 {
#[inline]
fn default() -> Self {
Self::IDENTITY }
}
impl PartialEq for Mat3 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.col0 == other.col0 && self.col1 == other.col1 && self.col2 == other.col2
}
}
impl Index<usize> for Mat3 {
type Output = Vec3;
#[inline]
fn index(&self, col_index: usize) -> &Self::Output {
match col_index {
0 => &self.col0,
1 => &self.col1,
2 => &self.col2,
_ => panic!("Mat3 column index out of bounds: {}", col_index),
}
}
}
impl IndexMut<usize> for Mat3 {
#[inline]
fn index_mut(&mut self, col_index: usize) -> &mut Self::Output {
match col_index {
0 => &mut self.col0,
1 => &mut self.col1,
2 => &mut self.col2,
_ => panic!("Mat3 column index out of bounds: {}", col_index),
}
}
}
impl fmt::Display for Mat3 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{:.3}, {:.3}, {:.3}]\n[{:.3}, {:.3}, {:.3}]\n[{:.3}, {:.3}, {:.3}]",
self.col0.x,
self.col1.x,
self.col2.x,
self.col0.y,
self.col1.y,
self.col2.y,
self.col0.z,
self.col1.z,
self.col2.z
)
}
}
impl approx::AbsDiffEq for Mat3 {
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)
&& self.col2.abs_diff_eq(&other.col2, epsilon)
}
}
impl approx::RelativeEq for Mat3 {
#[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)
&& self.col2.relative_eq(&other.col2, epsilon, max_relative)
}
}