Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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

share|improve this question
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

3 Answers 3

up vote 31 down vote accepted

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

\def\pgfaddtoshape#1#2{%
  \begingroup
  \def\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@\shape@name @#1\expandafter\endcsname
    \csname pgf@anchor@\shape@name @#2\endcsname
}
\newcommand{\anchoralias}[2]{%
    \expandafter
    \gdef\csname pgf@anchor@\shape@name @#1\expandafter\endcsname
    \expandafter{\csname pgf@anchor@\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).

share|improve this answer
    
Good idea and very useful ! thanks –  Alain Matthes Apr 1 '11 at 21:33
    
A good idea indeed! Works great! –  Jake Apr 1 '11 at 21:41
    
@Jake: I needed that quite often by myself. –  Martin Scharrer Apr 1 '11 at 21:58
    
thanks a lot martin for this great answer (+1). How can I put this in a package within my directory? I've copied your first code into tikz_anchors.sty and load the package with \usepackage{tikz_anchors} right after all tikz packages, but I get ERROR: Undefined control sequence. --- TeX said --- l.8 \pgfaddtoshape {rectangle}{% –  DaveBall aka user750378 Dec 8 '13 at 18:11
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

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.

share|improve this answer
    
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
4  
@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

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.