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