66
77import pytest
88
9- from matplotlib .testing .decorators import image_comparison
9+ from matplotlib .testing .decorators import (
10+ check_figures_equal , image_comparison , remove_ticks_and_titles )
1011import matplotlib .pyplot as plt
1112
1213from matplotlib import patches , transforms
@@ -230,7 +231,7 @@ def test_sine_plus_noise():
230231 assert simplified .vertices .size == 25240
231232
232233
233- @image_comparison (['simplify_curve' ], remove_text = True )
234+ @image_comparison (['simplify_curve' ], remove_text = True , tol = 0.017 )
234235def test_simplify_curve ():
235236 pp1 = patches .PathPatch (
236237 Path ([(0 , 0 ), (1 , 0 ), (1 , 1 ), (np .nan , 1 ), (0 , 0 ), (2 , 0 ), (2 , 2 ),
@@ -245,6 +246,155 @@ def test_simplify_curve():
245246 ax .set_ylim ((0 , 2 ))
246247
247248
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.
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+
248398@image_comparison (['hatch_simplify' ], remove_text = True )
249399def test_hatch ():
250400 fig , ax = plt .subplots ()
0 commit comments