# How to “pin” a node to an anchor (also for later references)

First, a disclaimer: I cannot believe that I'm the first one asking that, so I probably missed an obvious answer.

We can use anchors to position a node. But after positioning, the default anchor for the node is back to center.

Is there a way to set the default anchor of a node whenever it is referred to (e.g. in calc, or in a path)?

Take for example the case of section 17.5.2 of the manual, but using calc instead of paths:

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=center}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
\end{scope}
\end{tikzpicture}


The alignment is broken, so according to the manual I change the anchor to mid:

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=mid}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
%\path[draw,cyan] (x.mid) -- (y.mid) -- (z.mid);
\end{scope}
\end{tikzpicture}


Oh oh, now the path is not straight anymore. At least the alignment is correct because the center of x is also its mid, but if we use base instead:

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
%\path[draw,cyan] (x.base) -- (y.base) -- (z.base);
\end{scope}
\end{tikzpicture}


And now both the alignment and the path are broken.

To fix it, we would need to add the anchors also to each reference of the node in subsequent commands:

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\node (x) {x};
\node at ($(x.base)+(2,0)$) (y) {y};
\node at ($(x.base)+(4,0)$) (z) {z};
\path[draw,red] (x.base) -- (y.base) -- (z.base);
%\path[draw,cyan] (x.base) -- (y.base) -- (z.base);
\end{scope}
\end{tikzpicture}


Hence, the question: do we really have to? Or can we tell the nodes to "remember" that their anchor is mid, or base, or whatever?

Basically, I would want a way to modify the 3rd snippet at only one place and get the 4th output.

(As a bonus, if this "remembered" anchor could behave like the default one and obey the inner and outer seps, it would be even greater. But this seems to be another question altogether.)

MWE:

\documentclass[11pt]{article}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=center}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
\end{scope}
\end{tikzpicture}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=mid}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
%\path[draw,cyan] (x.mid) -- (y.mid) -- (z.mid);
\end{scope}
\end{tikzpicture}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\node (x) {x};
\node at ($(x)+(2,0)$) (y) {y};
\node at ($(x)+(4,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
%\path[draw,cyan] (x.base) -- (y.base) -- (z.base);
\end{scope}
\end{tikzpicture}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\node (x) {x};
\node at ($(x.base)+(2,0)$) (y) {y};
\node at ($(x.base)+(4,0)$) (z) {z};
\path[draw,red] (x.base) -- (y.base) -- (z.base);
%\path[draw,cyan] (x.base) -- (y.base) -- (z.base);
\end{scope}
\end{tikzpicture}

\end{document}

• You could use a global setting for \node text height and depth with \tikzset{every node/.append style={text height=2ex,text depth=1ex}} if you play a bit with the dimensions you will see that 1st and 2nd output will be the same or 1st and 3rd. – BambOo May 22 at 16:57
• @BambOo Thanks! I guess my example was too simplistic. How about a node which I'd have placed with west or 120? – T. Verron May 22 at 17:07
• I agree with @BambOo. You could do \documentclass[tikz,border=3mm]{standalone} \usetikzlibrary{calc} \begin{document} \begin{tikzpicture} \begin{scope}[nodes={anchor=base,text height=2ex,text depth=0.25ex}] \node (x) {x}; \node at ($(x.base)+(2,0)$) (y) {y}; \node at ($(x.base)+(4,0)$) (z) {z}; \path[draw,red] (x) -- (y) -- (z); \end{scope} \end{tikzpicture} \end{document}. Note the .base in \node at ($(x.base)+(2,0)$) (y) {y};. – user194703 May 22 at 17:48
• I think you are using the wrong tool, then. You should not use calc but positioning with base right=2cm of x. calc is not really intended for node positioning in the way you discuss here. – user194703 May 22 at 17:55
• I guess the main problem is that I do not understand what you want to achieve. As for "My question is precisely about whether it's possible to make that very .base implicit when defining the node x." : what does that mean? If you draw a line to x, should this line attach to its base? This you can achieve, but is probably not what you want. – user194703 May 22 at 18:40

I'll just propose some workarounds. They probably don't solve your actual questions. But if you point out why any of them don't work I'll have a better idea about what you actually want.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=center}]
\node (x) {x};
\node at ($(x)+(1,0)$) (y) {y};
\node at ($(x)+(2,0)$) (z) {z};
\path[draw,red] (x) -- (y) -- (z);
\end{scope}
\end{tikzpicture}

