Add more anchors to standard TikZ nodes

The standard nodes have anchors node.〈angle〉 where angle is between 0 (=east) and 360, measured counterclockwise. These nodes lie on the border of the node, on the line of the given angle from the center. For example,

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

\begin{document}
\begin{tikzpicture}
\tikzstyle{every node}=[draw,fill=yellow,minimum width=2cm,thin]
\tikzstyle{every path}=[-latex,ultra thick]
\node (A) {A};
\node (B) [below=2mm of A] {B};
\node (C) [below=2mm of B] {C};
\node (D) [right=of B,fill=green] {D};

\draw (A.east) -- (D.175);
\draw (B.east) -- (D.west);
\draw (C.east) -- (D.185);
\end{tikzpicture}
\end{document}

result


Maybe I should mention that the TikZ manual has wonderful pictures of the locations of all the anchors available by default in the “Shape Library” chapter.


I started to create some code which allows you to add more anchors to existing node shapes:

\def\pgfaddtoshape#1#2{%
  \begingroup
  \def\pgf@sm@shape@name{#1}%
  \let\anchor\pgf@sh@anchor
  #2%
  \endgroup
}

The above code was copied from the definition of \pgfdeclareshape and only includes the required parts and excludes the shape initialization part.

Using that, more node anchors can be added. This isn't that easy, because it has to be done using the lower level PGF macros and requires knowledge and understanding where the origin of the shape is. For example for the rectangle shape the origin lies at the left corner of the text baseline, not in the middle of the rectangle.

Here is some code which adds more points of the compass as anchors as well as some "pseudo-anchors" which return the width and/or height of the node. The latter are useful for the let syntax of the calc library, e.g. let \y1 = (somenode.size) in node { width=\x1, height=\y1 };

\def\useanchor#1#2{\csname pgf@anchor@#1@#2\endcsname}

\def\@shiftback#1#2#3#4#5#6{%
    \advance\pgf@x by -#5\relax
    \advance\pgf@y by -#6\relax
}

\pgfaddtoshape{rectangle}{%
  \anchor{west south west}{%
    \pgf@process{\northeast}%
    \pgf@ya=.5\pgf@y%
    \pgf@process{\southwest}%
    \pgf@y=1.5\pgf@y%
    \advance\pgf@y by \pgf@ya%
    \pgf@y=.5\pgf@y%
  }%
  \anchor{west north west}{%
    \pgf@process{\northeast}%
    \pgf@ya=1.5\pgf@y%
    \pgf@process{\southwest}%
    \pgf@y=.5\pgf@y%
    \advance\pgf@y by \pgf@ya%
    \pgf@y=.5\pgf@y%
  }%
  \anchor{east north east}{%
    \pgf@process{\southwest}%
    \pgf@ya=.5\pgf@y%
    \pgf@process{\northeast}%
    \pgf@y=1.5\pgf@y%
    \advance\pgf@y by \pgf@ya%
    \pgf@y=.5\pgf@y%
  }%
  \anchor{east south east}{%
    \pgf@process{\southwest}%
    \pgf@ya=1.5\pgf@y%
    \pgf@process{\northeast}%
    \pgf@y=.5\pgf@y%
    \advance\pgf@y by \pgf@ya%
    \pgf@y=.5\pgf@y%
  }%
  \anchor{north north west}{%
    \pgf@process{\southwest}%
    \pgf@xa=1.5\pgf@x%
    \pgf@process{\northeast}%
    \pgf@x=.5\pgf@x%
    \advance\pgf@x by \pgf@xa%
    \pgf@x=.5\pgf@x%
  }%
  \anchor{north north east}{%
    \pgf@process{\southwest}%
    \pgf@xa=.5\pgf@x%
    \pgf@process{\northeast}%
    \pgf@x=1.5\pgf@x%
    \advance\pgf@x by \pgf@xa%
    \pgf@x=.5\pgf@x%
  }%
  \anchor{south south west}{%
    \pgf@process{\northeast}%
    \pgf@xa=.5\pgf@x%
    \pgf@process{\southwest}%
    \pgf@x=1.5\pgf@x%
    \advance\pgf@x by \pgf@xa%
    \pgf@x=.5\pgf@x%
  }%
  \anchor{south south east}{%
    \pgf@process{\northeast}%
    \pgf@xa=1.5\pgf@x%
    \pgf@process{\southwest}%
    \pgf@x=.5\pgf@x%
    \advance\pgf@x by \pgf@xa%
    \pgf@x=.5\pgf@x%
  }%
  \anchor{width}{%
    \useanchor{rectangle}{west}%
    \pgf@xc=\pgf@x
    \useanchor{rectangle}{east}%
    \advance\pgf@x by -\pgf@xc
    \pgf@y=\z@
    \edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
    \expandafter\@shiftback\pgf@temp
  }
  \anchor{height}{%
    \useanchor{rectangle}{south}%
    \pgf@yc=\pgf@y
    \useanchor{rectangle}{north}%
    \advance\pgf@y by -\pgf@yc
    \pgf@x=\z@
    \edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
    \expandafter\@shiftback\pgf@temp
  }
  \anchor{size}{%
    \useanchor{rectangle}{south west}%
    \pgf@xc=\pgf@x
    \pgf@yc=\pgf@y
    \useanchor{rectangle}{north east}%
    \advance\pgf@x by -\pgf@xc
    \advance\pgf@y by -\pgf@yc
    \edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
    \expandafter\@shiftback\pgf@temp
  }
}

