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.

When I draw a thick line from the corner of one box to the same corner of another, it doesn't attach properly. See the code and picture below:

enter image description here

\documentclass[a4paper, 11pt]{article}

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



\begin{document}
\begin{tikzpicture}
  \node [draw, thick, minimum size=1cm] (a) {A};
  \node [draw, thick, minimum size=3cm, right=2cm of a] (b) {B};

  \draw [thick, dashed] (a.north west) -- (b.north west);
  \draw [thick, dashed] (a.north east) -- (b.north east);
  \draw [thick, dashed] (a.south west) -- (b.south west);
  \draw [thick, dashed] (a.south east) -- (b.south east);
\end{tikzpicture}
\end{document}

How do I make the lines attach exactly to the corners and not slightly off as in this example?

share|improve this question

3 Answers 3

up vote 24 down vote accepted

Very good question!

First, the explanation. When TikZ/PGF creates a node, then it stores various associated locations as "anchors". These can be used to refer to points on the node other than its main coordinate (its centre). A very common family of anchors is the border of the node, these can be referred to by angle, as in (node.45) or by compass direction, as in (node.north west). Although the node can define rules as it likes for these, the basic nodes tend to position these so that they are the sensible choice of point on the boundary of the node (an example of "sensible": on a rectangular node (node.north west) is always a corner).

The key phrase here is "on the boundary of the node". The line width is crucial here. If you write:

\node[red,draw, minimum size=2cm] (a) {};
\draw[<-] (a) -- ++(5,0);

then you want the arrow to stop at the edge of the boundary, not the middle of the boundary. If we make the line width ridiculous, then we can see the effect:

node boundary with line width

The arrow stops at the edge of the node boundary, not at the mid-point. To get the mid-point, we have to lengthen the line by half the line width. However, that's not a good solution since we need to use the line width that was in effect at the time that the node was drawn (which might not be the current line width) and it only works if the line is orthogonal to the boundary, otherwise we need to adjust the offset.

Better would be if the node itself knew of this "mid-boundary". There are two ways to do this that I can think of straight away. One is to define a new node shape where the anchor boundary was not adjusted by the line width. This would be fairly easy to do: copy the definition from the right file and make the necessary adjustments, but isn't something that I'd recommend for a one-shot picture or to someone not happy with \makeatletter ... \makeatother. If this is something that you're likely to use a lot then say so, and I'll help make the modifications if you need help.

A quicker (and dirtier) way of doing this is to separate the drawing of the border from its use as a way of setting the anchors. Two ways to do this spring to mind: use two nodes, or use one with a post- or pre-action. In the first case, you define a node which is not drawn but which defines the shape. Then you put on top a new node which is drawn. Then you refer to the first for the anchors. The second case is in effect the same, except that you combine the commands into one which avoids creating extra objects and unnecessary duplication of commands.

Here's the above example done the first way:

\node[minimum size=2cm,postaction={draw,thick,red},line width=0cm] (a) {};
\draw[<-] (a) -- ++(5,0);

We do have to specify the line width on the action, even if it is a pre-action, since the line width=0cm gets set immediately. It would be possible to define a way of saving the current line width and restoring it within the action.

Here's the same example with two nodes:

\node[minimum size=2cm,line width=0cm] (a) {};
\node[draw,red,minimum size=2cm] at (a) {};
\draw[<-] (a) -- ++(5,0);

The important thing with this one is to ensure that any size commands on the first are replicated. What would get difficult with this would be if the size of the node was determined by its contents.

Both of these produce the following:

node with arrow in boundary

Now we can see that the arrow goes where it should.

If we add this to your code, we can see its effect. To emphasis the effect, I've increased the line width to 1mm and drawn the dashed lines as solid red.

lines ending in corners

(Notice another effect of this shifting of anchors. Since the righthand square is positioned relative to the first, changing the node boundary changes its position ever-so slightly.)

To make it easier to code, I did what I suggested above and defined some keys. Thus the node itself is defined as:

\node [draw with anchor in boundary, minimum size=1cm] (a) {A};

The full code (for the above with both versions) is:

\documentclass[a4paper, 11pt]{article}
%\url{http://tex.stackexchange.com/q/29874/86}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\usetikzlibrary{positioning}

\newdimen\zerolinewidth

\tikzset{
  zero line width/.code={%
    \zerolinewidth=\pgflinewidth
    \tikzset{line width=0cm}%
  },
  use line width/.code={%
    \tikzset{line width=\the\zerolinewidth}%
  },
  draw with anchor in boundary/.style={
    zero line width,
    postaction={draw,use line width},
  },
}

\begin{document}
\begin{tikzpicture}[line width=1mm]
  \node [draw, minimum size=1cm] (a) {A};
  \node [draw, minimum size=3cm, right=2cm of a] (b) {B};

  \draw [red] (a.north west) -- (b.north west);
  \draw [red] (a.north east) -- (b.north east);
  \draw [red] (a.south west) -- (b.south west);
  \draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}

