64

I frequently find myself needing more than the standard anchors TikZ nodes provide, i.e. north, north east, east and so forth. I usually use coordinate calculations for this, as in the example below, but it's a bit tedious for such a seemingly simple task.

Do you have a better idea for accessing points on the sides of nodes? Ideally, what I'd like are anchors like north north east for the standard shapes.

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

\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};

% Is there an easier way to get the lines from A and C to off-centre points on the left side of D?
\draw (A.east) -- ($(D.south west)!0.75!(D.north west)$);
\draw (B.east) -- (D.west);
\draw (C.east) -- ($(D.south west)!0.25!(D.north west)$);
\end{tikzpicture}
\end{document}

boxes and arrows

  • 2
    I know that this was a set-up for Martin's answer ... – Loop Space Apr 1 '11 at 21:21
  • @Andrew,Caramdir: No problem, other solutions are very welcome. – Martin Scharrer Apr 1 '11 at 21:27
43

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.

  • 1
    Are you sure, that you copied the \pgfaddtoshape macro at the beginning of my answer as well? – Martin Scharrer Dec 8 '13 at 18:42
  • 1
    There seems to be a compatibility issue with TikZ 3.0. After updating to TikZ 3.0 your solution does not work anymore. – maetra Mar 8 '14 at 0:57
  • 1
    I keep running into the error ! Undefined control sequence. \anchor ... \csname pgf@anchor@\pgf@sm@shape@name @#1\endcsname {#2} With the new version of texlive-2015. Any thoughts? – maero21 Nov 6 '15 at 21:55
  • 1
    @maero21 Without the abbreviation part it works if you replace the \def\shape@name{#1} by \def\pgf@sm@shape@name{#1} in the first block. I don't understand this, to be honest, but found out that it works. I just can't get the abbreviation part to work... – Ktree Jan 7 '16 at 8:31
  • 1
    @Ktree: I updated the code. The shape name macro needed to be updated on several places. – Martin Scharrer Dec 10 '17 at 18:11
67

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.

  • The problem with this is that the angle depends on the dimensions of the rectangle node (D in this case), isn't it? – Martin Scharrer Apr 1 '11 at 21:20
  • @Martin: Sure, but whether this is a problem depends on what exactly you want to achieve. – Caramdir Apr 1 '11 at 21:22
  • 5
    @Martin: Also, I wanted to hijack the question set up for you ;) – Caramdir Apr 1 '11 at 21:24
  • 2
    One problem I have with angle anchors is that they aren't particularly "semantic". I can recall at least one case where I wanted (needed?) something like "west of the baseline of the first line of text". – Brent.Longborough Apr 1 '11 at 22:19
  • 4
    @Brent: that would be the base west anchor. – Caramdir Apr 1 '11 at 22:21
8

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

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.