\draw 中 foreach 循环的扩展

\draw 中 foreach 循环的扩展

我很难理解GitHub 问题 pgf-tikz/pgf#356

原始图片

原始提问者发布了以下 MWE。\begin{document}为了完整起见,我已将其包装起来。

\documentclass[tikz,border=2mm]{standalone}
\begin{document}
\begin{tikzpicture}
  \coordinate (v1)  at (0,0);
  \coordinate (c21) at (1,0);
  \coordinate (c22) at (2,0);
  \coordinate (v2)  at (2,1);
  \coordinate (c31) at (2,2);
  \coordinate (c32) at (3,2);
  \coordinate (v3)  at (4,2);
  \node[draw,inner sep=0pt,minimum size=6pt,circle] (a) at (v1) {};
  \draw (a) \foreach \i in {2,3} {.. controls (c\i1) and (c\i2) .. (v\i)};
\end{tikzpicture}
\end{document}

对链接问题的答复表明,\foreach扩大到

\draw (a) .. controls (c21) and (c22) .. (v2) 
      (a) .. controls (c31) and (c32) .. (v3);

(a)但是,如果将包含其 MWE 的行中的节点\foreach替换为坐标(v1),则\foreach循环不会扩展为

\draw (v1) .. controls (c21) and (c22) .. (v2) 
      (v1) .. controls (c31) and (c32) .. (v3);

为什么会像下面这样展开呢?

\draw (v1) .. controls (c21) and (c22) .. (v2) 
           .. controls (c31) and (c32) .. (v3);

在此处输入图片描述

答复没有解释这一点。

没有提到\iftikz@shapeborderPGF 和 Ti 中Z 手册,其第 88 章

请注意,node和路径命令也以特殊方式pic支持该语句。foreach

不幸的是,手册中没有关于“特殊方法”的解释。

答案1

坐标和节点之间的区别在于节点(通常)具有大小。也就是说,节点的高度、深度和宽度中至少有一个大于零。TiZ/pgf 会处理这个尺寸,并且当使用没有锚点的节点作为坐标时,无论是否绘制该边界,它都会始终从/到节点边界绘制。请参阅 pgfmanual,第 17.11 节“连接节点:使用节点作为坐标”(html) 了解更多信息。

然后是 的范围属性(\)foreach。为了支持路径,每次离开和进入循环foreach时,应该备份和恢复相当多的信息。TiforeachZ 已经处理了一些此类信息(见tikz.code.tex,的定义\tikz@foreach),但它远非全面。问题 #356是曲线到路径操作的一种情况,并且问题 #1047是另一种turn选择的情况。

对于OP当前的问题,可以认为坐标版本已经得到处理(通过备份和恢复\tikz@last[xy]\tikz@last[xy]saved),而节点版本尚未得到处理。

无论如何,对于 OP 的问题(以及问题 #356,似乎唯一缺少的信息是\tikz@moveto@waiting,它要么被设置为\relax,要么\edef被设置为持有\tikz@shapeborder@name(一个节点名)。

\documentclass[tikz,border=2mm]{standalone}
\usepackage{regexpatch}

\tikzset{
  expected/.style={blue!10, line width=5pt},
  simulated/.style={blue!50, line width=3pt, opacity=.5},
  actual/.style={line width=.6pt}
}

\newcommand\test[2][]{
  \begin{tikzpicture}
    \coordinate (v1)  at (0,0);
    \coordinate (c21) at (1,0);
    \coordinate (c22) at (2,0);
    \coordinate (v2)  at (2,1);
    \coordinate (c31) at (2,2);
    \coordinate (c32) at (3,2);
    \coordinate (v3)  at (4,2);
    \node[draw,inner sep=0pt,minimum size=6pt,circle] (a) at (v1) {};
    
    \begin{scope}[shift={(3,0)}, nodes={font=\sffamily\scriptsize}, y=.5cm]
      \node[anchor=south west, align=center] at  (0,1.5) {start from (#2)#1};
      \draw[expected]  (0, 1) -- +(1,0) node[right] {expected};
      \draw[simulated] (0,.5) -- +(1,0) node[right] {simulated};
      \draw[actual]    (0, 0) -- +(1,0) node[right] {actual};
    \end{scope}
    
    % expected
    \draw[expected]
      (#2) .. controls (c21) and (c22) .. (v2)
          .. controls (c31) and (c32) .. (v3);
  
    % simulated foreach
    \draw[simulated]
      (#2) .. controls (c21) and (c22) .. (v2)
      (#2) .. controls (c31) and (c32) .. (v3);
    
    % actual
    \draw[actual]
      (#2) \foreach \i in {2,3} {.. controls (c\i1) and (c\i2) .. (v\i)};
  \end{tikzpicture}
}

\begin{document}
\test[\\before patch]{a}
\test[\\before patch]{v1}

\makeatletter
% backup globally
\xpatchcmd*\tikz@foreach
  {\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}}
  {\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
   \global\let\tikz@foreach@moveto@waiting=\tikz@moveto@waiting
   }
  {}{\PatchFailed}

% restore locally
\xpatchcmd*\tikz@foreach
  {\tikz@lastysaved=\tikz@foreach@save@lastysaved}
  {\tikz@lastysaved=\tikz@foreach@save@lastysaved
   \let\tikz@moveto@waiting=\tikz@foreach@moveto@waiting
   }
  {}{\PatchFailed}
\makeatother

\test[\\after patch]{a}
\test[\\after patch]{v1}
\end{document}

在此处输入图片描述

答案2

我的理解是\foreach创建完整的本地子路径——所以它基本上相当于

\draw (a) { [current point is local] .. controls (c21) and (c22) .. (v2) }
          { [current point is local] .. controls (c31) and (c32) .. (v3) };

关于边界的事情:这个想法是,如果你使用一个节点(a)作为坐标,TiZ 将把坐标放在形状的边框上 --- 在这种情况下,使用(v1)就像使用 一样(a.center)

可玩代码:

\documentclass[tikz,border=2mm]{standalone}
\begin{document}
\begin{tikzpicture}
  \coordinate (v1)  at (0,0);
  \coordinate (c21) at (1,0);
  \coordinate (c22) at (2,0);
  \coordinate (v2)  at (2,1);
  \coordinate (c31) at (2,2);
  \coordinate (c32) at (3,2);
  \coordinate (v3)  at (4,2);
  \node[draw,inner sep=0pt,minimum size=6pt,circle] (a) at (v1) {};
  \draw (a) \foreach \i in {2,3} {.. controls (c\i1) and (c\i2) .. (v\i)};
  \draw[blue]  (a) .. controls (c21) and (c22) .. (v2)
                   .. controls (c31) and (c32) .. (v3);
  \draw[red, dashed]  (a) { [current point is local] .. controls (c21) and (c22) .. (v2) }
                  { [current point is local] .. controls (c31) and (c32) .. (v3) };
\end{tikzpicture}
\end{document}

上述代码片段的输出

相关内容