Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

To answer Creating gears in TikZ, I would use plot in a foreach loop used into a single path. But it seems that it is impossible...

Here, an example to show my problem:

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

This document should produce this result:

enter image description here

In fact, it produces following error:

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

Edit: I add a more complex example to show the problem.

Here, a code without \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}

And the result:

enter image description here

And the factorized code with \foreach (but this code produces the error):

\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}
share|improve this question
    
A complete example is in my answer tex.stackexchange.com/a/58828/14500 (a gear). –  Paul Gaborit Jun 6 '12 at 23:05
    
I'm going for bug. The TikZ command parser goes into an infinite loop (it bails out because TikZ has an inbuilt limit on infinity) where it keeps trying to expand the same thing over and over again. –  Loop Space Jun 7 '12 at 10:29

3 Answers 3

up vote 10 down vote accepted

It's a bug.

The \foreach that occurs when you do \draw ... \foreach ... is not the same as the \foreach that occurs when you do \foreach ... \draw .... It's a wrapper around the real \foreach that sets up some hooks first before calling the real command. The wrapper command is:

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

The hooks, \pgffor@beginhook and so on, get called at specific places in the \foreach code. The important one is what happens at the end of the beginhook: \expandafter\tikz@scan@next@command\pgfutil@firstofone. The practical upshot of this is that it puts the TikZ parser into the right mode for scanning the body of the foreach loop: it is expecting a path construction command. This is what makes \draw (0,0) \foreach \i in {1} { -- (1,0) }; work: when TikZ finally gets to see the -- (1,0) it is in the right "mode".

The plot function works by calling \foreach to generate all of the points it will draw a path through. But when the plot function is working, it doesn't want the TikZ parser to be in effect, it wants to do its own stuff. So the \pgffor@beginhook is completely in the wrong place here because TikZ will now be expecting a path construction command when it starts generating the points for the path, but the plot code isn't ready to hand control back to the parser at that point. What ends up happening is that the parser goes into an infinite loop, but TikZ has a builtin check for these loops and bails out ... eventually.

One fix is to blank the hooks before the plot function calls its loops. I don't know how reliable this is - I've not tested it extensively. Here's some code that does that:

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

This produces the desired result:

TikZ plot fix

As I said, there are probably better fixes but think of this as a plaster until the real fix is implemented.

share|improve this answer
    
Thank you for this perfect analysis. Christophe Jorssen has completed my bug report with a link to your answer. Now, I'm waiting for the real fix (even if I can compile pgfmanual with your fix). –  Paul Gaborit Jun 7 '12 at 17:34
1  
With the latest CVS version of TikZ/pgf (since 2013-07-17), this bug is fixed. –  Paul Gaborit Jul 29 '13 at 14:32

I assumed the problem to be that it is illegal to cut the \draw command with a \foreach loop as you did. Seems I was wrong about that one. Nevertheless, a solution to circumvent this bug (as suggested by other posters), I propose to use a lower level command, namely \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}

First the start of the path is set to the origin of the coordinate system of the canvas. Then in the \foreach loop the successive segments are added to the path and finally the path is actually drawn. If you omit \pgfusepathqstroke, you will have a path, but it won't be visible.

share|improve this answer
1  
It's possible to cut the \draw command with a \foreach loop, for example : \draw (0,0) \foreach \myvar in {1,2,3}{ -- (\myvar,\myvar*\myvar-\myvar)} ;. I think but I'm not sure that the problem comes from a conflict between plot and \foreach –  Alain Matthes Jun 6 '12 at 21:33
    
Using \foreach loop into \draw is legal and useful. My real problem is in the question : using plot into \foreach loop into \draw... –  Paul Gaborit Jun 6 '12 at 22:30
    
I see this garnered a couple of down-votes. I think it was a reasonable attempt to answer the question. In such a situation, it is better to leave a comment explaining why the answer doesn't answer the question and give the answerer a chance to respond. –  Loop Space Jun 7 '12 at 13:11
    
@AndrewStacey Ok. I delete my down-vote. –  Paul Gaborit Jun 8 '12 at 12:11
    
@PolGab Thank you. (Note that I had no way to know who had voted down this answer.) I notice you're fairly new to this site so I hope you don't mind my comments. We tend to be fairly sparing in our down-votes - I've only voted down 16 times in nearly 2 years (compared to over 4000 votes up). Note that Count Zero has edited the answer to make it clear that the original assumption was wrong so there's no misinformation that might confuse later viewers of this question. –  Loop Space Jun 8 '12 at 12:32

It's possible to cut the \draw command with a \foreach loop, for example : \draw (0,0) \foreach \myvar in {1,2,3}{ -- (\myvar,\myvar*\myvar-\myvar)} ;.

I think but I'm not sure that the problem comes from a conflict between plot and \foreach

Here a solution to get the result

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

enter image description here

share|improve this answer
    
Ok for this simple transformation. But my example was willingly simple to show the problem with a simple plot. My real problem (drawing gears) uses a lot of complex plots into a single path... –  Paul Gaborit Jun 6 '12 at 22:50
    
I have edited my question to add a more complex example. –  Paul Gaborit Jun 6 '12 at 23:01
    
@PolGab I wrote that the problem comes from plot inside a foreach but I don't know why. –  Alain Matthes Jun 7 '12 at 5:01
    
I submitted a [bug report to sourceforge]( sourceforge.net/tracker/…) –  Paul Gaborit Jun 7 '12 at 6:58
    
Not sure about the bug, perhaps it's (tex)chnical problem. –  Alain Matthes Jun 7 '12 at 7:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.