[Tex/LaTex] How to establish node-anchor-like points on a (tikz) rectangle path (is there a better method than the one described)

tikz-pgf

Background and desired functionality

Rectangles can be drawn with tikz in a number of ways, one way of doing so is to define a rectangle node as follows:

\node (rectangle) [draw, rectangle] {A rectangle};
% Strictly speaking, rectangle is not needed in the [] as nodes are rectangular by default

A useful feature of this is that, as a node, it has anchor points around it such as rectangle.north, rectangle.south west and so on.

Another way is to draw a rectangle path, like this:

\draw (0,0) rectangle (3,2);

I would like to be able to use this second path-based method* but also be able to have (some) named anchors on the path.

Approach from the time of asking

MWE:

The method I had for this is demonstrated by the following MWE (inspired by this inventive answer and later improved by Altermundus' comment):

\documentclass{report}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

    \draw
        (0,0)
            coordinate [xshift=-{0.5\pgflinewidth},yshift=-{0.5\pgflinewidth}] (rectangle south west)
        rectangle
            coordinate (rectangle center)
        (3,2)
            coordinate [xshift={0.5\pgflinewidth},yshift={0.5\pgflinewidth}] (rectangle north east)
        edge [draw=none]
            coordinate (rectangle north)
        (rectangle north east -| rectangle south west)
    ;

\end{tikzpicture}

\end{document}

Obviously this only defines anchors for center, north, north east, and south west, but it can be fairly easily extended to provide south, east, west, north west and south east (as demonstrated in my answer).

Caveat:

As pointed out by Andrew Stacey in his answer, there was a slight discrepancy between the positioning of these pseudo-anchor coordinates and the anchors on a normal rectangular node. The edge-based solution above originally placed the pseudo-anchors exactly on the coordinates of the rectangle whereas on a rectangular node they would be placed at the outside edge of the lines of the rectangle. Although this was only likely to cause issues with thick line widths I have now added some x/yshifting to the above code to compensate.

The following image shows the difference in anchor positioning between standard node rectangles, the original edge-based solution (without x/yshifting) and the above (revised) edge-based solution (with x/yshifting). The line widths for the lower pictures are set to 0.5cm.

Comparison of rectangular nodes and edge-based solutions (with and without x/yshifting), illustrating the discrepancy in anchor positioning

Node rectangle labelling code to illustrate the key to the colours (and for the generally curious):

% styling yoinked from Altermundus' answer
\tikzset{dot/.style={circle,fill=#1,inner sep=0,minimum size=4pt}}
\node [dot=red] at (rectangle.south west) {};
\node [dot=blue] at (rectangle.center) {};
\node [dot=purple] at (rectangle.north east) {};
\node [dot=green] at (rectangle.north) {};
\node [dot=orange] at (rectangle.north west) {};
\node [dot=yellow] at (rectangle.south) {};
\node [dot=brown] at (rectangle.south east) {};
\node [dot=black] at (rectangle.east) {};
\node [dot=pink] at (rectangle.west) {};

Back to the question

Is there a neater/better way to establish node-anchor-like points on a rectangle path* than the method given in the MWE above?

* = An equivalent drawing method that also allows specifying a rectangle by corner coordinates would be more than acceptable!

Best Answer

I think that the best way to establish node-like anchors is to use a node. I'm guessing that the annoyance of using an actual node to draw the rectangle is that specifying a node rectangle by its coordinates is a little more complicated than just saying (0,0) rectangle (3,2). So here's some code that puts an invisible (rectangular - but that's only because the default is a rectangle) node around the current path. If the path is more complicated then the node is guaranteed to contain the rectangular bounding box.

Here's the code:

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{fit}
\makeatletter
\tikzset{
  fitting node/.style={
    inner sep=0pt,
    fill=none,
    draw=none,
    reset transform,
    fit={(\pgf@pathminx,\pgf@pathminy) (\pgf@pathmaxx,\pgf@pathmaxy)}
  },
  reset transform/.code={\pgftransformreset}
}
\makeatother

\begin{document}
\begin{tikzpicture}[line width=.5cm]
\draw[thick,green] (-2,-2) -- (4,4);
\draw (0,0) rectangle (3,2) node[fitting node] (rect) {};
\draw[->,red] (5,5) -- (rect.north east);
\draw[->,red] (0,5) -- (rect.north);
\end{tikzpicture}
\end{document}

The green line is to show that the bounding box used is that of the path and not the current picture. The thick lines are to show that the node anchors are on the proper border of the path, not the "theoretical" path[1].

Here's the result:

path with anchors

[1]: With your method of specifying anchors via coordinates, the anchors would be on the "theoretical" path, namely the north west anchor would be at (3,2) not (3 + half line width, 2 + half line width). If you prefer this, it's easy to modify this method to do that.

Edit Now copes with scale=2 as Altermundus asked about. With more complicated transformations then it gets increasingly difficult to keep track since nodes work differently, and it is working on the actual bounding box rather than the path itself. So in those cases, caveat texer.

Related Question