7

If I compile the following code :

\documentclass[12pt]{article}

\usepackage[french]{babel}
\usepackage{tikz}
\usetikzlibrary{calc}


\begin{document}

\begin{tikzpicture}
\foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
\fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}

\end{document}

I have no problem and this give me that I want. But the expression between "$" is ill-parenthesized.

If I replace $(\i+(0.7,0.7)$ by $\i+(0.7,0.7)$ I have the following error message :

Runaway argument?
\i +(0.7,0.7)$); \pgffor@endhook \ifx \pgffor@assign@after@code \pgfutil@empty 
\ETC.
! Paragraph ended before \tikz@cc@parse@factor was complete.
<to be read again> 
               \par 
l.15 

And If I replace $(\i+(0.7,0.7)$ by $(\i+(0.7,0.7))$ or $(\i)+(0.7,0.7)$ I have the following error message :

! Package tikz Error: + or - expected.

See the tikz package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.12 ...htgray] \i rectangle ($(\i)+(0.7,0.7)$); }

It is a bug or I do something wrong ? (or it is just a correct strange syntax ?)

4 Answers4

8

In the example given by the OP,

\fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); 

could (and probably should) be

\fill[lightgray] \i rectangle ++(0.7,0.7); 

However, assuming this is a MWE and the actual application unavoidably requires the use of the calc library, this is (as has already been pointed out) an expansion problem. As the calc library is being used the following may be suitable:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
\fill let \p1=\i in [lightgray] (\p1) rectangle ($(\p1)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}
\end{document}

Although

\fill let \p1=\i in [lightgray] (\p1) rectangle (\x1+.7, \y1+.7);

would (I think) be marginally more efficient.

enter image description here

But if you want to live on the edge, put

\makeatletter
\def\pgffor@scanround(#1)#2,{\def\pgffor@value{#1#2}\pgffor@scanned}

in your preamble. Then the ($(\i)+(0.7,0.7)$) can be used inside the foreach loop.

Mark Wibrow
  • 70,437
6

I think this is a dupe of this Using math in TikZ Long story short it's a weird interaction such that it needs an explicit (hence fully expanded) opening parenthesis to get the context right and uses the closing parenthesis of \i to finish of the syntax correctly. If it sees another one trips up. The proper way is to brace everything in their own context and be pretty much {} verbose.

Here is a better way to see what is happening with a nice demonstration of how TeX and TikZ communicate about parsing tokens (though highly nonintuitive). Notice the missing parens certain places including the list.

Main reason for such things is that TikZ branches off by investigating what the next char is found on the stream. So if you branch off to a wrong place and put the correct terms afterwards, it cannot go back and branch properly. I don't think in this type of programming you can eliminate all these things.

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\foreach \i in {{(0,0},{(0,2.1},{(2.1,2.1},{(2.1,0}} {
\fill[lightgray] \i) rectangle ($(\i)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}
\end{document}

enter image description here

percusse
  • 157,807
  • Why you keep the left paranthesis ? You can simply use \foreach \i in {{0,0},{0,2.1},{2.1,2.1},{2.1,0}} and then \fill[red] (\i) rectangle ($(\i)+(0.7,0.7)$);. No ? – Kpym Dec 16 '14 at 22:25
  • @Kpym To show that the parenthesis is needed to switch to the right parsing branch namely oh this is a coordinate expression branch. It works just after fill but doesn't understand inside the calc expression unless it sees an explicit ( (even if you have two consecutive (( right now). That's the catch I wanted to demonstrate. Fixing the code is easy anyhow so I didn't bother to offer an alternative. – percusse Dec 16 '14 at 22:40
5

For what it's worth, the following code, which has no extra braces and is set-up with correctly matched parentheses, compiles and gives the expected result:

\documentclass[12pt]{article}

\usepackage[french]{babel}
\usepackage{tikz}
\usetikzlibrary{calc}

\usepackage{xinttools}

\begin{document}

% \begin{tikzpicture}
% \foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
% \fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); }
% \draw (0,0) grid[step=0.7] (2.8,2.8);
% \end{tikzpicture}

\begin{tikzpicture}
\xintForpair  #1#2 in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} \do {
\fill[lightgray] (#1,#2) rectangle ($(#1,#2)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}

\end{document}
1

All previous solutions are perfect. Some of them explains why calc mess up in your code. Some of them explains what you should change to be able to use foreach that runs through "points" (x_i,y_i).

In my opinion your foreach is "wrong" in the spirit of TikZ:

  • when you say \coordinate (A) at (x,y); then A represent x,y and not (x,y) and you use it as (A).
  • when you say let \p1=(x,y) then \p1 represent x,y and not (x,y) and you use it as (\p1).

So I think that when we want to make foreach that run trough points we should do

\foreach \i in {{x_1,y_1},...,{x_n,y_n}}

and then use it as (\i).

In your case this becomes :

\documentclass[tikz,varwidth,border=5]{standalone}
\usetikzlibrary{calc}
\begin{document}
  \begin{tikzpicture}
    \foreach \i in {{0,0},{0,2.1},{2.1,2.1},{2.1,0}}
      \fill[lightgray] (\i) rectangle ($(\i)+(0.7,0.7)$); 
    \draw (0,0) grid[step=0.7] (2.8,2.8);
  \end{tikzpicture}
\end{document} 

enter image description here

Kpym
  • 23,002