Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
add comment

1 Answer

up vote 17 down vote accepted

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
        }
    }
}
\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}
share|improve this answer
    
Thank you, the [xshift] gave me the right idea. I added some comments to my question. –  Ulrike Fischer Dec 29 '12 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 '12 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. –  Ulrike Fischer Dec 30 '12 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 '12 at 17:22
    
That works fine and is really flexible. I could give the nodes names by using the option "name=C". But I didn't find a way to store the orientation of a sloped node for later reference :-(e.g. to add another node with the same rotation somewhere else). I played a bit with \pdf@x and ´\pgf@pt@aa but didn't find a sensible place to grap the values without hacking \pgftransformlineattime. But one can use instead two anchors of a (rectangle) node to calculate the "slopeness". –  Ulrike Fischer Jan 3 '13 at 14:57
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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