1use crate::{PathEl, Point};
5
6#[cfg(not(feature = "std"))]
7use crate::common::FloatFuncs;
8
9#[derive(Debug, Default, Copy, Clone)]
16pub struct Moments {
17 pub moment_x: f64,
19 pub moment_y: f64,
21 pub moment_xx: f64,
24 pub moment_xy: f64,
27 pub moment_yy: f64,
30}
31
32impl core::ops::Add<Self> for Moments {
33 type Output = Self;
34
35 fn add(self, rhs: Self) -> Self::Output {
36 Self {
37 moment_x: self.moment_x + rhs.moment_x,
38 moment_y: self.moment_y + rhs.moment_y,
39 moment_xx: self.moment_xx + rhs.moment_xx,
40 moment_xy: self.moment_xy + rhs.moment_xy,
41 moment_yy: self.moment_yy + rhs.moment_yy,
42 }
43 }
44}
45
46impl Moments {
47 fn handle_line(&mut self, p0: Point, p1: Point) {
50 let (x0, y0) = (p0.x, p0.y);
51 let (x1, y1) = (p1.x, p1.y);
52 let r0 = x1 * y0;
53 let r1 = x1 * y1;
54 let r2 = x1.powi(2);
55 let r3 = r2 * y1;
56 let r4 = y0 - y1;
57 let r5 = r4 * x0;
58 let r6 = x0.powi(2);
59 let r7 = 2.0 * y0;
60 let r8 = y0.powi(2);
61 let r9 = y1.powi(2);
62 let r10 = x1.powi(3);
63 let r11 = y0.powi(3);
64 let r12 = y1.powi(3);
65 self.moment_x += -r2 * y0 / 6.0 - r3 / 3.0 - r5 * x1 / 6.0 + r6 * (r7 + y1) / 6.0;
66 self.moment_y +=
67 -r0 * y1 / 6.0 - r8 * x1 / 6.0 - r9 * x1 / 6.0 + x0 * (r8 + r9 + y0 * y1) / 6.0;
68 self.moment_xx += -r10 * y0 / 12.0 - r10 * y1 / 4.0 - r2 * r5 / 12.0 - r4 * r6 * x1 / 12.0
69 + x0.powi(3) * (3.0 * y0 + y1) / 12.0;
70 self.moment_xy += -r2 * r8 / 24.0 - r2 * r9 / 8.0 - r3 * r7 / 24.0
71 + r6 * (r7 * y1 + 3.0 * r8 + r9) / 24.0
72 - x0 * x1 * (r8 - r9) / 12.0;
73 self.moment_yy += -r0 * r9 / 12.0 - r1 * r8 / 12.0 - r11 * x1 / 12.0 - r12 * x1 / 12.0
74 + x0 * (r11 + r12 + r8 * y1 + r9 * y0) / 12.0;
75 }
76
77 fn handle_quad(&mut self, p0: Point, p1: Point, p2: Point) {
78 let (x0, y0) = (p0.x, p0.y);
79 let x1 = p1.x;
80 let y1 = p1.y;
81 let x2 = p2.x;
82 let y2 = p2.y;
83
84 let r0 = 2.0 * y1;
85 let r1 = r0 * x2;
86 let r2 = x2 * y2;
87 let r3 = 3.0 * r2;
88 let r4 = 2.0 * x1;
89 let r5 = 3.0 * y0;
90 let r6 = x1.powi(2);
91 let r7 = x2.powi(2);
92 let r8 = 4.0 * y1;
93 let r9 = 10.0 * y2;
94 let r10 = 2.0 * y2;
95 let r11 = r4 * x2;
96 let r12 = x0.powi(2);
97 let r13 = 10.0 * y0;
98 let r14 = r4 * y2;
99 let r15 = x2 * y0;
100 let r16 = 4.0 * x1;
101 let r17 = r0 * x1 + r2;
102 let r18 = r2 * r8;
103 let r19 = y1.powi(2);
104 let r20 = 2.0 * r19;
105 let r21 = y2.powi(2);
106 let r22 = r21 * x2;
107 let r23 = 5.0 * r22;
108 let r24 = y0.powi(2);
109 let r25 = y0 * y2;
110 let r26 = 5.0 * r24;
111 let r27 = x1.powi(3);
112 let r28 = x2.powi(3);
113 let r29 = 30.0 * y1;
114 let r30 = 6.0 * y1;
115 let r31 = 10.0 * r7 * x1;
116 let r32 = 5.0 * y2;
117 let r33 = 12.0 * r6;
118 let r34 = 30.0 * x1;
119 let r35 = x1 * y1;
120 let r36 = r3 + 20.0 * r35;
121 let r37 = 12.0 * x1;
122 let r38 = 20.0 * r6;
123 let r39 = 8.0 * r6 * y1;
124 let r40 = r32 * r7;
125 let r41 = 60.0 * y1;
126 let r42 = 20.0 * r19;
127 let r43 = 4.0 * r19;
128 let r44 = 15.0 * r21;
129 let r45 = 12.0 * x2;
130 let r46 = 12.0 * y2;
131 let r47 = 6.0 * x1;
132 let r48 = 8.0 * r19 * x1 + r23;
133 let r49 = 8.0 * y1.powi(3);
134 let r50 = y2.powi(3);
135 let r51 = y0.powi(3);
136 let r52 = 10.0 * y1;
137 let r53 = 12.0 * y1;
138 self.moment_x += -r11 * (-r10 + y1) / 30.0 + r12 * (r13 + r8 + y2) / 30.0 + r6 * y2 / 15.0
139 - r7 * r8 / 30.0
140 - r7 * r9 / 30.0
141 + x0 * (r14 - r15 - r16 * y0 + r17) / 30.0
142 - y0 * (r11 + 2.0 * r6 + r7) / 30.0;
143 self.moment_y += -r18 / 30.0 - r20 * x2 / 30.0 - r23 / 30.0 - r24 * (r16 + x2) / 30.0
144 + x0 * (r0 * y2 + r20 + r21 + r25 + r26 + r8 * y0) / 30.0
145 + x1 * y2 * (r10 + y1) / 15.0
146 - y0 * (r1 + r17) / 30.0;
147 self.moment_xx += r12 * (r1 - 5.0 * r15 - r34 * y0 + r36 + r9 * x1) / 420.0
148 + 2.0 * r27 * y2 / 105.0
149 - r28 * r29 / 420.0
150 - r28 * y2 / 4.0
151 - r31 * (r0 - 3.0 * y2) / 420.0
152 - r6 * x2 * (r0 - r32) / 105.0
153 + x0.powi(3) * (r30 + 21.0 * y0 + y2) / 84.0
154 - x0 * (r0 * r7 + r15 * r37 - r2 * r37 - r33 * y2 + r38 * y0 - r39 - r40 + r5 * r7)
155 / 420.0
156 - y0 * (8.0 * r27 + 5.0 * r28 + r31 + r33 * x2) / 420.0;
157 self.moment_xy += r12 * (r13 * y2 + 3.0 * r21 + 105.0 * r24 + r41 * y0 + r42 + r46 * y1)
158 / 840.0
159 - r16 * x2 * (r43 - r44) / 840.0
160 - r21 * r7 / 8.0
161 - r24 * (r38 + r45 * x1 + 3.0 * r7) / 840.0
162 - r41 * r7 * y2 / 840.0
163 - r42 * r7 / 840.0
164 + r6 * y2 * (r32 + r8) / 210.0
165 + x0 * (-r15 * r8 + r16 * r25 + r18 + r21 * r47 - r24 * r34 - r26 * x2
166 + r35 * r46
167 + r48)
168 / 420.0
169 - y0 * (r16 * r2 + r30 * r7 + r35 * r45 + r39 + r40) / 420.0;
170
171 self.moment_yy += -r2 * r42 / 420.0
172 - r22 * r29 / 420.0
173 - r24 * (r14 + r36 + r52 * x2) / 420.0
174 - r49 * x2 / 420.0
175 - r50 * x2 / 12.0
176 - r51 * (r47 + x2) / 84.0
177 + x0 * (r19 * r46
178 + r21 * r5
179 + r21 * r52
180 + r24 * r29
181 + r25 * r53
182 + r26 * y2
183 + r42 * y0
184 + r49
185 + 5.0 * r50
186 + 35.0 * r51)
187 / 420.0
188 + x1 * y2 * (r43 + r44 + r9 * y1) / 210.0
189 - y0 * (r19 * r45 + r2 * r53 - r21 * r4 + r48) / 420.0;
190 }
191
192 fn handle_cubic(&mut self, p0: Point, p1: Point, p2: Point, p3: Point) {
193 let x0 = p0.x;
194 let y0 = p0.y;
195 let x1 = p1.x;
196 let y1 = p1.y;
197 let x2 = p2.x;
198 let y2 = p2.y;
199 let x3 = p3.x;
200 let y3 = p3.y;
201
202 let r0 = 6.0 * y2;
203 let r1 = r0 * x3;
204 let r2 = 10.0 * y3;
205 let r3 = r2 * x3;
206 let r4 = 3.0 * y1;
207 let r5 = 6.0 * x1;
208 let r6 = 3.0 * x2;
209 let r7 = 6.0 * y1;
210 let r8 = 3.0 * y2;
211 let r9 = x2.powi(2);
212 let r10 = 45.0 * r9;
213 let r11 = r10 * y3;
214 let r12 = x3.powi(2);
215 let r13 = r12 * y2;
216 let r14 = r12 * y3;
217 let r15 = 7.0 * y3;
218 let r16 = 15.0 * x3;
219 let r17 = r16 * x2;
220 let r18 = x1.powi(2);
221 let r19 = 9.0 * r18;
222 let r20 = x0.powi(2);
223 let r21 = 21.0 * y1;
224 let r22 = 9.0 * r9;
225 let r23 = r7 * x3;
226 let r24 = 9.0 * y2;
227 let r25 = r24 * x2 + r3;
228 let r26 = 9.0 * x2;
229 let r27 = x2 * y3;
230 let r28 = -r26 * y1 + 15.0 * r27;
231 let r29 = 3.0 * x1;
232 let r30 = 45.0 * x1;
233 let r31 = 12.0 * x3;
234 let r32 = 45.0 * r18;
235 let r33 = 5.0 * r12;
236 let r34 = r8 * x3;
237 let r35 = 105.0 * y0;
238 let r36 = 30.0 * y0;
239 let r37 = r36 * x2;
240 let r38 = 5.0 * x3;
241 let r39 = 15.0 * y3;
242 let r40 = 5.0 * y3;
243 let r41 = r40 * x3;
244 let r42 = x2 * y2;
245 let r43 = 18.0 * r42;
246 let r44 = 45.0 * y1;
247 let r45 = r41 + r43 + r44 * x1;
248 let r46 = y2 * y3;
249 let r47 = r46 * x3;
250 let r48 = y2.powi(2);
251 let r49 = 45.0 * r48;
252 let r50 = r49 * x3;
253 let r51 = y3.powi(2);
254 let r52 = r51 * x3;
255 let r53 = y1.powi(2);
256 let r54 = 9.0 * r53;
257 let r55 = y0.powi(2);
258 let r56 = 21.0 * x1;
259 let r57 = 6.0 * x2;
260 let r58 = r16 * y2;
261 let r59 = r39 * y2;
262 let r60 = 9.0 * r48;
263 let r61 = r6 * y3;
264 let r62 = 3.0 * y3;
265 let r63 = r36 * y2;
266 let r64 = y1 * y3;
267 let r65 = 45.0 * r53;
268 let r66 = 5.0 * r51;
269 let r67 = x2.powi(3);
270 let r68 = x3.powi(3);
271 let r69 = 630.0 * y2;
272 let r70 = 126.0 * x3;
273 let r71 = x1.powi(3);
274 let r72 = 126.0 * x2;
275 let r73 = 63.0 * r9;
276 let r74 = r73 * x3;
277 let r75 = r15 * x3 + 15.0 * r42;
278 let r76 = 630.0 * x1;
279 let r77 = 14.0 * x3;
280 let r78 = 21.0 * r27;
281 let r79 = 42.0 * x1;
282 let r80 = 42.0 * x2;
283 let r81 = x1 * y2;
284 let r82 = 63.0 * r42;
285 let r83 = x1 * y1;
286 let r84 = r41 + r82 + 378.0 * r83;
287 let r85 = x2 * x3;
288 let r86 = r85 * y1;
289 let r87 = r27 * x3;
290 let r88 = 27.0 * r9;
291 let r89 = r88 * y2;
292 let r90 = 42.0 * r14;
293 let r91 = 90.0 * x1;
294 let r92 = 189.0 * r18;
295 let r93 = 378.0 * r18;
296 let r94 = r12 * y1;
297 let r95 = 252.0 * x1 * x2;
298 let r96 = r79 * x3;
299 let r97 = 30.0 * r85;
300 let r98 = r83 * x3;
301 let r99 = 30.0 * x3;
302 let r100 = 42.0 * x3;
303 let r101 = r42 * x1;
304 let r102 = r10 * y2 + 14.0 * r14 + 126.0 * r18 * y1 + r81 * r99;
305 let r103 = 378.0 * r48;
306 let r104 = 18.0 * y1;
307 let r105 = r104 * y2;
308 let r106 = y0 * y1;
309 let r107 = 252.0 * y2;
310 let r108 = r107 * y0;
311 let r109 = y0 * y3;
312 let r110 = 42.0 * r64;
313 let r111 = 378.0 * r53;
314 let r112 = 63.0 * r48;
315 let r113 = 27.0 * x2;
316 let r114 = r27 * y2;
317 let r115 = r113 * r48 + 42.0 * r52;
318 let r116 = x3 * y3;
319 let r117 = 54.0 * r42;
320 let r118 = r51 * x1;
321 let r119 = r51 * x2;
322 let r120 = r48 * x1;
323 let r121 = 21.0 * x3;
324 let r122 = r64 * x1;
325 let r123 = r81 * y3;
326 let r124 = 30.0 * r27 * y1 + r49 * x2 + 14.0 * r52 + 126.0 * r53 * x1;
327 let r125 = y2.powi(3);
328 let r126 = y3.powi(3);
329 let r127 = y1.powi(3);
330 let r128 = y0.powi(3);
331 let r129 = r51 * y2;
332 let r130 = r112 * y3 + r21 * r51;
333 let r131 = 189.0 * r53;
334 let r132 = 90.0 * y2;
335 self.moment_x += r11 / 840.0 - r13 / 8.0 - r14 / 3.0 - r17 * (-r15 + r8) / 840.0
336 + r19 * (r8 + 2.0 * y3) / 840.0
337 + r20 * (r0 + r21 + 56.0 * y0 + y3) / 168.0
338 + r29 * (-r23 + r25 + r28) / 840.0
339 - r4 * (10.0 * r12 + r17 + r22) / 840.0
340 + x0 * (12.0 * r27 + r30 * y2 + r34 - r35 * x1 - r37 - r38 * y0 + r39 * x1 - r4 * x3
341 + r45)
342 / 840.0
343 - y0 * (r17 + r30 * x2 + r31 * x1 + r32 + r33 + 18.0 * r9) / 840.0;
344 self.moment_y += -r4 * (r25 + r58) / 840.0
345 - r47 / 8.0
346 - r50 / 840.0
347 - r52 / 6.0
348 - r54 * (r6 + 2.0 * x3) / 840.0
349 - r55 * (r56 + r57 + x3) / 168.0
350 + x0 * (r35 * y1
351 + r40 * y0
352 + r44 * y2
353 + 18.0 * r48
354 + 140.0 * r55
355 + r59
356 + r63
357 + 12.0 * r64
358 + r65
359 + r66)
360 / 840.0
361 + x1 * (r24 * y1 + 10.0 * r51 + r59 + r60 + r7 * y3) / 280.0
362 + x2 * y3 * (r15 + r8) / 56.0
363 - y0 * (r16 * y1 + r31 * y2 + r44 * x2 + r45 + r61 - r62 * x1) / 840.0;
364 self.moment_xx += -r12 * r72 * (-r40 + r8) / 9240.0
365 + 3.0 * r18 * (r28 + r34 - r38 * y1 + r75) / 3080.0
366 + r20
367 * (r24 * x3 - r72 * y0 - r76 * y0 - r77 * y0
368 + r78
369 + r79 * y3
370 + r80 * y1
371 + 210.0 * r81
372 + r84)
373 / 9240.0
374 - r29
375 * (r12 * r21 + 14.0 * r13 + r44 * r9 - r73 * y3 + 54.0 * r86
376 - 84.0 * r87
377 - r89
378 - r90)
379 / 9240.0
380 - r4 * (70.0 * r12 * x2 + 27.0 * r67 + 42.0 * r68 + r74) / 9240.0
381 + 3.0 * r67 * y3 / 220.0
382 - r68 * r69 / 9240.0
383 - r68 * y3 / 4.0
384 - r70 * r9 * (-r62 + y2) / 9240.0
385 + 3.0 * r71 * (r24 + r40) / 3080.0
386 + x0.powi(3) * (r24 + r44 + 165.0 * y0 + y3) / 660.0
387 + x0 * (r100 * r27 + 162.0 * r101 + r102 + r11 + 63.0 * r18 * y3 + r27 * r91
388 - r33 * y0
389 - r37 * x3
390 + r43 * x3
391 - r73 * y0
392 - r88 * y1
393 + r92 * y2
394 - r93 * y0
395 - 9.0 * r94
396 - r95 * y0
397 - r96 * y0
398 - r97 * y1
399 - 18.0 * r98
400 + r99 * x1 * y3)
401 / 9240.0
402 - y0 * (r12 * r56
403 + r12 * r80
404 + r32 * x3
405 + 45.0 * r67
406 + 14.0 * r68
407 + 126.0 * r71
408 + r74
409 + r85 * r91
410 + 135.0 * r9 * x1
411 + r92 * x2)
412 / 9240.0;
413 self.moment_xy += -r103 * r12 / 18480.0 - r12 * r51 / 8.0 - 3.0 * r14 * y2 / 44.0
414 + 3.0 * r18 * (r105 + r2 * y1 + 18.0 * r46 + 15.0 * r48 + 7.0 * r51) / 6160.0
415 + r20
416 * (1260.0 * r106
417 + r107 * y1
418 + r108
419 + 28.0 * r109
420 + r110
421 + r111
422 + r112
423 + 30.0 * r46
424 + 2310.0 * r55
425 + r66)
426 / 18480.0
427 - r54 * (7.0 * r12 + 18.0 * r85 + 15.0 * r9) / 18480.0
428 - r55 * (r33 + r73 + r93 + r95 + r96 + r97) / 18480.0
429 - r7 * (42.0 * r13 + r82 * x3 + 28.0 * r87 + r89 + r90) / 18480.0
430 - 3.0 * r85 * (r48 - r66) / 220.0
431 + 3.0 * r9 * y3 * (r62 + 2.0 * y2) / 440.0
432 + x0 * (-r1 * y0 - 84.0 * r106 * x2
433 + r109 * r56
434 + 54.0 * r114
435 + r117 * y1
436 + 15.0 * r118
437 + 21.0 * r119
438 + 81.0 * r120
439 + r121 * r46
440 + 54.0 * r122
441 + 60.0 * r123
442 + r124
443 - r21 * x3 * y0
444 + r23 * y3
445 - r54 * x3
446 - r55 * r72
447 - r55 * r76
448 - r55 * r77
449 + r57 * y0 * y3
450 + r60 * x3
451 + 84.0 * r81 * y0
452 + 189.0 * r81 * y1)
453 / 9240.0
454 + x1 * (r104 * r27 - r105 * x3 - r113 * r53 + 63.0 * r114 + r115 - r16 * r53
455 + 28.0 * r47
456 + r51 * r80)
457 / 3080.0
458 - y0 * (54.0 * r101 + r102 + r116 * r5 + r117 * x3 + 21.0 * r13 - r19 * y3
459 + r22 * y3
460 + r78 * x3
461 + 189.0 * r83 * x2
462 + 60.0 * r86
463 + 81.0 * r9 * y1
464 + 15.0 * r94
465 + 54.0 * r98)
466 / 9240.0;
467 self.moment_yy += -r103 * r116 / 9240.0
468 - r125 * r70 / 9240.0
469 - r126 * x3 / 12.0
470 - 3.0 * r127 * (r26 + r38) / 3080.0
471 - r128 * (r26 + r30 + x3) / 660.0
472 - r4 * (r112 * x3 + r115 - 14.0 * r119 + 84.0 * r47) / 9240.0
473 - r52 * r69 / 9240.0
474 - r54 * (r58 + r61 + r75) / 9240.0
475 - r55 * (r100 * y1 + r121 * y2 + r26 * y3 + r79 * y2 + r84 + 210.0 * x2 * y1) / 9240.0
476 + x0 * (r108 * y1
477 + r110 * y0
478 + r111 * y0
479 + r112 * y0
480 + 45.0 * r125
481 + 14.0 * r126
482 + 126.0 * r127
483 + 770.0 * r128
484 + 42.0 * r129
485 + r130
486 + r131 * y2
487 + r132 * r64
488 + 135.0 * r48 * y1
489 + 630.0 * r55 * y1
490 + 126.0 * r55 * y2
491 + 14.0 * r55 * y3
492 + r63 * y3
493 + r65 * y3
494 + r66 * y0)
495 / 9240.0
496 + x1 * (27.0 * r125
497 + 42.0 * r126
498 + 70.0 * r129
499 + r130
500 + r39 * r53
501 + r44 * r48
502 + 27.0 * r53 * y2
503 + 54.0 * r64 * y2)
504 / 3080.0
505 + 3.0 * x2 * y3 * (r48 + r66 + r8 * y3) / 220.0
506 - y0 * (r100 * r46 + 18.0 * r114
507 - 9.0 * r118
508 - 27.0 * r120
509 - 18.0 * r122
510 - 30.0 * r123
511 + r124
512 + r131 * x2
513 + r132 * x3 * y1
514 + 162.0 * r42 * y1
515 + r50
516 + 63.0 * r53 * x3
517 + r64 * r99)
518 / 9240.0;
519 }
520}
521
522pub trait ParamCurveMoments<'a> {
524 fn moments(&'a self) -> Moments;
526}
527
528impl<'a, T: 'a> ParamCurveMoments<'a> for T
529where
530 &'a T: IntoIterator<Item = PathEl>,
531{
532 fn moments(&'a self) -> Moments {
533 let mut moments = Moments::default();
534 let mut start_pt: Point = Point::ZERO;
535 let mut cur: Point = Point::ZERO;
536 for el in self {
537 match el {
538 PathEl::MoveTo(p) => {
539 start_pt = p;
540 cur = p;
541 }
542 PathEl::LineTo(p) => {
543 moments.handle_line(cur, p);
544 cur = p;
545 }
546 PathEl::QuadTo(p0, p1) => {
547 moments.handle_quad(cur, p0, p1);
548 cur = p1;
549 }
550 PathEl::CurveTo(p1, p2, p3) => {
551 moments.handle_cubic(cur, p1, p2, p3);
552 cur = p3;
553 }
554 PathEl::ClosePath => {
555 if cur != start_pt {
556 moments.handle_line(cur, start_pt);
557 cur = start_pt;
558 }
559 }
560 }
561 }
562 moments
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 use crate::BezPath;
569
570 use super::*;
571
572 macro_rules! assert_approx_eq {
573 ($x: expr, $y: expr) => {
574 assert!(($x - $y).abs() < 1e-8, "{} != {}", $x, $y);
575 };
576 }
577
578 #[test]
579 fn test_moments() {
580 let path = BezPath::from_vec(vec![
581 PathEl::MoveTo(Point::new(0.0, 0.0)),
582 PathEl::LineTo(Point::new(1.0, 0.0)),
583 PathEl::LineTo(Point::new(1.0, 1.0)),
584 PathEl::LineTo(Point::new(0.0, 1.0)),
585 PathEl::ClosePath,
586 ]);
587 let moments = path.moments();
588 assert_approx_eq!(moments.moment_x, 0.5);
589 assert_approx_eq!(moments.moment_y, 0.5);
590 assert_approx_eq!(moments.moment_xx, 0.3333333333333333);
591 assert_approx_eq!(moments.moment_xy, 0.25);
592 assert_approx_eq!(moments.moment_yy, 0.3333333333333333);
593 }
594}