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;