use core::ops::{Mul, MulAssign};
use crate::fixnum::{FixedWidthSignedInteger, Num, SignedNumber, Vector2D, num, vec2};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[allow(missing_docs)]
pub struct AffineMatrix<T = Num<i32, 8>> {
pub a: T,
pub b: T,
pub c: T,
pub d: T,
pub x: T,
pub y: T,
}
impl<T: SignedNumber> AffineMatrix<T> {
#[must_use]
pub fn identity() -> Self {
AffineMatrix {
a: T::one(),
b: T::zero(),
c: T::zero(),
d: T::one(),
x: T::zero(),
y: T::zero(),
}
}
#[must_use]
pub fn from_translation(position: Vector2D<T>) -> Self {
AffineMatrix {
a: T::one(),
b: T::zero(),
c: T::zero(),
d: T::one(),
x: position.x,
y: position.y,
}
}
#[must_use]
pub fn position(&self) -> Vector2D<T> {
vec2(self.x, self.y)
}
#[must_use]
pub fn from_scale(scale: Vector2D<T>) -> Self {
Self {
a: scale.x,
b: T::zero(),
c: T::zero(),
d: scale.y,
x: T::zero(),
y: T::zero(),
}
}
#[must_use]
pub fn from_shear(shear: Vector2D<T>) -> AffineMatrix<T> {
AffineMatrix {
a: shear.x * shear.y + T::one(),
b: shear.x,
c: shear.y,
d: T::one(),
x: T::zero(),
y: T::zero(),
}
}
}
impl<I, const N: usize> AffineMatrix<Num<I, N>>
where
I: FixedWidthSignedInteger,
{
#[must_use]
pub fn from_rotation(angle: Num<I, N>) -> Self {
let cos = angle.cos();
let sin = angle.sin();
AffineMatrix {
a: cos,
b: -sin,
c: sin,
d: cos,
x: num!(0),
y: num!(0),
}
}
#[must_use]
pub fn change_base<J, const M: usize>(self) -> AffineMatrix<Num<J, M>>
where
J: FixedWidthSignedInteger + From<I>,
{
AffineMatrix {
a: self.a.change_base(),
b: self.b.change_base(),
c: self.c.change_base(),
d: self.d.change_base(),
x: self.x.change_base(),
y: self.y.change_base(),
}
}
}
impl<T: SignedNumber> Default for AffineMatrix<T> {
fn default() -> Self {
AffineMatrix::identity()
}
}
impl<T: SignedNumber> Mul for AffineMatrix<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
AffineMatrix {
a: self.a * rhs.a + self.b * rhs.c,
b: self.a * rhs.b + self.b * rhs.d,
c: self.c * rhs.a + self.d * rhs.c,
d: self.c * rhs.b + self.d * rhs.d,
x: self.a * rhs.x + self.b * rhs.y + self.x,
y: self.c * rhs.x + self.d * rhs.y + self.y,
}
}
}
impl<T: SignedNumber> MulAssign for AffineMatrix<T> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: SignedNumber> Mul<Vector2D<T>> for AffineMatrix<T> {
type Output = Vector2D<T>;
fn mul(self, rhs: Vector2D<T>) -> Self::Output {
vec2(
self.a * rhs.x + self.b * rhs.y + self.x,
self.c * rhs.x + self.d * rhs.y + self.y,
)
}
}
#[cfg(test)]
mod tests {
use crate::fixnum::num;
use super::*;
#[test_case]
fn test_simple_multiply(_: &mut crate::Gba) {
let position: Vector2D<Num<i32, 8>> = (20, 10).into();
let a = AffineMatrix::from_translation(position);
let b = AffineMatrix::default();
let c = a * b;
assert_eq!(c.position(), position);
let d = AffineMatrix::from_rotation(num!(0.5));
let e = a * d;
assert_eq!(e.position(), position);
assert_eq!(d * d, AffineMatrix::identity());
}
}