use std::ops;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Matrix(pub [[f32; 2]; 3]);
impl Matrix {
#[inline]
pub fn identity() -> Matrix {
Matrix([
[1.0, 0.0],
[0.0, 1.0],
[0.0, 0.0],
])
}
#[inline]
pub fn scale(factor: f32) -> Matrix {
Matrix([
[factor, 0.0 ],
[ 0.0 , factor],
[ 0.0 , 0.0 ],
])
}
#[inline]
pub fn scale_wh(w: f32, h: f32) -> Matrix {
Matrix([
[ w, 0.0],
[0.0, h ],
[0.0, 0.0],
])
}
#[inline]
pub fn translate(x: f32, y: f32) -> Matrix {
Matrix([
[1.0, 0.0],
[0.0, 1.0],
[ x, y ],
])
}
#[inline]
pub fn rotate(radians: f32) -> Matrix {
let cos = radians.cos();
let sin = radians.sin();
Matrix([
[cos, -sin],
[sin, cos],
[0.0, 0.0],
])
}
#[inline]
pub fn skew_x(radians: f32) -> Matrix {
let tan = radians.tan();
Matrix([
[1.0, 0.0],
[tan, 1.0],
[0.0, 0.0],
])
}
pub fn invert(&self) -> Option<[[f32; 3]; 3]> {
let me = self.0;
let det = me[0][0] * me[1][1] - me[1][0] * me[0][1];
if det == 0.0 || det != det {
return None;
}
let det_inv = 1.0 / det;
Some([
[det_inv * me[1][1], det_inv * -me[0][1], 0.0],
[det_inv * -me[1][0], det_inv * me[0][0], 0.0],
[
det_inv * (me[1][0]*me[2][1]-me[2][0]*me[1][1]),
det_inv * (me[2][0]*me[0][1]-me[0][0]*me[2][1]),
det_inv * (me[0][0]*me[1][1]-me[1][0]*me[0][1])
]
])
}
}
impl ops::Mul for Matrix {
type Output = Matrix;
#[inline]
fn mul(self, other: Matrix) -> Matrix {
let me = self.0;
let other = other.0;
let a = me[0][0] * other[0][0] + me[1][0] * other[0][1];
let b = me[0][0] * other[1][0] + me[1][0] * other[1][1];
let c = me[0][0] * other[2][0] + me[1][0] * other[2][1] + me[2][0];
let d = me[0][1] * other[0][0] + me[1][1] * other[0][1];
let e = me[0][1] * other[1][0] + me[1][1] * other[1][1];
let f = me[0][1] * other[2][0] + me[1][1] * other[2][1] + me[2][1];
Matrix([
[a, d],
[b, e],
[c, f],
])
}
}
impl ops::Mul<[f32; 3]> for Matrix {
type Output = [f32; 3];
#[inline]
fn mul(self, other: [f32; 3]) -> [f32; 3] {
let me = self.0;
let x = me[0][0] * other[0] + me[1][0] * other[1] + me[2][0] * other[2];
let y = me[0][1] * other[0] + me[1][1] * other[1] + me[2][1] * other[2];
let z = other[2];
[x, y, z]
}
}
impl Into<[[f32; 3]; 3]> for Matrix {
#[inline]
fn into(self) -> [[f32; 3]; 3] {
let me = self.0;
[
[me[0][0], me[0][1], 0.0],
[me[1][0], me[1][1], 0.0],
[me[2][0], me[2][1], 1.0],
]
}
}
impl Into<[[f32; 4]; 4]> for Matrix {
#[inline]
fn into(self) -> [[f32; 4]; 4] {
let m = self.0;
[
[m[0][0], m[0][1], 0.0, 0.0],
[m[1][0], m[1][1], 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0],
[m[2][0], m[2][1], 0.0, 1.0]
]
}
}
#[cfg(test)]
mod tests {
use std::f32::consts::PI;
use matrix::Matrix;
#[test]
fn multiply() {
assert_eq!(Matrix::scale(2.0) * Matrix::scale(3.0),
Matrix::scale(6.0));
assert_eq!(Matrix::translate(1.0, 2.0) * Matrix::translate(4.0, -1.0),
Matrix::translate(5.0, 1.0));
}
#[test]
fn invert() {
assert_eq!(Matrix::scale(2.0).invert().unwrap(),
Into::<[[f32; 3]; 3]>::into(Matrix::scale(0.5)));
assert_eq!(Matrix::translate(4.0, 0.5).invert().unwrap(),
Into::<[[f32; 3]; 3]>::into(Matrix::translate(-4.0, -0.5)));
assert_eq!(Matrix::rotate(PI).invert().unwrap(),
Into::<[[f32; 3]; 3]>::into(Matrix::rotate(-PI)));
}
}