5

I've been experimenting with "xkcdifying" a plot I want to make, based on the information found here and some pasting from other sources (this is my first time using pgfplots). Everything worked fine so far, until I wanted to give the axes arrows. But it seems that the random steps decoration always turns up the arrow head on the x axis:

enter image description here

If the marked line in the below code is commented out, the arrow is displayed correctly again, but of course the nice random effect is gone:

\documentclass{article}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\pgfplotsset{every axis/.append style={line width=1pt}}

\begin{axis}[%
  axis lines=middle,
  decoration={%
    random steps, % CRITICAL LINE
    segment length=1mm,
    amplitude=0.2pt
  }, 
  every tick/.style={thick,black,decorate},
  enlarge x limits=true,
  y tick style={draw=none},
  yticklabel=\empty,
  every inner x axis line/.append style={->},
  every inner y axis line/.append style={->},
]
\begin{scope}[decoration={random steps,segment length=3pt,amplitude=0.5pt},decorate]
\addplot [blue,samples=50, domain=0:120] {(2479*x^6)/5513508000000-(13697*x^5)/61261200000+(7935509*x^4)/220540320000-(1889983*x^3)/816816000+(2038007*x^2)/40840800+(335201*x)/1021020};
\end{scope}
\end{axis}

\end{tikzpicture}
\end{document}

Now, am I right in my guess that this is an error due to the randomization? And how can it be fixed?

1 Answers1

5

I think this is due to how axis lines are drawn and seems to me a candidate as a bug. Consider the following;

\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}[mydeco/.style={
  decoration={%
    random steps, % CRITICAL LINE
    segment length=1mm,
    amplitude=1pt,#1,
    post length=10mm,
    post= lineto % Change to " moveto "!
  },
  decorate,
}
]
\begin{axis}[%
  line width=1pt,
  axis lines*=middle,
  every inner x axis line/.style={->,mydeco},
  every inner y axis line/.style={->,mydeco},
]
\begin{scope}[mydeco={segment length=3pt,amplitude=0.5pt},decorate]
\addplot [blue,samples=50, domain=0:120,->] {(2479*x^6)/5513508000000-(13697*x^5)/61261200000+(7935509*x^4)/220540320000-(1889983*x^3)/816816000+(2038007*x^2)/40840800+(335201*x)/1021020};
\end{scope}
\end{axis}

\draw[mydeco,->,red,ultra thick] (0,0) -- (5,6);
\end{tikzpicture}
\end{document}

enter image description here

Here I tried to stop the decoration 10mm before the path end. It is OK for paths but not for axis lines. This means there is a move operation somewhere that changes the current path such that post length and post path drawing mechanism is not applied to the axis line. As an example change lineto to moveto and we can replicate that behavior on other paths too.

I will attempt to have a look but that part of the code is really involved due to 3D/2D settings. Maybe Christian can help here. Otherwise we can open a ticket. Then randomized paths would have the arrow head automatically without any post=... tweak.


It turns out that this behaviour is due to a missing expansion in an internal PGFPlots macro. PGFPlots uses a decoration to place axis discontinuity marks, which requires setting pre length and post length values for the decoration. PGFPlots sets pre length and post length using macros (called \xdisstart, \xdisend, etc.). If no discontinuity is used, these macros are set to 0pt. However, for some reason, arrow tips aren't placed correctly when the pre length and post length values contain unexpanded macros. So in order to fix the behaviour, you can redefine an internal PGFPlots macro by placing the following code in your preamble:

\makeatletter
\def\pgfplots@drawaxis@innerlines@onorientedsurf#1#2#3{%
    \if2\csname pgfplots@#1axislinesnum\endcsname
        \draw[/pgfplots/every inner #1 axis line,%
            decorate,%
            #1discont,%
            decoration={% Expand the macros before setting the values
                pre length/.expand twice=\csname #1disstart\endcsname,
                post length/.expand twice=\csname #1disend\endcsname
            }
            ]
        \pgfextra
        \csname pgfplotspointonorientedsurfaceabsetupforset#3\endcsname{\csname pgfplots@logical@ZERO@#3\endcsname}{2}%
        \pgfpathmoveto{\pgfplotspointonorientedsurfaceab{\csname pgfplots@#1min\endcsname}{\csname pgfplots@logical@ZERO@#2\endcsname}}%
        \pgfpathlineto{\pgfplotspointonorientedsurfaceab{\csname pgfplots@#1max\endcsname}{\csname pgfplots@logical@ZERO@#2\endcsname}}%
        \endpgfextra
        ;
    \fi
}%
\makeatother

Jake
  • 232,450
percusse
  • 157,807
  • The problem seems to originate from \pgfplots@drawaxis@innerlines@onorientedsurf in pgfplots.code.tex, which draws the axis lines when using axis lines=middle. In the \draw command, the every inner #1 axis line style is followed by a hard coded decoration for the axis discontinuity, which sets post length=\csname #1disend\endcsname. \xdisend happens to be 0pt when no discontinuity is used, which causes the arrow head problem. The problem can be fixed somewhat heavy-handedly by setting xdisend/.store in=\xdisend,xdisend=1bp in the every inner x axis line/.style. – Jake Feb 18 '14 at 09:56
  • @Jake Cool! Thanks for the surgery. If you want I can delete this and you write an answer? However, it doesn't explain the arrow head mismatch since you can have correct arrowheads with the decoration on regular paths (just remove the post related stuff from the style). Hence, there is additional action after the axis lines are plotted. Maybe the if conditions are disrupting by installing empty paths. I can see some relevant stuff but I don't exactly understand what those conditions do. – percusse Feb 18 '14 at 11:44
  • Ah, what a thriller: It seems that the problem is due to the \csname #1disend\endcsname not being expanded. If pre length=\csname #1disstart\endcsname, post length=\csname #1disend\endcsname is replaced with pre length/.expand twice=\csname #1disstart\endcsname, post length/.expand twice=\csname #1disend\endcsname, the arrow heads are placed correctly, even without specifying a post length. Don't ask me why that happens, though. Maybe this would be worth opening a bug report for. – Jake Feb 18 '14 at 12:23
  • I agree that there is something wrong with that code. I believe it should not activate / reconfigure decorations unless one really has discontinuities. I see that there is an expansion issue around it as well (something with '0pt' which should be '0pt '), although I cannot reproduce @Jake's last comment. – Christian Feuersänger Feb 18 '14 at 19:20
  • @ChristianFeuersänger We found more evidence in the chat. Starting around here you can see more :) http://chat.stackexchange.com/transcript/message/13834867#13834867 – percusse Feb 18 '14 at 19:38
  • @ChristianFeuersänger: I edited the answer with an explanation of one way of fixing this behaviour. – Jake Feb 19 '14 at 08:28
  • Thanks for all your expert help! Given that now the author knows about this, should a bug still be filed? (I don't even think I can do that without a Souceforge account; or at least I didn't find the right place.) – phipsgabler Feb 19 '14 at 09:31
  • Guys, thanks for your analysis! That's indeed good work. I'll take this as a bug report, no need to file a ticket on sourceforge. – Christian Feuersänger Feb 21 '14 at 21:15
  • @ChristianFeuersänger Thank you for your wonderful support for bugs and quirks we come up with all the time :) – percusse Feb 22 '14 at 00:37