33

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. – Patrick Bard Feb 21 '17 at 14:22
  • 3
    No, what I want is NOT oblong :) – Yauhen Yakimenka Feb 21 '17 at 20:14
  • 1
    I cannot edit anymore, I meant what you want to say, not what you've requested. – Patrick Bard Feb 22 '17 at 11:52
  • 7
    I mean, of course, every square is also a rectangle - but I think it's perfectly understandable what was the question. – Yauhen Yakimenka Jan 30 '19 at 12:48
18

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! – Yauhen Yakimenka Mar 21 '16 at 18:13
51

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? – Yauhen Yakimenka Mar 21 '16 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 '16 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! – Yauhen Yakimenka Mar 21 '16 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 '16 at 19:12
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. – Yauhen Yakimenka Mar 21 '16 at 12:09
7

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. – Anush Aug 24 '20 at 15:55
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 '16 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 '16 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 '16 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 – Yauhen Yakimenka Aug 26 '20 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. – Graham501617 Aug 26 '20 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 '20 at 9:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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