use core::ops::Mul;
use super::{Angle, Scale, Vector};
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Matrix {
angle: f32,
scale: f32,
columns: ((f32, f32), (f32, f32)),
}
impl Matrix {
#[must_use]
pub fn new(angle: f32, scale: f32) -> Self {
let sin_scale = angle.sin() * scale;
let cos_scale = angle.cos() * scale;
Self {
angle,
scale,
columns: ((cos_scale, sin_scale), (-sin_scale, cos_scale)),
}
}
#[must_use]
pub const fn identity() -> Self {
Self {
angle: 0.0,
scale: 1.0,
columns: ((1.0, 0.0), (0.0, 1.0)),
}
}
#[must_use]
pub const fn transform_x(&self, vector: &Vector) -> f32 {
vector.x * self.columns.0.0 + vector.y * self.columns.1.0
}
#[must_use]
pub const fn transform_y(&self, vector: &Vector) -> f32 {
vector.x * self.columns.0.1 + vector.y * self.columns.1.1
}
#[must_use]
pub const fn transform_z(&self, vector: &Vector) -> f32 {
vector.z * self.scale
}
}
impl Angle for Matrix {
fn angle(&self) -> f32 {
self.angle
}
fn set_angle(&mut self, value: f32) {
*self = Self::new(value, self.scale);
}
}
impl Scale for Matrix {
fn scale(&self) -> f32 {
self.scale
}
fn set_scale(&mut self, value: f32) {
*self = Self::new(self.angle, value);
}
}
impl Mul<Vector> for Matrix {
type Output = Vector;
fn mul(self, rhs: Self::Output) -> Self::Output {
Self::Output::new(
self.transform_x(&rhs),
self.transform_y(&rhs),
self.transform_z(&rhs),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry;
#[test]
fn new() {
const ANGLE: f32 = geometry::into_rads(45.0);
const SCALE: f32 = 7.0;
let matrix = Matrix::new(ANGLE, SCALE);
assert_eq!(matrix.angle(), ANGLE);
assert_eq!(matrix.scale(), SCALE);
}
#[test]
fn identity() {
const MATRIX: Matrix = Matrix::identity();
const VECTOR: Vector = Vector::new(2.0, 3.0, 6.0);
assert_eq!(MATRIX.angle(), 0.0);
assert_eq!(MATRIX.scale(), 1.0);
assert_eq!(MATRIX * VECTOR, VECTOR);
}
#[test]
fn transform_x() {
const SCALE: f32 = 7.0;
const MAGNITUDE2: f32 = 3.6055512;
const Z: f32 = 6.0;
const VECTOR: Vector = Vector::new(MAGNITUDE2, 0.0, Z);
let angle = geometry::into_rads(45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_x(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).x
);
let angle = geometry::into_rads(135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_x(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).x
);
let angle = geometry::into_rads(-135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_x(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).x
);
let angle = geometry::into_rads(-45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_x(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).x
);
}
#[test]
fn transform_y() {
const SCALE: f32 = 7.0;
const MAGNITUDE2: f32 = 3.6055512;
const Z: f32 = 6.0;
const VECTOR: Vector = Vector::new(MAGNITUDE2, 0.0, Z);
let angle = geometry::into_rads(45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_y(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).y
);
let angle = geometry::into_rads(135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_y(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).y
);
let angle = geometry::into_rads(-135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_y(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).y
);
let angle = geometry::into_rads(-45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_y(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).y
);
}
#[test]
fn transform_z() {
const SCALE: f32 = 7.0;
const MAGNITUDE2: f32 = 3.6055512;
const Z: f32 = 6.0;
const VECTOR: Vector = Vector::new(MAGNITUDE2, 0.0, Z);
let angle = geometry::into_rads(45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_z(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).z
);
let angle = geometry::into_rads(135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_z(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).z
);
let angle = geometry::into_rads(-135.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_z(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).z
);
let angle = geometry::into_rads(-45.0);
assert_eq!(
Matrix::new(angle, SCALE).transform_z(&VECTOR),
(Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE).z
);
}
#[test]
fn set_angle() {
const ANGLE: f32 = 45.0;
let mut matrix = Matrix::identity();
matrix.set_angle(ANGLE);
assert_eq!(matrix.angle(), ANGLE);
assert_eq!(matrix.scale(), 1.0);
}
#[test]
fn set_scale() {
const SCALE: f32 = 7.0;
let mut matrix = Matrix::identity();
matrix.set_scale(SCALE);
assert_eq!(matrix.scale(), SCALE);
assert_eq!(matrix.angle(), 0.0);
}
#[test]
fn mul() {
const SCALE: f32 = 7.0;
const MAGNITUDE2: f32 = 3.6055512;
const Z: f32 = 6.0;
const VECTOR: Vector = Vector::new(MAGNITUDE2, 0.0, Z);
let angle = geometry::into_rads(45.0);
assert_eq!(
Matrix::new(angle, SCALE) * VECTOR,
Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE
);
let angle = geometry::into_rads(135.0);
assert_eq!(
Matrix::new(angle, SCALE) * VECTOR,
Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE
);
let angle = geometry::into_rads(-135.0);
assert_eq!(
Matrix::new(angle, SCALE) * VECTOR,
Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE
);
let angle = geometry::into_rads(-45.0);
assert_eq!(
Matrix::new(angle, SCALE) * VECTOR,
Vector::from_angle(angle, MAGNITUDE2, Z) * SCALE
);
}
}