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.

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!

share|improve this question
    
@Andrew (/other experienced users): Would it make sense to put the method I was using as an answer instead of including it in the question? (I ask as I've just gained the privilege to do so, without waiting 8 hours or so.) –  Staves Mar 12 '12 at 10:27
1  
No. The right place for your code was in the question as the question was "Here's what I'd like to do, here's what I've tried, is there a better way?" so the code is a definite "plus" to the question as it shows you've put some effort in already. Anyone reading this will see your code and can use it if they wish so moving it to an answer doesn't actually help anyone. –  Loop Space Mar 12 '12 at 19:01
    
That's cool, thanks (still getting used to this!) ^^ –  Staves Mar 12 '12 at 21:22
    
@Staves edge [draw opacity=0] is not pretty and you can insert after (3,2) coordinate (path_rectangle center) –  Alain Matthes Mar 12 '12 at 23:50
    
@Altermundus: I think you must have meant after rectangle you can insert the center coordinate - but that's neat, I'll add it in to the question code, cheers. With regards to the edge [draw opacity=0] bit, would you consider edge [draw=none] prettier? (Now that I know about [draw=none], from Andrew's answer, I'm going to add it in anyway as it seems somewhat cleaner to me.) –  Staves Mar 13 '12 at 11:41

6 Answers 6

up vote 17 down vote accepted

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.

share|improve this answer
    
That's great - thank you very much ^^ You're quite correct that the reason I'm not using a node is that it cannot be simply defined by corner coordinates, as the path can. Also I'm glad you pointed out the "theoretical" path versus proper border aspect. I hadn't noticed the difference as the lines I'm using are predominately thin. Are the draw=none and fill=none lines in the tikzset necessary? (They don't seem to affect the output for me - perhaps a better question is: could you give a case using this where they would come into play?) –  Staves Mar 12 '12 at 9:13
    
@Staves: It's protection against an every node/.style={draw} type command earlier in the path or scope or picture. There are probably other settings that should also be reset, but those were the most obvious ones. The point (such as there was one) was to ensure that the node was truly invisible. –  Loop Space Mar 12 '12 at 9:17
    
Ok cool, that makes sense - cheers again! –  Staves Mar 12 '12 at 9:23
    
And what you do after scale=2? –  Alain Matthes Mar 12 '12 at 16:56
    
@Altermundus: Good catch. Fixed that, but working with more complicated transforms rapidly gets ... complicated so I shan't attempt it. –  Loop Space Mar 12 '12 at 18:58

Update

Finally I found what I want Without node, you can use scale easily.

Code

\documentclass{article}

\usepackage{tikz}

\tikzset{add reference/.style={insert path={%
    coordinate [pos=0,xshift=-0.5\pgflinewidth,yshift=-0.5\pgflinewidth] (#1 south west) 
    coordinate [pos=1,xshift=0.5\pgflinewidth,yshift=0.5\pgflinewidth]   (#1 north east)
    coordinate [pos=.5] (#1 center)                        
    (#1 south west |- #1 north east)     coordinate (#1 north west)
    (#1 center     |- #1 north east)     coordinate (#1 north)
    (#1 center     |- #1 south west)     coordinate (#1 south)
    (#1 south west -| #1 north east)     coordinate (#1 south east)
    (#1 center     -| #1 south west)     coordinate (#1 west)
    (#1 center     -| #1 north east)     coordinate (#1 east)   
}}}  

\begin{document}

    \begin{tikzpicture}

        \draw (1,0) rectangle (4,3) [add reference=R1];  

        % to verify if the points are good:
        \tikzset{pt/.style={circle,fill=#1,inner sep=0mm,minimum size=4pt}}
        \node[pt=red]    at (R1 south west){};
        \node[pt=red]    at (R1 north west){};  
        \node[pt=red]    at (R1 north east){};
        \node[pt=red]    at (R1 south east){}; 
        \node[pt=black]  at (R1 center){};
        \node[pt=orange] at (R1 west){};
        \node[pt=orange] at (R1 south){};
        \node[pt=orange] at (R1 east){};
        \node[pt=orange] at (R1 north){};

    \end{tikzpicture}

\end{document} 

Edit: Compensated for the anchor-positioning issue with some nifty x/yshifting - Staves ^^

Picture

enter image description here

share|improve this answer
    
No the code works with pgf 2.1 and 2.1 cvs –  Alain Matthes Mar 13 '12 at 16:08
    
@Staves You have pgf 2.0 ? –  Alain Matthes Mar 13 '12 at 16:17
    
@Staves /pgfversion after \begin{document} –  Alain Matthes Mar 13 '12 at 16:18
    
That's a nice solution, I agree that it's cleaner than the edge-based one I presented :) –  Staves Mar 13 '12 at 17:03
1  
Wow, this is great. One question: Why the use of coordinate in \draw (1,0) coordinate rectangle (4,3) [add reference=R1] ;. I have always used that without coordinate and your example seems to work fine without it. So, just wondering if there is some hidden benefit to including coordinate? Or, are there other commands with similar syntax that require the use of coordinate? –  Peter Grill Mar 14 '12 at 16:08

We can see, there are many solutions, I'll propose a new

you can choose the number of points

\documentclass{article}
\usepackage{tikz}
\usepackage{calc}

\newcommand{\MyRectangle}[3]{
\draw (#2) rectangle (#3);
\foreach \ii in{0,1,...,#1}{
\pgfmathsetmacro{\pos}{\ii/#1}
\path (#2) -- (#2-|#3) coordinate[pos=\pos](south\ii);
\path  (#2-|#3)--(#3) coordinate[pos=\pos](east\ii);
\path (#3) -- (#3-|#2) coordinate[pos=\pos](north\ii);
\path (#3-|#2) -- (#2) coordinate[pos=\pos](west\ii);
}
}


\begin{document}

\begin{tikzpicture}

\begin{scope}[rotate=12]
\coordinate(a) at (0,0);
\coordinate(b) at (5,3);
\MyRectangle{12}{a}{b}
\end{scope}


\foreach \ii in {0,...,12}{
\draw (south\ii) circle (0.1);
\draw[red] (east\ii) circle (0.1);
\draw[blue] (north\ii) circle (0.1);
\draw[green] (west\ii) circle (0.1);
}


\draw (north3) to[bend right=120] (west6);


\end{tikzpicture}

\end{document}

enter image description here

share|improve this answer
    
That's rather neat, nicely done ^^ –  Staves Mar 13 '12 at 13:57

You can use the fit library to draw a rectangular node.

Illustrative example:

\documentclass[border=3mm]{standalone}

\usepackage{tikz}
\usetikzlibrary{fit}

\begin{document}

\begin{tikzpicture}
    \draw[blue]
        (0,0)
            coordinate (a)
        rectangle
        (3,2)
            coordinate (b);
    \node[draw=red,fit=(a) (b),inner sep=0pt] (box) {};
    \draw (box.30)--(box.210);
\end{tikzpicture}

\end{document}

You don't need to draw the original rectangle, just two corners. With inner sep=0pt the fitting rectangle adjusts to the defined corners.

The result of this is as follows (the initial blue rectangle is drawn over by the node command):

enter image description here

A more minimal example for general use:

\documentclass[border=3mm]{standalone}

\usepackage{tikz}
\usetikzlibrary{fit}

\tikzset{corners/.style={draw,fit={#1},rectangle,inner sep=0}}

\begin{document}

\begin{tikzpicture}
    \node [corners={(0,0) (3,2)}] (box) {};
\end{tikzpicture}

\end{document}
share|improve this answer
    
Nice, thank you for this it also helped me to understand @Andrew's answer better. As a side-note, I'm not sure if it's worth drawing the rectangle with an initial \draw command, as opposed to relying on the \node command to draw it and simply defining a and b beforehand as follows: \coordinate at (0,0) (a); \coordinate at (3,2) (b); \node [draw=red,fit=(a) (b),inner sep=0] (box) {}; –  Staves Mar 12 '12 at 9:22
    
@Staves No, it's not worth. I just wanted to show you that fitting rectangle with inner sep=0 had exactly the same size of the original one. As you can see in the example, the red rectangle covers the blue one. –  Ignasi Mar 12 '12 at 9:56
    
Or rather I couldn't see! ;) But thanks, I understand what you mean :) –  Staves Mar 13 '12 at 15:50
    
With further experimentation I've realised the coordinates don't even need to be defined before the \node command, so the whole rectangle can be created with: \node [fit={(0,0) (3,2)},inner sep=0pt] (box) {}; Just in case anyone else didn't know that besides me :) –  Staves Mar 13 '12 at 17:14
    
I've proposed an edit to this answer with an additional, and slightly more minimal, example given which is the basis for the code I'll actually be using. (Hopefully Ignasi will approve/adapt it as I'd rather that happen than posting my own minor variation of this answer.) –  Staves Mar 13 '12 at 18:08

I add another answer because in my first answer I did not want to use the notion of node. I think it's not in the spirit of TikZ to impose these kinds of conditions to a node but if you want a simple code to this::

\documentclass{article}
\usepackage{tikz}
 \makeatletter
\tikzset{set dim/.style={insert path={% 
   coordinate [pos=0] (tmpa) 
   coordinate [pos=1] (tmpb) 
     \pgfextra{%
      \pgfextractx{\pgf@x}{\pgfpointanchor{tmpa}{center}}
      \pgfextracty{\pgf@y}{\pgfpointanchor{tmpa}{center}}
      \pgf@xa\pgf@x %
      \pgf@ya\pgf@y %
      \pgfextractx{\pgf@x}{\pgfpointanchor{tmpb}{center}}
      \pgfextracty{\pgf@y}{\pgfpointanchor{tmpb}{center}}
      \pgf@xb\pgf@x %
      \pgf@yb\pgf@y %
      \advance\pgf@xb by -\pgf@xa 
      \advance\pgf@yb by -\pgf@ya
      }%
     }, minimum width=\pgf@xb,minimum height=\pgf@yb
     }%
  }%
  \makeatother

\begin{document} 
    \begin{tikzpicture} 
        \draw[ help lines,lightgray](0,0) grid (5,5);
        \draw[line width=5mm,draw=gray] (0,1) rectangle node[set dim] (R1){} (4,3) ;



        % next line is to compare with a node
        %\node [line width=5mm,
                draw=gray,
                minimum width=4cm,
                minimum height=2cm] (R1) at (2,2){};    
        % to verify if the points are good:
        \tikzset{pt/.style={circle,fill=#1,inner sep=0mm,minimum size=4pt}}
        \node[pt=red]   at (R1.south west){};
        \node[pt=red]   at (R1.north west){};  
        \node[pt=red]   at (R1.north east){};
        \node[pt=red]   at (R1.south east){}; 
        \node[pt=black] at (R1.center){};
        \node[pt=green] at (R1.west){};
        \node[pt=green] at (R1.south){};
        \node[pt=green] at (R1.east){};
        \node[pt=green] at (R1.north){};
    \end{tikzpicture}  
\end{document} 

enter image description here

share|improve this answer

I mentioned in my question that the edge-based solution I presented could be expanded to provide the remaining anchors. Here is a quick demonstration of how this could be done:

\documentclass{report}

\usepackage{tikz}

% dot styling yoinked from Altermundus' answer
\tikzset{dot/.style={%
    circle,
    fill=#1,
    inner sep=0,
    minimum size=4pt
}}

% establish a macro to take care of all the drawing
\newcommand{\drawRectangle}[4][]{%
    \draw
        [#1]
        (#3)
            coordinate [xshift=-{0.5\pgflinewidth},yshift=-{0.5\pgflinewidth}] (#2 south west)
        rectangle
            coordinate (#2 center)
        (#4)
            coordinate [xshift={0.5\pgflinewidth},yshift={0.5\pgflinewidth}] (#2 north east)
        edge [draw=none]
            coordinate (#2 north)
        (#2 north east -| #2 south west)
        edge [draw=none]
            coordinate (#2 east)
        (#2 south west -| #2 north east)
        coordinate (#2 south east) at (#2 south west -| #2 east)
        edge [draw=none]
            coordinate (#2 south)
        (#2 south west)
        coordinate (#2 north west) at (#2 north -| #2 south west)
        edge [draw=none]
            coordinate (#2 west)
        (#2 south west)
    ;
}

\begin{document}

% draw a standard-width rectangle
\begin{tikzpicture}
% call the macro to draw the rectangle:
    \drawRectangle[gray]{rectangle}{0,0}{3,2}
% draw some markers to show the anchors:
    \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) {};
\end{tikzpicture}

\vspace{2em}

% draw a thicker rectangle
\begin{tikzpicture}[line width=.5cm]
    \drawRectangle[gray]{rectangle}{0,0}{3,2}
    \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) {};
\end{tikzpicture}

\end{document}

Edit: Added some nifty x/yshifting to compensate for the anchor-positioning problem ^^

Which produces this (cropped):

a thin rectangle and a thick rectangle with their anchors marked, as produced by the above code

This is definitely not the most elegant solution, but I'm providing it just in case it's informative to somebody.

share|improve this answer
    
(I figured this would be better contained in an answer as opposed to bloating the question more - if this was a bad idea, let me know and I'll merge it into the question.) –  Staves Mar 14 '12 at 19:00

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.