\begin{tikzpicture}[line width=1mm]
  \node [draw with anchor in boundary, minimum size=1cm] (a) {A};
  \node [draw with anchor in boundary, minimum size=3cm, right=2cm of a] (b) {B};

  \draw [red] (a.north west) -- (b.north west);
  \draw [red] (a.north east) -- (b.north east);
  \draw [red] (a.south west) -- (b.south west);
  \draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}
\end{document}

Update: The choice of preaction or postaction was initially arbitrary, but having seen the updated question I realise that I chose the wrong one. It should (as it now is) be a postaction. This is so that if the node is filled (which can be done on the main node as it isn't affected by line width) and then drawn then the draw should be done second so that it is on top of the fill. Otherwise the fill takes out half of the line width (as seen in the updated question).

share|improve this answer
    
Excellent answer! However, I have one problem: the lines of the boxes are no longer semithick (my default setting) but TikZ default thickness which is thin. Since we're explicitly dealing with line width here, how do I set it to be semithick? –  gablin Sep 29 '11 at 8:39
    
@gablin: The easy solution is to specify the semithick option before the draw with anchor in boundary option. That seems to work. The draw with anchor in boundary saves the line width that is in effect at the time that it is used. Ensuring that it worked with setting the line width afterwards would be possible, but would involve Deep Hackery. –  Loop Space Sep 29 '11 at 9:34
    
Hm, the problem seems to be with the fill option. Please see my updated question. –  gablin Sep 29 '11 at 10:21
1  
Hawt diggity! That did it! Thank you so much! –  gablin Sep 29 '11 at 11:24
1  
Excellent question, answer and the last bit :) –  percusse Sep 29 '11 at 11:44

Why not use outer sep=0pt for the first nodes, By default outer sep=0.5\pgflinewidth I think it's enough to get a good result. The two last pictures are the same with draw with anchor in boundaryor outer sep=0pt

\documentclass[a4paper, 11pt]{article}
%\url{http://tex.stackexchange.com/q/29874/86}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\usetikzlibrary{positioning}

\newdimen\zerolinewidth

\tikzset{
  zero line width/.code={%
    \zerolinewidth=\pgflinewidth
    \tikzset{line width=0cm}%
  },
  use line width/.code={%
    \tikzset{line width=\the\zerolinewidth}%
  },
  draw with anchor in boundary/.style={
    zero line width,
    postaction={draw,use line width},
  },
}

\begin{document} 

  \begin{tikzpicture}
  \node [draw, thick, minimum size=1cm,outer sep=0pt] (a) {A};
  \node [draw, thick, minimum size=3cm, right=2cm of a,outer sep=0pt] (b) {B};

  \draw [thick, dashed] (a.north west) -- (b.north west);
  \draw [thick, dashed] (a.north east) -- (b.north east);
  \draw [thick, dashed] (a.south west) -- (b.south west);
  \draw [thick, dashed] (a.south east) -- (b.south east);
\end{tikzpicture}


\begin{tikzpicture}[line width=1mm]
  \node [draw, minimum size=1cm,outer sep=0pt] (a) {A};
  \node [draw, minimum size=3cm, right=2cm of a,outer sep=0pt] (b) {B};

  \draw [red] (a.north west) -- (b.north west);
  \draw [red] (a.north east) -- (b.north east);
  \draw [red] (a.south west) -- (b.south west);
  \draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}

\begin{tikzpicture}[line width=1mm]
  \node [draw with anchor in boundary, minimum size=1cm] (a) {A};
  \node [draw with anchor in boundary, minimum size=3cm, right=2cm of a] (b) {B};

  \draw [red] (a.north west) -- (b.north west);
  \draw [red] (a.north east) -- (b.north east);
  \draw [red] (a.south west) -- (b.south west);
  \draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer

Andrew Stacey gives an excellent explanation why the lines only touch the node’s border and Alain Matthes gives a solution that is in this simple case superior (and essentially the same).

I want to give another solution that does not rely on the outer seps settings or in fact on the original anchors (the original anchors on the boundary are lost and even a simple \draw (a) -- (b); line will extend into the rectangle’s border).

Inspired by Martin Scharrer’s answer to a question that asks for more anchors I actually added more anchors to the rectangular shape.

All anchors that lie on the border (all compass points as well as .mid west, .base west, .mid east and .base east) are duplicated to line <old anchor name>, e.g. .line north west, .line north, .line mid east and so forth.

Just like all original anchors are based on two “saved” anchors (namely north east and south west) the “line” anchors are based on line north east and line south west whose calculation is as simple as it gets: Their definition is nearly the same as from north east and south west; only the values of outer xsep and outer ysep are not used.

In this answer three outputs are given:

  • with the settings given from the question’s code
  • with extra “debug” settings (thicker and solid lines, opacity)
  • and a reduced clone of the Rectangle figure of the PGF manual (section 48.2 “Predefined Shapes”, page 420) that shows the new anchors and their old companions.

Code

\documentclass[tikz]{standalone}

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

