11import platform
22import re
3+ from collections import namedtuple
34
45import numpy as np
5-
66from numpy .testing import assert_array_equal
77import pytest
88
@@ -88,25 +88,29 @@ def test_contains_points_negative_radius():
8888 np .testing .assert_equal (result , [True , False , False ])
8989
9090
91- _test_paths = [
91+ _ExampleCurve = namedtuple ('_ExampleCurve' , ['path' , 'extents' , 'area' ])
92+ _test_curves = [
9293 # interior extrema determine extents and degenerate derivative
93- Path ([[0 , 0 ], [1 , 0 ], [1 , 1 ], [0 , 1 ]],
94- [Path .MOVETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ]),
95- # a quadratic curve
96- Path ([[0 , 0 ], [0 , 1 ], [1 , 0 ]], [Path .MOVETO , Path .CURVE3 , Path .CURVE3 ]),
94+ _ExampleCurve (Path ([[0 , 0 ], [1 , 0 ], [1 , 1 ], [0 , 1 ]],
95+ [Path .MOVETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ]),
96+ extents = (0. , 0. , 0.75 , 1. ), area = 0.6 ),
97+ # a quadratic curve, clockwise
98+ _ExampleCurve (Path ([[0 , 0 ], [0 , 1 ], [1 , 0 ]],
99+ [Path .MOVETO , Path .CURVE3 , Path .CURVE3 ]),
100+ extents = (0. , 0. , 1. , 0.5 ), area = - 1 / 3 ),
97101 # a linear curve, degenerate vertically
98- Path ([[0 , 1 ], [1 , 1 ]], [Path .MOVETO , Path .LINETO ]),
102+ _ExampleCurve (Path ([[0 , 1 ], [1 , 1 ]], [Path .MOVETO , Path .LINETO ]),
103+ extents = (0. , 1. , 1. , 1. ), area = 0. ),
99104 # a point
100- Path ([[1 , 2 ]], [Path .MOVETO ]),
105+ _ExampleCurve (Path ([[1 , 2 ]], [Path .MOVETO ]), extents = (1. , 2. , 1. , 2. ),
106+ area = 0. ),
107+ # non-curved triangle
108+ _ExampleCurve (Path ([(1 , 1 ), (2 , 1 ), (1.5 , 2 )]), extents = (1 , 1 , 2 , 2 ), area = 0.5 ),
101109]
102110
103111
104- _test_path_extents = [(0. , 0. , 0.75 , 1. ), (0. , 0. , 1. , 0.5 ), (0. , 1. , 1. , 1. ),
105- (1. , 2. , 1. , 2. )]
106-
107-
108- @pytest .mark .parametrize ('path, extents' , zip (_test_paths , _test_path_extents ))
109- def test_exact_extents (path , extents ):
112+ @pytest .mark .parametrize ('precomputed_curve' , _test_curves )
113+ def test_exact_extents (precomputed_curve ):
110114 # notice that if we just looked at the control points to get the bounding
111115 # box of each curve, we would get the wrong answers. For example, for
112116 # hard_curve = Path([[0, 0], [1, 0], [1, 1], [0, 1]],
@@ -116,6 +120,7 @@ def test_exact_extents(path, extents):
116120 # the way out to the control points.
117121 # Note that counterintuitively, path.get_extents() returns a Bbox, so we
118122 # have to get that Bbox's `.extents`.
123+ path , extents = precomputed_curve .path , precomputed_curve .extents
119124 assert np .all (path .get_extents ().extents == extents )
120125
121126
@@ -129,6 +134,28 @@ def test_extents_with_ignored_codes(ignored_code):
129134 assert np .all (path .get_extents ().extents == (0. , 0. , 1. , 1. ))
130135
131136
137+ @pytest .mark .parametrize ('precomputed_curve' , _test_curves )
138+ def test_signed_area (precomputed_curve ):
139+ path , area = precomputed_curve .path , precomputed_curve .area
140+ np .testing .assert_allclose (path .signed_area (), area )
141+ # now flip direction, sign of *signed_area* should flip
142+ rcurve = Path (path .vertices [::- 1 ], path .codes )
143+ np .testing .assert_allclose (rcurve .signed_area (), - area )
144+
145+
146+ def test_signed_area_unit_rectangle ():
147+ rect = Path .unit_rectangle ()
148+ assert rect .signed_area () == 1
149+
150+
151+ def test_signed_area_unit_circle ():
152+ circ = Path .unit_circle ()
153+ # Not a "real" circle, just an approximation of a circle made out of bezier
154+ # curves. The actual value is 3.1415935732517166, which is close enough to
155+ # pass here.
156+ assert np .isclose (circ .signed_area (), np .pi )
157+
158+
132159def test_point_in_path_nan ():
133160 box = np .array ([[0 , 0 ], [1 , 0 ], [1 , 1 ], [0 , 1 ], [0 , 0 ]])
134161 p = Path (box )
0 commit comments