6
6
7
7
import pytest
8
8
9
- from matplotlib .testing .decorators import image_comparison
9
+ from matplotlib .testing .decorators import (
10
+ check_figures_equal , image_comparison , remove_ticks_and_titles )
10
11
import matplotlib .pyplot as plt
11
12
12
13
from matplotlib import patches , transforms
@@ -230,7 +231,7 @@ def test_sine_plus_noise():
230
231
assert simplified .vertices .size == 25240
231
232
232
233
233
- @image_comparison (['simplify_curve' ], remove_text = True )
234
+ @image_comparison (['simplify_curve' ], remove_text = True , tol = 0.017 )
234
235
def test_simplify_curve ():
235
236
pp1 = patches .PathPatch (
236
237
Path ([(0 , 0 ), (1 , 0 ), (1 , 1 ), (np .nan , 1 ), (0 , 0 ), (2 , 0 ), (2 , 2 ),
@@ -245,6 +246,155 @@ def test_simplify_curve():
245
246
ax .set_ylim ((0 , 2 ))
246
247
247
248
249
+ @check_figures_equal ()
250
+ def test_closed_path_nan_removal (fig_test , fig_ref ):
251
+ ax_test = fig_test .subplots (2 , 2 ).flatten ()
252
+ ax_ref = fig_ref .subplots (2 , 2 ).flatten ()
253
+
254
+ # NaN on the first point also removes the last point, because it's closed.
<
8000
/code>
255
+ path = Path (
256
+ [[- 3 , np .nan ], [3 , - 3 ], [3 , 3 ], [- 3 , 3 ], [- 3 , - 3 ]],
257
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .CLOSEPOLY ])
258
+ ax_test [0 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
259
+ path = Path (
260
+ [[- 3 , np .nan ], [3 , - 3 ], [3 , 3 ], [- 3 , 3 ], [- 3 , np .nan ]],
261
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .LINETO ])
262
+ ax_ref [0 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
263
+
264
+ # NaN on second-last point should not re-close.
265
+ path = Path (
266
+ [[- 2 , - 2 ], [2 , - 2 ], [2 , 2 ], [- 2 , np .nan ], [- 2 , - 2 ]],
267
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .CLOSEPOLY ])
268
+ ax_test [0 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
269
+ path = Path (
270
+ [[- 2 , - 2 ], [2 , - 2 ], [2 , 2 ], [- 2 , np .nan ], [- 2 , - 2 ]],
271
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .LINETO ])
272
+ ax_ref [0 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
273
+
274
+ # Test multiple loops in a single path (with same paths as above).
275
+ path = Path (
276
+ [[- 3 , np .nan ], [3 , - 3 ], [3 , 3 ], [- 3 , 3 ], [- 3 , - 3 ],
277
+ [- 2 , - 2 ], [2 , - 2 ], [2 , 2 ], [- 2 , np .nan ], [- 2 , - 2 ]],
278
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .CLOSEPOLY ,
279
+ Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .CLOSEPOLY ])
280
+ ax_test [1 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
281
+ path = Path (
282
+ [[- 3 , np .nan ], [3 , - 3 ], [3 , 3 ], [- 3 , 3 ], [- 3 , np .nan ],
283
+ [- 2 , - 2 ], [2 , - 2 ], [2 , 2 ], [- 2 , np .nan ], [- 2 , - 2 ]],
284
+ [Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .LINETO ,
285
+ Path .MOVETO , Path .LINETO , Path .LINETO , Path .LINETO , Path .LINETO ])
286
+ ax_ref [1 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
287
+
288
+ # NaN in first point of CURVE3 should not re-close, and hide entire curve.
289
+ path = Path (
290
+ [[- 1 , - 1 ], [1 , - 1 ], [1 , np .nan ], [0 , 1 ], [- 1 , 1 ], [- 1 , - 1 ]],
291
+ [Path .MOVETO , Path .LINETO , Path .CURVE3 , Path .CURVE3 , Path .LINETO ,
292
+ Path .CLOSEPOLY ])
293
+ ax_test [2 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
294
+ path = Path (
295
+ [[- 1 , - 1 ], [1 , - 1 ], [1 , np .nan ], [0 , 1 ], [- 1 , 1 ], [- 1 , - 1 ]],
296
+ [Path .MOVETO , Path .LINETO , Path .CURVE3 , Path .CURVE3 , Path .LINETO ,
297
+ Path .CLOSEPOLY ])
298
+ ax_ref [2 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
299
+
300
+ # NaN in second point of CURVE3 should not re-close, and hide entire curve
301
+ # plus next line segment.
302
+ path = Path (
303
+ [[- 3 , - 3 ], [3 , - 3 ], [3 , 0 ], [0 , np .nan ], [- 3 , 3 ], [- 3 , - 3 ]],
304
+ [Path .MOVETO , Path .LINETO , Path .CURVE3 , Path .CURVE3 , Path .LINETO ,
305
+ Path .LINETO ])
306
+ ax_test [2 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
307
+ path = Path (
308
+ [[- 3 , - 3 ], [3 , - 3 ], [3 , 0 ], [0 , np .nan ], [- 3 , 3 ], [- 3 , - 3 ]],
309
+ [Path .MOVETO , Path .LINETO , Path .CURVE3 , Path .CURVE3 , Path .LINETO ,
310
+ Path .LINETO ])
311
+ ax_ref [2 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
312
+
313
+ # NaN in first point of CURVE4 should not re-close, and hide entire curve.
314
+ path = Path (
315
+ [[- 1 , - 1 ], [1 , - 1 ], [1 , np .nan ], [0 , 0 ], [0 , 1 ], [- 1 , 1 ], [- 1 , - 1 ]],
316
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
317
+ Path .LINETO , Path .CLOSEPOLY ])
318
+ ax_test [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
319
+ path = Path (
320
+ [[- 1 , - 1 ], [1 , - 1 ], [1 , np .nan ], [0 , 0 ], [0 , 1 ], [- 1 , 1 ], [- 1 , - 1 ]],
321
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
322
+ Path .LINETO , Path .CLOSEPOLY ])
323
+ ax_ref [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
324
+
325
+ # NaN in second point of CURVE4 should not re-close, and hide entire curve.
326
+ path = Path (
327
+ [[- 2 , - 2 ], [2 , - 2 ], [2 , 0 ], [0 , np .nan ], [0 , 2 ], [- 2 , 2 ], [- 2 , - 2 ]],
328
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
329
+ Path .LINETO , Path .LINETO ])
330
+ ax_test [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
331
+ path = Path (
332
+ [[- 2 , - 2 ], [2 , - 2 ], [2 , 0 ], [0 , np .nan ], [0 , 2 ], [- 2 , 2 ], [- 2 , - 2 ]],
333
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
334
+ Path .LINETO , Path .LINETO ])
335
+ ax_ref [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
336
+
337
+ # NaN in third point of CURVE4 should not re-close, and hide entire curve
338
+ # plus next line segment.
339
+ path = Path (
340
+ [[- 3 , - 3 ], [3 , - 3 ], [3 , 0 ], [0 , 0 ], [0 , np .nan ], [- 3 , 3 ], [- 3 , - 3 ]],
341
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
342
+ Path .LINETO , Path .LINETO ])
343
+ ax_test [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
344
+ path = Path (
345
+ [[- 3 , - 3 ], [3 , - 3 ], [3 , 0 ], [0 , 0 ], [0 , np .nan ], [- 3 , 3 ], [- 3 , - 3 ]],
346
+ [Path .MOVETO , Path .LINETO , Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ,
347
+ Path .LINETO , Path .LINETO ])
348
+ ax_ref [3 ].add_patch (patches .PathPatch (path , facecolor = 'none' ))
349
+
350
+ # Keep everything clean.
351
+ for ax in [* ax_test .flat , * ax_ref .flat ]:
352
+ ax .set (xlim = (- 3.5 , 3.5 ), ylim = (- 3.5 , 3.5 ))
353
+ remove_ticks_and_titles (fig_test )
354
+ remove_ticks_and_titles (fig_ref )
355
+
356
+
357
+ @check_figures_equal ()
358
+ def test_closed_path_clipping (fig_test , fig_ref ):
359
+ vertices = []
360
+ for roll in range (8 ):
361
+ offset = 0.1 * roll + 0.1
362
+
363
+ # A U-like pattern.
364
+ pattern = [
365
+ [- 0.5 , 1.5 ], [- 0.5 , - 0.5 ], [1.5 , - 0.5 ], [1.5 , 1.5 ], # Outer square
366
+ # With a notch in the top.
367
+ [1 - offset / 2 , 1.5 ], [1 - offset / 2 , offset ],
368
+ [offset / 2 , offset ], [offset / 2 , 1.5 ],
369
+ ]
370
+
371
+ # Place the initial/final point anywhere in/out of the clipping area.
372
+ pattern = np .roll (pattern , roll , axis = 0 )
373
+ pattern = np .concatenate ((pattern , pattern [:1 , :]))
374
+
375
+ vertices .append (pattern )
376
+
377
+ # Multiple subpaths are used here to ensure they aren't broken by closed
378
+ # loop clipping.
379
+ codes = np .full (len (vertices [0 ]), Path .LINETO )
380
+ codes [0 ] = Path .MOVETO
381
+ codes [- 1 ] = Path .CLOSEPOLY
382
+ codes = np .tile (codes , len (vertices ))
383
+ vertices = np .concatenate (vertices )
384
+
385
+ fig_test .set_size_inches ((5 , 5 ))
386
+ path = Path (vertices , codes )
387
+ fig_test .add_artist (patches .PathPatch (path , facecolor = 'none' ))
388
+
389
+ # For reference, we draw the same thing, but unclosed by using a line to
390
+ # the last point only.
391
+ fig_ref .set_size_inches ((5 , 5 ))
392
+ codes = codes .copy ()
393
+ codes [codes == Path .CLOSEPOLY ] = Path .LINETO
394
+ path = Path (vertices , codes )
395
+ fig_ref .add_artist (patches .PathPatch (path , facecolor = 'none' ))
396
+
397
+
248
398
@image_comparison (['hatch_simplify' ], remove_text = True )
249
399
def test_hatch ():
250
400
fig , ax = plt .subplots ()
0 commit comments