My question is: how to perform calculations in \foreach variables.

Here is my code

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes}
\begin{document}

\begin{tikzpicture}
%-> USEFUL DEFINITIONS
\def\tfs{0.1} %... the fence width and height
\def\tth{2} %... the tower height
\def\ttl{5*\tfs} %... the tower length
\def\twh{1} %... the wall height
\def\twl{20*\tfs} %... the wall length
\def\envcolor{gray!50} %... color for everything

%-> DRAW THE CASTLE
\fill[\envcolor]
%... tower
(0,0) rectangle(\ttl,\tth)
    ;
\foreach \x in {0,0.2,...,0.4}
    \fill[\envcolor]
        (\x,\tth) rectangle(\x+\tfs,\tth+\tfs)
        ;
%... wall
\fill[\envcolor]
    (\ttl,0) rectangle(\twl,\twh)
    ;
\foreach \x in {0.6,0.8,...,1.8}
    \fill[\envcolor]
        (\x,\twh) rectangle(\x+\tfs,\twh+\tfs)
        ;
%... tower bricks
\foreach \y in {0,0.2,...,\tth}
    \foreach \x in {0,0.2,...,2}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
\foreach \y in {0.1,0.3,...,\tth}
    \foreach \x in {0.1,0.3,...,2}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
%... wall bricks
\foreach \y in {0,0.2,...,\tth}
    \foreach \x in {0,0.2,...,2}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
\foreach \y in {0.1,0.3,...,\tth}
    \foreach \x in {0.1,0.3,...,2}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
\end{tikzpicture}
\end{document}

But I would like to do something like below:

%... tower bricks
\foreach \y in {0,2*\tfs,...,\tth}
    \foreach \x in {0,2*\tfs,...,\ttl-\tfs}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
\foreach \y in {\tfs,3*\tfs,...,\tth}
    \foreach \x in {\tfs,3*\tfs,...,\ttl-\tfs}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
%... wall bricks
\foreach \y in {0,2*\tfs,...,\twh}
    \foreach \x in {\ttl,\ttl+2*\tfs,...,\twl-\tfs}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
\foreach \y in {\tfs,3*\tfs,...,\twh}
    \foreach \x in {\ttl+\tfs,\ttl+3*\tfs,...,\twl-\tfs}
        \draw[white]
            (\x,\y) rectangle(\x+2*\tfs,\y+\tfs)
            ;
  • The content you're looping over really ought to be in { brackets }. – A.Ellett Oct 18 at 15:51
  • 1
    The answer to your question would be to use xfp's \fpeval{<expr>} wherever you need an expandable calculation within your \foreach. – Werner Oct 18 at 16:47
  • @A.Ellet, I tried it, but still didn't work. – Brasil Oct 19 at 14:06
  • @Brasil What did you try? How is it not working? – A.Ellett Oct 19 at 15:00
  • @A.Ellet, I tried to use the suggested brackets to enclose the math, but it didn't work. I beleive the problem was in fact that I need to define the macro via \pgfmathsetmacro command, as suggested in the answer below. – Brasil Oct 19 at 15:29
up vote 9 down vote accepted

I've simplified your code a bit. I think you're thinking too much like a brick-layer adding the mortar as you go along. I fill in the mortar at the end.

enter image description here

Here's the code:

\documentclass[tikz,border=3.14mm]{standalone}
\colorlet{main wall color}{gray!50}
\colorlet{tower wall color}{blue}
\colorlet{tower fence color}{green}
\colorlet{main fence color}{red}
\colorlet{mortar color}{magenta}

\colorlet{main wall color}{gray!50}
\colorlet{tower wall color}{main wall color}
\colorlet{tower fence color}{main wall color}
\colorlet{main fence color}{main wall color}
\colorlet{mortar color}{white}
\begin{document}

\begin{tikzpicture}[%%
%  x={(1mm,0)},%% uncomment to get desired scale
%  y={(0,1mm)},%% uncomment to get desired scale
  ]
%-> USEFUL DEFINITIONS
\def\tfs{1}                   %% the fence width and height 
\def\tth{20}                  %% the tower height           
\pgfmathsetmacro\ttl{5*\tfs}  %% the tower length           
\def\twh{10}                  %% the wall height            
\pgfmathsetmacro\twl{20*\tfs} %% the wall length            

%... tower
%% you used `\fill` here which causes a seam to appear between
%% the main portion of the castle body and the tower.  I've modified
%% the code in these next two lines to remove that effect.
%% The second draw prevents bricks from sticking out of the sides.
\draw[tower wall color,fill] (0,0) rectangle (\ttl,\tth) ;
\draw[mortar color] (\ttl,\tth-\twh) -- (\ttl,\tth);

%% the fence along the top of the tower
\foreach \x in {0,2,...,4}
  {
    %% use draw instead of fill so that the brick doesn't seem to overlap the mortar
    \draw[mortar color,fill=tower fence color] (\x,\tth) rectangle (\x+\tfs,\tth+\tfs) ;
  }

%% you used `\fill` here which causes a seam to appear between
%% the main portion of the castle body and the tower.  I've modified
%% the code in these next two lines to remove that effect.
%% The second draw prevents bricks from sticking out of the sides.
\draw[main wall color,fill] (\ttl,0) rectangle(\twl,\twh) ;
\draw[mortar color] (\twl,0) -- (\twl,\twh);

%% the fence along the main body of the castle
\foreach \x in {6,8,...,18}
  {
    %% use draw instead of fill so that the brick doesn't seem to overlap the mortar
    \draw[mortar color,fill=main fence color] (\x,\twh) rectangle(\x+\tfs,\twh+\tfs) ;
  }

%% horizontal seams of mortar
\foreach \y in {0,1,...,20}
  {
    \draw[mortar color] (0,\y) -- (20,\y);
  }

%% create the vertical seams of mortar which are offset
%% differently for odd and even layers of mortar.
\foreach \x in {0,1,...,20}
  {
    \foreach \y in {0,1,...,20}
      {
        \ifodd\y\relax
          \ifodd\x\relax
            \draw[mortar color] (\x,\y) -- ++(0,1);
          \fi
        \else
          \ifodd\x\relax
          \else
            \draw[mortar color] (\x,\y) -- ++(0,1);
          \fi
        \fi
      }
  }

\end{tikzpicture}                                      
\end{document}

There are a couple things to notice here:

Color coding to facilitate construction

I've color-coded the parts of the diagram. There are several reasons for this. First is that I want to be able to see what it is that I'm doing. This makes the different parts stand out. Only for the final finished product will I remove the color as I did above.

If you comment out the following lines, you can see this.

\colorlet{main wall color}{gray!50}
\colorlet{tower wall color}{main wall color}
\colorlet{tower fence color}{main wall color}
\colorlet{main fence color}{main wall color}
\colorlet{mortar color}{white}

Then the castle will look as follows:

enter image description here

Also, by naming the colors for each part, you make it easier for yourself weeks or months later when you want to tweak the castle but don't quite remember what each part of the code does. You could put in comments; always a good thing. But, naming the colors this way also helps you see what is supposed to be happening in each part of the castle construction.

Scale

The next thing I did was the scale. Work on a scale that makes this easy for you to see what's going on. You can readjust the scale to something closer to what you want by uncommenting the lines:

%  x={(1mm,0)},%% uncomment to get desired scale
%  y={(0,1mm)},%% uncomment to get desired scale

which will produce

enter image description here

Also, just in terms of computations, I prefer to work with integers over decimal because then I have more control over the precision and need to worry much less about round-off errors and the like. If you're going to scale like this, though, you need to be aware of what exactly is being changed. So, you should probably read up on this in the TikZ manual. Any rigidly defined lengths are not going to be rescaled. There is also a scale key which you might find useful too.

Using relative coordinates

Instead of writing:

\draw[mortar color,fill=main fence color] (\x,\twh) rectangle(\x+\tfs,\twh+\tfs) ;

You could write

\draw[mortar color,fill=main fence color] (\x,\twh) rectangle ++(\tfs,\tfs) ;

This is a step toward using relative coordinates which can ultimately make your picture much more amendable to future modifications. Also, I believe the ++(<coordinate>) notation makes much clearer in the code what it is that you're doing. But that's probably just personal style on my part.

Here's what the code could look like if you try to make as much of this relative to a corner stone coordinate. (I've probably overlooked a few things, but this should give you the general idea of how this could be approached.)

\documentclass[tikz,border=3.14mm]{standalone}
\colorlet{main wall color}{gray!50}
\colorlet{tower wall color}{blue}
\colorlet{tower fence color}{green}
\colorlet{main fence color}{red}
\colorlet{mortar color}{magenta}
\colorlet{background color}{orange!30}

%% \colorlet{main wall color}{gray!50}
%% \colorlet{tower wall color}{main wall color}
%% \colorlet{tower fence color}{main wall color}
%% \colorlet{main fence color}{main wall color}
%% \colorlet{mortar color}{white}
%% \colorlet{background color}{white}

\begin{document}

\def\mytikzunit{4mm}
\begin{tikzpicture}[%%                                                
  x={(\mytikzunit,0)},%% uncomment to get desired scale                      
  y={(0,\mytikzunit)},%% uncomment to get desired scale                      
  ]                                                                   
%-> USEFUL DEFINITIONS                                                
%% define all other units with respect to basic unit
\def\basicunit{2}%%
\pgfmathsetmacro\fenceunit{1*\basicunit}      %% the fence width and height           
\pgfmathsetmacro\brickheight{1*\basicunit}
\pgfmathsetmacro\brickwidth{2*\basicunit}
\pgfmathsetmacro\towerheight{20*\basicunit}   %% the tower height                     
\pgfmathsetmacro\towerwidth{5*\basicunit}     %% the tower length                     
\pgfmathsetmacro\castleheight{10*\basicunit}  %% the wall height                      
\pgfmathsetmacro\castlewidth{20*\basicunit}   %% the wall length                      

%% establish various position of the picture relative to the "corner stone" 
%% define the following coordinate:                                         
%%                         "corner stone"                                   
%% for the tower:          "tower top right"                                
%%                         "tower top left"                                 
%% for the fences:         "fence top right"                                
%%                         "fence width"                                    
%% for the bricks:         "brick"                                          
%% for the castle body:    "castle top right"                               
%%                         "castle top left"                                
\coordinate (corner stone)     at (0,0);                                  
\path       (corner stone)     ++ (\towerwidth,\towerheight) coordinate (tower top right);
\coordinate (tower top left)   at (corner stone|-tower top right);
\path       (corner stone)     ++ (\fenceunit,\fenceunit)    coordinate (fence top right);    
\coordinate (fence width)      at (corner stone-|fence top right);
\coordinate (brick)            at (\brickwidth,\brickheight);
\path       (corner stone)     ++ (\castlewidth,\castleheight) coordinate (castle top right);
\coordinate (castle top left)  at (castle top right-|tower top right);

%% create a background that's not necessarily the same color as the mortar.
\draw[background color,fill] (corner stone) rectangle ++(\castlewidth,\towerheight+\fenceunit);

%% constructing the fences  
\pgfmathsetmacro\xmax{int(\castlewidth/\basicunit/2)}
\foreach \x [evaluate=\x as \pos using int(2*\fenceunit*(\x-1))]in {1,...,\xmax}
  {                                                                   
    \ifnum\x<4\relax
      \coordinate (fence starting point) at (tower top left);
    \else
      \coordinate (fence starting point) at (tower top left|-castle top left);
    \fi
    \draw[background color,fill=tower fence color] (fence starting point) ++(\pos,0) rectangle ++(fence top right) ;
    \draw[mortar color]                            (fence starting point) ++(\pos,0) --        ++(fence width) ;
  }

%% constructing the brick and mortar in tower
\begin{scope}
  \path[clip] (corner stone) |- (tower top right) |- (castle top right) |- cycle;
  \pgfmathsetmacro\ymax{int(\towerheight/\brickheight)}
  \pgfmathsetmacro\xmax{int(\castlewidth/\brickwidth)}
  \foreach \y [evaluate=\y as \ypos using int(\brickheight*\y)] in {0,...,\ymax}
    {
      \foreach \x [evaluate=\x as \xpos using int(\brickwidth*\x)] in {0,1,...,\xmax}
        {
          \coordinate (brick starting point) at (corner stone);
          \ifodd\y\relax
            \path (corner stone) ++ (-\basicunit,0) coordinate (brick starting point);
          \else
          \fi 
          \draw[mortar color,fill=tower wall color] (brick starting point) ++ (\xpos,\ypos) rectangle ++(brick);
        }           
    }               
\end{scope}         

%% redraw background color around edges to trim away misbehaving bricks
\draw[background color] (corner stone) rectangle (current bounding box.north east);

%% uncomment this next line to view the layout a bit better 
%\draw[line width=0.01pt,yellow] (current bounding box.south west) grid[step=\mytikzunit] (current bounding box.north east);      

\end{tikzpicture}                                      

\end{document}

One important point to notice here is that I produce the bricks and mortar all at the same time (much as my maligned bricklayer mentioned above). But, I don't want to have to think too much about where to begin and end this laying of bricks, so I use the scope environment and clip feature of \path to create a presentation of just the portion of the bricks I want visible (remove the clip and you'll see the full force of what I've drawn).

Creating more flexibility

Additionally, I've added a few variables for things such as brick width and brick height and then calculate \xmax and \ymax. This provides another layer of flexibility in case you want the bricks to have a different dimension.

For example, in this image I've made the bricks have dimension 3x2.

enter image description here

This is achieve by changing two parts of the code:

\pgfmathsetmacro\brickheight{2*\basicunit}
\pgfmathsetmacro\brickwidth{3*\basicunit}

And in the scope where the bricks are drawn:

\begin{scope}
  \path[clip] (corner stone) |- (tower top right) |- (castle top right) |- cycle;
  \pgfmathsetmacro\ymax{int(\towerheight/\brickheight)}
  \pgfmathsetmacro\xmax{int(\castlewidth/\brickwidth+1)}
  \foreach \y [evaluate=\y as \ypos using int(\brickheight*\y)] in {0,...,\ymax}
    {
      \foreach \x [evaluate=\x as \xpos using int(\brickwidth*\x)] in {0,1,...,\xmax}
        {
          \pgfmathsetmacro\aeoffset{mod(\y,3)}
          \path (corner stone) ++ (-\aeoffset*\basicunit,0) coordinate (brick starting point);
          \draw[mortar color,fill=tower wall color] (brick starting point) ++ (\xpos,\ypos) rectangle ++(brick);
        }           
    }               
\end{scope}         

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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