use std::f32::consts::PI;
use std::ops::Mul;
use mupdf_sys::*;
use crate::{impl_ffi_traits, Point};
#[derive(
Debug, Clone, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
)]
#[repr(C)]
pub struct Matrix {
pub a: f32,
pub b: f32,
pub c: f32,
pub d: f32,
pub e: f32,
pub f: f32,
}
impl Matrix {
pub const IDENTITY: Matrix = Matrix {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: 0.0,
f: 0.0,
};
pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
Self { a, b, c, d, e, f }
}
pub fn new_scale(x: f32, y: f32) -> Self {
Self::new(x, 0.0, 0.0, y, 0.0, 0.0)
}
pub fn new_translate(x: f32, y: f32) -> Self {
Self::new(1.0, 0.0, 0.0, 1.0, x, y)
}
pub fn new_rotate(degrees: f32) -> Self {
let mut degrees = degrees;
while degrees < 0.0 {
degrees += 360.0;
}
while degrees >= 360.0 {
degrees -= 360.0
}
let (sin, cos) = if (0.0 - degrees).abs() < 0.0001 {
(0.0, 1.0)
} else if (90.0 - degrees).abs() < 0.0001 {
(1.0, 0.0)
} else if (180.0 - degrees).abs() < 0.0001 {
(0.0, -1.0)
} else if (270.0 - degrees).abs() < 0.0001 {
(-1.0, 0.0)
} else {
((degrees * PI / 180.0).sin(), (degrees * PI / 180.0).cos())
};
Self::new(cos, sin, -sin, cos, 0.0, 0.0)
}
pub fn concat(&mut self, m: Matrix) -> &mut Self {
let a = self.a * m.a + self.b * m.c;
let b = self.a * m.b + self.b * m.d;
let c = self.c * m.a + self.d * m.c;
let d = self.c * m.b + self.d * m.d;
let e = self.e * m.a + self.f * m.c + m.e;
let f = self.e * m.b + self.f * m.d + m.f;
self.a = a;
self.b = b;
self.c = c;
self.d = d;
self.e = e;
self.f = f;
self
}
pub fn scale(&mut self, sx: f32, sy: f32) -> &mut Self {
self.a *= sx;
self.b *= sx;
self.c *= sy;
self.d *= sy;
self
}
pub fn rotate(&mut self, degrees: f32) -> &mut Self {
let degrees = degrees.rem_euclid(360.0);
if (0.0 - degrees).abs() < 0.0001 {
} else if (90.0 - degrees).abs() < 0.0001 {
let save_a = self.a;
let save_b = self.b;
self.a = self.c;
self.b = self.d;
self.c = -save_a;
self.d = -save_b;
} else if (180.0 - degrees).abs() < 0.0001 {
self.a = -self.a;
self.b = -self.b;
self.c = -self.c;
self.d = -self.d;
} else if (270.0 - degrees).abs() < 0.0001 {
let save_a = self.a;
let save_b = self.b;
self.a = -self.c;
self.b = -self.d;
self.c = save_a;
self.d = save_b;
} else {
let sin = (degrees * PI / 180.0).sin();
let cos = (degrees * PI / 180.0).cos();
let save_a = self.a;
let save_b = self.b;
self.a = cos * save_a + sin * self.c;
self.b = cos * save_b + sin * self.d;
self.c = -sin * save_a + cos * self.c;
self.d = -sin * save_b + cos * self.d;
}
self
}
pub fn pre_translate(&mut self, x: f32, y: f32) -> &mut Self {
self.e += x * self.a + y * self.c;
self.f += x * self.b + y * self.d;
self
}
pub fn pre_shear(&mut self, h: f32, v: f32) -> &mut Self {
let a = self.a;
let b = self.b;
self.a += v * self.c;
self.b += v * self.d;
self.c += h * a;
self.d += h * b;
self
}
pub fn expansion(&self) -> f32 {
(self.a * self.d - self.b * self.c).abs().sqrt()
}
#[inline]
pub fn invert(&self) -> Option<Self> {
let sa = self.a as f64;
let sb = self.b as f64;
let sc = self.c as f64;
let sd = self.d as f64;
let det = sa * sd - sb * sc;
if det.abs() > f64::EPSILON {
let det = 1.0 / det;
let da = sd * det;
let db = -sb * det;
let dc = -sc * det;
let dd = sa * det;
let de = -(self.e as f64) * da - (self.f as f64) * dc;
let df = -(self.e as f64) * db - (self.f as f64) * dd;
return Some(Self {
a: da as f32,
b: db as f32,
c: dc as f32,
d: dd as f32,
e: de as f32,
f: df as f32,
});
}
None }
#[inline(always)]
pub fn transform_xy(&self, x: f32, y: f32) -> (f32, f32) {
(
x * self.a + y * self.c + self.e,
x * self.b + y * self.d + self.f,
)
}
}
impl Default for Matrix {
fn default() -> Self {
Matrix::IDENTITY
}
}
impl Mul<Matrix> for Matrix {
type Output = Self;
fn mul(mut self, rhs: Matrix) -> Self::Output {
self.concat(rhs);
self
}
}
impl Mul<&Matrix> for Matrix {
type Output = Self;
fn mul(mut self, rhs: &Matrix) -> Self::Output {
self.concat(rhs.clone());
self
}
}
impl Mul<Matrix> for &Matrix {
type Output = Matrix;
fn mul(self, rhs: Matrix) -> Self::Output {
self.clone() * rhs
}
}
impl Mul<&Matrix> for &Matrix {
type Output = Matrix;
fn mul(self, rhs: &Matrix) -> Self::Output {
self.clone() * rhs.clone()
}
}
impl Mul<Point> for Matrix {
type Output = Point;
fn mul(self, rhs: Point) -> Self::Output {
rhs.transform(&self)
}
}
impl Mul<Point> for &Matrix {
type Output = Point;
fn mul(self, rhs: Point) -> Self::Output {
rhs.transform(self)
}
}
impl_ffi_traits!(Matrix, fz_matrix);
impl From<&Matrix> for fz_matrix {
fn from(val: &Matrix) -> Self {
let Matrix { a, b, c, d, e, f } = *val;
fz_matrix { a, b, c, d, e, f }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Point;
fn assert_matrix_near(actual: &Matrix, expected: &Matrix, epsilon: f32) {
assert!((actual.a - expected.a).abs() <= epsilon);
assert!((actual.b - expected.b).abs() <= epsilon);
assert!((actual.c - expected.c).abs() <= epsilon);
assert!((actual.d - expected.d).abs() <= epsilon);
assert!((actual.e - expected.e).abs() <= epsilon);
assert!((actual.f - expected.f).abs() <= epsilon);
}
fn assert_point_near(actual: Point, expected: Point, epsilon: f32) {
assert!((actual.x - expected.x).abs() <= epsilon);
assert!((actual.y - expected.y).abs() <= epsilon);
}
#[test]
fn matrix_multiplication_matches_concat() {
let scale = Matrix::new_scale(2.0, 3.0);
let translate = Matrix::new_translate(4.0, 5.0);
let mut expected = scale.clone();
expected.concat(translate.clone());
assert_matrix_near(&(scale.clone() * translate.clone()), &expected, 1e-6);
assert_matrix_near(&(&scale * &translate), &expected, 1e-6);
let mut expected_reverse = translate.clone();
expected_reverse.concat(scale.clone());
assert_matrix_near(&(translate * scale), &expected_reverse, 1e-6);
}
#[test]
fn matrix_point_multiplication_matches_transform() {
let matrix = Matrix::new(1.5, 0.0, 0.0, 2.0, 10.0, 20.0);
let point = Point::new(3.0, 4.0);
assert_point_near(&matrix * point, point.transform(&matrix), 1e-6);
assert_point_near(Matrix::IDENTITY * point, point, 1e-6);
}
}