1#[inline]
9pub fn round10(v: f64) -> f64 {
10 (v * 1e10).round() / 1e10
11}
12
13#[derive(Clone, Copy, Debug)]
19pub struct Matrix {
20 pub a: f64,
21 pub b: f64,
22 pub c: f64,
23 pub d: f64,
24 pub tx: f64,
25 pub ty: f64,
26}
27
28impl Matrix {
29 pub fn identity() -> Self {
31 Self {
32 a: 1.0,
33 b: 0.0,
34 c: 0.0,
35 d: 1.0,
36 tx: 0.0,
37 ty: 0.0,
38 }
39 }
40
41 pub fn new(a: f64, b: f64, c: f64, d: f64, tx: f64, ty: f64) -> Self {
43 Self { a, b, c, d, tx, ty }
44 }
45
46 pub fn translate(tx: f64, ty: f64) -> Self {
48 Self {
49 a: 1.0,
50 b: 0.0,
51 c: 0.0,
52 d: 1.0,
53 tx,
54 ty,
55 }
56 }
57
58 pub fn scale(sx: f64, sy: f64) -> Self {
60 Self {
61 a: sx,
62 b: 0.0,
63 c: 0.0,
64 d: sy,
65 tx: 0.0,
66 ty: 0.0,
67 }
68 }
69
70 pub fn rotate(angle: f64) -> Self {
72 let rad = angle.to_radians();
73 let (sin, cos) = (rad.sin(), rad.cos());
74 Self {
75 a: round10(cos),
76 b: round10(sin),
77 c: round10(-sin),
78 d: round10(cos),
79 tx: 0.0,
80 ty: 0.0,
81 }
82 }
83
84 #[inline]
88 pub fn multiply(&self, other: &Matrix) -> Matrix {
89 Matrix {
90 a: round10(self.a * other.a + self.c * other.b),
91 b: round10(self.b * other.a + self.d * other.b),
92 c: round10(self.a * other.c + self.c * other.d),
93 d: round10(self.b * other.c + self.d * other.d),
94 tx: round10(self.a * other.tx + self.c * other.ty + self.tx),
95 ty: round10(self.b * other.tx + self.d * other.ty + self.ty),
96 }
97 }
98
99 #[inline]
103 pub fn concat(&self, other: &Matrix) -> Matrix {
104 self.multiply(other)
105 }
106
107 #[inline]
109 pub fn transform_point(&self, x: f64, y: f64) -> (f64, f64) {
110 (
111 round10(self.a * x + self.c * y + self.tx),
112 round10(self.b * x + self.d * y + self.ty),
113 )
114 }
115
116 #[inline]
118 pub fn transform_delta(&self, dx: f64, dy: f64) -> (f64, f64) {
119 (
120 round10(self.a * dx + self.c * dy),
121 round10(self.b * dx + self.d * dy),
122 )
123 }
124
125 pub fn determinant(&self) -> f64 {
127 self.a * self.d - self.b * self.c
128 }
129
130 pub fn invert(&self) -> Option<Matrix> {
132 let det = self.determinant();
133 if det.abs() < 1e-20 {
134 return None;
135 }
136 let inv_det = 1.0 / det;
137 Some(Matrix {
138 a: round10(self.d * inv_det),
139 b: round10(-self.b * inv_det),
140 c: round10(-self.c * inv_det),
141 d: round10(self.a * inv_det),
142 tx: round10((self.c * self.ty - self.d * self.tx) * inv_det),
143 ty: round10((self.b * self.tx - self.a * self.ty) * inv_det),
144 })
145 }
146
147 pub fn to_array(&self) -> [f64; 6] {
149 [self.a, self.b, self.c, self.d, self.tx, self.ty]
150 }
151}
152
153#[derive(Clone, Debug)]
155pub enum PathSegment {
156 MoveTo(f64, f64),
157 LineTo(f64, f64),
158 CurveTo {
159 x1: f64,
160 y1: f64,
161 x2: f64,
162 y2: f64,
163 x3: f64,
164 y3: f64,
165 },
166 ClosePath,
167}
168
169#[derive(Clone, Debug)]
171pub struct PsPath {
172 pub segments: Vec<PathSegment>,
173}
174
175impl PsPath {
176 pub fn new() -> Self {
178 Self {
179 segments: Vec::new(),
180 }
181 }
182
183 pub fn is_empty(&self) -> bool {
185 self.segments.is_empty()
186 }
187
188 pub fn clear(&mut self) {
190 self.segments.clear();
191 }
192
193 pub fn transform(&self, m: &Matrix) -> PsPath {
195 let segments = self
196 .segments
197 .iter()
198 .map(|seg| match *seg {
199 PathSegment::MoveTo(x, y) => {
200 let (tx, ty) = m.transform_point(x, y);
201 PathSegment::MoveTo(tx, ty)
202 }
203 PathSegment::LineTo(x, y) => {
204 let (tx, ty) = m.transform_point(x, y);
205 PathSegment::LineTo(tx, ty)
206 }
207 PathSegment::CurveTo {
208 x1,
209 y1,
210 x2,
211 y2,
212 x3,
213 y3,
214 } => {
215 let (tx1, ty1) = m.transform_point(x1, y1);
216 let (tx2, ty2) = m.transform_point(x2, y2);
217 let (tx3, ty3) = m.transform_point(x3, y3);
218 PathSegment::CurveTo {
219 x1: tx1,
220 y1: ty1,
221 x2: tx2,
222 y2: ty2,
223 x3: tx3,
224 y3: ty3,
225 }
226 }
227 PathSegment::ClosePath => PathSegment::ClosePath,
228 })
229 .collect();
230 PsPath { segments }
231 }
232}
233
234impl Default for PsPath {
235 fn default() -> Self {
236 Self::new()
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_matrix_identity() {
246 let m = Matrix::identity();
247 let (x, y) = m.transform_point(3.0, 4.0);
248 assert!((x - 3.0).abs() < 1e-10);
249 assert!((y - 4.0).abs() < 1e-10);
250 }
251
252 #[test]
253 fn test_matrix_translate() {
254 let m = Matrix::translate(10.0, 20.0);
255 let (x, y) = m.transform_point(3.0, 4.0);
256 assert!((x - 13.0).abs() < 1e-10);
257 assert!((y - 24.0).abs() < 1e-10);
258 }
259
260 #[test]
261 fn test_matrix_scale() {
262 let m = Matrix::scale(2.0, 3.0);
263 let (x, y) = m.transform_point(5.0, 7.0);
264 assert!((x - 10.0).abs() < 1e-10);
265 assert!((y - 21.0).abs() < 1e-10);
266 }
267
268 #[test]
269 fn test_matrix_rotate_90() {
270 let m = Matrix::rotate(90.0);
271 let (x, y) = m.transform_point(1.0, 0.0);
272 assert!(x.abs() < 1e-10);
273 assert!((y - 1.0).abs() < 1e-10);
274 }
275
276 #[test]
277 fn test_matrix_multiply() {
278 let t = Matrix::translate(10.0, 0.0);
279 let s = Matrix::scale(2.0, 2.0);
280 let m = t.multiply(&s);
281 let (x, y) = m.transform_point(5.0, 3.0);
282 assert!((x - 20.0).abs() < 1e-10);
283 assert!((y - 6.0).abs() < 1e-10);
284 }
285
286 #[test]
287 fn test_matrix_concat() {
288 let ctm = Matrix::identity();
289 let t = Matrix::translate(10.0, 20.0);
290 let result = ctm.concat(&t);
291 let (x, y) = result.transform_point(0.0, 0.0);
292 assert!((x - 10.0).abs() < 1e-10);
293 assert!((y - 20.0).abs() < 1e-10);
294 }
295
296 #[test]
297 fn test_matrix_invert() {
298 let m = Matrix::new(2.0, 0.0, 0.0, 3.0, 10.0, 20.0);
299 let inv = m.invert().unwrap();
300 let (x, y) = m.transform_point(5.0, 7.0);
301 let (x2, y2) = inv.transform_point(x, y);
302 assert!((x2 - 5.0).abs() < 1e-8);
303 assert!((y2 - 7.0).abs() < 1e-8);
304 }
305
306 #[test]
307 fn test_matrix_invert_singular() {
308 let m = Matrix::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
309 assert!(m.invert().is_none());
310 }
311
312 #[test]
313 fn test_matrix_transform_delta() {
314 let m = Matrix::translate(100.0, 200.0);
315 let (dx, dy) = m.transform_delta(5.0, 3.0);
316 assert!((dx - 5.0).abs() < 1e-10);
317 assert!((dy - 3.0).abs() < 1e-10);
318 }
319}