42

I googled but cannot find a solution. Is that possible to draw a node with TikZ which will be a square and NOT a rectangle of arbitrary proportions?

Minimum working example can be something like this:

\documentclass{article}
\usepackage{tikz}

\begin{document}
    \begin{tikzpicture}
        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node at (3,0) [rectangle,draw] (v100) {$v_{100}$};
        \draw (c100) -- (v100);
    \end{tikzpicture}
\end{document}

While circle is perfectly round, rectangle is just a rectangle:

Circle and rectangular nodes with TikZ

And I want it to be a square. I did not find any shape like square or so.

4
  • 1
    Actually, that's impossible since every square IS a rectangle, what you want is an Oblong. Feb 21, 2017 at 14:22
  • 3
    No, what I want is NOT oblong :) Feb 21, 2017 at 20:14
  • 1
    I cannot edit anymore, I meant what you want to say, not what you've requested. Feb 22, 2017 at 11:52
  • 8
    I mean, of course, every square is also a rectangle - but I think it's perfectly understandable what was the question. Jan 30, 2019 at 12:48

6 Answers 6

22

This is surprisingly difficult. In the following code, I've defined a custom node shape square that inherits from the rectangle shape, but forces its size to be square. Unfortunately, the inheritance mechanism doesn't allow redefinition of inherited saved anchors, so I had to copy some code from the TikZ rectangle definition.

On the plus side, it should be correct in all cases (defining different values for inner [xy]sep, minimum {width,height} and outer [xy]sep) and has all the anchors of the rectangle shape.

\documentclass[tikz,margin=5pt]{standalone}
\usepackage{tikz}

\makeatletter
% the contents of \squarecorner were mostly stolen from pgfmoduleshapes.code.tex
\def\squarecorner#1{
    % Calculate x
    %
    % First, is width < minimum width?
    \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%
        % yes, too small. Enlarge...
        \pgf@x=\pgf@xb%
    \fi%
    % Calculate y
    %
    % First, is height+depth < minimum height?
    \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%
        % yes, too small. Enlarge...
        \pgf@y=\pgf@yb%
    \fi%
    %
    % this \ifdim is the actual part that makes the node dimensions square.
    \ifdim\pgf@x<\pgf@y%
        \pgf@x=\pgf@y%
    \else
        \pgf@y=\pgf@x%
    \fi
    %
    % Now, calculate right border: .5\wd\pgfnodeparttextbox + .5 \pgf@x + #1outer sep
    \pgf@x=#1.5\pgf@x%
    \advance\pgf@x by.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/outer xsep}}%
    \advance\pgf@x by#1\pgf@xa%
    % Now, calculate upper border: .5\ht-.5\dp + .5 \pgf@y + #1outer sep
    \pgf@y=#1.5\pgf@y%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
    \advance\pgf@y by.5\ht\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}%
    \advance\pgf@y by#1\pgf@ya%
}
\makeatother

\pgfdeclareshape{square}{
    \savedanchor\northeast{\squarecorner{}}
    \savedanchor\southwest{\squarecorner{-}}

    \foreach \x in {east,west} \foreach \y in {north,mid,base,south} {
        \inheritanchor[from=rectangle]{\y\space\x}
    }
    \foreach \x in {east,west,north,mid,base,south,center,text} {
        \inheritanchor[from=rectangle]{\x}
    }
    \inheritanchorborder[from=rectangle]
    \inheritbackgroundpath[from=rectangle]
}

\begin{document}
    \begin{tikzpicture}
        \node[draw,square] {square with rectangular node text};
    \end{tikzpicture}
\end{document}

unbelievably exciting result

1
  • This looks like the most precise solution. But of course, not the easiest one :) Well, once put in the doc it is then easy to use. Thanks! Mar 21, 2016 at 18:13
64

Use the regular polygon shape from the shapes.geometric library. As Alenanno mentions in a comment, you can define your own style to reduce the verbosity.

I don't know how the size of that is calculated, there is a lot of whitespace in it. Setting inner sep to a negative value can reduce the size, but the value has to be modified depending on the length of the node label.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\begin{document}
    \begin{tikzpicture}[square/.style={regular polygon,regular polygon sides=4}]
        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node at (3,0) [square,draw] (v100) {$v_{100}$};
        \node at (3,-5) [square,inner sep=-1.3em,draw] {a much longer node label};
        \node at (3,-5) [circle,draw] {a much longer node label};
        \draw (c100) -- (v100);
    \end{tikzpicture}
\end{document}

enter image description here

4
  • That seems like a good solution. Except that I need to write such a long phrase each time -- regular polygon,regular polygon sides=4. Is there a way to define a shortcut? And also the square is bigger than a circle with the same label - is there a way to make it have smaller inside margins? Mar 21, 2016 at 12:31
  • 5
    @YauhenYakimenka Define a tikz style (using \tikzset) like mysquare/.style={regular polygon,regular polygon sides=4, inner sep=0}, then just add mysquare to the nodes you want to affect. For the size, inner sep=0 solves it.
    – Alenanno
    Mar 21, 2016 at 12:34
  • Perfect! I put style definition \tikzset{square/.style={regular polygon,regular polygon sides=4,inner sep=0}} in the beginning of the document as I am going to use it a lot. Inner separator of 0 seems to be okay for not too big shapes. Thanks guys! Mar 21, 2016 at 13:00
  • 1
    @YauhenYakimenka I'd also add another style like every node/.style={draw} so they are all drawn by default. If you want to have one that is not drawn, just add draw=none to the exception node.
    – Alenanno
    Mar 21, 2016 at 19:12