\def\cs#1{\texttt{\string#1}}

First workaround: \cs\strut{} or minimum size

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=center}]
\node (x) {x\strut};
\node at ($(x)+(1,0)$) (y) {y\strut};
\node at ($(x)+(2,0)$) (z) {z\strut};
\path[draw,red] (x) -- (y) -- (z);
\end{scope}
\end{tikzpicture}

Second workaround: declare an anchor as a coordinate.

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\node (x) {x};
\coordinate (x!!) at (x.base);
\node at ($(x!!)+(1,0)$) (y) {y};
\coordinate (y!!) at (y.base);
\node at ($(x!!)+(2,0)$) (z) {z};
\coordinate (z!!) at (z.base);
\path[draw,red] (x!!) -- (y!!) -- (z!!);
\end{scope}
\end{tikzpicture}

Remember that you can do
\texttt{\cs\coordinate{} (z<) at (z.base west);} and
\texttt{\cs\coordinate{} (z>) at (z.base east);}.

\end{document}

• Thanks! I guess the second one would work if we swap the coordinate and node declaration: the node would have anchor base from the scope, and the coordinate would be the pin I want. I'd need to declare each "reference" node twice, but at least we can change the default anchor from a single place. And both can probably be encapsulated in a macro. – T. Verron May 23 at 7:49
• The first one, as the first workaround from the comments, only works for mid/base/center, not for example for west. – T. Verron May 23 at 7:50
• Do you mind if I edit with what I used in the end before accepting the answer? – T. Verron May 27 at 8:55
• You can edit my answer or make a new one (and accept your own answer, yes it's legal) – Symbol 1 May 27 at 23:13
• Okay that's what I did in the end. I first thought that it would add more noise than necessary to have two answers, but it turns out that my final solution is different enough that both answers might be useful to different people. – T. Verron May 29 at 9:15

(Based on the excellent suggestion by Symbol1)

You can declare a coordinate at the wanted pin point:

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base}]
\coordinate (x!!);
\node (x) at (x!!) {x};
\coordinate (y!!) at ($(x!!)+(1,0)$);
\node (y) at (y!!) {y};
\coordinate (z!!) at ($(x!!)+(2,0)$);
\node (z) at (z!!) {x};
\path[draw,red] (x!!) -- (y!!) -- (z!!);
\end{scope}
\end{tikzpicture}


Note that base appears only in the scope options and can be changed at will.

If necessary, the two lines coordinate+node can be grouped in a macro. In that case, you can also define coordinates which will be placed relative to the pinned anchor.

All together, it gives:

\documentclass[11pt]{article}

\usepackage{tikz}
\usetikzlibrary{calc,positioning,shapes}

\begin{document}
\newcommand{\pinnode}[4][]{%
% Options, positioning, name, text
\coordinate (#3!!) #2;
\node[#1] at (#3!!) (#3) {#4};
\path[draw=none] (#3!!) -| (#3.east)
coordinate[pos=0.5] (#3>);
\path[draw=none] (#3!!) -| (#3.west)
coordinate[pos=0.5] (#3<);
\path[draw=none] (#3!!) |- (#3.south)
coordinate[pos=0.5] (#3v);
\path[draw=none] (#3!!) |- (#3.north)
coordinate[pos=0.5] (#3^);
}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=base, inner sep=10pt}]
\pinnode[draw,rectangle]{}{x}{Text of the node};
\begin{scope}[every node/.style={anchor=center,inner sep=1pt,circle,fill=blue},
every path/.style={draw=cyan}]
\draw (x<) -- (x>);
\draw (xv) -- (x^);
\node[red] at (x!!) {};
\node at (x<) {};
\node at (x>) {};
\node at (xv) {};
\node at (x^) {};
\end{scope}
\end{scope}
\end{tikzpicture}

\vspace{1cm}

\begin{tikzpicture}
\begin{scope}[every node/.style={anchor=170, inner sep=5pt}]
\pinnode[draw,shape=ellipse]{}{x}{Text of the node};
\begin{scope}[every node/.style={anchor=center,inner sep=1pt,circle,fill=blue},
every path/.style={draw=cyan}]
\draw (x<) -- (x>);
\draw (xv) -- (x^);
\node[red] at (x!!) {};
\node at (x<) {};
\node at (x>) {};
\node at (xv) {};
\node at (x^) {};
\end{scope}
\end{scope}
\end{tikzpicture}


Again note that the only reference to the anchor is in the outer scope.