From 4cd75cdf87361a5995cf3de7569ee87c94addbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Seppa=CC=88nen?= Date: Fri, 6 Apr 2012 17:50:07 +0300 Subject: [PATCH] Fix issue #804 by altering path clipping --- CHANGELOG | 6 + .../baseline_images/test_axes/hist_log.pdf | Bin 0 -> 1798 bytes .../baseline_images/test_axes/hist_log.png | Bin 0 -> 5130 bytes .../baseline_images/test_axes/hist_log.svg | 343 ++++++++++++++++++ lib/matplotlib/tests/test_axes.py | 9 + lib/matplotlib/tests/test_transforms.py | 26 +- src/path_converters.h | 23 +- 7 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/hist_log.png create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg 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 0000000000000000000000000000000000000000..cad585dd20ae4291a1f8a1c606d1da990429bdf6 GIT binary patch literal 1798 zcmZWqc~BEq7^eqdgQz`#T6BD18AOiV%?Su7Bm_ky7>JhEqMKv`!H~`DE)vAb(9uqz zt>;WzQl#U7sANQp)sA++smBBak0Q)8D#tX#q18HoN}*`qhD45R{z%?$zwh_H@AuyK zOGt)98i_^mIU(lzpzb0E11KPs`JA{ofMgXL2!M!j2G^0f0Lj2}2^w%=fE*+wa0tDc z4UC?a@QF;BPpB9ma>T#{h9M|D5U@)Ql0lKGOo9QQK`Mz9(i27oAZrRBZIbg_?EGE> zJ_ks$kx9v9aE1WmT0^uaRl+-hT=lDHU4u(R&)j$eG78(HCiZ~M>5@MTH zMaWa&W^k27;d}qu~J|Tha6~165o8=(u2(=bZB8{+gmR=wd0zQw+)+i@v zvXD|iU05CDE+SScxG5|Uvd|#L@ouU}cI~QBs?|Y}03y}FP9=njRKv_PLZ8dz0gOEj zhuAbj5qJT|Sas-o+0FZ4?zICbaxO9B{n+aVhHC*fW^wPdN8($9KVvu5OQS=YYNj+kk zQ`5Y>@?b-ytncVVZD+2fF5c5S+4kb1q(A7`qOdLF*U#o1XwwKDrGI;LaN%1!D8KD< zPlm7T+|;1_I#RNkTkg{@-DTyA`54ZI4l=aqP#Y+k%q ziM(;vr$I6j=C6A^X37@*)L~wM9UH6+zW%VHwdSrz+}HvznzZlJdF7%OjisV+>9s#c zD%RgV+**70M*BZ?otu-}pS&Got4>^S%4f*3I$m}nqB1m#)=Q!TbKW%uShhfROR@1p$EYyHM_k0e)6 zn%Cy4dTdmJ?MmNZOU`LM^xyB@)~lyJ($u&%>e`yoX8FVGtk9?U*#(}jvA-iO{BbbS zm%g)?w6Bh;E8gHD?_0StYuJ3wxqI5>Zgq98i^kr1%H&#f%WAs(1h-P1h0kx7(bXkO zhRc4uFw%SOx1FP78S!5j7N5)(mU`UUv+*|T3eLOg^asZsW-q%?#B+HDJG9RGKQ$EW zkh_7bb-01-{JVjy=eU8aSvf&CjfHTcF@UVqs%bdM4yTZ_Lpltijvu2X z^%B;OmPul{D8@zks1OsOd;uDc@OvKq=Vb*rW6+U|POAh4wFW40)mEqo z3=y+bx3VtY61ju6;Lo9+!B}dVkC_TZ1O>Kohbw1lw-Zn>Rz#Y?DaIio#uvnJLPC;d GQqKR>bAo~Z literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5ba2d81475f150d75b04b9cbb9c2c7ac5c03ed51 GIT binary patch literal 5130 zcmeHL2~d+)9)CG3p$i7u9bqX~b(M8SSJ+wR^=~l2U2m;|y6bppI5Q({n)Ma&2 zgHzkp5(!v3!Uhr)!m&sax)x{`AyOofLI{W$$tOt6d;|!`zTmcY+8vKtN2b$nCX<)s zO}_8_-|zar<2yr1wl6xp2mr7Rd1Ko<0N~sKz(x^XfZr4z%FBdrcxrG+I00TPLeeq# z**f!$-BbXGiIx|pJh1FN0A8*P*|sH|es)abmHvuO-a4cphJQi8U&#D;6L zk#N8oN42%yR1!)7nB756%W80#IDFY^_m!C?(mxXZkVpPo{V`hRzJA`RQ(xXC9anZY zu&q8EneJdog_Qa}3I;yNYy}GMpO8zi1HgSd7mmHKcmR&sy8{sP0f1BX;0XZOtqKBw z>xjVu@WroFou#hL#RD>CWX3?#KTAtX8S}G;5g!0pH|ESH6sc0EDk7`&Ej$OY&Mz99 zL~1&0Jb%l082KFlvsMlc4(JSus9tkjkEvcIpS~p!%@|){_!fD`;XrfQu8N2boiSHi zZ!i%~x)@p<;=?0)vVFx8#Hk7c1{bI~+E(rG*4vN0Jq8{j=gOXoO=2AY zcVvl8<=n`#u_3%~%dV zai(I@)A@43j+NLdQ~N}4u5Og1`da~R zSVKJ1k)Hr1Z7E6Il`ROSzdmYR%AY<7*P41vY>SXEGaU1s^3Qn-b|7oR{#`S6=n}<0 zPn6}H>MiIw_^(O{5)BzO`0`I@=tBy;gyjF%+B_4(J@dW_2k4C|gr@25mkRB3==7)! z@$xi(T!M$S9SGo|402mrn>cwRl}1|~4CaYgynBM17GJrmLL_QzXeb}e{)WDzGo@BE z&64NDXK)p7asg|P784sC~%d8uknku`yyBlRPivKdM%gP`S ze7<`$4*u|GsI?1%g?xwSmN{;fpkO>^-HO*gc}sNHtT)S{4{&9hY_{57-YMufkACE_ zg?s0W(P*?-gblQaMy979e{aYB8Wq*mpLF4;yDH^T6VTHQmX^G9}~~vmR8z$ypGQ2fIpb*VaOx`fk5@zr?4eK ztecbT3UZZ1A`uyBKP^sfE`2qqNf&w0o zR~TA$LVYtfE>1N%`krQBpiYSOyKqUNPEJmy9X$B;15e424i($EU%i^`tEt$8G1E8V zCOf*iy5iBKsr0I;Lgcs1$Zdu06`nm)6=7s$D+l% z#2#RRJ896(3=Jj9Sku+0jq9tJ;CJB^enCNu^1!W`nMO+}#c>s*a3~VN=5Tm5C6-J+ zZa1HbJ$in8fE61XdoA0S(zHf*?_PCiSQs)nS%>}>%pbW_Jp7|_c{tJ?u}6zMi$YEM z9nAr9krn+vd_I3tul1VpU=duNjM@glRScMlS={!w&4*;gJgCB2j+rut#G8$K^aX+Q zWx5 zWX429JXpkiy448{x2;dQ7*Tb++N90ZGPYmY%ttR11EtUbXKhI&(xMA^@e}+%nDap( zu{L`AG7cpm-ME45sflwU5ock}1bfo^N)utu(PU`lbEaa6d6cS=Fx?u`)iUC^#0-xa zPMr2j|F&`G%unke;})IB%>o=mT@|CGOQGJ=P2*%4Jbr*^&`%q6mgaxrIMFTOT3>wz z(o+$M-e}>$9J78gXinMrK3%f-p^`tI+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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;