kurbo/
moments.rs

1// Copyright 2025 the Kurbo Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::{PathEl, Point};
5
6#[cfg(not(feature = "std"))]
7use crate::common::FloatFuncs;
8
9/// A collect of moment integrals for a path, used for computing
10/// shape properties like area, centroid, and inertia.
11///
12/// The moments are computed using Green's theorem, which relates the
13/// integrals of a function over a closed curve to the integrals of its
14/// partial derivatives over the region enclosed by the curve.
15#[derive(Debug, Default, Copy, Clone)]
16pub struct Moments {
17    /// The first moment of area about the x-axis.
18    pub moment_x: f64,
19    /// The first moment of area about the y-axis.
20    pub moment_y: f64,
21    /// The second moment of area about the x-axis (also known as the
22    /// area moment of inertia about the x-axis).
23    pub moment_xx: f64,
24    /// The second moment of area about the xy-axis (also known as the
25    /// product of inertia).
26    pub moment_xy: f64,
27    /// The second moment of area about the y-axis (also known as the
28    /// area moment of inertia about the y-axis).
29    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    // The code below is derived from fontTools's fontTools.pens.momentsPen, which is in turn generated
48    // by running sympy on the Green's theorem integral expressions.
49    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
522/// A trait for types that can provide moment integrals for a path using Green's theorem.
523pub trait ParamCurveMoments<'a> {
524    /// Returns the moment integrals for the path using Green's theorem.
525    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}