8

To make an animation (here), I used a found code (here: @Jake answers). It works but not for all increments. It works when increments are 0.02 to 0.02, but not when increments are 0.04 to 0.04, 0.05 to 0.05 or 0.1 to 0.1.

Here with an increment of 0.04, the animation does not make the complete turn:

animation-1

Here, with an increment of 0.02, the animation makes the complete turn but the path is not closed:

animation-2coin-inferieur

First problem:

  • Why doesn't it work with any increment?
  • How do I make the decoration work for any increment?

Edition: By replacing \foreach by \multido the increments do not pose any more problems, the complete turn is carried out.

\multido{\npos=0.00+0.05}{21}{
%\multido{\npos=0.00+0.04}{26}{
%\multido{\npos=0.00+0.1}{11}{
%\foreach \npos  in {0,.1,...,1}{% 
            \begin{tikzpicture}[scale=1]
                \fill[green!40](0,0) rectangle (4,4);
                \draw[start segment=\npos,blue,ultra thick] (0,0) -- (4,0) -- (4,4) -- (0,4) --cycle ;
            \end{tikzpicture} 
 } 

Edition 2: Ruixi Zhang's solution allows you to go around the entire path using a \foreach loop.

%\foreach \x [evaluate =\x as \npos using \x/50] in {0,1,...,50}{% increment of 0.02
%\foreach \x [evaluate =\x as \npos using \x/25] in {0,1,...,25}{% increment of 0.04
\foreach \x [evaluate =\x as \npos using \x/10] in {0,1,...,10}{% increment of 0.1
            \begin{tikzpicture}[scale=1]
                \fill[green!40](0,0) rectangle (4,4);
                \draw[start segment=\npos,blue,ultra thick] (0,0) -- (4,0) -- (4,4) -- (0,4) --cycle ;
            \end{tikzpicture} 

Second problem:

How to close the path?

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{decorations}

\pgfdeclaremetadecoration{start}{initial}{
    \state{initial}[width={\pgfmetadecoratedpathlength*\pgfdecorationsegmentlength},next state=final]{
        \decoration{lineto}
    }
    \state{final}{}
}

\tikzset{start segment/.style={decoration={start,raise=2mm,segment length=#1},decorate}} 

\begin{document}
\foreach \rpos  in {0,.02,.04,...,1}{% 
            \begin{tikzpicture}[scale=1]
                \fill[green!40](0,0) rectangle (4,4);
                \draw[start segment=\rpos,blue,ultra thick] (0,0) -- (4,0) -- (4,4) -- (0,4) --cycle ;
            \end{tikzpicture} 
 } 
\end{document}
  • How do I close it completely?
AndréC
  • 24,137

1 Answers1

7

You have to close the path when it is done. (Unfortunately there are some roundings in place. This explains why the check reads >0.999pt instead of =1pt.) One can check with \pgfkeys{/pgf/decoration automaton/if input segment is closepath=...} whether the last segment is the close path segment and then close the path if one is close to its end. This avoids closing paths that are not to be closed. Notice that 0.999pt is an empirical value. If you have a very extreme path in which the close path segment is very short, such as a path (0,0) -- (4,0) -- (4,4) -- (0,4) -- (0,0) --cycle ;, this answer may not work.

As for the question on "any increment": of course, if you use the above and try \foreach \rpos in {0,.03,...,1} ..., say, the contour won't close. However, this is because you instructed TikZ to draw 99% of the path in the last step as the last \rpos is 0.99 (roughly) in this loop. How would TikZ know that it is supposed to close? In this case, one has to add the closing point to the loop and say \foreach \rpos in {0,.03,...,1,0.9999}.

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{decorations}
\pgfkeys{pgf/.cd,
close/.code={\pgfpathclose}}

\pgfdeclaremetadecoration{draw part of a path}{initial}{
    \state{initial}[width={\pgfmetadecoratedpathlength*\pgfdecorationsegmentlength},next state=final]{
        \decoration{lineto}
    }
    \state{final}{\ifdim\pgfdecorationsegmentlength>0.999pt
        \pgfkeys{/pgf/decoration automaton/if input segment is closepath=/pgf/close}
    \fi 
    }
}
%
\tikzset{start segment/.style={decoration={draw part of a path,raise=2mm,segment length=#1},decorate}} 

\begin{document}
\foreach \rpos  in {0,.03,...,1,0.9999}{% 
            \begin{tikzpicture}[scale=1]
                \fill[green!40](0,0) rectangle (4,4);
                \draw[start segment=\rpos,blue,ultra thick] (0,0) -- (4,0) -- (4,4) -- (0,4) --cycle ;
                \begin{scope}[xshift=5cm]
                \fill[red!40](0,0) rectangle (4,4);
                \draw[start segment=\rpos,blue,ultra thick] (0,0) -- (4,0) -- (4,4) -- (0,4);
                \end{scope}
            \end{tikzpicture} 
 } 
\end{document}

enter image description here

This also works properly for \foreach \rpos in {0,.02,...,1,0.9999}{%.

And yes, the path is really closed at the end.

enter image description here

EDITS: I streamlined the code and removed the \typeouts.

  • You are right for an increment of 0.03 since 0.03 x 33 +0.01 =1. And so the foreach loop never reaches 1. But the path does not close with an increment of 0.1 whereas 0.1 x 10 = 1 and the same, with 0.04 whereas 0.04 x 25 =1 – AndréC Aug 14 '18 at 07:31
  • @AndréC I have just tried out myself. The lower part of the code does close both for \foreach \rpos in {0,.1,...,1,0.9999}{% and \foreach \rpos in {0,.04,...,1,0.9999}{% on my TeXLive 2018 distribution. (The upper part can in principle removed.) –  Aug 14 '18 at 13:04
  • Indeed, I understood that the problem comes from the \foreach command and to get further explanations, I asked a question about it : https://tex.stackexchange.com/q/446008/138900 – AndréC Aug 14 '18 at 13:10
  • 1
    @AndréC Yes, but this is really just one out of two issues. Just compare \draw (0,0) -- (4,0) -- (4,4) -- (0,4) --cycle ; with \draw (0,0) -- (4,0) -- (4,4) -- (0,4) -- (0,0);. That is \pgfpathclose is important, too. –  Aug 14 '18 at 13:13
  • Yes, your solution is clever. Can you explain the \typeout{near\space end} command? – AndréC Aug 14 '18 at 13:25
  • @AndréC There is the possibility to find out whether or not the point is on the closing path segment, namely \pgfkeys{/pgf/decoration automaton/if input segment is closepath=. Of course, only closed paths have such a segment. Then I have to check whether or the point is (with some tolerance) coincident with the end point (which coincides with the start point for a closed path). This is what the check \ifdim\pgfdecorationsegmentlength>0.999pt does. If one has competed 99.9% of the path, then this routine will close the path. –  Aug 14 '18 at 13:29
  • I understood that, it's the command \typeout{} that I don't understand. – AndréC Aug 14 '18 at 13:32
  • 1
    @AndréC \typeout is irrelevant. It just prints something on your console. I used it for debugging in order to find out what's going on. That's why I wrote "If you tell me to get rid of the \typeouts I'll be happy to do so.", and the offer is still good. (Probably one should also drop the upper part of the answer.) –  Aug 14 '18 at 13:34
  • Don't delete the line \typeout{}, it allowed me to learn something new. Write a small comment in the code about it: its use, its role in debugging, etc.. – AndréC Aug 14 '18 at 18:53
  • @AndréC I don't know, that answer is there to be used by everyone, and others may get annoyed by that glibberish it is producing. You can put \typeout in most positions, for instance before or after \ifdim\pgfdecorationsegmentlength>0.999pt you could put \typeout{\the\pgfdecorationsegmentlength}. Notice that there is a \the because it is a dimension. Other useful debugging commands are \show and latexdef that is to be typed on the console. I am sure that there is a much better overview of these tricks on this site. –  Aug 14 '18 at 19:13
  • Why did you write \state{final} {\ifdim\pgfdecorationsegmentlength>0.999pt          \pgfkeys {/pgf/decoration automaton /if input segment is closepath=/pgf/close}\fi} and not \state{final} {\ifdim\pgfdecorationsegmentlength>0.999pt \pgfpathclose\fi}? – AndréC Aug 20 '18 at 08:25
  • @AndréC Because there is a difference between closed and unclosed paths.Compare \draw[ultra thick] (0,0) -- (2,0) -- (1,1) -- (0,0); and \draw[ultra thick] (0,0) -- (2,0) -- (1,1) -- cycle;. They are not the same –  Aug 20 '18 at 10:07
  • Okay, your code closes the path if and only if the entry path is closed. I tried to do otherwise and that does not close the way, do you know why? state{final}[if input segment is closepath=/pgf/close]{ } Otherwise, the manual says, "You can use this option to handle a closepath in some special way, for instance, switching to a new state in which \pgfpathclose is executed. "How do I switch to a new state with if input segment is closepath=? – AndréC Aug 20 '18 at 12:27
  • 1
    @AndréC First of all, I do not know. I would guess, however, that you could do in a state that is not the final state something like state{almost final}[next state=final,if input segment is closepath={next state=close path}]{ } or something along these lines. I would have to research this, but would be happy to do so if you ask me to and tell me what you want to achieve. An even better way would be to ask a new question since this is interesting and examples are still sparse here. –  Aug 20 '18 at 14:30