Skip to main content

otf_pdf_content/
matrix.rs

1//! Affine transformation matrices, `[a b c d e f]` (ISO 32000-2 ยง8.3.4):
2//! a point `[x y 1]` times the matrix.
3
4/// An affine transformation.
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub struct Matrix {
7    /// Row 1, column 1.
8    pub a: f64,
9    /// Row 1, column 2.
10    pub b: f64,
11    /// Row 2, column 1.
12    pub c: f64,
13    /// Row 2, column 2.
14    pub d: f64,
15    /// Translation x.
16    pub e: f64,
17    /// Translation y.
18    pub f: f64,
19}
20
21impl Matrix {
22    /// The identity.
23    pub const IDENTITY: Matrix = Matrix::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
24
25    /// A matrix from its six numbers.
26    pub const fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> Self {
27        Self { a, b, c, d, e, f }
28    }
29
30    /// A translation.
31    pub const fn translate(x: f64, y: f64) -> Self {
32        Self::new(1.0, 0.0, 0.0, 1.0, x, y)
33    }
34
35    /// From six operands, if they are all finite numbers.
36    pub fn from_slice(v: &[f64]) -> Option<Self> {
37        match v {
38            [a, b, c, d, e, f] if v.iter().all(|x| x.is_finite()) => {
39                Some(Self::new(*a, *b, *c, *d, *e, *f))
40            }
41            _ => None,
42        }
43    }
44
45    /// `self` then `other`: points are transformed by `self` first.
46    pub fn then(&self, other: &Matrix) -> Matrix {
47        Matrix {
48            a: self.a * other.a + self.b * other.c,
49            b: self.a * other.b + self.b * other.d,
50            c: self.c * other.a + self.d * other.c,
51            d: self.c * other.b + self.d * other.d,
52            e: self.e * other.a + self.f * other.c + other.e,
53            f: self.e * other.b + self.f * other.d + other.f,
54        }
55    }
56
57    /// Transforms a point.
58    pub fn apply(&self, x: f64, y: f64) -> (f64, f64) {
59        (
60            x * self.a + y * self.c + self.e,
61            x * self.b + y * self.d + self.f,
62        )
63    }
64
65    /// Transforms a vector (no translation).
66    pub fn apply_vector(&self, x: f64, y: f64) -> (f64, f64) {
67        (x * self.a + y * self.c, x * self.b + y * self.d)
68    }
69}
70
71impl Default for Matrix {
72    fn default() -> Self {
73        Self::IDENTITY
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn composition_order() {
83        let scale = Matrix::new(2.0, 0.0, 0.0, 2.0, 0.0, 0.0);
84        let shift = Matrix::translate(10.0, 0.0);
85        // Scale first, then shift.
86        assert_eq!(scale.then(&shift).apply(1.0, 1.0), (12.0, 2.0));
87        // Shift first, then scale.
88        assert_eq!(shift.then(&scale).apply(1.0, 1.0), (22.0, 2.0));
89        assert_eq!(Matrix::IDENTITY.then(&shift), shift);
90        assert_eq!(scale.apply_vector(1.0, 0.0), (2.0, 0.0));
91        assert!(Matrix::from_slice(&[1.0, 0.0, 0.0, 1.0, 0.0]).is_none());
92        assert!(Matrix::from_slice(&[f64::NAN, 0.0, 0.0, 1.0, 0.0, 0.0]).is_none());
93    }
94}