TransWikia.com

Modular arithmetic on node names in TikZ?

TeX - LaTeX Asked by Leah Wrenn Berman on June 11, 2021

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[->] (vi) -- (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}{ (vi) -- (vj)} 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

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

Correct answer by Christian on June 11, 2021

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{...}letjpgfmathresult), 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] (vi) at (pgfmathresult:2) {i} ; % Polar coordinates
    }

    foreach i in {0,1,...,5} {
      pgfmathparse{mod(i+2,6)}
      draw[->] (vi) -- (vpgfmathresult) ;
    }
  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.

Answered by Antal Spector-Zabusky on June 11, 2021

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 (vi) at (0,2) {i};
  end{scope}
}
foreach i in {0,...,8}{
  pgfmathparse{int(Mod(i + 2,9))}
  edefj{pgfmathresult}
  draw[->] (vi) -- (vj);
}
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 edefj{pgfmathresult} stored that (the int is to convert the result of Mod - a decimal - to an integer).

Answered by Andrew Stacey on June 11, 2021

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(vi){vi};
  }

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

Answered by Phenwoods on June 11, 2021

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 (vi) at (i*36:2) {i};

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

enter image description here

Answered by Ignasi on June 11, 2021

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};
     }
  }
   pgfmathsetmacrol{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

Answered by PatrickT on June 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP