1use std::ops::{Mul, MulAssign};
2
3use wasm_bindgen::prelude::*;
4
5use super::point2d::{Path2D, Point2D};
6
7#[wasm_bindgen]
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct TransformationMatrix {
11 pub a: f32,
13 pub b: f32,
15 pub c: f32,
17 pub d: f32,
19 pub e: f32,
21 pub f: f32,
23}
24
25#[wasm_bindgen]
26impl TransformationMatrix {
27 #[wasm_bindgen(constructor, return_description = "A 2D transformation matrix following the CSS matrix transform format.")]
29 pub fn new(
30 #[wasm_bindgen(param_description = "The a component of the matrix.")]
31 a: f32,
32 #[wasm_bindgen(param_description = "The b component of the matrix.")]
33 b: f32,
34 #[wasm_bindgen(param_description = "The c component of the matrix.")]
35 c: f32,
36 #[wasm_bindgen(param_description = "The d component of the matrix.")]
37 d: f32,
38 #[wasm_bindgen(param_description = "The e component of the matrix.")]
39 e: f32,
40 #[wasm_bindgen(param_description = "The f component of the matrix.")]
41 f: f32
42 ) -> TransformationMatrix {
43 TransformationMatrix { a, b, c, d, e, f }
44 }
45
46 #[wasm_bindgen(return_description = "The identity matrix.")]
48 pub fn identity() -> TransformationMatrix {
49 TransformationMatrix {
50 a: 1.0,
51 b: 0.0,
52 c: 0.0,
53 d: 1.0,
54 e: 0.0,
55 f: 0.0,
56 }
57 }
58
59 #[wasm_bindgen(return_description = "The translated matrix.")]
61 pub fn translate(
62 #[wasm_bindgen(param_description = "The x value to translate by.")]
63 x: f32,
64 #[wasm_bindgen(param_description = "The y value to translate by.")]
65 y: f32
66 ) -> TransformationMatrix {
67 TransformationMatrix {
68 a: 1.0,
69 b: 0.0,
70 c: 0.0,
71 d: 1.0,
72 e: x,
73 f: y,
74 }
75 }
76
77 #[wasm_bindgen(js_name = clone)]
79 pub fn copy(&self) -> TransformationMatrix {
80 *self
81 }
82
83 #[wasm_bindgen(return_description = "The scaled matrix.")]
85 pub fn scale(
86 #[wasm_bindgen(param_description = "The x value to scale by.")]
87 x: f32,
88 #[wasm_bindgen(param_description = "The y value to scale by.")]
89 y: f32
90 ) -> TransformationMatrix {
91 TransformationMatrix {
92 a: x,
93 b: 0.0,
94 c: 0.0,
95 d: y,
96 e: 0.0,
97 f: 0.0,
98 }
99 }
100
101 #[wasm_bindgen]
103 pub fn apply(&mut self, other: &TransformationMatrix) {
104 *self = *other * *self;
105 }
106
107 #[wasm_bindgen(return_description = "The rotated matrix.")]
109 pub fn rotate(
110 #[wasm_bindgen(param_description = "The angle in radians to rotate by.")]
111 angle: f32
112 ) -> TransformationMatrix {
113 let (sin, cos) = angle.sin_cos();
114 TransformationMatrix {
115 a: cos,
116 b: sin,
117 c: -sin,
118 d: cos,
119 e: 0.0,
120 f: 0.0,
121 }
122 }
123
124 #[wasm_bindgen(return_description = "The inverse matrix.")]
126 pub fn inverse(self) -> TransformationMatrix {
127 let det = self.a * self.d - self.b * self.c;
128 TransformationMatrix {
129 a: self.d / det,
130 b: -self.b / det,
131 c: -self.c / det,
132 d: self.a / det,
133 e: (self.c * self.f - self.d * self.e) / det,
134 f: (self.b * self.e - self.a * self.f) / det,
135 }
136 }
137}
138
139impl TransformationMatrix {
140 pub fn from_svg_transform(transform_matrix: usvg::Transform) -> TransformationMatrix {
141 TransformationMatrix {
142 a: transform_matrix.sx,
143 b: transform_matrix.kx,
144 c: transform_matrix.ky,
145 d: transform_matrix.sy,
146 e: transform_matrix.tx,
147 f: transform_matrix.ty,
148 }
149 }
150}
151
152impl Mul<Vec<Point2D>> for TransformationMatrix {
153 type Output = Vec<Point2D>;
154
155 fn mul(self, points: Vec<Point2D>) -> Vec<Point2D> {
156 points.into_iter().map(|point| self * point).collect()
157 }
158}
159
160impl Mul<TransformationMatrix> for TransformationMatrix {
161 type Output = TransformationMatrix;
162
163 fn mul(self, other: TransformationMatrix) -> TransformationMatrix {
164 TransformationMatrix {
165 a: self.a * other.a + self.c * other.b,
166 b: self.b * other.a + self.d * other.b,
167 c: self.a * other.c + self.c * other.d,
168 d: self.b * other.c + self.d * other.d,
169 e: self.a * other.e + self.c * other.f + self.e,
170 f: self.b * other.e + self.d * other.f + self.f,
171 }
172 }
173}
174
175impl MulAssign<TransformationMatrix> for TransformationMatrix {
176 fn mul_assign(&mut self, other: TransformationMatrix) {
177 *self = *self * other;
178 }
179}
180
181impl Mul<Point2D> for TransformationMatrix {
182 type Output = Point2D;
183
184 fn mul(self, point: Point2D) -> Point2D {
185 Point2D {
186 x: self.a * point.x + self.c * point.y + self.e,
187 y: self.b * point.x + self.d * point.y + self.f,
188 }
189 }
190}
191
192impl Mul<Path2D> for TransformationMatrix {
193 type Output = Path2D;
194
195 fn mul(self, path: Path2D) -> Path2D {
196 Path2D::new(path.points().iter().map(|point| self * *point).collect())
197 }
198}
199
200#[wasm_bindgen(return_description = "The product of the two matrices.", unchecked_return_type = "number[]")]
202pub fn matrix_product(
203 #[wasm_bindgen(param_description = "The first matrix to multiply as a flat array.", unchecked_param_type = "number[]")]
204 a: Vec<f32>,
205 #[wasm_bindgen(param_description = "The second matrix to multiply as a flat array.", unchecked_param_type = "number[]")]
206 b: Vec<f32>,
207 #[wasm_bindgen(param_description = "The number of rows in the first matrix.")]
208 a_rows: usize,
209 #[wasm_bindgen(param_description = "The number of columns in the first matrix.")]
210 a_columns: usize,
211 #[wasm_bindgen(param_description = "The number of columns in the second matrix.")]
212 b_columns: usize,
213) -> Vec<f32> {
214 let mut result = vec![0.0; a_rows * b_columns];
215 for i in 0..a_rows {
216 for j in 0..b_columns {
217 for k in 0..a_columns {
218 result[i * b_columns + j] += a[i * a_columns + k] * b[k * b_columns + j];
219 }
220 }
221 }
222 result
223}
224
225#[wasm_bindgen(return_description = "The product of the matrix and the path.")]
227pub fn matrix_product_path(
228 #[wasm_bindgen(param_description = "The matrix to multiply as a flat array.", unchecked_param_type = "number[]")]
229 matrix: Vec<f32>,
230 #[wasm_bindgen(param_description = "The path to multiply.")]
231 path: &Path2D,
232 #[wasm_bindgen(param_description = "The number of rows in the first matrix.")]
233 a_rows: usize,
234 #[wasm_bindgen(param_description = "The number of columns in the first matrix.")]
235 a_columns: usize,
236 #[wasm_bindgen(param_description = "The number of columns in the second matrix.")]
237 b_columns: usize
238) -> Path2D {
239 let result = matrix_product(matrix, path.points().iter().map(|point| vec![point.x, point.y]).flatten().collect(), a_rows, a_columns, b_columns);
240 Path2D::new(result.chunks(2).map(|chunk| Point2D { x: chunk[0], y: chunk[1] }).collect())
241}