diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg index 3f7c4c2a57ce..eb1c1194c131 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg @@ -27,8 +27,7 @@ z " style="fill:#ffffff;"/> - +" id="mdc24d94838" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m110d6fc078" style="stroke:#000000;stroke-width:0.5;"/> - + - + - - - + + + - + - + @@ -207,21 +206,21 @@ L 12.40625 0 z " id="DejaVuSans-31"/> - - - + + + - + - + @@ -251,9 +250,9 @@ Q 44.1875 33.984375 37.640625 27.21875 Q 31.109375 20.453125 19.1875 8.296875 " id="DejaVuSans-32"/> - - - + + + @@ -262,176 +261,176 @@ Q 31.109375 20.453125 19.1875 8.296875 +" id="md652217826" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m85d89dbb30" style="stroke:#000000;stroke-width:0.5;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -442,37 +441,25 @@ L 0 2 +" id="m0525a49047" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m60671aa7ca" style="stroke:#000000;stroke-width:0.5;"/> - + - - + + - - + + - - + + - + - + - - + + - + - + - + - - + + - + @@ -554,104 +553,104 @@ z +" id="mbe24b9f137" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m7965eaa9d8" style="stroke:#000000;stroke-width:0.5;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -659,8 +658,8 @@ L -2 0 - - + + diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index df16d0de3e5f..ac0ef91f2a4f 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -212,6 +212,22 @@ def test_clipping_with_nans(): ax.set_ylim(-0.25, 0.25) +def test_clipping_full(): + p = path.Path([[1e30, 1e30]] * 5) + simplified = list(p.iter_segments(clip=[0, 0, 100, 100])) + assert simplified == [] + + p = path.Path([[50, 40], [75, 65]], [1, 2]) + simplified = list(p.iter_segments(clip=[0, 0, 100, 100])) + assert ([(list(x), y) for x, y in simplified] == + [([50, 40], 1), ([75, 65], 2)]) + + p = path.Path([[50, 40]], [1]) + simplified = list(p.iter_segments(clip=[0, 0, 100, 100])) + assert ([(list(x), y) for x, y in simplified] == + [([50, 40], 1)]) + + if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False) diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index d961741c06ac..b804219a4e79 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -209,10 +209,7 @@ def test_clipping_of_log(): simplify=False) tpoints, tcodes = list(zip(*result)) - # Because y coordinate -99 is outside the clip zone, the first - # line segment is effectively removed. That means that the closepoly - # operation must be replaced by a move to the first point. - assert np.allclose(tcodes, [M, M, L, L, L, C]) + assert np.allclose(tcodes, [M, L, L, L, C]) class NonAffineForTest(mtrans.Transform): diff --git a/src/path_converters.h b/src/path_converters.h index c13cc7c91a72..d19b17dc17ae 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -278,7 +278,7 @@ class PathNanRemover : protected EmbeddedQueue<4> clipped, but are always included in their entirety. */ template -class PathClipper +class PathClipper : public EmbeddedQueue<3> { VertexSource *m_source; bool m_do_clipping; @@ -286,14 +286,9 @@ class PathClipper double m_lastX; double m_lastY; bool m_moveto; - double m_nextX; - double m_nextY; - bool m_has_next; - bool m_end_poly; double m_initX; double m_initY; bool m_has_init; - bool m_broke_path; public: PathClipper(VertexSource &source, bool do_clipping, double width, double height) @@ -301,10 +296,7 @@ class PathClipper m_do_clipping(do_clipping), m_cliprect(-1.0, -1.0, width + 1.0, height + 1.0), m_moveto(true), - m_has_next(false), - m_end_poly(false), - m_has_init(false), - m_broke_path(false) + m_has_init(false) { // empty } @@ -314,10 +306,7 @@ class PathClipper m_do_clipping(do_clipping), m_cliprect(rect), m_moveto(true), - m_has_next(false), - m_end_poly(false), - m_has_init(false), - m_broke_path(false) + m_has_init(false) { m_cliprect.x1 -= 1.0; m_cliprect.y1 -= 1.0; @@ -327,11 +316,30 @@ class PathClipper inline void rewind(unsigned path_id) { - m_has_next = false; + m_has_init = false; m_moveto = true; m_source->rewind(path_id); } + int draw_clipped_line(double x0, double y0, double x1, double y1) + { + unsigned moved = agg::clip_line_segment(&x0, &y0, &x1, &y1, m_cliprect); + // moved >= 4 - Fully clipped + // moved & 1 != 0 - First point has been moved + // moved & 2 != 0 - Second point has been moved + if (moved < 4) { + if (moved & 1 || m_moveto) { + queue_push(agg::path_cmd_move_to, x0, y0); + } + queue_push(agg::path_cmd_line_to, x1, y1); + + m_moveto = false; + return 1; + } + + return 0; + } + unsigned vertex(double *x, double *y) { unsigned code; @@ -339,74 +347,69 @@ class PathClipper if (m_do_clipping) { /* This is the slow path where we actually do clipping */ - if (m_end_poly) { - m_end_poly = false; - return (agg::path_cmd_end_poly | agg::path_flags_close); - } - - if (m_has_next) { - m_has_next = false; - *x = m_nextX; - *y = m_nextY; - return agg::path_cmd_line_to; + if (queue_pop(&code, x, y)) { + return code; } while ((code = m_source->vertex(x, y)) != agg::path_cmd_stop) { - if (code == (agg::path_cmd_end_poly | agg::path_flags_close)) { + switch (code) { + case (agg::path_cmd_end_poly | agg::path_flags_close): if (m_has_init) { - *x = m_initX; - *y = m_initY; - code = agg::path_cmd_line_to; - m_end_poly = true; - } else { - continue; + draw_clipped_line(m_lastX, m_lastY, m_initX, m_initY); } - } - - if (code == agg::path_cmd_move_to) { - m_initX = *x; - m_initY = *y; + queue_push( + agg::path_cmd_end_poly | agg::path_flags_close, + m_lastX, m_lastY); + goto exit_loop; + + case agg::path_cmd_move_to: + m_initX = m_lastX = *x; + m_initY = m_lastY = *y; m_has_init = true; m_moveto = true; - } - if (m_moveto) { - m_moveto = false; - code = agg::path_cmd_move_to; break; - } else if (code == agg::path_cmd_line_to) { - double x0, y0, x1, y1; - x0 = m_lastX; - y0 = m_lastY; - x1 = *x; - y1 = *y; + + case agg::path_cmd_line_to: + if (draw_clipped_line(m_lastX, m_lastY, *x, *y)) { + m_lastX = *x; + m_lastY = *y; + goto exit_loop; + } m_lastX = *x; m_lastY = *y; - unsigned moved = agg::clip_line_segment(&x0, &y0, &x1, &y1, m_cliprect); - // moved >= 4 - Fully clipped - // moved & 1 != 0 - First point has been moved - // moved & 2 != 0 - Second point has been moved - if (moved < 4) { - if (moved & 1) { - *x = x0; - *y = y0; - m_nextX = x1; - m_nextY = y1; - m_has_next = true; - m_broke_path = true; - return agg::path_cmd_move_to; - } - *x = x1; - *y = y1; - return code; - } - } else { break; + + default: + if (m_moveto) { + queue_push(agg::path_cmd_move_to, m_lastX, m_lastY); + m_moveto = false; + } + + queue_push(code, *x, *y); + m_lastX = *x; + m_lastY = *y; + goto exit_loop; } } - m_lastX = *x; - m_lastY = *y; - return code; + exit_loop: + + if (queue_pop(&code, x, y)) { + return code; + } + + if (m_moveto && + m_lastX >= m_cliprect.x1 && + m_lastX <= m_cliprect.x2 && + m_lastY >= m_cliprect.y1 && + m_lastY <= m_cliprect.y2) { + *x = m_lastX; + *y = m_lastY; + m_moveto = false; + return agg::path_cmd_move_to; + } + + return agg::path_cmd_stop; } else { // If not doing any clipping, just pass along the vertices // verbatim