\makeatletter
% The macro \pgfaddtoshape is originally from Martin Scharrer
& http://tex.stackexchange.com/a/14772/16595
% and here extended to be able to use \savedanchor and \nodeparts/\pgfnodepartstextbox
\def\pgfaddtoshape#1#2{%
  \begingroup
  \def\shape@name{#1}
  \let\savedanchor=\pgf@sh@savedanchor
  \let\anchor=\pgf@sh@anchor
  \let\nodeparts=\pgf@sh@boxes
  \nodeparts{text}%
  #2%
  \endgroup
}
%

\pgfaddtoshape{rectangle}{
    \savedanchor\linesouthwest{%
        \pgf@x=\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/inner xsep}}%
        \advance\pgf@x by 2\pgf@xc%
        \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/minimum width}}%
        \ifdim\pgf@x<\pgf@xb%
            \pgf@x=\pgf@xb%
        \fi%
        \pgf@x=-.5\pgf@x%
        \advance\pgf@x by.5\wd\pgfnodeparttextbox%
        \pgf@y=\ht\pgfnodeparttextbox%
        \advance\pgf@y by\dp\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/inner ysep}}%
        \advance\pgf@y by 2\pgf@yc%
        \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/minimum height}}%
        \ifdim\pgf@y<\pgf@yb
            \pgf@y=\pgf@yb
        \fi%
        \pgf@y=-.5\pgf@y%
        \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
        \advance\pgf@y by.5\ht\pgfnodeparttextbox%
    }
    \anchor{line south west}{\linesouthwest}
    \savedanchor\linenortheast{%
        \pgf@x=\the\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/inner xsep}}%
        \advance\pgf@x by 2\pgf@xc%
        \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/minimum width}}%
        \ifdim\pgf@x<\pgf@xb%
          \pgf@x=\pgf@xb%
        \fi%
        \pgf@x=.5\pgf@x%
        \advance\pgf@x by.5\wd\pgfnodeparttextbox%
        \pgf@y=\ht\pgfnodeparttextbox%
        \advance\pgf@y by\dp\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/inner ysep}}%
        \advance\pgf@y by 2\pgf@yc%
        \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/minimum height}}%
        \ifdim\pgf@y<\pgf@yb%
          \pgf@y=\pgf@yb%
        \fi%
        \pgf@y=.5\pgf@y%
        \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
        \advance\pgf@y by.5\ht\pgfnodeparttextbox%
    }
    \anchor{line north east}{\linenortheast}
    \anchor{line north west}{
        \pgf@process{\linesouthwest}
        \pgf@xa=\pgf@x
        \pgf@process{\linenortheast}
        \pgf@x=\pgf@xa
    }
    \anchor{line south east}{
        \pgf@process{\linesouthwest}
        \pgf@ya=\pgf@y
        \pgf@process{\linenortheast}
        \pgf@y=\pgf@ya
    }
    \anchor{line north}{
        \pgf@process{\linesouthwest}%
        \pgf@xa=.5\pgf@x%
        \pgf@process{\linenortheast}%
        \pgf@x=.5\pgf@x%
        \advance\pgf@x by \pgf@xa%
    }
    \anchor{line south}{
        \pgf@process{\linenortheast}%
        \pgf@xa=.5\pgf@x%
        \pgf@process{\linesouthwest}%
        \pgf@x=.5\pgf@x%
        \advance\pgf@x by \pgf@xa%
    }
    \anchor{line west}{
        \pgf@process{\linenortheast}%
        \pgf@ya=.5\pgf@y%
        \pgf@process{\linesouthwest}%
        \pgf@y=.5\pgf@y%
        \advance\pgf@y by \pgf@ya%
    }
    \anchor{line mid west}{\linesouthwest\pgfmathsetlength\pgf@y{.5ex}}
    \anchor{line base west}{\linesouthwest\pgf@y=0pt}
    \anchor{line east}{%
        \pgf@process{\linesouthwest}%
        \pgf@ya=.5\pgf@y%
        \pgf@process{\linenortheast}%
        \pgf@y=.5\pgf@y%
        \advance\pgf@y by \pgf@ya%
    }
    \anchor{line mid east}{\linenortheast\pgfmathsetlength\pgf@y{.5ex}}
    \anchor{line base east}{\linenortheast\pgf@y=0pt}
}
\makeatother
\begin{document}
\begin{tikzpicture}[
%   thick/.style={line width=10pt},% debug
%   dashed/.style={solid},% debug
%   opacity=.5
]
  \node [draw, thick, minimum size=1cm] (a) {A};
  \node [draw, thick, minimum size=3cm, right=2cm of a] (b) {B};

  \draw [thick, dashed] (a.line north west) -- (b.line north west);
  \draw [thick, dashed] (a.line north east) -- (b.line north east);
  \draw [thick, dashed] (a.line south west) -- (b.line south west);
  \draw [thick, dashed] (a.line south east) -- (b.line south east);
\end{tikzpicture}
\end{document}

Output

enter image description here

Output (with debug settings)

enter image description here

Rectangle’s anchors

enter image description here

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.