use crate::bindgen::FS_MATRIX;
use crate::error::PdfiumError;
use crate::pdf::points::PdfPoints;
use crate::{create_transform_getters, create_transform_setters};
use std::hash::{Hash, Hasher};
use std::ops::{Add, Mul, Sub};
use vecmath::{mat3_add, mat3_det, mat3_inv, mat3_sub, mat3_transposed, row_mat3_mul, Matrix3};
pub type PdfMatrixValue = f32;
#[derive(Debug, Copy, Clone)]
pub struct PdfMatrix {
matrix: Matrix3<PdfMatrixValue>,
}
impl PdfMatrix {
pub const ZERO: PdfMatrix = Self::zero();
pub const IDENTITY: PdfMatrix = Self::identity();
#[inline]
pub(crate) fn from_pdfium(matrix: FS_MATRIX) -> Self {
Self::new(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f)
}
#[inline]
pub const fn new(
a: PdfMatrixValue,
b: PdfMatrixValue,
c: PdfMatrixValue,
d: PdfMatrixValue,
e: PdfMatrixValue,
f: PdfMatrixValue,
) -> Self {
Self {
matrix: [[a, b, 0.0], [c, d, 0.0], [e, f, 1.0]],
}
}
#[inline]
pub const fn zero() -> Self {
Self::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
}
#[inline]
pub const fn identity() -> Self {
Self::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
}
#[inline]
pub fn a(&self) -> PdfMatrixValue {
self.matrix[0][0]
}
#[inline]
pub fn set_a(&mut self, a: PdfMatrixValue) {
self.matrix[0][0] = a;
}
#[inline]
pub fn b(&self) -> PdfMatrixValue {
self.matrix[0][1]
}
#[inline]
pub fn set_b(&mut self, b: PdfMatrixValue) {
self.matrix[0][1] = b;
}
#[inline]
pub fn c(&self) -> PdfMatrixValue {
self.matrix[1][0]
}
#[inline]
pub fn set_c(&mut self, c: PdfMatrixValue) {
self.matrix[1][0] = c;
}
#[inline]
pub fn d(&self) -> PdfMatrixValue {
self.matrix[1][1]
}
#[inline]
pub fn set_d(&mut self, d: PdfMatrixValue) {
self.matrix[1][1] = d;
}
#[inline]
pub fn e(&self) -> PdfMatrixValue {
self.matrix[2][0]
}
#[inline]
pub fn set_e(&mut self, e: PdfMatrixValue) {
self.matrix[2][0] = e;
}
#[inline]
pub fn f(&self) -> PdfMatrixValue {
self.matrix[2][1]
}
#[inline]
pub fn set_f(&mut self, f: PdfMatrixValue) {
self.matrix[2][1] = f;
}
#[inline]
pub(crate) fn as_pdfium(&self) -> FS_MATRIX {
FS_MATRIX {
a: self.a(),
b: self.b(),
c: self.c(),
d: self.d(),
e: self.e(),
f: self.f(),
}
}
#[inline]
pub fn invert(&self) -> PdfMatrix {
Self {
matrix: mat3_inv(self.matrix),
}
}
#[inline]
pub fn transpose(&self) -> PdfMatrix {
Self {
matrix: mat3_transposed(self.matrix),
}
}
#[inline]
pub fn determinant(&self) -> PdfMatrixValue {
mat3_det(self.matrix)
}
#[inline]
pub fn add(&self, other: PdfMatrix) -> PdfMatrix {
Self {
matrix: mat3_add(self.matrix, other.matrix),
}
}
#[inline]
pub fn subtract(&self, other: PdfMatrix) -> PdfMatrix {
Self {
matrix: mat3_sub(self.matrix, other.matrix),
}
}
#[inline]
pub fn multiply(&self, other: PdfMatrix) -> PdfMatrix {
Self {
matrix: row_mat3_mul(self.matrix, other.matrix),
}
}
#[inline]
pub fn apply_to_points(&self, x: PdfPoints, y: PdfPoints) -> (PdfPoints, PdfPoints) {
(
PdfPoints::new(self.a() * x.value + self.c() * y.value + self.e()),
PdfPoints::new(self.b() * x.value + self.d() * y.value + self.f()),
)
}
create_transform_setters!(
Self,
Result<Self, PdfiumError>,
"this [PdfMatrix]",
"this [PdfMatrix].",
"this [PdfMatrix],"
);
fn transform_impl(
mut self,
a: PdfMatrixValue,
b: PdfMatrixValue,
c: PdfMatrixValue,
d: PdfMatrixValue,
e: PdfMatrixValue,
f: PdfMatrixValue,
) -> Result<Self, PdfiumError> {
let result = row_mat3_mul(self.matrix, [[a, b, 0.0], [c, d, 0.0], [e, f, 1.0]]);
if mat3_det(result) == 0.0 {
Err(PdfiumError::InvalidTransformationMatrix)
} else {
self.matrix = result;
Ok(self)
}
}
fn reset_matrix_impl(mut self, matrix: PdfMatrix) -> Result<Self, PdfiumError> {
self.set_a(matrix.a());
self.set_b(matrix.b());
self.set_c(matrix.c());
self.set_d(matrix.d());
self.set_e(matrix.e());
self.set_f(matrix.f());
Ok(self)
}
create_transform_getters!("this [PdfMatrix]", "this [PdfMatrix].", "this [PdfMatrix],");
#[inline]
fn get_matrix_impl(&self) -> Result<PdfMatrix, PdfiumError> {
Ok(*self)
}
}
impl PartialEq for PdfMatrix {
fn eq(&self, other: &Self) -> bool {
(self.a() - other.a()).abs() < 0.0001
&& (self.b() - other.b()).abs() < 0.0001
&& (self.c() - other.c()).abs() < 0.0001
&& (self.d() - other.d()).abs() < 0.0001
&& (self.e() - other.e()).abs() < 0.0001
&& (self.f() - other.f()).abs() < 0.0001
}
}
impl Eq for PdfMatrix {}
impl Hash for PdfMatrix {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u32(self.a().to_bits());
state.write_u32(self.b().to_bits());
state.write_u32(self.c().to_bits());
state.write_u32(self.d().to_bits());
state.write_u32(self.e().to_bits());
state.write_u32(self.f().to_bits());
}
}
impl Add for PdfMatrix {
type Output = PdfMatrix;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::add(&self, rhs)
}
}
impl Sub for PdfMatrix {
type Output = PdfMatrix;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
self.subtract(rhs)
}
}
impl Mul for PdfMatrix {
type Output = PdfMatrix;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
self.multiply(rhs)
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_matrix_apply_to_points() {
let delta_x = PdfPoints::new(50.0);
let delta_y = PdfPoints::new(-25.0);
let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
let x = PdfPoints::new(300.0);
let y = PdfPoints::new(400.0);
let result = matrix.apply_to_points(x, y);
assert_eq!(result.0, x + delta_x);
assert_eq!(result.1, y + delta_y);
}
}