4

I'm trying the answer of @Jake from Automate derivative representation in TikZ, which is

\documentclass{article}
\usepackage{pgfplots}
\usetikzlibrary{matrix}

\begin{document}

\pgfplotsset{point legend/.style={},
    point 1/.style={anchor=south},
    point 2/.style={anchor=south}
}
\newcommand{\derivative}[5]{
\begin{scope}[declare function={f(\x)=#1;}]
    \addplot [thick, red, latex-latex] {f(x)} node [anchor=west] {#2};
    \addplot [black, mark=*] coordinates {(#3,{f(#3)}) (#4,{f(#4)})}
        node [pos=0,/pgfplots/point 1] {$P_1$}
        node [pos=1,/pgfplots/point 2] {$P_2$};
    \pgfplotsextra{
        \pgfmathsetmacro\first{f(#3)}
        \pgfmathsetmacro\second{(f(#4)}
        \pgfmathsetmacro\xdiff{#4-#3}
        \pgfmathsetmacro\ydiff{f(#4)-f(#3)}
        \draw (axis cs:#3,\first) -| (axis cs:#4,\second);
        \draw [|-|,yshift=-2ex] (axis cs:#3,\first) -- node [inner sep=1pt,fill=white] {\pgfmathprintnumber{\xdiff}} (axis cs:#4,\first);
        \draw [|-|,xshift=2ex] (axis cs:#4,\first) -- node [inner sep=1pt, fill=white] {\pgfmathprintnumber{\ydiff}} (axis cs:#4,\second);
        \matrix at (rel axis cs:1,1) [matrix of nodes,/pgfplots/point legend] {#5\\};
    }
    \end{scope}
}

\pgfplotsset{
    azetinaplot/.style={
        width=7cm,
        height=7cm,
        axis lines=middle,
        xlabel=$x$,
        ylabel=$y$,
        enlarge y limits,
        clip=false
    }
}

\begin{tikzpicture}
    \begin{axis}[
        azetinaplot,
        domain=-20:370, samples=100,
        xmin=-20, xmax=370,
        point 1/.append style={anchor=south east},
        point 2/.append style={anchor=east}]
    \derivative{sin(\x)}
        {$f(x)=\sin(x)$}
        {290}{340}
        {$P_1=(290\,,\,-0.94)$\\$P_2=(340\,,\,-0.34)$}
    \end{axis}
\end{tikzpicture}
\end{document}

But I have error Math Error: The function 'f' already exists. Does anyone know why the error occurs, I tried both in TexStudio and Overleaf

  • Workaround: remove the declare function from the scope and put it as an argument to the tikzpicture (i.e.: \begin{tikzpicture}[declare function={f(\x)=sin(\x);}]) – Marijn Jul 03 '18 at 11:39
  • @Marijn Thank you very much! It works! Do you know why it doesn't work for me, although it should? – Faceb Faceb Jul 03 '18 at 11:45
  • Probably the current version of pgfplots uses scope and declare function in a different way compared to the 2012 version from the other question. I'm not sure what the problem is exactly, I would expect it to work, but in this way to issue is avoided. – Marijn Jul 03 '18 at 12:04
  • You could also just move it outside the scope, i.e. \newcommand{\derivative}[5]{ \tikzset{declare function={f(\x)=#1;}} \begin{scope} ..... That way you can use the macro the same way as in Jake's answer. I also do not think it has to do with the pgfplots version since the error arises also for older versions of pgfplots, so maybe a TikZ update is responsible instead. –  Jul 03 '18 at 14:12
  • The one potential drawback with both suggested workarounds is that you can use the \derivative macro only once per tikzpicture. Use it twice, and you'll get the same error, albeit for a more obvious reason (you actually do declare f twice). – Torbjørn T. Jul 03 '18 at 21:46
  • @TorbjørnT. oh ok, do you know how it can be solved? – Faceb Faceb Jul 04 '18 at 06:46
  • I was actually a bit inaccurate, you can't use it more than once per axis environment, but if you have two axis in the same tikzpicture, then you can have a \derivative in each. One workaround for that, if you actually need it, is to make the function name an (optional) argument to the \derivative macro. I can post an answer with that, if you want to. – Torbjørn T. Jul 04 '18 at 07:33
  • I should say though that the macro should in that case be rewritten more, as the legend is placed at a fixed location, and you can't have two legends in the same place, so using it more than once in the same axis doesn't work that well. – Torbjørn T. Jul 04 '18 at 07:36
  • @TorbjørnT. So it is not possible to plot multiple curves in one graph? Is there workaround for it? – Faceb Faceb Jul 04 '18 at 11:22
  • I never said it wasn't possible. It is possible, but the \derivative macro needs some modifications. – Torbjørn T. Jul 04 '18 at 11:32
  • @TorbjørnT. Ah ok! Do you have suggestion how to modify it? – Faceb Faceb Jul 04 '18 at 11:43

1 Answers1

7

Here is an alternative definition of \derivative. It differs from Jake's original in a few respects, and is used like this:

\derivative[<optional function name, default f>]
           {<mandatory function definition>}
           {<mandatory function label>}
           {<mandatory x_1>}{<mandatory x_2>}
           [<optional position of legend, default rel axis cs:1,1>]

The first optional argument can be used to avoid the "function already exists"-error. If not specified, e.g. \derivative{sin(\x)}..., then f is used. The second time you invoke \derivative in an axis, specify some other name, e.g. \derivative[g]{cos(\x)}....

The last optional argument is used to define the position of the legend. If you don't specify it, the legend is placed in the top right corner of the axis.

I removed the last argument of the original macro, where the legend entries were written, instead making those part of the macro definition.

enter image description here

\documentclass{article}
\usepackage{xparse} % for \NewDocumentCommand
\usepackage{pgfplots}
\usetikzlibrary{matrix}

\pgfplotsset{point legend/.style={},
    point 1/.style={anchor=south},
    point 2/.style={anchor=south}
}

\NewDocumentCommand{\derivative}{O{f} m m m m O{rel axis cs:1,1}}{
\tikzset{declare function={#1(\x)=#2;}}
    \addplot [thick, red, latex-latex] {#1(x)} node [anchor=west] {#3};
    \addplot [black, mark=*] coordinates {(#4,{#1(#4)}) (#5,{#1(#5)})}
        node [pos=0,/pgfplots/point 1] {$P_1$}
        node [pos=1,/pgfplots/point 2] {$P_2$};
    \pgfplotsextra{
        \pgfmathsetmacro\first{#1(#4)}
        \pgfmathsetmacro\second{#1(#5)} % removed first (
        \pgfmathsetmacro\xdiff{#5-#4}
        \pgfmathsetmacro\ydiff{#1(#5)-#1(#4)}
        \draw (axis cs:#4,\first) -| (axis cs:#5,\second);
        \draw [|-|,yshift=-2ex] (axis cs:#4,\first) -- node [inner sep=1pt,fill=white] {\pgfmathprintnumber{\xdiff}} (axis cs:#5,\first);
        \draw [|-|,xshift=2ex] (axis cs:#5,\first) -- node [inner sep=1pt, fill=white] {\pgfmathprintnumber{\ydiff}} (axis cs:#5,\second);
        \matrix at (#6) [matrix of math nodes,/pgfplots/point legend] {
         P_1=(#4\,,\,\pgfmathparse{#1(#4)}\pgfmathprintnumber{\pgfmathresult})\\
         P_2=(#5\,,\,\pgfmathparse{#1(#5)}\pgfmathprintnumber{\pgfmathresult})\\};
    }
}

\begin{document}



\pgfplotsset{
    azetinaplot/.style={
        width=10cm,
        height=8cm,
        axis lines=middle,
        xlabel=$x$,
        ylabel=$y$,
        enlarge y limits,
        clip=false
    }
}

\begin{tikzpicture}
    \begin{axis}[
        azetinaplot,
        domain=-20:370, samples=100,
        xmin=-20, xmax=370,
        point 1/.append style={anchor=south east},
        point 2/.append style={anchor=east}]
    \derivative{sin(\x)}
        {$f(x)=\sin(x)$}
        {290}{340}
        [axis cs:150,-1] % position for the legend


    % above we used the default function name f
    % here we use the first optional argument to give the function the name g instead
    \derivative[g]{cos(\x)+2}
        {$f(x)=\cos(x)$}
        {200}{300}
        [axis cs:170,2.5] % position for the legend

    \end{axis}

\end{tikzpicture}
\end{document}
Torbjørn T.
  • 206,688
  • Note this doesn't actually answer your question the way you phrased it. I have no idea why the original version doesn't work now. – Torbjørn T. Jul 04 '18 at 11:50
  • Is in \pgfmathsetmacro\second{(#1(#5)} there the bracket missing? – Faceb Faceb Jul 04 '18 at 14:23
  • @FacebFaceb No, it's the first ( that is unnecessary. (That's the way you had it in your original code by the way, I didn't notice it before.) Edit: fixed code in answer. – Torbjørn T. Jul 04 '18 at 14:30
  • haha I also noticed it after I posted my question. But when I put the \begin{tikzpicture} inside \begin{figure}[H] \centering \begin{subfigure}[t]{0.46\textwidth} \centering, then it's not centered anymore – Faceb Faceb Jul 04 '18 at 18:28
  • @FacebFaceb That is a completely separate and different issue, so normally I'd suggest you ask a new question. If I were to guess, I'd guess that the diagram is wider than the 0.46\textwidth of the subfigure, which makes it poke out the right side of the subfigure. Make the plot narrower. (Note I increased the width in the azetinaplot style from 7cm to 10cm, because it looked better for the specific plot I made for the example. If you copied that setting as well, that could be the cause.) – Torbjørn T. Jul 04 '18 at 19:37
  • @TorbjørnT. Would you mind also posting your answer on the original question? Then that could be marked as the accepted answer, which would prevent others stumbling on the same problem – Jake Jul 04 '18 at 20:47