11

Another solution, not mentioned so far. The idea is to set the desired node as a circle, but don't draw it. Then use the fit tikz library to draw a rectangle which 'fits' around the node.

\documentclass[tikz,border=2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{fit}

\begin{document}
    \begin{tikzpicture}
        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node at (3,0) [circle] (v100) {$v_{100}$};
        \node [rectangle,draw,fit=(v100),inner sep=0] {};
        \draw (c100) -- (v100);
    \end{tikzpicture}
\end{document}

square-node

1
  • 1
    This is a lovely trick.
    – Simd
    Aug 24, 2020 at 15:55
7

Use minimum size= which changes both height and width which you can change singularly as well. Also, keep in mind that rectangle is the default shape, so no need to specify it.

Writing \node at (3,0) [minimum size=1cm,draw] (v100) {$v_{100}$}; you should get

enter image description here

1
  • 1
    This might be good starting workaround. However, as far as I understand, I should manually find a size which fits the node's label. Because if, say, it is $v_{100000000}$ the node will become rectangular again. Mar 21, 2016 at 12:09
6

Another solution. It uses width function to define node's size. As the same parameter is used as node contents and size delimiter, node's name and position must be declared before options or inside options.

\documentclass[border=2mm]{standalone}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}%
        [square/.style={%
            draw,
            minimum width=width("#1"),
            minimum height=width("#1")+2*\pgfshapeinnerysep,
            node contents={#1}}]

        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node at (3,0) (v100) [square={$v_{100}$}];
%or
%       \node (v100) at (3,0)[square={$v_{100}$}];
%or
%       \node at (3,0) [square={$v_{100}$}, name=v100];
%or
%       \node[square={$v_{100}$}, name=v100, at={(3,0)}];

        \node at (2,-3) (B) [square=a much longer node text];
        \draw (c100) -- (v100);
          \draw (B.south west) -- ++(45:6cm);
    \end{tikzpicture}
\end{document}

enter image description here

3
  • 1
    Very interesting solution (altough I wonder whether a future version of TikZ can just have the rectangle shape respect the aspect ratio related options). One corner case that doesn't work: \node[square=I];
    – wrtlprnft
    Apr 14, 2016 at 10:09
  • @wrtlprnft Yes I must admit that this solution only works when text horizontal component is larger than vertical one. Which doesn't happens with I.
    – Ignasi
    Apr 14, 2016 at 11:06
  • How about /utils/exec={\pgfmathsetmacro\squaresize{max(width("#1") + 2 * \pgfshapeinnerxsep, height("#1") + depth("#1") + 2 * \pgfshapeinnerysep)}},minimum size=\squaresize? Unfortunately, directly passing the expression to minimum size didn't work, hence the macro definition.
    – wrtlprnft
    Apr 14, 2016 at 11:32
4

This solution is based in Graham501617's answer. It uses a not drawn circle node which is drawn as an square with help of an extra path inside an append after command option:

\documentclass[tikz,border=2mm]{standalone}

\tikzset{
    square/.style={%
        draw=none,
        circle,
        append after command={%
            \pgfextra \draw[black] (\tikzlastnode.north-|\tikzlastnode.west) rectangle 
                (\tikzlastnode.south-|\tikzlastnode.east);\endpgfextra}
    }
}

\begin{document}
    \begin{tikzpicture}
        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node [square] at (3,0) (v100) {$v_{100}$};
        \draw (c100) -- (v100);
    \end{tikzpicture}
\end{document}

enter image description here

Update: square customization

\documentclass[tikz,border=2mm]{standalone}

\tikzset{
    square/.style={%
        draw=none,
        circle,
        append after command={%
            \pgfextra \draw[#1] (\tikzlastnode.north-|\tikzlastnode.west) rectangle 
                (\tikzlastnode.south-|\tikzlastnode.east);\endpgfextra}
    },
    square/.default=black
}

\begin{document}
    \begin{tikzpicture}
        \node at (0,0) [circle,draw] (c100) {$c_{100}$};
        \node [square={red, ultra thick, fill=red!30}] at (3,0) (v100) {$v_{100}$};
        \draw (c100) -- (v100);
    \end{tikzpicture}
\end{document}

enter image description here

3
  • this is a very elegant way Aug 26, 2020 at 12:15
  • Is there any pgfkeys hack that could be made to the style to make options apply to the rectangle rather than the circle? E.g. to get \node [square,red] ... to apply the red colour to the square. Aug 26, 2020 at 13:22
  • 1
    @Graham501617 Take a look at new definition, hope it helps. Now square accepts one parameter to change its aspect. if you want to change the text (color, font, ...) use something like \node[square={fill=red}, font=\sffamily, green] {xxx};
    – Ignasi
    Aug 28, 2020 at 9:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .