Непонятное поведение библиотеки calc с «переменной foreach»

Непонятное поведение библиотеки calc с «переменной foreach»

Если я скомпилирую следующий код:

\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+(0.7,0.7)$на , $\i+(0.7,0.7)$то получаю следующее сообщение об ошибке:

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 

И если я заменю $(\i+(0.7,0.7)$на $(\i+(0.7,0.7))$или $(\i)+(0.7,0.7)$у меня появится следующее сообщение об ошибке:

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

Это ошибка или я что-то делаю не так? (или это просто правильный странный синтаксис?)

решение1

В примере, приведенном ОП,

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

может (и, вероятно, должно) быть

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

Однако, если предположить, что это MWE и фактическое приложение неизбежно требует использования библиотеки calc, это (как уже было указано) проблема расширения. Поскольку calcбиблиотека используется, следующее может быть подходящим:

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

Хотя

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

(я думаю) было бы немного эффективнее.

введите описание изображения здесь

Но если вы хотите жить на грани, поставьте

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

в вашей преамбуле. Затем ($(\i)+(0.7,0.7)$)можно использовать внутри цикла foreach.

решение2

Я думаю, что это подделка этогоИспользование математики в TikZКороче говоря, это странное взаимодействие, так как ему нужна явная (следовательно, полностью развернутая) открывающая скобка, чтобы получить правильный контекст, и используется закрывающая скобка, \iчтобы правильно завершить синтаксис. Если он видит еще одну, он спотыкается. Правильный способ — заключить все в их собственный контекст и быть довольно {}многословным.

Вот лучший способ увидеть, что происходит, с хорошей демонстрацией того, как TeX и TikZ общаются о парсинге токенов (хотя это очень неинтуитивно). Обратите внимание на отсутствующие скобки в некоторых местах, включая список.

Основная причина таких вещей в том, что TikZ разветвляется, исследуя, какой следующий символ находится в потоке. Так что если вы разветвляетесь в неправильном месте и помещаете правильные термины после этого, он не может вернуться и разветвляться правильно. Я не думаю, что в этом типе программирования вы можете устранить все эти вещи.

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

введите описание изображения здесь

решение3

Если это имеет значение, то следующий код, не имеющий дополнительных скобок и настроенный с правильно подобранными скобками, компилируется и дает ожидаемый результат:

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

решение4

Все предыдущие решения идеальны. Некоторые из них объясняют, почему calc портит ваш код. Некоторые из них объясняют, что вам следует изменить, чтобы иметь возможность использовать, foreachчто проходит через "точки" (x_i,y_i).

По-моему, вы foreach«не правы» в духе TikZ:

  • когда вы говорите, \coordinate (A) at (x,y);то Aпредставляете x,y, а не (x,y)и используете его как (A).
  • когда вы говорите, let \p1=(x,y)то \p1представляете x,y, а не (x,y)и используете его как (\p1).

Поэтому я думаю, что когда мы хотим пройти foreachэтот этап, мы должны сделать это

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

а затем использовать его как (\i).

В вашем случае это будет выглядеть так:

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

введите описание изображения здесь

Связанный контент