12

I'd like to place a label (most of the times a single letter) right after the end of a line. That is, the node should be moved into the direction of the line from its endpoint, just as far that is doesn't overlap with the line.

I know that I could do

\tikz (a)--node[pos=1.1]{b}(b);

but I don't want the distance of the node from the line end to depend on the length of the line. Also I'd like this to work for arbitrary curved paths, not just straight lines.

What is the easiest way to do this in Tikz?

  • Is there any way to automatically figure out the angle of the path relative to its endpoint? Then one could choose the anchor of the node at just that angle. – Andi Bauer May 15 '18 at 12:30
  • I just discovered a solution that however only works in the case of straight lines: tex.stackexchange.com/q/89484/163063. But no idea how to adapt this for curved paths. – Andi Bauer May 15 '18 at 15:29
8

(this is the first time I add another answer --- if it's not the correct thing to do, please tell me; but this is so different from my first one that... should I mark it community wiki?)

You can use a transform on a decoration, thanks to the fact that the coordinate system in a decoration is along the path; that will shift the node after the end of the path by a distance specified as the second argument:

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\begin{document}
\begin{tikzpicture}[
    endnode/.style n args={2}{
        decoration={
        markings,
        % transform={xshift=0.1*\pgfdecoratedpathlength}, % relative to lenght
        transform={xshift=#2},                            % absolute 
        % maybe add a endnodestyle like in Torbjørn T.'s answer
        mark=at position 1 with {\node[draw=red] {#1};}   % just to debug 
        },
     postaction={decorate},
  },
]
\foreach \Ang/\Len in {0/1,45/1.2,90/1.4,135/1.6,180/1.8,225/2,270/2.2,315/2.4}
{
    \draw [endnode={A}{2mm}] (0,0) -- (\Ang:\Len);
    \draw [endnode={B}{4mm}] (6,0) to[bend right]  ++(\Ang:\Len);
}
\end{tikzpicture}
\end{document}

enter image description here

This makes the node centered at a fixed distance from the end of the path, along the path. It doesn't change the node anchors, unfortunately... and auto is not working here.

Thanks to: Length of curve in TikZ, Torbjørn T.'s answer, \tikzset key with multiple arguments.

  • Great, that's exactly what I was looking for. But it doesn't seem work when the bend angle of the to path is greater than 34. In this case the node simply disappears! Can anyone explain this weird behavior? Is this a bug in Tikz? Do you encounter the same issue? – Andi Bauer May 15 '18 at 19:05
  • I just found a very dirty workaround: replacing the "at position 1" by "at position 0.999" seems to solve the issue and doesn't make much difference to my purposes. Funnily, using like 0.99999 instead of 0.999 makes it work for a little bigger angles, but still not all of them :D – Andi Bauer May 15 '18 at 19:09
  • I played around a little more: angles near to 0 and 180 degrees work fine, angles around 90 seem to be the most problematic ones. The number the closest to 1 that one can use such that it works for all angles (including 90) is around 0.9997. – Andi Bauer May 15 '18 at 19:19
  • Ah, ok. Wild guess: there is a tan(angle) somewhere in the code that goes awry. – Rmano May 15 '18 at 19:57
14

You can put the node at pos=1 and then left (or right, depending on where) align it:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[every node/.style={draw=red},
    ]
         \draw (0,0) -- (1,0) node[right]{a} ;
         \draw (1,0.5) --node[pos=1, left]{b}  (0,0.5) ;
         \draw (0,-1) -- node[pos=1,  right]{c} (1, -2)  ;
         \draw (0,-1.3) -- node[pos=1,  sloped, right]{d} (1, -2.3)  ;
         \draw (0,-2) .. controls (0,-1) and (0.5,-1.5) .. node[pos=1,  right]{e} (1, -1)  ;
\end{tikzpicture}
\end{document}

enter image description here

  • Thanks! I forgot to mention that the node should not be rotated. Can one fix this also? – Andi Bauer May 15 '18 at 9:54
  • removing the sloped key should do. – Rmano May 15 '18 at 9:55
  • but that places the node 'right' in absolute coordinates again, not in the direction of the line... – Andi Bauer May 15 '18 at 10:01
  • I am not sure I understand. I changed the example to show the node boxes. – Rmano May 15 '18 at 10:13
  • The point is, I think, to not have to specify right or left manually, but to let TikZ figure out the anchor in such a way that the node is placed "after" the path regardless of the direction of the path. – Torbjørn T. May 15 '18 at 11:35
11

One can get somewhat close using the decorations.markings library. The code below makes use of the fact that a markings decoration has an internal coordinate system where the x-axis is parallel to the path, so I made a short path along the y-axis, and placed an auto node along it.

It's not perfect, and I can't guarantee that it will always work. For the bent to paths in the example below, I had to rotate the nodes by -90 degrees, and I'm not exactly sure why. You can also see that the position of the nodes is perhaps not perfect.

enter image description here

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\begin{document}
\begin{tikzpicture}[
  endnode style/.style={},
  endnodestyle/.style={endnode style/.append style={#1}},
  endnode/.style={
    decoration={
     markings,
     mark=at position 1 with {
       \path (0,-1pt) -- node[auto,swap,endnode style] {#1} (0,1pt);
     }
    },
    postaction={decorate}
  }
]
\foreach \Ang in {0,45,...,350}
{
  % works fine for straight lines
  \draw [endnode=a] (0,0) -- (\Ang:2cm);
  % needs a rotation for these curved paths
  \draw [endnodestyle={rotate=-90},endnode=foo] (6,0) to[bend right] ++(\Ang:2cm);
}
\end{tikzpicture}
\end{document}

Better output, less convenient to use

You could save two coordinates on the path, one very close to the end (I randomly used pos=0.97 here, but I suspect the ideal value will depend on the path), one at the end (pos=1). Then in a separate path, calculate the angle between those two coordinates with the let operation (manual section 14.15), and use that to set the anchor of a node. This does not have the problem the decorations.markings version above suffered, of failing at larger bend angles, but is as mentioned less convenient to use.

enter image description here

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings, calc}
\newcommand{\addnode}[2][tmp]{%
\path let
  \p1=($(#1-A)-(#1-B)$),\n1={atan2(\y1,\x1)}
  in
  node[anchor=\n1] at (#1-B) {#2};
}

\begin{document}
\begin{tikzpicture}
\foreach \Ang in {0,45,...,350}
{
  % works fine for straight lines
  \draw (0,0) -- 
        coordinate[pos=0.97] (tmp-A) % near end
        coordinate[pos=1]    (tmp-B) % at end
  (\Ang:2cm); % close the path
  \addnode{a} % add node in separate step


  \draw (6,0) to[bend right=80]
       coordinate[pos=0.97] (tmp-A)
       coordinate[pos=1]    (tmp-B)
    ++(\Ang:2cm);
  \addnode{foo}
}
\end{tikzpicture}
\end{document}
  • Cool! But the rotation by -90 is strange. Also I found something else really funny: For e.g. "to[bend right=x]" it works for x<32, but not for x>=32. In the latter case no node is shown at all... – Andi Bauer May 15 '18 at 12:19
  • @AndiBauer Indeed. I came up with a different method, which does have some other shortcomings, but also gives better results. See above. – Torbjørn T. May 15 '18 at 13:25
  • Thanks, the output of your second solution is exactly what I want. But now the typing effort is probably bigger than calculating the angles at the line end by hand. (I really have lots of such labels in my document) – Andi Bauer May 15 '18 at 15:28
3

Here is another answer using decorations.markings that should work without manual tweaking : place the node node right after the end of the line, without rotation and with the right anchor.

The trick is to place first one node that is used as reference to determine the right anchor position.

\documentclass[tikz,border=7pt]{standalone}
\usetikzlibrary{decorations.markings,calc}
\begin{document}
\begin{tikzpicture}[
  endnode/.style={
    decoration={
     markings,
     mark=at position .999 with {
      \path (0,0) node[overlay](refnode){}
        let \p1=(refnode.west), \n1={-atan2(\y1,\x1)} in {
          node[anchor=\n1, draw=red] {#1}
        };
     }
    },
    postaction={decorate}
  }
]
\foreach \Ang in {0,20,...,350}
{
  \draw [endnode=a] (0,0) -- (\Ang:2cm);
  \draw [endnode=foo] (6,0) to[bend right=77] ++(\Ang:2cm);
}
\end{tikzpicture}
\end{document}

enter image description here

EDIT : As @Torbjørn T. suggest in his comment I replaced pos=1 with pos=.999 to avoid disappearing due to rounding tex errors. I also added overlay to the reference node to avoid bounding box surprises.

  • Try bend right=80. – Torbjørn T. May 16 '18 at 20:43
  • @TorbjørnT. This is another issue : just replace pos=1 with pos=.999 (as you probably already know). I'll edit my code to avoid this : thanks ! – Kpym May 17 '18 at 5:25

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.