[Tex/LaTex] TikZ: positioning label at the end of a path

labelspathstikz-pgf

I'm trying in TikZ to find a general way to draw an arrow and label it at the end. Something like this works reasonably well:

\draw[->] (0, 0) -- (1, 3) node[pos=1.1]{$\vec{v}$};

However, depending on the orientation, if the label is too long, then the label can overlap the arrow:

\draw[->] (0, 0) -- (3, 0) node[pos=1.1]{$\vec{v} + \vec{w}$};

What I would ideally like is, for the latter arrow, the left side is at pos=1.1. I know I can do this in this specific case by using node[pos=1.1, anchor=west], but I'm looking for something more general (so that I could abstract this all and define a command to do it automatically).

One approach I considered was trying to find the width and height of the label and move the label using those values. So, 2 questions: Is there a way to find the width/height? Or, alternatively, is there a better approach?

Best Answer

For straight lines (--) a special to path could be the solution.

I have defined three to styles:

  1. a=<node text>: relative positioning
    • a position=<pos amount> (default 1.1)
  2. aa=<node text>: absolute positioning
    • aa distance=<length> (default 1ex)
  3. bb: absolute positioning but saves the coordinate and the angle for later use:
    • \xVecN: the x value,
    • \yVecN: the y value,
    • \aVecB: the anchor angle.
    • The bb style uses these values internally.
    • aa distance=<length> (default 1ex)

The lines in the code sample that are marked with % debug can be removed; they are only there to show how the styles work.

Code

\documentclass[tikz,border=2pt]{standalone}
\usetikzlibrary{calc}
\newlength{\qrrAadistance}
\setlength{\qrrAadistance}{1ex}
\newcommand*{\qrrAposition}{1.1}
\tikzset{
    a style/.style={% this style should be set to further change the behaviour of the nodes that are now hidden inside
        draw, % debug
    },
    a position/.code={\pgfmathsetmacro\qrrAposition{#1}},
    a/.style={% relative position
        to path={
            let \p1=(\tikztostart),
                \p2=(\tikztotarget),
                \n1={atan2(\x2-\x1,\y2-\y1)} in
              -- (\tikztotarget) \tikztonodes node[pos=\qrrAposition, anchor=\n1-180, a style] {#1}
                                              node[pos=\qrrAposition, fill=black, circle, inner sep=0.6pt] {}% debug
        }
    },
    aa distance/.code={\pgfmathsetlength\qrrAadistance{#1}},
    aa distance/.initial=1ex,
    aa/.style={% fixed distance
        to path={
            let \p1=(\tikztostart),
                \p2=(\tikztotarget),
                \n1={atan2(\x2-\x1,\y2-\y1)} in
              -- (\tikztotarget) \tikztonodes node[anchor=\n1-180, a style] at ($(\tikztotarget)+(\n1:\the\qrrAadistance)$) {#1}
                                              node[fill=black, circle, inner sep=0.6pt] at ($(\tikztotarget)+(\n1:\the\qrrAadistance)$) {} % debug
        }
    },
    b/.style={% later usage
        to path={
            let \p1=(\tikztostart),
                \p2=(\tikztotarget),
                \n1={atan2(\x2-\x1,\y2-\y1)},
                \p{node}=($(\tikztotarget)+(\n1:\the\qrrAadistance)$)
                 in
              -- (\tikztotarget) \tikztonodes
              \pgfextra{\xdef\aVecN{\n1-180}\xdef\xVecN{\x{node}}\xdef\yVecN{\y{node}}}
        }
    },
    bb style/.style={
        anchor=\aVecN,
        at={(\xVecN,\yVecN)},
        a style,
    },
}
\begin{document}
\begin{tikzpicture}
\draw[->] (0, 0) to[a=$\vec{v}$] (1, 3);
\draw[->] (0, 0) to[aa=$\vec{v} + \vec{w}$] (3, 0);
\draw[->] (0, 0) to[bb] (1, -3);
\node[bb style] {$\vec{v} + \vec{w} + (0, -6)$};
\end{tikzpicture}
\end{document}

Output

enter image description here

% debug

How it works

\foreach \angle in {0,2,...,358}{
\begin{tikzpicture}
\path[use as bounding box] (-2.5,-2) -- (2.5,2);
\draw[->] (0, 0) to[aa=$\vec{v} + \vec{w}$] (\angle:1cm);
\end{tikzpicture}
}

Difference between absolute and relative positioning

\foreach \l in {0.1,0.2,...,2.9,3.0,2.9,...,0.2}{
\begin{tikzpicture}
\path (0,0) -- (0:4.5cm);
\draw[->]              (0, 0) to[a =$\vec{v} + \vec{w}$] (0:\l);
\draw[yshift=-.7cm,->] (0, 0) to[aa=$\vec{v} + \vec{w}$] (0:\l);
\end{tikzpicture}
}

Output

enter image description here enter image description here

Related Question