diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index 0a5c215eff30..a052c24cb655 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -518,3 +518,54 @@ def test_clipping_full(): simplified = list(p.iter_segments(clip=[0, 0, 100, 100])) assert ([(list(x), y) for x, y in simplified] == [([50, 40], 1)]) + + +def test_simplify_closepoly(): + # The values of the vertices in a CLOSEPOLY should always be ignored, + # in favor of the most recent MOVETO's vertex values + paths = [Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]), + Path([(1, 1), (2, 1), (2, 2), (40, 50)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])] + expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1), (1, 1), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.STOP]) + + for path in paths: + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) + + # test that a compound path also works + path = Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan), + (-1, 0), (-2, 0), (-2, 1), (np.nan, np.nan)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]) + expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1), + (-1, 0), (-2, 0), (-2, 1), (-1, 0), (-1, 0), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.STOP]) + + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) + + # test for a path with an invalid MOVETO + # CLOSEPOLY with an invalid MOVETO should be ignored + path = Path([(1, 0), (1, -1), (2, -1), + (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1), + (2, 2), (0, -1)], + [Path.MOVETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.CLOSEPOLY, Path.LINETO]) + expected_path = Path([(1, 0), (1, -1), (2, -1), + (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1), + (0, -1), (0, -1), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.LINETO, Path.STOP]) + + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) diff --git a/src/path_converters.h b/src/path_converters.h index db732e126c3f..a78643d64ed5 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -644,6 +644,13 @@ class PathSimplifier : protected EmbeddedQueue<9> m_after_moveto(false), m_clipped(false), + // whether the most recent MOVETO vertex is valid + m_has_init(false), + + // the most recent MOVETO vertex + m_initX(0.0), + m_initY(0.0), + // the x, y values from last iteration m_lastx(0.0), m_lasty(0.0), @@ -754,6 +761,15 @@ class PathSimplifier : protected EmbeddedQueue<9> _push(x, y); } m_after_moveto = true; + + if (std::isfinite(*x) && std::isfinite(*y)) { + m_has_init = true; + m_initX = *x; + m_initY = *y; + } else { + m_has_init = false; + } + m_lastx = *x; m_lasty = *y; m_moveto = false; @@ -768,6 +784,19 @@ class PathSimplifier : protected EmbeddedQueue<9> } m_after_moveto = false; + if(agg::is_close(cmd)) { + if (m_has_init) { + /* If we have a valid initial vertex, then + replace the current vertex with the initial vertex */ + *x = m_initX; + *y = m_initY; + } else { + /* If we don't have a valid initial vertex, then + we can't close the path, so we skip the vertex */ + continue; + } + } + /* NOTE: We used to skip this very short segments, but if you have a lot of them cumulatively, you can miss maxima or minima in the data. */ @@ -919,6 +948,8 @@ class PathSimplifier : protected EmbeddedQueue<9> bool m_moveto; bool m_after_moveto; bool m_clipped; + bool m_has_init; + double m_initX, m_initY; double m_lastx, m_lasty; double m_origdx;