I have these both code blocks as pgf-extrect.sty in my local TEXMF tree and load it as package if they are required.


Another possible improvement is to define alternative anchor names, i.e. 'nnw' instead of 'north north west'. This can be done using the following macros:

\newcommand{\anchorlet}[2]{%
    \global\expandafter
    \let\csname pgf@anchor@\pgf@sm@shape@name @#1\expandafter\endcsname
    \csname pgf@anchor@\pgf@sm@shape@name @#2\endcsname
}
\newcommand{\anchoralias}[2]{%
    \expandafter
    \gdef\csname pgf@anchor@\pgf@sm@shape@name @#1\expandafter\endcsname
    \expandafter{\csname pgf@anchor@\pgf@sm@shape@name @#2\endcsname}%
}

\pgfaddtoshape{rectangle}{%
  \anchorlet{se}{south east}%
  \anchorlet{sw}{south west}%
  \anchorlet{ne}{north east}%
  \anchorlet{nw}{north west}%
  \anchorlet{wsw}{west south west}%
  \anchorlet{wnw}{west north west}%
  \anchorlet{ene}{east north east}%
  \anchorlet{ese}{east south east}%
  \anchorlet{nnw}{north north west}%
  \anchorlet{nne}{north north east}%
  \anchorlet{ssw}{south south west}%
  \anchorlet{sse}{south south east}%
}

Here \anchorlet links the alternative name to the current definition of the original name, while \anchoralias links to the name. This makes a difference if the anchor is not yet defined or might be redefined (unlikely).


Edited 10.12.2017

Updated for the new version of PGF which changed the internal shape name macro used here.


Defining a new node shape is not hard, and it is possible to put in extra anchors. Here's a node shape that I use for knots; the extra anchors are "further out" versions of the standard compass directions which I use for the control points of incoming bezier curves.

Here's the code. It could probably be streamlined considerably (it was the first node shape I tried defining).

% This sets a new round of anchors at a specified multiple of the current ones
\def\pgf@sh@@knotanchor#1#2{%
  \anchor{#2 north west}{%
    \csname pgf@anchor@knot #1@north west\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 north east}{%
    \csname pgf@anchor@knot #1@north east\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 south west}{%
    \csname pgf@anchor@knot #1@south west\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 south east}{%
    \csname pgf@anchor@knot #1@south east\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 north}{%
    \csname pgf@anchor@knot #1@north\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 east}{%
    \csname pgf@anchor@knot #1@east\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 west}{%
    \csname pgf@anchor@knot #1@west\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
  \anchor{#2 south}{%
    \csname pgf@anchor@knot #1@south\endcsname%
    \pgf@x=#2\pgf@x%
    \pgf@y=#2\pgf@y%
  }%
}
% this defines the new node shape, inheriting most from the circle shape
\pgfdeclareshape{knot crossing}
{
  \inheritsavedanchors[from=circle] % this is nearly a circle
  \inheritanchorborder[from=circle]
  \inheritanchor[from=circle]{north}
  \inheritanchor[from=circle]{north west}
  \inheritanchor[from=circle]{north east}
  \inheritanchor[from=circle]{center}
  \inheritanchor[from=circle]{west}
  \inheritanchor[from=circle]{east}
  \inheritanchor[from=circle]{mid}
  \inheritanchor[from=circle]{mid west}
  \inheritanchor[from=circle]{mid east}
  \inheritanchor[from=circle]{base}
  \inheritanchor[from=circle]{base west}
  \inheritanchor[from=circle]{base east}
  \inheritanchor[from=circle]{south}
  \inheritanchor[from=circle]{south west}
  \inheritanchor[from=circle]{south east}
  \inheritanchorborder[from=circle]
  \pgf@sh@@knotanchor{crossing}{2}
  \pgf@sh@@knotanchor{crossing}{3}
  \pgf@sh@@knotanchor{crossing}{4}
  \pgf@sh@@knotanchor{crossing}{8}
  \pgf@sh@@knotanchor{crossing}{16}
  \pgf@sh@@knotanchor{crossing}{32}
  \backgroundpath{
    \pgfutil@tempdima=\radius%
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
    \ifdim\pgf@xb<\pgf@yb%
      \advance\pgfutil@tempdima by-\pgf@yb%
    \else%
      \advance\pgfutil@tempdima by-\pgf@xb%
    \fi%
  }
}

And here it is in use:

\begin{tikzpicture}[every node/.style={knot crossing,transform shape,inner sep=1.5pt},every path/.style={red,line width=2pt}]
\foreach \brk in {0,1,2} {
\begin{scope}[rotate=\brk * 120]
\node (k\brk) at (0,-1) {};
\end{scope}
}
\draw (0,0) \foreach \brk in {0,1,2} {let \n0=\brk, \n1={int(Mod(\brk+1,3))}, \n2={int(Mod(\brk+2,3))} in (k\n0) .. controls (k\n0.16 south east) and (k\n1.16 south west) .. (k\n1.center) .. controls (k\n1.4 north east) and (k\n2.4 north west) .. (k\n2)} (k2);
\end{tikzpicture}

Notice the use of the expanded directions for the control points. Result:

trefoil