Graphics, Figures & Tablesforeach loop vs. no foreach loop

Information and discussion about graphics, figures & tables in LaTeX documents.
Post Reply
topsquark
Posts: 71
Joined: Wed Oct 05, 2022 10:30 pm

foreach loop vs. no foreach loop

Post by topsquark »

I'm flustered: I've got to be missing something simple.

I've programmed a little code that will take a regular polygon and rotates it a bit smaller, then rotates it a bit smaller, etc. I'm sure you've seen something like it on the net. (You'll see what it is supposed to look like below.)

But something odd is happening. When I create the polygons (hexagons in this case) doing it case by case, it works just like it's supposed to:

Code: Select all

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfmath}
\usetikzlibrary{intersections}

\begin{document}

\newcommand\nextr[3]{
     \path[name path=outer] (#3:#2) -- ({#3+360/\n}:#2);
     \pgfmathsetmacro{\ang}{#1*5}
     \path[name path=radial] (O) -- (#3:#2);
     \path[name intersections={of=outer and radial,by=a}];
     \path[transform canvas] (a);
     \pgfgetlastxy{\xcoord}{\ycoord}
     \pgfmathsetmacro{\Xcoord}{scalar{\xcoord}/28.452756}
     \pgfmathsetmacro{\Ycoord}{scalar{\ycoord}/28.452756}
     \def\x{\Xcoord}
     \def\y{\Ycoord} 
     \pgfmathsetmacro{\r}{sqrt((\x)^2+(\y)^2)}
}

\def\n{6}
\def\r{8}
\def\ang{0}

\begin{tikzpicture}
     \coordinate (O) at (0,0);

     \draw (0:\r) foreach \t in {1,...,\n} { -- ({\t*(360/\n)}:\r) };

     \def\s{1}
     \nextr{1}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

     \def\s{2}
     \nextr{2}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

     \def\s{3}
     \nextr{3}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

     \def\s{4}
     \nextr{4}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

     \def\s{5}
     \nextr{5}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

     \def\s{6}
     \nextr{6}{\r}{\ang};
     \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };

\end{tikzpicture}

\end{document}
Now, if I shorten this up by using a foreach loop:

Code: Select all

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfmath}
\usetikzlibrary{intersections}

\begin{document}

\newcommand\nextr[3]{
     \path[name path=outer] (#3:#2) -- ({#3+360/\n}:#2);
     \pgfmathsetmacro{\ang}{#1*5}
     \path[name path=radial] (O) -- (#3:#2);
     \path[name intersections={of=outer and radial,by=a}];
     \path[transform canvas] (a);
     \pgfgetlastxy{\xcoord}{\ycoord}
     \pgfmathsetmacro{\Xcoord}{scalar{\xcoord}/28.452756}
     \pgfmathsetmacro{\Ycoord}{scalar{\ycoord}/28.452756}
     \def\x{\Xcoord}
     \def\y{\Ycoord} 
     \pgfmathsetmacro{\r}{sqrt((\x)^2+(\y)^2)}
}

\def\n{6}
\def\r{8}
\def\ang{0}

\begin{tikzpicture}
     \coordinate (O) at (0,0);

     \draw (0:\r) foreach \t in {1,...,\n} { -- ({\t*(360/\n)}:\r) };

     \foreach \s in {1,...,5}{
          \nextr{\s}{\r}{\ang};
          \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };
     };
\end{tikzpicture}

\end{document}
A similar result, but the radius isn't shortening up like it did in the first code.

What gives? (This reminds me of the time I programmed a timer in machine code for 10 seconds... It was running too slow and I couldn't figure it out. Finally, someone pointed out that I had programmed it to count to 10 instead of to A.)

Thanks!

-Dan

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

rais
Posts: 419
Joined: Sun Nov 16, 2014 8:51 pm

foreach loop vs. no foreach loop

Post by rais »

Hi Dan,
that's because the loop's, hmm, body is executed in a TeX group, so when \s is incremented, \ang and \r revert to their contents they had before the inner \foreach started (r=8, ang=0).

Code: Select all

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfmath}
\usetikzlibrary{intersections}

\begin{document}

\newcommand\nextr[3]{
     \path[name path=outer] (#3:#2) -- ({#3+360/\n}:#2);
     \pgfmathsetmacro{\ang}{#1*5}
     \global\let\ang=\ang
     \path[name path=radial] (O) -- (#3:#2);
     \path[name intersections={of=outer and radial,by=a}];
     \path[transform canvas] (a);
     \pgfgetlastxy{\xcoord}{\ycoord}
     \pgfmathsetmacro{\Xcoord}{scalar{\xcoord}/28.452756}
     \pgfmathsetmacro{\Ycoord}{scalar{\ycoord}/28.452756}
     \def\x{\Xcoord}
     \def\y{\Ycoord} 
     \pgfmathsetmacro{\r}{sqrt((\x)^2+(\y)^2)}
     \global\let\r=\r
}

\def\n{6}
\def\r{8}
\def\ang{0}

\begin{tikzpicture}
     \coordinate (O) at (0,0);

     \draw (0:\r) foreach \t in {1,...,\n} { -- ({\t*(360/\n)}:\r) };

     \foreach \s in {1,...,5}{
          \nextr{\s}{\r}{\ang}%;
          \draw (\ang:\r) foreach \t in {1,...,\n} { -- ({\ang+\t*(360/\n)}:\r) };
     }%;
\end{tikzpicture}

\end{document}
globalizes the definitions of \ang and \r to have them keep their new values after said TeX group closes.

KR
Rainer
topsquark
Posts: 71
Joined: Wed Oct 05, 2022 10:30 pm

foreach loop vs. no foreach loop

Post by topsquark »

Once it's explained, that makes a lot of sense, and I should have foreseen the problem.

I'm interested in the \let statement... In order to redefine a variable value I've been writing
\pgfmathsetmacro{\temp}{\r}
\gdef\r{\temp}

I can do that simply with
\global\let\r=\r
without any iterative definition problems?

Thanks!

-Dan
rais
Posts: 419
Joined: Sun Nov 16, 2014 8:51 pm

foreach loop vs. no foreach loop

Post by rais »

topsquark wrote: I can do that simply with
\global\let\r=\r
without any iterative definition problems?
Yes. Unlike \def (or \[re]newcommand), \let creates a copy of what's on the right side of the (optional) equal sign.
Come to think of it, you might as well use

Code: Select all

\xdef\r{\r}
which expands {\r} before it gets globally assigned to \r.

KR
Rainer
topsquark
Posts: 71
Joined: Wed Oct 05, 2022 10:30 pm

foreach loop vs. no foreach loop

Post by topsquark »

rais wrote:
topsquark wrote: I can do that simply with
\global\let\r=\r
without any iterative definition problems?
Yes. Unlike \def (or \[re]newcommand), \let creates a copy of what's on the right side of the (optional) equal sign.
Come to think of it, you might as well use

Code: Select all

\xdef\r{\r}
which expands {\r} before it gets globally assigned to \r.

KR
Rainer
Thanks. That'll make some of my coding cleaner. :). (I never really learned how to flowchart, so it's all kind of stream of consciousness.)

-Dan
Post Reply