30

I'm trying to put some arrows on the left and right side of the maximum of a curve. The arrows should point along the curve. My problem is that if I use the [pos=X] notation I don't know where I am in relation to the maximum. And if I use absolute coordinates the node is not sloped.

\documentclass{article}
\usepackage{pgfplots}
\usetikzlibrary{shapes.geometric}
\pgfplotsset{compat=1.7}

\begin{document}
\begin{tikzpicture}
\tikzset{myarrow/.style=
         {sloped,isosceles triangle,anchor=apex,fill=black,inner sep=2pt}}
\begin{axis}
\addplot [smooth] {-x^2}
 node[pos=0.4,myarrow,rotate=180]{} %ok
 node[pos=0.6,myarrow]{}; %ok

\addplot[smooth,green] {-x^2+2*x-4}
 node[pos=0.4,myarrow,rotate=180]{}
 node[pos=0.6,myarrow]{} %wrong side
 node[myarrow,fill=red] at (axis cs:4,-12){}; %wrong rotation!
\end{axis}

\end{tikzpicture} 
\end{document}

enter image description here

Edit: Some remark about Jake's answer

Jake's code (naturally) works but

  • for my case it is a bit too complicated. Jake gets the correct rotation for the arrow by drawing a short path from x-1pt to x (cool idea). He gets the coordinates for this small path with intersections. But as my plot is based on a function I can simply calculate the values (e.g. with \pgfmathparse) and then draw the small path after the plot:

    \path  (axis cs:3.99,-11.94012) -- (axis cs:4,-12) node[pos=1,myarrow]{};
    

    The same can also be achieved by redrawing the plot but with a restricted domain:

    \addplot[draw=none,domain=3.99:4] {\MyFunction{x}}
     node[pos=1,myarrow,fill=yellow]{};
    
  • I also looked if there is a way to decide where I am relative to the maximum (so that I can change the rotation) and in theory it is possible: If I use the code found here pgfplots: mark max/min value of a function \pgfplots@metamax is known after the plot and can be used in calculations.

1 Answer 1

27

Here's one approach based on my answer to Label plots in pgfplots without entering coordinates manually. It finds the intersections between the plot and two vertical lines to position and rotate the node. Using this, you can place an arbitrary node at x=4 by adding the option add node at x={4}{[<node options>]{<node text>}} or add node at x={4}{<node text>} to the \addplot options. I've also added an equivalent style add node at y, which places nodes at a specified vertical coordinate on the plot.

Here's an example for using these styles:

\addplot [
    add node at x={-3}{[myarrow, blue]{}},
    add node at x={3}{[orange, fill=white, opacity=0.75,text opacity=1, sloped]{Sloped}}
    ] {-x^2};

\addplot[
    green,
    add node at y={-30}{[myarrow, green]{}},
    add node at y={-5}{Some text}
] {-x^2+2*x-4};

\documentclass{standalone}
\usepackage{pgfplots}
\usetikzlibrary{shapes.geometric, intersections}
\pgfplotsset{compat=1.7}

\begin{document}
\begin{tikzpicture}

\tikzset{
    myarrow/.style={
        sloped,
        isosceles triangle,
        anchor=apex,
        fill=black,
        inner sep=2pt
    }
}

\makeatletter
\def\parsenode[#1]#2\pgf@nil{%
    \tikzset{label node/.style={#1}}
    \def\nodetext{#2}
}

\tikzset{
    add node at x/.style 2 args={
        name path global=plot line,
        /pgfplots/execute at end plot visualization/.append={
                \begingroup
                \@ifnextchar[{\parsenode}{\parsenode[]}#2\pgf@nil
            \path [name path global = position line #1-1]
                ({axis cs:#1,0}|-{rel axis cs:0,0}) --
                ({axis cs:#1,0}|-{rel axis cs:0,1});
            \path [xshift=1pt, name path global = position line #1-2]
                ({axis cs:#1,0}|-{rel axis cs:0,0}) --
                ({axis cs:#1,0}|-{rel axis cs:0,1});
            \path [
                name intersections={
                    of={plot line and position line #1-1},
                    name=left intersection
                },
                name intersections={
                    of={plot line and position line #1-2},
                    name=right intersection
                },
                label node/.append style={pos=1}
            ] (left intersection-1) -- (right intersection-1)
            node [label node]{\nodetext};
            \endgroup
        }
    },
    add node at y/.style 2 args={
        name path global=plot line,
        /pgfplots/execute at end plot visualization/.append={
                \begingroup
                \@ifnextchar[{\parsenode}{\parsenode[]}#2\pgf@nil
            \path [name path global = position line #1-1]
                ({axis cs:0,#1}-|{rel axis cs:0,0}) --
                ({axis cs:0,#1}-|{rel axis cs:1,1});
            \path [yshift=1pt, name path global = position line #1-2]
                ({axis cs:0,#1}-|{rel axis cs:0,0}) --
                ({axis cs:0,#1}-|{rel axis cs:1,1});
            \path [
                name intersections={
                    of={plot line and position line #1-1},
                    name=left intersection
                },
                name intersections={
                    of={plot line and position line #1-2},
                    name=right intersection
                },
                label node/.append style={pos=1}
            ] (left intersection-1) -- (right intersection-1)
            node [label node] {\nodetext};
            \endgroup
        }
    }
}
\makeatother
\begin{axis}[smooth]
\addplot [
    add node at x={-3}{[myarrow, blue]{}},
    add node at x={3}{[orange, fill=white, opacity=0.75,text opacity=1, sloped]{Sloped}}
    ] {-x^2};

\addplot[
    green,
    add node at y={-30}{[myarrow, green]{}},
    add node at y={-5}{Some text}
] {-x^2+2*x-4};
\end{axis}

\end{tikzpicture} 
\end{document}
9
  • Thank you, the [xshift] gave me the right idea. I added some comments to my question. Dec 29, 2012 at 17:54
  • @UlrikeFischer: I thought about using the equation to calculate the position and slope, but then decided to go for a more general solution. The code I posted will work with data from tables as well, and does not require you to repeat the equation in case you want to change it later on. Furthermore, you do not need to decide in axis units what delta x to use, which can be useful if you decide to zoom in or out a lot. If you decided to go with a different approach, though, it would be good if you could post and accept a self-answer, otherwise the question will look unsolved.
    – Jake
    Dec 29, 2012 at 19:02
  • Your code is more general regarding the plot types but it is quite specific when handling the resulting node. It would need some adaptions to enable the manipulation of individual nodes, e.g. to rotate them or to change the color or to connect two arrows with a straight line or to compare them with the maximum. That's why I adopted the more direct way. Dec 30, 2012 at 15:05
  • @UlrikeFischer: I've changed the code to now allow placing arbitrary nodes using add node at x={<coordinate>}{[<node options>]}{<node text>} or add node at x={<coordinate>}{<node text>}, and the equivalent style for placing nodes at specified y coordinates.
    – Jake
    Dec 30, 2012 at 17:22
  • 1
    @kadrach: Sure! Use add node at x={-3}{[name=My Name]{Some text}}
    – Jake
    May 5, 2015 at 9:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .