23

Say I have a bunch of nodes I've already created in TikZ---maybe v0, v1, ..., vn-1. I'd like to connect certain of these nodes in a systematic way, by doing arithmetic on the indices, ideally mod n. For example, something like

\foreach \i \in {0,1,...,n-1} {\draw[->] (v\i) -- (v\{i+2})} But that doesn't work, and I can't seem to figure out the right syntax. And when I try to do complicated things with \pgfmathmod to do do the modular arithmetic on the node names, instead of the arrows connecting nicely between the nodes, I get the arrow connecting across the node to an inappropriate edge.

When I try something like \foreach \i / \j in {0 / 2, ..., n-2 / n}{ (v\i) -- (v\j)} I get a strange error message "no node named v0" (or something like that).

Any suggestions? This seems like a rather obvious thing to want to be able to do.

6 Answers 6

30

The let command is useful here and allows for "inline computation":

\begin{tikzpicture}
  \foreach \i in {0,...,9} \draw (\i*36:2cm) node(\i){\i};
  \foreach \i in {0,...,9} \draw let \n1={int(mod(\i+2,10))} in (\i) -- (\n1);
\end{tikzpicture}

the result

4
  • 4
    Very nice! I wasn't aware of the let command. I now need to go back and rewrite all my horrible code to use that! Oct 18, 2010 at 18:46
  • That's beautiful. Thank you very much---this was making me totally crazy. Only, when I try to compile it, first I got an error saying I needed to include \usetikzlibrary{calc}. Ok, so I did that. Now I'm getting an error saying that ` ! Package PGF Math Error: Unknown function `int'. Oct 18, 2010 at 19:54
  • Ok, I don't know why int doesn't work: I thought it was because I didn't have PGF 2.0 installed, but that appears not to be the case. If I replace it with floor (to try to force an int), the arrows don't connect nicely to the nodes, but rather connect to the theta=0 part of a circle, which looks ugly and causes arrows to cross over the nodes. Oct 20, 2010 at 17:59
  • 2
    That's because floor leads to <node number>.0 instead <node number>. Adding ".0" means the same as connect to "<node number>.E". It is an anchor and every number between 0 and 360 leads to an obvious result.
    – Christian
    Oct 20, 2010 at 18:11
6

If int is not working, it may be because you need to upgrade your packages.

However, a solution that does not require int is to use the \pgfmathtruncatemacro macro. For example,

\begin{tikzpicture}
  \foreach \i in {0,...,9} 
  {
      \draw (\i*36:2cm) node(v\i){v\i};
  }

\foreach \i in {0,...,9} 
{
    \pgfmathtruncatemacro{\j}{mod(\i+2,10)} 
    \draw [->] (v\i) -- (v\j);
}
\end{tikzpicture} 
5

You can use the \pgfmathparse macro to do this. It's documented in the (amazing) TikZ and PGF manual, in Part VII, but all you need to know right now is this: \pgfmathparse{...} evaluates the mathematical expression that is ... and stores the result in \pgfmathresult. The ... should look something like 3 + 4 * 5 - sin(6). I don't know that there's a better syntax for this, but if you use it to effectively declare variable up front (e.g., \pgfmathparse{...}\let\j\pgfmathresult), it's not bad.

Before I get to sample code, two other remarks. First, the reason the second case fails is that, as far as I understand, you can only use the ... in the one-variable case. Secondly, the first case—with \{i+2}—is sufficiently wrong that I feel it's worth pointing it out. On the one hand, \i is is a macro which expands to some value: first 0, then 1, and so on. On the other hand, i is a character which, when typeset, produces "i". There is no connection between the two. Thus, when you write \{i+2}, you're looking at five tokens: \{, i, +, 2, and }. The first one represents {, the second three represent themselves, and the last one will try to end the group after \foreach.

Anyway, using \pgfmathparse, we can get something like this:

\documentclass{article}

\usepackage{tikz}

\begin{document}
  \begin{center}\begin{tikzpicture}
    \foreach \i in {0,1,...,5} {
      \pgfmathparse{60*\i}
      \node[draw] (v\i) at (\pgfmathresult:2) {\i} ; % Polar coordinates
    }

    \foreach \i in {0,1,...,5} {
      \pgfmathparse{mod(\i+2,6)}
      \draw[->] (v\i) -- (v\pgfmathresult) ;
    }
  \end{tikzpicture}\end{center}
\end{document}

The first \foreach writes out the nodes in polar coordinates (which are (angle:radius)); the second one connects them as you desired.

3
  • Snap! (And I keep forgetting about polar coordinates - much better than using scope environments to rotate the picture) Oct 18, 2010 at 18:21
  • Yeah, I knew the scary bracing in v{\i+2} was bad, but I couldn't figure out how to fix the obviously bad v\i+2, either. This is very helpful---thanks! Oct 18, 2010 at 19:54
  • 1
    FYI, if you try this, it gives the arrows to the right vertices, but the placement of the arrows is ugly (some arrows cross nodes, they're connecting to the edge of the node, etc.). I'd actually gotten something roughly like this, but figured I was doing something wrong because the output wasn't looking right. Oct 18, 2010 at 20:02
1

I suspect that the problem actually lies with the ... bit in the \foreach list definition. I guess it's a bit tricky to work out how to fill in the dots on a double list. One could argue that this case is simple enough that it ought to "just work", but I guess that it would very quickly get complicated.

Anyway, why is perhaps less important than how to achieve the same result. I've been doing something a bit like this myself recently and here's how I did it:

\documentclass{article}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
\foreach \i in {0,...,8} {
  \begin{scope}[rotate=\i * 40]
  \node (v\i) at (0,2) {\i};
  \end{scope}
}
\foreach \i in {0,...,8}{
  \pgfmathparse{int(Mod(\i + 2,9))}
  \edef\j{\pgfmathresult}
  \draw[->] (v\i) -- (v\j);
}
\end{tikzpicture}
\end{document}

which produces:

alt text

One important point: I found that \pgfmathsetmacro{\j}{int(Mod(\i + 2, 9))} would set \j to, say, 1.0 which wasn't what was wanted. However, using int meant that \pgfmathresult correctly stored the integer so a simple \edef\j{\pgfmathresult} stored that (the int is to convert the result of Mod - a decimal - to an integer).

1

Another option could be translating arithmetic to foreach header with evaluate option:

evaluate=<variable> as <macro> using <formula>

<variable> represents the variable to be evaluated, <macro> where to store the result if <variable> is not used, and the <formula> to be evaluated which must contain some reference to <variable>.

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

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
\foreach \i in {0,...,9} 
  \node (v\i) at (\i*36:2) {\i};

\foreach \i [evaluate=\i as \j using {int(mod(\i+2,10))}]in {0,...,9}
  \draw[->] (v\i) -- (v\j);
\end{tikzpicture}
\end{document}

enter image description here

1

This question has some great answers and I realize that my contribution is redundant, but I was looking for a way to make a modular arithmetic wheel and didn't find it right away, so I put together the wheel below based on related contributions found here and there. I'm posting here because the OP has "modular arithmetic" in the title.

The command \wheel{<modulus>}{<layers>} takes two arguments: the modulus and the number of layers of numbers to show on the wheel.

\documentclass[border=3mm,class=article,tikz]{standalone}
\usetikzlibrary{calc}

\newcommand*{\wheel}[2]{
  \pgfmathtruncatemacro{\modulus}{#1}%
  \pgfmathtruncatemacro{\modulo}{\modulus-1}%
  \pgfmathtruncatemacro{\layers}{#2}%
  \pgfmathtruncatemacro{\arc}{360}%
  \pgfmathsetmacro{\height}{0.5}%
  \pgfmathsetmacro{\inner}{1.25}%
  \pgfmathsetmacro{\outer}{\inner+\layers*\height}%

  \foreach \x [count=\i] in {0,...,\modulo} {%
     \pgfmathsetmacro{\angle}{90+\arc/\modulus*(1-\i)}%
     \draw [black,thick] (\angle-\arc/\modulus/2:\inner) -- (\angle-\arc/\modulus/2:\outer);
     \foreach \y in {1,...,\layers} {
        \pgfmathsetmacro{\radius}{\inner-1/4+\y/2}
        \pgfmathtruncatemacro{\value}{\x+(\y-1)*\modulus}
        \node[rotate=-90+\angle] at (\angle:\radius) {\value};
     }
  }
   \pgfmathsetmacro\l{\layers+1}
   \foreach \r in {1,...,\l}  {%
       \draw [gray] (0,0) circle (\inner-1/2+\r/2);
  }
}

\begin{document}

\begin{tikzpicture}[scale=2]
\wheel{12}{5}
\end{tikzpicture}

\begin{tikzpicture}[scale=2]
\wheel{7}{5}
\end{tikzpicture}

\end{document}

enter image description here

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .