2

I'm trying to draw adjacent columns of boxes of heights decremented by 1 from left to right used nested \foreach loops in tikz.

\documentclass{amsart}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}
\foreach \x in {0,0.25,...,4.75}
{
\foreach \y in {-4,-3.75,...,0.75 - \x}
\filldraw[draw=black, fill=blue, opacity=0.2] (0, \y) rectangle (0.25, \y + 0.25);
}
\end{tikzpicture}


\end{document}

Since each column should have 1 less box than the one to its left, I need to change the limits of the \foreach \y loop based on what \x is. But this syntax apparently isn't correct, and I have no idea how to make it accept an expression involving \x.

I also don't understand why the opacity argument of the \filldraw command is ignored.

11
  • That doesn't seem to work. I'm getting an undefined control sequence error. Oct 4 at 2:23
  • Yes, sorry, it should be \fpeval{...}. To ensure we are on the same page, you should type in \foreach \y in {-4,-3.75,...,\ fpeval{0.75 - \x}}. Oct 4 at 2:24
  • Yes. It doesn't seem to recognize the \fpeval command. Is this from the fp package? Oct 4 at 2:27
  • I honestly don't know where it is exactly defined in but it is in the recent LaTeX distributions by default (don't quote me but I think starting from Jan 2021). I tried multiple times to find where it was created but couldn't. It wasn't mentioned in texdoc interface3 nor texdoc source3. Oct 4 at 2:30
  • Does that answer you question? Oct 4 at 2:48

1 Answer 1

2

You need the last term in the set {-4,...,0.75-\x} to be evaluated before the code tries to read it in setting up the loop. There are different ways to do this. One is to use pgf's evaluate option,

\foreach \i [evaluate=\i as \k using 0.75-\i] in {0,0.25,...,4.75}
{
  \foreach \j in {-4,-3.75,...,\k}
    \filldraw[draw=black, fill=blue,opacity=0.2] (0,\j) rectangle (0.25,\j+0.25);
}

This results in the following image.

graduated blues

I'm not quite sure what the result is meant to look like. The opacity=.2 is effective, but if you layer enough blobs of 20% opacity one on top of another, they no longer look opaque. To see this, try shifting the blocks rather than painting them one on top of another each time you iterate through the loop. For example,

  \foreach \j in {-4,-3.75,...,\k}
    \filldraw[draw=black, fill=blue,opacity=0.2] (\i,\j) rectangle (\i+0.25,\j+0.25);

produces

graduated steps of non-graduated blue

Or perhaps you'd like the opacity to be applied to the result of the two loops,

  \begin{scope}[transparency group,opacity=.2]
    \foreach \i [evaluate=\i as \k using 0.75-\i] in {0,0.25,...,4.75}
    {
      \foreach \j in {-4,-3.75,...,\k}
      \filldraw[draw=black, fill=blue] (0,\j) rectangle (0.25,\j+0.25);
    }
  \end{scope}

consistent opacity using transparency group

But you may also be hoping for some other image I've not managed to guess ;).

Complete code:

\documentclass[tikz]{standalone}
\begin{document}

\begin{tikzpicture}
\foreach \i [evaluate=\i as \k using 0.75-\i] in {0,0.25,...,4.75}
{
  \foreach \j in {-4,-3.75,...,\k}
    \filldraw[draw=black, fill=blue,opacity=0.2] (0,\j) rectangle (0.25,\j+0.25);
}
\end{tikzpicture}
\begin{tikzpicture}
\foreach \i [evaluate=\i as \k using 0.75-\i] in {0,0.25,...,4.75}
{
  \foreach \j in {-4,-3.75,...,\k}
    \filldraw[draw=black, fill=blue,opacity=0.2] (\i,\j) rectangle (\i+0.25,\j+0.25);
}
\end{tikzpicture}
\begin{tikzpicture}
  \begin{scope}[transparency group,opacity=.2]
    \foreach \i [evaluate=\i as \k using 0.75-\i] in {0,0.25,...,4.75}
    {
      \foreach \j in {-4,-3.75,...,\k}
      \filldraw[draw=black, fill=blue] (0,\j) rectangle (0.25,\j+0.25);
    }
  \end{scope}
\end{tikzpicture}

\end{document}

EDIT

If I wanted the second image (but less a square), I'd do something more like this,

\begin{tikzpicture}[x=2.5mm,y=2.5mm]
\foreach \i [evaluate=\i as \k using int(19-\i)] in {0,...,19}
{
  \foreach \j  in {0,...,\k}
    \filldraw[draw=black, fill=blue,opacity=0.2] (\i,\j) rectangle ++(1,1);
}
\end{tikzpicture}

steps in non-graduated blue

This involves fewer calculations, is easier to understand and will produce a more accurate result (because calculating with decimals introduces imprecision, though the accumulation probably doesn't matter here).

2
  • 1
    The second picture is what I was aiming for, with 1 less square in the final column. Thanks for this detailed answer and sorry for the confusion. Oct 4 at 3:15
  • @EthanAlwaise No worries. It's always hard to figure out what won't be obvious, but giving the error is a good rule. Often somebody will know what's wrong without having to copy-paste-compile to see. Re. squares, please see edit. I don't know your real use, of course, but, at least for the example, I think you can make this simpler and more efficient.
    – cfr
    Oct 4 at 3:21

You must log in to answer this question.

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