1

I want to draw functions in a standard way (like you would find in a textbook) using PGFplots. This question relates to the end arrows on a function.

Using restrict x to domain (and y) works for most functions, but has a problem with asymptotes, as in tan(deg(x)):

\documentclass{standalone}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
    \begin{axis}[
        samples=50,smooth,axis lines=middle,axis equal image=true,xmin=-3,xmax=3,ymin=-2,ymax=2,
        restrict y to domain={\pgfkeysvalueof{/pgfplots/ymin}:\pgfkeysvalueof{/pgfplots/ymax}},
        restrict x to domain={\pgfkeysvalueof{/pgfplots/xmin}:\pgfkeysvalueof{/pgfplots/xmax}}
        ]
        \addplot[ultra thick,<->,samples=150,smooth,red] {tan(deg(x))};
        \end{axis}
    \end{tikzpicture}
\end{document}

Graph of tan(x) using PGFplots

The desired output should have arrows on the other intervals (and lines for the asymptotes would be nice, but that may be another issue altogether)

Graph of tan(x) using PGFplots - desired output

My goal is to plot any function in one command, like \addplot[color,arrows] {function(x)}; without micromanaging domain restrictions. I know I could restrict the domain manually and add three plots of the same function.

Is there a generic way to make PGFplots show the proper arrows for all (most) functions, including those with asymptotes? This would include elementary functions like x^2, \frac{1}{x}, sin(x), log(x). A perfect solution would also do polar functions like \frac{1}{1-2*cos(\theta)}, though that is outside the immediate scope of this question.

1

It is possible to tell pgfplots to add arrows whenever it jumps. Originally I was hoping to achieve this with jump mark left and jump mark right, see p. 80 of the pgfplotsmanual v.16. However, I could not make this work. So I went a more complicated way.

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usetikzlibrary{decorations.pathreplacing,calc}
\newcounter{ppoint}
\begin{document}
\begin{tikzpicture}[reset ppoint/.code={\setcounter{ppoint}{0}},
my arrows/.style={reset ppoint,
decoration={show path construction,
            curveto code={\stepcounter{ppoint}
      \ifnum\value{ppoint}=1        
      \draw[<-]  (\tikzinputsegmentfirst)  .. controls
        (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
        ..(\tikzinputsegmentlast);
      \else 
      \path let \p1=($(prev0)-(\tikzinputsegmentfirst)$),\n1={veclen(\x1,\y1)}
      in \pgfextra{\ifdim\n1<1cm
      \xdef\NewStart{0}
      \else
      \xdef\NewStart{1}
      \fi};
       \ifnum\NewStart=1
        \draw[<-]  (\tikzinputsegmentfirst)  .. controls
          (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
          ..(\tikzinputsegmentlast);            
        \draw[->]  (prev0)  .. controls
          (preva) and (prevb)
          ..(prev1); 
       \else
        \draw[-]  (\tikzinputsegmentfirst)  .. controls
          (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
          ..(\tikzinputsegmentlast);
       \fi    
      \fi   
      \path (\tikzinputsegmentfirst) coordinate (prev0)
       (\tikzinputsegmentsupporta) coordinate (preva)
       (\tikzinputsegmentsupporta) coordinate (prevb)
       (\tikzinputsegmentlast) coordinate (prev1);
    }}}]
    \begin{axis}[
        samples=50,smooth,axis lines=middle,axis equal image=true,xmin=-3,xmax=3,ymin=-2,ymax=2,
        restrict y to domain={\pgfkeysvalueof{/pgfplots/ymin}:\pgfkeysvalueof{/pgfplots/ymax}},
        restrict x to domain={\pgfkeysvalueof{/pgfplots/xmin}:\pgfkeysvalueof{/pgfplots/xmax}},
        %unbounded coords=jump,
        ]
        \addplot[ultra thick,samples=150,red,smooth,
        my arrows,postaction={-,decorate},->
        ] {tan(deg(x))};
        \end{axis}
    \end{tikzpicture}
\end{document}

enter image description here

Note that this version only works for smooth plots. If you want to make it work for non-smooth curves, too, you need also to add a similar lineto code.

One may also add the asymptotes. However, this version tacitly assumes that the asymptotes are vertical.

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usetikzlibrary{decorations.pathreplacing,calc}
\newcounter{ppoint}
\begin{document}
\begin{tikzpicture}[reset ppoint/.code={\setcounter{ppoint}{0}},
my arrows/.style={reset ppoint,
decoration={show path construction,
            curveto code={\stepcounter{ppoint}
      \ifnum\value{ppoint}=1        
      \draw[<-]  (\tikzinputsegmentfirst)  .. controls
        (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
        ..(\tikzinputsegmentlast);
      \else 
      \path let \p1=($(prev0)-(\tikzinputsegmentfirst)$),\n1={veclen(\x1,\y1)}
      in \pgfextra{\ifdim\n1<1cm
      \xdef\NewStart{0}
      \else
      \xdef\NewStart{1}
      \fi};
       \ifnum\NewStart=1
        \draw[<-]  (\tikzinputsegmentfirst)  .. controls
          (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
          ..(\tikzinputsegmentlast);            
        \draw[->]  (prev0)  .. controls
          (preva) and (prevb)
          ..(prev1); 
        \path ($(prev1)!0.5!(\tikzinputsegmentfirst)$) coordinate (aux);
        \draw[dashed,cyan,thick,<->] (prev1-|aux) --
        (aux|-\tikzinputsegmentfirst);
       \else
        \draw[-]  (\tikzinputsegmentfirst)  .. controls
          (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
          ..(\tikzinputsegmentlast);
       \fi    
      \fi   
      \path (\tikzinputsegmentfirst) coordinate (prev0)
       (\tikzinputsegmentsupporta) coordinate (preva)
       (\tikzinputsegmentsupporta) coordinate (prevb)
       (\tikzinputsegmentlast) coordinate (prev1);
    }}}]
    \begin{axis}[
        samples=50,smooth,axis lines=middle,axis equal image=true,xmin=-3,xmax=3,ymin=-2,ymax=2,
        restrict y to domain={\pgfkeysvalueof{/pgfplots/ymin}:\pgfkeysvalueof{/pgfplots/ymax}},
        restrict x to domain={\pgfkeysvalueof{/pgfplots/xmin}:\pgfkeysvalueof{/pgfplots/xmax}},
        %unbounded coords=jump,
        ]
        \addplot[ultra thick,samples=150,red,smooth,
        my arrows,postaction={-,decorate},->
        ] {tan(deg(x))};
        \end{axis}
    \end{tikzpicture}
\end{document}

enter image description here

  • This solution works for almost every function I have given it. But using 0.2/sin(deg(3*x)) shows the vertical line restrict y to domain= should have blocked. Not all the function lines cut off at the ymin and ymax parameters set manually. I tried setting samples=150 or higher, but it doesn't help. Even tan(deg(x)) as plotted above cuts off around -1.8 at x=-1 but closer to -2 at x=2. – onomou Dec 27 '18 at 11:35
  • @onomou I guess that you need to set the right amount of samples. In general, odd numbers are better for symmetric domains. And if the number of samples is too small (or you are unlucky), these things will happen. Yet this is not specific to this solution, only the impacts are particularly visible. – user121799 Dec 27 '18 at 17:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.