Scaling the sieve of Eratosthenes in TikZ

Consider the following code, taken from Alain Matthes' answer here.

Code

\documentclass{article}

\usepackage{tikz}

\makeatletter
\pgfutil@ifundefined{pgfmath@function@isprime}{%
\newif\ifpgf@unknown
\newif\ifpgf@isaprime
\pgfmathdeclarefunction{isprime}{1}{%
\begingroup%
\pgf@unknowntrue
\c@pgfmath@counta#1\relax%
\ifcase#1\relax
% |#1| = 0
\pgf@isaprimefalse\pgf@unknownfalse
\or
% |#1| = 1
\pgf@isaprimefalse\pgf@unknownfalse
\or
% |#1| = 2
\pgf@isaprimetrue\pgf@unknownfalse
\or
% |#1| = 3
\pgf@isaprimetrue\pgf@unknownfalse
\else
% |#1| > 3
\ifodd#1\relax
\else
\pgf@isaprimefalse\pgf@unknownfalse
\fi
\fi
\ifpgf@unknown
\c@pgfmath@countd=\thr@@ \pgf@isaprimetrue
\loop
\c@pgfmath@countb=\c@pgfmath@counta
\divide\c@pgfmath@countb by\c@pgfmath@countd
\ifnum\c@pgfmath@countb>\c@pgfmath@countd \pgf@unknowntrue
\else\pgf@unknownfalse\fi
\multiply\c@pgfmath@countb by\c@pgfmath@countd
\ifnum\c@pgfmath@countb=\c@pgfmath@counta
\global\pgf@isaprimefalse\pgf@unknownfalse\fi
\repeat
\fi
\ifpgf@isaprime
\def\pgfmathresult{1}%
\else
\def\pgfmathresult{0}%
\fi
\pgfmath@smuggleone\pgfmathresult%
\endgroup
}}{}
\makeatother

\begin{document}

\def\scaling{0.5}
\begin{tikzpicture}[y=-1cm,scale=\scaling]
\foreach \x in {0,...,9}
\foreach \y in {1,...,10}
{\draw (\x,\y) +(-0.5cm,-0.5cm) rectangle ++(0.5cm,0.5cm);
\pgfmathtruncatemacro{\nb}{\x*10+\y}
\ifnum\nb=1
\def\pgfmathresult{1}
\else
\node[minimum size=\scaling cm](last) at (\y-1,\x+1) {\nb};
\pgfmathisprime{\nb}
\fi
\ifnum\pgfmathresult=0
\draw[red](last.north west)--(last.south east)
(last.north east)--(last.south west);
\fi}
\end{tikzpicture}

\end{document}


Output

Question

How do I draw the red lines correct when scaling the picture? If I use

\def\scaling{1}


everything is okay, but with

\def\scaling{0.5}


(as in the picture), the lines are not drawn from, and to, the corners of the squares.

P.S. I don't know TikZ at all, so the problem is probably a trivial mistake by me.

Add transform shape after the scaling and with the original code (so that the red lines occupy the whole squares):

\documentclass{article}
\usepackage{tikz}

\makeatletter
\pgfutil@ifundefined{pgfmath@function@isprime}{%
\newif\ifpgf@unknown
\newif\ifpgf@isaprime
\pgfmathdeclarefunction{isprime}{1}{%
\begingroup%
\pgf@unknowntrue
\c@pgfmath@counta#1\relax%
\ifcase#1\relax
% |#1| = 0
\pgf@isaprimefalse\pgf@unknownfalse
\or
% |#1| = 1
\pgf@isaprimefalse\pgf@unknownfalse
\or
% |#1| = 2
\pgf@isaprimetrue\pgf@unknownfalse
\or
% |#1| = 3
\pgf@isaprimetrue\pgf@unknownfalse
\else
% |#1| > 3
\ifodd#1\relax
\else
\pgf@isaprimefalse\pgf@unknownfalse
\fi
\fi
\ifpgf@unknown
\c@pgfmath@countd=\thr@@ \pgf@isaprimetrue
\loop
\c@pgfmath@countb=\c@pgfmath@counta
\divide\c@pgfmath@countb by\c@pgfmath@countd
\ifnum\c@pgfmath@countb>\c@pgfmath@countd \pgf@unknowntrue
\else\pgf@unknownfalse\fi
\multiply\c@pgfmath@countb by\c@pgfmath@countd
\ifnum\c@pgfmath@countb=\c@pgfmath@counta
\global\pgf@isaprimefalse\pgf@unknownfalse\fi
\repeat
\fi
\ifpgf@isaprime
\def\pgfmathresult{1}%
\else
\def\pgfmathresult{0}%
\fi
\pgfmath@smuggleone\pgfmathresult%
\endgroup
}}{}
\makeatother

\begin{document}

\def\scaling{0.5}
\begin{tikzpicture}[y=-1cm,scale=\scaling,transform shape]
\foreach \x in {0,...,9}
\foreach \y in {1,...,10}
{ \draw (\x,\y) +(-.5cm,-.5cm) rectangle ++(.5cm,.5cm);
\pgfmathtruncatemacro{\nb}{\x*10+\y}
\ifnum\nb=1 \def\pgfmathresult{1}
\else
\node[minimum size= 1cm](last) at (\y-1,\x+1) {\nb} ;
\pgfmathisprime{\nb}
\fi
\ifnum\pgfmathresult=0
\draw[red](last.north west)--(last.south east)
(last.north east)--(last.south west);
\fi}
\end{tikzpicture}

\end{document}


• I would like the red crosses to start at the corners of the squares (as in the original answer), but make the entire figure scalable. – Svend Tveskæg May 4 '13 at 22:52
• @SvendTveskæg please see the last example in my updated answer. – Gonzalo Medina May 4 '13 at 22:54
• @SvendTveskæg Sorry and shame on me to propose a "no scaling" solution. Generally I always prefer a scaling method but Gonzalo gives you a good way to repair my mistake but if you need to scale the numbers too, Lay's method is another possibility with Qrrbrbirlbel's comment. – Alain Matthes May 5 '13 at 5:36
• @AlainMatthes No worry. :) Thanks for the initial code! – Svend Tveskæg May 5 '13 at 9:01

The reason this happens is because each number displayed in the grid is displayed via:

\node[minimum size=\scaling cm](last) at (\y-1,\x+1) {\nb};


and the crosses are displayed via:

\draw[red](last.north west)--(last.south east)
(last.north east)--(last.south west);


The first line defines nodes that are at least \scaling cm large. If the font is small enough, it makes them exactly \scaling cm large. If the font is too big compared to \scaling cm, then their size will increase to fit the number.

The next two lines draw a cross between the extremities of these nodes. If the nodes are \scaling cm large, they correspond to the grid (which is drawn in another part of the code). If the nodes are larger than \scaling cm (because of the size of the font), then the crosses will be too large.

As you can see in your result, it looks like with a 0.5 scaling value, the font size is small enough to fit the grid, so why is there a problem? The reason is that tikz enforces a minimum separator between the content of a node (here, the numbers) and its border. This can be circumvented by forcing that separator to 0:

\node[inner sep=0,minimum size=\scaling cm](last) at (\y-1,\x+1) {\nb};


The result is slightly different from that of Gonazlo Medina because the font is not scaled:

You could also scale the font by adding e.g. \small after the \begin{tikzpicture}.

• @SvendTveskæg In addition, you can also use the option label=center:\nb in the \node and lose the actual node text. This will never re-size the node (outside any width/height and inner separator settings) but the text may overlap/protrude the node’s path. – Qrrbrbirlbel May 5 '13 at 2:54