otf_pdf_content/
matrix.rs1#[derive(Debug, Clone, Copy, PartialEq)]
6pub struct Matrix {
7 pub a: f64,
9 pub b: f64,
11 pub c: f64,
13 pub d: f64,
15 pub e: f64,
17 pub f: f64,
19}
20
21impl Matrix {
22 pub const IDENTITY: Matrix = Matrix::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
24
25 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 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 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 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 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 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 assert_eq!(scale.then(&shift).apply(1.0, 1.0), (12.0, 2.0));
87 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}