Comportamiento incomprensible de la biblioteca calc con una "variable foreach"

Comportamiento incomprensible de la biblioteca calc con una "variable foreach"

Si compilo el siguiente código:

\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}

No tengo ningún problema y esto me da lo que quiero. Pero la expresión entre "$" no está entre paréntesis.

Si reemplazo $(\i+(0.7,0.7)$por $\i+(0.7,0.7)$tengo el siguiente mensaje de error:

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 

Y si reemplazo $(\i+(0.7,0.7)$por $(\i+(0.7,0.7))$o $(\i)+(0.7,0.7)$tengo el siguiente mensaje de error:

! 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)$); }

¿Es un error o hago algo mal? (¿O es simplemente una sintaxis extraña y correcta?)

Respuesta1

En el ejemplo dado por el OP,

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

podría (y probablemente debería) ser

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

Sin embargo, suponiendo que se trate de un MWE y que la aplicación real requiera inevitablemente el uso de la calcbiblioteca, esto es (como ya se ha señalado) un problema de expansión. Dado que calcse utiliza la biblioteca, lo siguiente puede ser adecuado:

\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}

A pesar de

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

sería (creo) marginalmente más eficiente.

ingrese la descripción de la imagen aquí

Pero si quieres vivir al límite, ponte

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

en su preámbulo. Luego ($(\i)+(0.7,0.7)$)se puede usar dentro del bucle foreach.

Respuesta2

Creo que esto es un engaño de esto.Usando matemáticas en TikZEn pocas palabras, es una interacción extraña que necesita un paréntesis de apertura explícito (por lo tanto, completamente expandido) para obtener el contexto correcto y utiliza el paréntesis de cierre de \ipara finalizar la sintaxis correctamente. Si ve otro, tropieza. La forma correcta es centrar todo en su propio contexto y ser bastante {}detallado.

Aquí hay una mejor manera de ver lo que está sucediendo con una buena demostración de cómo TeX y TikZ se comunican sobre el análisis de tokens (aunque muy poco intuitivo). Observe los padres que faltan en ciertos lugares, incluida la lista.

La razón principal de tales cosas es que TikZ se bifurca al investigar cuál es el siguiente carácter que se encuentra en la transmisión. Entonces, si se bifurca en un lugar equivocado y luego coloca los términos correctos, no puede regresar y bifurcarse correctamente. No creo que en este tipo de programación puedas eliminar todas estas cosas.

\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}

ingrese la descripción de la imagen aquí

Respuesta3

Por si sirve de algo, el siguiente código, que no tiene llaves adicionales y está configurado con paréntesis que coinciden correctamente, se compila y proporciona el resultado esperado:

\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}

Respuesta4

Todas las soluciones anteriores son perfectas. Algunos de ellos explican por qué calc se estropea en su código. Algunos de ellos explican qué debes cambiar para poder usar foreachel que corre por "puntos" (x_i,y_i).

En mi opinión, estás foreach"equivocado" en el espíritu de TikZ:

  • cuando dices \coordinate (A) at (x,y);entonces Arepresenta x,yy no (x,y)y lo usas como (A).
  • cuando dices let \p1=(x,y)entonces \p1representa x,yy no (x,y)y lo usas como (\p1).

Así que creo que cuando queramos llegar a foreachesos puntos deberíamos hacer

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

y luego usarlo como (\i).

En su caso esto se convierte en:

\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} 

ingrese la descripción de la imagen aquí

información relacionada