10

I was looking for a possibility to adjust the anchor of the text of a pin. Tikz does not allow this out of the box.

This question was already asked on the mailing list pgf-users@lists.sourceforge.net, Sept. 2009 without any reply till today or feature addition to pgf/tikz:

\documentclass[tikz]{standalone} 
\begin{document} 
    \begin{tikzpicture}[ 
        every pin/.style={anchor=west}, 
        every pin edge/.style={anchor=west}] 
        \node [pin=60:test] at (0,1) {}; 
        \node [pin={[anchor=west]60:test}] at (0,0) {}; 
    \end{tikzpicture} 
\end{document}

There is also a question with a suggested workaround here on tex.stackexchange.com by defining an aligned pin style consisting of another node positioned at the end of the pin using \tikzlastnode:

\tikzset{
    aligned pin/.style args={[#1,#2]#3:#4}{
        pin={[inner sep=0pt,%
              pin distance=#2,% new option, default = 3ex
              label={[append after command={%
                    node[inner sep=0pt,%
                        xshift=-0.1cm,% modified, default = 0
                        at=(\tikzlastnode.#3),%
                        anchor=#1,%
                    ]{#4}%
                }%
            ]center:{}}%
        ]#3:{}}%
    }
}

In this code, I have added a second unnamed option for pin distance within the square brackets.

MWE using the workaround showing the desired result:

\documentclass[tikz]{standalone} 
\tikzset{
    aligned pin/.style args={[#1,#2]#3:#4}{
        pin={[inner sep=0pt,%
            pin distance=#2,% new option, default = 3ex
            label={[append after command={%
                    node[%
                        inner sep=0pt,%
                        xshift=-0.1cm,% modified, default = 0
                        at=(\tikzlastnode.#3),%
                        anchor=#1,%
                    ]{#4}%
                }%
            ]center:{}}%
        ]#3:{}}%
    }
}
\begin{document}
\tikz[pin distance=15mm, every pin/.style={fill=blue,text=white}]
\node [circle,draw,
    aligned pin={[west,3ex]0:X-Value},%
    aligned pin={[west,3ex]45:Y-Value},%
    aligned pin={[west,3ex]90:Z-Value}] {my circle};
\end{document}

enter image description here

I would like to add further options such as pin edge to adjust the style of the edge. Another addition would be xshift to finetune the position of the text (and yshift).

Is it possible to change the workaround to allow named options with default values if not specified?

Usage could look like this (without using necessarily all options):

node[aligned pin={[pin anchor=west,pin distance=1cm,pin edge={thick}]90:{txt}}]{};

If someone is interested in the implementation of the pin feature in tikz see pgf/base/tex/generic/frontendlayer/tikz.code.tex and do a textsearch for pin.

Update after answer by Qrrbrbirlbel:

To achieve almost the same result as my MWE use following code

% use preamble from Qrrbrbirlbel answer
\begin{document} 
\begin{tikzpicture}[every pin/.append style=draw, every pin edge/.style={thick}]
\node[c] [
pin={[pin anchor=south west,pin edge pin anchor=south west,pin distance=6ex]90:Z-Value},
pin={[pin anchor=west,pin edge pin anchor=west,pin distance=6ex]45:Y-Value},
pin={[pin anchor=west,pin edge pin anchor=west,pin distance=6ex]0:X-Value}
] at (0,0) {};
\end{tikzpicture} 
\end{document}

enter image description here

However, I would like to finetune the text positioning using inner sep=0 and a slight shift:

\newlength{\xshiftlength}
\setlength{\xshiftlength}{0.5\widthof{Z}}

Following works:

\tikzset{
  pin inner xsep/.style={tikz@pin@post/.append style={inner xsep=#1}}
}

Apparently xshift cannot be added in the same way. It shifts more than only the text. How could a pin text xshift be added?

2
  • Am I seeing this correct? You want a pin-node which has a label-node which has yet another node (internally similar to a label)? What is the actual problem with labels/pins in your example? Does How can I set the TikZ label anchor explicitly? help? May 26, 2015 at 16:42
  • Thanks for showing me the question. But as far as I can see the answers are either similar (defines my label as aligned pin) or do not give me the desired result (accepted answer). The accepted answer looked promising but my MWE with \tikzset{pin anchor/.style={tikz@pin@post/.append style={anchor=#1}}} did not put the anchor of the text to the correct position. As you can see I have a solution which gives me the desired otuput. However, I want to adjust the pin as documented. I really do not want to change the usage pattern.
    – Hotschke
    May 27, 2015 at 8:53

1 Answer 1

18

When TikZ places a label (or a pin) it calculates a point that is label distance (or pin distance) away from a point on the anchor-border of its parent node.

This point on the anchor-border is directly dependent on the angle/direction before the : (or the default label position/pin position). In the same direction the * distance is added.

This is the new point where the label/pin is placed. (In other words: all the options like direction/angle and distance have an influence on the newly placed node, not the pin edge.)

The new node is placed at a specific anchor which is calculated internally by TikZ (and snaps to the eight main compass directions and in one special case to the .center). This is (one of) the cause for the problems in other questions:

The pin edge (with all its options and defaults and styles) is done in an append after command (the label/pin placement itself is a append after command which \tikzlastnode has been saved in \tikz@save@last@node so that the inner append after command can actually access both nodes).

The default pin edge is a line to path (= (<parent node>) -- (<pin node>)). This is drawn with no regard to any anchors calculated or placed at. The -- line is drawn directly between the center of nodes stopping at the nodes’ borders.


The label and pin keys aren't really made for precise placement. (Don’t ask my why.)

However, with a few keys we could get what you want (or at least I assume this is what you want):

  • The label anchor key is taken from my other answer to the first linked question above.
  • The pin anchor is defined similar.
  • The pin edge pin anchor key can be used to change the anchor at which the pin edge should end (at the pin).
  • The pin edge parent anchor key can be used to change the anchor at which the pin edge should start (at the parent node).

The latter keys can also be given an empty value which resets the anchor to “automatically” (as TikZ sees fit).


I have added some auxiliary lines to the drawings that should help understand what I have said before. The arc labelled “60°” shows where the angle 60 is used and the double-arrowed line angled at 60° shows where the pin distance (3ex per default) is measured. The bottom-left start of this line points to (<parent node>.60) and the top-right end of this line points to the point that pin distance away from the start and where the pin will be placed with its (automatically determined or via pin anchor set) anchor.

The second double-arrowed line shows how TikZ connects the the nodes (via the line to edge). In the first two cases (without any pin edge * anchor set) it connects the nodes on a straight line between their centers. In the third case it uses the specified south west anchor to draw the line to. In the fourth, the additional north anchor to start the line from.

Code

\documentclass[tikz]{standalone}
\makeatletter
\newcommand*\ifStrInTF[2]{%
  \edef\tikz@temp{{#1}{#2}}%
  \expandafter\pgfutil@in@\tikz@temp
  \ifpgfutil@in@\expandafter\pgfutil@firstoftwo\else\expandafter\pgfutil@secondoftwo\fi}
\newcommand*\ifStrEmptyTF[1]{%
  \def\tikz@temp{#1}\ifx\tikz@temp\pgfutil@empty
    \expandafter\pgfutil@firstoftwo\else\expandafter\pgfutil@secondoftwo\fi}

\def\tikz@swapanchor#1.#2\tikz@stop#3#4{#1#4#3}
\newcommand*\tikzAddAnchor[2]{%
  \ifStrInTF{.}{#1}{%
    \ifStrEmptyTF{#2}
      {\edef#1{\expandafter\tikz@swapanchor#1\tikz@stop{}{}}}
      {\edef#1{\expandafter\tikz@swapanchor#1\tikz@stop{#2}{.}}}%
  }{%
    \ifStrEmptyTF{#2}{}% no true
      {\edef#1{#1.#2}}%
  }}
\tikzset{
  pin anchor/.style={tikz@pin@post/.append style={anchor=#1}},
  label anchor/.style={tikz@label@post/.append style={anchor=#1}},
  pin edge pin anchor/.style={
    append after command={\pgfextra\tikzAddAnchor{\tikzlastnode}{#1}\endpgfextra}%
  },
  pin edge parent anchor/.style={
    append after command={\pgfextra\tikzAddAnchor{\tikz@save@last@node}{#1}\endpgfextra}%
  }
}
\makeatother
\tikzset{c/.style={circle, minimum size=1cm, draw, densely dotted}}
\begin{document} 
\begin{tikzpicture}[every pin/.append style=draw, every pin edge/.style={thick}]
  \node [c] [pin=60:test] at (0,0) {};

  \node[c] [pin={[pin anchor=west]60:test}] at (2,0) {};

  \node[c] [pin={[pin anchor=south,
                  pin edge pin anchor=south west]60:test}] at (4,0) {}; 

  \node[c] [pin={[pin anchor=south,
                  pin edge pin anchor=south west,
                  pin edge parent anchor=north]60:test}] at (6,0) {}; 

\end{tikzpicture} 
\end{document}

Output

enter image description here

5
  • I feel I could have used better (and more consistent) examples but hopefully this suffices. May 27, 2015 at 22:01
  • First of all a big thanks to you. Your answer is informative and helped me to understand how the pin edge and pin text positions are determined. IMHO your solution by introducing additional anchors is a great way to stick to the default pin feature.
    – Hotschke
    May 28, 2015 at 8:31
  • I wish this (or similar) could make it into the TiKZ main code or at least into a library. It would be less efficient to put it into a personal package that you have to import every time.
    – Andyc
    May 4 at 17:45
  • @Andyc Load with \usetikzlibrary{qrr.labels-pins-plus}. You could always put the code yourself more or less in a tikzlibrary<library name>.code.tex file. Jul 22 at 20:43
  • @Qrrbrbirlbel Sure, I could do that. But it would be nice if a maintainer would collect the most useful and beautiful answers, tips and tricks in forums and put them in an organized library that's available to everybody. Nonetheless thank you for the nice library.
    – Andyc
    Jul 24 at 8:24

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.