Comportamento incompreensível da biblioteca calc com uma "variável foreach"

Comportamento incompreensível da biblioteca calc com uma "variável foreach"

Se eu compilar o seguinte 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}

Não tenho nenhum problema e isso me dá o que eu quero. Mas a expressão entre "$" não está entre parênteses.

Se eu substituir $(\i+(0.7,0.7)$por, $\i+(0.7,0.7)$recebo a seguinte mensagem de erro:

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 

E se eu substituir $(\i+(0.7,0.7)$por $(\i+(0.7,0.7))$ou $(\i)+(0.7,0.7)$tiver a seguinte mensagem de erro:

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

É um bug ou fiz algo errado? (ou é apenas uma sintaxe estranha correta?)

Responder1

No exemplo dado pelo OP,

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

poderia (e provavelmente deveria) ser

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

No entanto, assumindo que este é um MWE e a aplicação real requer inevitavelmente o uso da calcbiblioteca, este é (como já foi apontado) um problema de expansão. À medida que a calcbiblioteca está sendo usada, o seguinte pode ser adequado:

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

Embora

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

seria (eu acho) um pouco mais eficiente.

insira a descrição da imagem aqui

Mas se você quer viver no limite, coloque

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

em seu preâmbulo. Então o ($(\i)+(0.7,0.7)$)pode ser usado dentro do loop foreach.

Responder2

Eu acho que isso é uma bobagem dissoUsando matemática no TikZResumindo, é uma interação estranha que precisa de um parêntese de abertura explícito (portanto, totalmente expandido) para acertar o contexto e usa o parêntese de fechamento \ipara finalizar a sintaxe corretamente. Se vir outro tropeça. A maneira correta é colocar tudo em seu próprio contexto e ser bastante {}detalhado.

Aqui está uma maneira melhor de ver o que está acontecendo com uma boa demonstração de como o TeX e o TikZ se comunicam sobre a análise de tokens (embora altamente não intuitivo). Observe os parênteses ausentes em alguns lugares, incluindo a lista.

A principal razão para tais coisas é que o TikZ se ramifica investigando qual o próximo caractere encontrado no stream. Portanto, se você ramificar para um lugar errado e colocar os termos corretos depois, ele não poderá voltar e ramificar corretamente. Não creio que neste tipo de programação você possa eliminar todas essas coisas.

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

insira a descrição da imagem aqui

Responder3

Pelo que vale, o código a seguir, que não possui colchetes extras e é configurado com parênteses correspondentes corretamente, compila e fornece o 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}

Responder4

Todas as soluções anteriores são perfeitas. Alguns deles explicam por que o calc atrapalha seu código. Alguns deles explicam o que você deve alterar para poder usar foreacho que passa por "pontos" (x_i,y_i).

Na minha opinião, você foreachestá "errado" no espírito do TikZ:

  • quando você diz \coordinate (A) at (x,y);então Arepresenta x,ye não (x,y)e você usa como (A).
  • quando você diz let \p1=(x,y)então \p1representa x,ye não (x,y)e você usa como (\p1).

Então eu acho que quando quisermos fazer foreachessa análise dos pontos, deveríamos fazer

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

e então use-o como (\i).

No seu caso, isso se torna:

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

insira a descrição da imagem aqui

informação relacionada