diff --git a/CHANGELOG b/CHANGELOG index 387dddd8d139..29d73bd6a3a6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +2012-04-06 When path clipping changes a LINETO to a MOVETO, it also + changes any CLOSEPOLY command to a LINETO to the initial + point. This fixes a problem with pdf and svg where the + CLOSEPOLY would then draw a line to the latest MOVETO + position instead of the intended initial position. - JKS + 2012-01-23 The radius labels in polar plots no longer use a fixed padding, but use a different alignment depending on the quadrant they are in. This fixes numerical problems when diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf new file mode 100644 index 000000000000..cad585dd20ae Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.png new file mode 100644 index 000000000000..5ba2d81475f1 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg new file mode 100644 index 000000000000..174822dff4d2 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 6fccf20d13fc..e4e3dc5c541d 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -631,6 +631,15 @@ def test_markevery_line(): ax.plot(x, y, '-+', markevery=(5, 20), label='mark every 5 starting at 10') ax.legend() +@image_comparison(baseline_images=['hist_log']) +def test_hist_log(): + data0 = np.linspace(0,1,200)**3 + data = np.r_[1-data0, 1+data0] + fig = plt.figure() + ax = fig.add_subplot(111) + ax.hist(data, fill=False, log=True) + ax.set_xticks([]) + ax.set_yticks([]) if __name__=='__main__': import nose diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index a17dab121156..01e04e8e8b8a 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -1,6 +1,8 @@ from nose.tools import assert_equal from numpy.testing import assert_almost_equal -from matplotlib.transforms import Affine2D +from matplotlib.transforms import Affine2D, BlendedGenericTransform +from matplotlib.path import Path +from matplotlib.scale import LogScale import numpy as np def test_Affine2D_from_values(): @@ -38,3 +40,25 @@ def test_Affine2D_from_values(): actual = t.transform(points) expected = np.array( [[0,6],[0,6],[0,6]] ) assert_almost_equal(actual,expected) + +def test_clipping_of_log(): + # issue 804 + M,L,C = Path.MOVETO, Path.LINETO, Path.CLOSEPOLY + points = [ (0.2, -99), (0.4, -99), (0.4, 20), (0.2, 20), (0.2, -99) ] + codes = [ M, L, L, L, C ] + path = Path(points, codes) + + # something like this happens in plotting logarithmic histograms + trans = BlendedGenericTransform(Affine2D(), + LogScale.Log10Transform('clip')) + tpath = trans.transform_path_non_affine(path) + result = tpath.iter_segments(trans.get_affine(), + clip=(0, 0, 100, 100), + simplify=False) + + tpoints, tcodes = 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 ]) + assert np.allclose(tpoints[-1], tpoints[0]) diff --git a/src/path_converters.h b/src/path_converters.h index faacf15ad924..9f007778f6db 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -285,13 +285,17 @@ class PathClipper double m_nextX; double m_nextY; bool m_has_next; + 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) : m_source(&source), 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_has_next(false), m_has_init(false), m_broke_path(false) { // empty } @@ -299,7 +303,8 @@ class PathClipper PathClipper(VertexSource& source, bool do_clipping, const agg::rect_base& rect) : m_source(&source), m_do_clipping(do_clipping), - m_cliprect(rect), m_moveto(true), m_has_next(false) + m_cliprect(rect), m_moveto(true), m_has_next(false), + m_has_init(false), m_broke_path(false) { m_cliprect.x1 -= 1.0; m_cliprect.y1 -= 1.0; @@ -334,6 +339,12 @@ class PathClipper while ((code = m_source->vertex(x, y)) != agg::path_cmd_stop) { + if (!m_has_init) + { + m_initX = *x; + m_initY = *y; + m_has_init = true; + } if (m_moveto) { m_moveto = false; @@ -362,6 +373,7 @@ class PathClipper m_nextX = x1; m_nextY = y1; m_has_next = true; + m_broke_path = true; return agg::path_cmd_move_to; } *x = x1; @@ -369,6 +381,13 @@ class PathClipper return code; } } + else if (code == agg::path_cmd_end_poly | agg::path_flags_close + && m_broke_path && m_has_init) + { + *x = m_initX; + *y = m_initY; + return agg::path_cmd_line_to; + } else { break;