如何在 foreach 循环中使用 plot 绘制单一路径?

如何在 foreach 循环中使用 plot 绘制单一路径?

回答在 TikZ 中创建齿轮,我会plot在 foreach 循环中将其用于单个路径。但似乎不可能...

以下举例说明我的问题:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \draw
  (0,0)
  \foreach \myvar in {1,2,3}{
    --
    plot[domain=0:1] (\myvar-\x,\myvar)
  };
\end{tikzpicture}
\end{document}

本文档应产生以下结果:

在此处输入图片描述

事实上,它会产生以下错误:

! Package tikz Error: Giving up on this path. Did you forget a semicolon?.

编辑:我添加了一个更复杂的例子来展示这个问题。

这里,没有的代码\foreach

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \draw
  (0,0)
  --
  plot[domain=0:120] ({1-cos(3*\x)},{1-sin(\x)})
  --
  plot[domain=0:120] ({2-cos(3*\x)},{2-sin(2*\x)})
  --
  plot[domain=0:120] ({3-cos(3*\x)},{3-sin(3*\x)})
  ;
\end{tikzpicture}
\end{document}

结果如下:

在此处输入图片描述

分解后的代码如下\foreach(但此代码会产生错误):

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \draw[dahsed]
  (0,0)
  \foreach \myvar in {1,2,3}{
    --
    plot[domain=0:120] ({\myvar-cos(3*\x)},{\myvar-sin(\myvar*\x)})
  };
\end{tikzpicture}
\end{document}

答案1

这是一个错误。

\foreach执行 时发生的和执行 时发生的\draw ... \foreach ...不一样。它是\foreach\foreach ... \draw ...真实的 \foreach在调用实际命令之前,先设置一些钩子。包装器命令是:

\def\tikz@foreach{%
  \def\pgffor@beginhook{%
    \tikz@lastx=\tikz@foreach@save@lastx%
    \tikz@lasty=\tikz@foreach@save@lasty%
    \tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
    \tikz@lastysaved=\tikz@foreach@save@lastysaved%
    \setbox\tikz@figbox=\box\tikz@tempbox\expandafter\tikz@scan@next@command\pgfutil@firstofone}%
  \def\pgffor@endhook{\pgfextra{%
      \xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
      \xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
      \xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
      \xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
      \global\setbox\tikz@tempbox=\copy\tikz@figbox\pgfutil@gobble}}%
  \def\pgffor@afterhook{%
    \tikz@lastx=\tikz@foreach@save@lastx%
    \tikz@lasty=\tikz@foreach@save@lasty%
    \tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
    \tikz@lastysaved=\tikz@foreach@save@lastysaved%
    \setbox\tikz@figbox=\box\tikz@tempbox\tikz@scan@next@command}%
  \global\setbox\tikz@tempbox=\copy\tikz@figbox%
  \xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
  \xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
  \xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
  \xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
  \foreach}

钩子\pgffor@beginhook等在代码中的特定位置被调用。重要的是在:\foreach末尾发生的事情。这样做的实际结果是,它将 TikZ 解析器置于扫描 foreach 循环主体的正确模式:它正在等待路径构造命令。这就是工作的原因:当 TikZ 最终看到它处于正确的“模式”时。beginhook\expandafter\tikz@scan@next@command\pgfutil@firstofone\draw (0,0) \foreach \i in {1} { -- (1,0) };-- (1,0)

plot 函数通过调用来\foreach生成绘制路径时经过的所有点。但是当 plot 函数运行时,它不希望 TikZ 解析器生效,而是想做自己的事情。所以\pgffor@beginhook这里完全是错误的地方,因为 TikZ 现在会在开始生成路径点时期待路径构造命令,但 plot 代码还没有准备好将控制权交还给解析器。最终发生的是解析器进入无限循环,但 TikZ 内置了对这些循环的检查并最终退出...

一个解决方法是在 plot 函数调用其循环之前清空钩子。我不知道这有多可靠 - 我没有对其进行过广泛的测试。以下是一些执行此操作的代码:

\documentclass{article}
%\url{http://tex.stackexchange.com/q/58829/86}
\usepackage{tikz}
%\usepackage{trace}

\makeatletter

\def\tikz@clear@foreach{%
\let\pgffor@beginhook=\pgfutil@empty
\let\pgffor@endhook=\pgfutil@empty
\let\pgffor@afterhook=\pgfutil@empty
}

\def\tikz@plot@expression(#1){%
  \edef\tikz@plot@data{\noexpand\tikz@clear@foreach\noexpand\pgfplotfunction{\expandafter\noexpand\tikz@plot@var}{\tikz@plot@samplesat}}%
  \expandafter\def\expandafter\tikz@plot@data\expandafter{\tikz@plot@data{\tikz@scan@one@point\pgfutil@firstofone(#1)}}%
  \tikz@@@plot%
}
\makeatother
\begin{document}
\begin{tikzpicture}
%\traceon
  \draw[dashed]
  (0,0) \foreach \myvar in {1,2,3} {%
   -- plot[domain=0:120] ({\myvar-cos(3*\x)},{\myvar-sin(\myvar*\x)})
%-- ++(\myvar,1)
  }%
;
%\traceoff
\end{tikzpicture}
\end{document}

这将产生所需的结果:

TikZ 绘图修复

正如我所说的,可能有更好的解决办法,但在实施真正的解决办法之前,可以将其视为一种创可贴。

答案2

假定问题是,像你那样\draw用循环来切断命令是非法的\foreach。看来我错了。不过,一个解决方案可以规避这个问题漏洞(正如其他海报所建议的),我建议使用较低级别的命令,即\pgfpathlineto

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \pgfpathmoveto{\pgfpointorigin}
  \foreach \myvar/\x in {1cm/0cm,1cm/1cm,2cm/0cm,2cm/1cm,3cm/0cm,3cm/1cm}{
    \pgfpathlineto{\pgfpoint{\myvar-\x}{\myvar}}
  };
  \pgfusepathqstroke
\end{tikzpicture}
\end{document}

首先将路径的起点设置为画布坐标系的原点。然后在循环中将\foreach连续的线段添加到路径中,最后实际绘制路径。如果省略\pgfusepathqstroke,您将获得一条路径,但它不可见。

答案3

可以用 \foreach 循环剪切 \draw 命令,例如:\draw (0,0) \foreach \myvar in {1,2,3}{ -- (\myvar,\myvar*\myvar-\myvar)} ;

我认为,但我不确定问题是否来自于plot和之间的冲突\foreach

这是获得结果的解决方案

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture} 
 \draw (0,0)
  \foreach \myvar in {1,2,3}{%
   -- (\myvar,\myvar) -- (\myvar-1,1+\myvar)} ;
\end{tikzpicture}
\end{document} 

在此处输入图片描述

相关内容