use crate::{
mat4::Mat4,
vec::{Vec3, Vec4},
Quat,
};
use approx::AbsDiffEq;
use std::f32::consts::FRAC_PI_2;
fn mat4_approx_eq(a: &Mat4, b: &Mat4) -> bool {
a.col0.abs_diff_eq(&b.col0, 1e-5)
&& a.col1.abs_diff_eq(&b.col1, 1e-5)
&& a.col2.abs_diff_eq(&b.col2, 1e-5)
&& a.col3.abs_diff_eq(&b.col3, 1e-5)
}
#[test]
fn test_construction_and_constants() {
let m = Mat4::new(
Vec4::new(1.0, 0.0, 0.0, 0.0),
Vec4::new(0.0, 1.0, 0.0, 0.0),
Vec4::new(0.0, 0.0, 1.0, 0.0),
Vec4::new(0.0, 0.0, 0.0, 1.0),
);
assert_eq!(m.col0, Vec4::new(1.0, 0.0, 0.0, 0.0));
assert_eq!(m, Mat4::IDENTITY);
assert_eq!(Mat4::default(), Mat4::IDENTITY);
assert_eq!(
Mat4::ZERO,
Mat4::new(Vec4::ZERO, Vec4::ZERO, Vec4::ZERO, Vec4::ZERO)
);
assert_eq!(Mat4::IDENTITY.determinant(), 1.0);
assert!(Mat4::NAN.is_nan());
}
#[test]
fn test_from_and_to_array_and_tuple() {
let m = Mat4::from_rows(
Vec4::new(1., 2., 3., 4.),
Vec4::new(5., 6., 7., 8.),
Vec4::new(9., 10., 11., 12.),
Vec4::new(13., 14., 15., 16.),
);
let col_major_arr = m.to_array_col_major();
assert_eq!(
col_major_arr,
[1., 5., 9., 13., 2., 6., 10., 14., 3., 7., 11., 15., 4., 8., 12., 16.]
);
assert!(mat4_approx_eq(&m, &Mat4::from_array(col_major_arr)));
let row_major_arr = m.to_array_row_major();
assert_eq!(
row_major_arr,
[1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16.]
);
assert_eq!(m.transpose().to_array_col_major(), row_major_arr);
}
#[test]
fn test_transformation_constructors() {
let t = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
let p = t.transform_point3(Vec3::new(10.0, 10.0, 10.0));
assert!(p.abs_diff_eq(&Vec3::new(11.0, 12.0, 13.0), 1e-6));
let s = Mat4::from_scale(Vec3::new(2.0, 3.0, 4.0));
let v = s.transform_vector3(Vec3::new(1.0, 1.0, 1.0));
assert!(v.abs_diff_eq(&Vec3::new(2.0, 3.0, 4.0), 1e-6));
let q = Quat::from_rotation_y(FRAC_PI_2);
let r = Mat4::from_quat(q);
let rotated_v = r.transform_vector3(Vec3::new(1.0, 0.0, 0.0));
assert!(rotated_v.abs_diff_eq(&Vec3::new(0.0, 0.0, -1.0), 1e-6));
}
#[test]
fn test_from_trs() {
let translation = Vec3::new(1.0, 2.0, 3.0);
let rotation = Quat::from_rotation_y(FRAC_PI_2);
let scale = Vec3::new(2.0, 3.0, 4.0);
let m = Mat4::from_trs(translation, rotation, scale);
let point = Vec3::new(1.0, 0.0, 0.0);
let transformed = m.transform_point3(point);
assert!(transformed.abs_diff_eq(&Vec3::new(1.0, 2.0, 1.0), 1e-6));
}
#[test]
fn test_look_at_and_look_to() {
let eye = Vec3::new(0.0, 2.0, 5.0);
let target = Vec3::new(0.0, 2.0, 0.0);
let up = Vec3::new(0.0, 1.0, 0.0);
let view_matrix = Mat4::look_at(eye, target, up);
let view_matrix_to = Mat4::look_to(eye, target - eye, up);
assert!(mat4_approx_eq(&view_matrix, &view_matrix_to));
let transformed_eye = view_matrix.transform_point3(eye);
assert!(transformed_eye.abs_diff_eq(&Vec3::ZERO, 1e-6));
let transformed_target = view_matrix.transform_point3(target);
assert!(transformed_target.abs_diff_eq(&Vec3::new(0.0, 0.0, -5.0), 1e-6));
}
#[test]
fn test_determinant_and_inverse() {
let m = Mat4::from_trs(
Vec3::new(5.0, -2.0, 3.0),
Quat::from_euler_angles(0.5, 1.2, -0.3),
Vec3::new(1.0, 2.0, 0.5),
);
let det = m.determinant();
assert!((det - (1.0 * 2.0 * 0.5)).abs() < 1e-5);
let inv_m = m.inverse().unwrap();
let identity = m * inv_m;
assert!(mat4_approx_eq(&identity, &Mat4::IDENTITY));
let singular = Mat4::from_scale(Vec3::new(1.0, 0.0, 1.0));
assert!(!singular.is_invertible());
assert!(singular.inverse().is_none());
}
#[test]
fn test_perspective_projection() {
let proj = Mat4::perspective(FRAC_PI_2, 16.0 / 9.0, 0.1, 100.0);
let point_in_front = Vec3::new(0.0, 0.0, -10.0);
let projected = proj.project_point3(point_in_front);
assert!(projected.x.abs() < 1e-6);
assert!(projected.y.abs() < 1e-6);
assert!(projected.z > -1.0 && projected.z < 1.0);
let point_on_near = Vec3::new(1.0, 1.0, -0.1);
let projected_near = proj.project_point3(point_on_near);
assert!((projected_near.z - (-1.0)).abs() < 1e-6);
}
#[test]
fn test_orthographic_projection() {
let proj = Mat4::orthographic(-10.0, 10.0, -10.0, 10.0, 0.1, 100.0);
let center = proj.project_point3(Vec3::new(0.0, 0.0, -50.0));
assert!(center.x.abs() < 1e-6 && center.y.abs() < 1e-6);
let top_right = proj.project_point3(Vec3::new(10.0, 10.0, -10.0));
assert!((top_right.x - 1.0).abs() < 1e-6);
assert!((top_right.y - 1.0).abs() < 1e-6);
}
#[test]
fn test_operator_overloads() {
let m1 = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
let m2 = Mat4::from_scale(Vec3::new(2.0, 2.0, 2.0));
let sum = m1 + m2;
assert_eq!(sum.col0.x, 3.0);
assert_eq!(sum.col3.w, 2.0);
let diff = m1 - Mat4::IDENTITY;
assert_eq!(diff.col0.x, 0.0);
assert_eq!(diff.col3, Vec4::new(1.0, 2.0, 3.0, 0.0));
let scaled = Mat4::IDENTITY * 5.0;
assert_eq!(scaled.determinant(), 5.0_f32.powi(4));
let neg = -Mat4::IDENTITY;
assert_eq!(neg.col0, Vec4::new(-1.0, 0.0, 0.0, 0.0));
let v = Vec4::new(1.0, 2.0, 3.0, 1.0);
let transformed = m1 * v;
assert_eq!(transformed, Vec4::new(2.0, 4.0, 6.0, 1.0));
}
#[test]
fn test_assignment_operators() {
let mut m1 = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
let m2 = Mat4::IDENTITY;
m1 += m2;
assert_eq!(m1.col0.x, 2.0);
assert_eq!(m1.col3.w, 2.0);
m1 -= m2;
assert_eq!(m1.col0.x, 1.0);
let mut m3 = Mat4::from_scale(Vec3::new(2.0, 2.0, 2.0));
m3 *= m3;
assert_eq!(m3.col0.x, 4.0);
m3 /= 2.0;
assert_eq!(m3.col0.x, 2.0);
}
#[test]
fn test_index_access() {
let mut m = Mat4::from_rows(
Vec4::new(1., 2., 3., 4.),
Vec4::new(5., 6., 7., 8.),
Vec4::new(9., 10., 11., 12.),
Vec4::new(13., 14., 15., 16.),
);
assert_eq!(m[0], Vec4::new(1.0, 5.0, 9.0, 13.0));
assert_eq!(m[1], Vec4::new(2.0, 6.0, 10.0, 14.0));
m[2] = Vec4::ZERO;
assert_eq!(m[2], Vec4::ZERO);
}
#[test]
#[should_panic]
fn test_index_out_of_bounds() {
let m = Mat4::IDENTITY;
let _ = m[4]; }