@@ -658,6 +658,41 @@ def intersects_bbox(self, bbox, filled=True):
658
658
return _path .path_intersects_rectangle (
659
659
self , bbox .x0 , bbox .y0 , bbox .x1 , bbox .y1 , filled )
660
660
661
+ def signed_area (self , ** kwargs ):
662
+ """
663
+ Get signed area filled by path.
664
+
665
+ All sub paths are treated as if they had been closed. That is, if there
666
+ is a MOVETO without a preceding CLOSEPOLY, one is added.
667
+
668
+ Signed area means that if a path is self-intersecting, the drawing rule
669
+ "even-odd" is used and only the filled area is counted.
670
+
671
+ Returns
672
+ -------
673
+ area : float
674
+ The (signed) enclosed area of the path.
675
+ """
676
+ area = 0
677
+ prev_point = None
678
+ prev_code = None
679
+ start_point = None
680
+ for cpoints , code in self .iter_curves (** kwargs ):
681
+ if code == Path .MOVETO :
682
+ if prev_code is not None and prev_code is not Path .CLOSEPOLY :
683
+ B = BezierSegment (np .array ([prev_point , start_point ]))
684
+ area += B .arc_area ()
685
+ start_point = cpoints [0 ]
686
+ B = BezierSegment (cpoints )
687
+ area += B .arc_area ()
688
+ prev_point = cpoints [- 1 ]
689
+ prev_code = code
690
+ if start_point is not None \
691
+ and not np .all (np .isclose (start_point , prev_point )):
692
+ B = BezierSegment (np .array ([prev_point , start_point ]))
693
+ area += B .arc_area ()
694
+ return area
695
+
661
696
def interpolated (self , steps ):
662
697
"""
663
698
Returns a new path resampled to length N x steps. Does not
0 commit comments