3

How would I go about in tikz to simply draw a cycle graph using a for loop? I have this at the moment, which doesn't work due to the addition not working as expected, but instead being registered as node '0+1' for example (instead of simply node '1').

\begin{tikzpicture}
%counterclockwise order starting top right
\node[draw=none,minimum size=3.15cm,regular polygon,regular polygon sides=6] (a) {};
\node (0) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 1) {};
\node (1) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 2) {};
\node (2) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 3) {};
\node (3) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 4) {};
\node (4) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 5) {};
\node (5) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 6) {};

\foreach \x in {0,1,...,5} { \draw (\x) to (\x+1); } \end{tikzpicture}

Also, while I'm here, is there a way to avoid copy/pasting 6 times the node creation lines? Maybe there's a one-liner in tikz which creates a cycle of given length directly, but I'm also asking in case I'd like to create something more complex for which a one-liner might not exist.

EDIT: I'm trying to make the following work right now (using mod as well), but I get an Arithmetic overflow, how to solve?

\begin{tikzpicture}
%counterclockwise order starting top right
\node[draw=none,minimum size=3.15cm,regular polygon,regular polygon sides=6] (a) {};
\node (0) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 1) {};
\node (1) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 2) {};
\node (2) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 3) {};
\node (3) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 4) {};
\node (4) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 5) {};
\node (5) [inner sep=2pt, circle, fill, draw=black] at  (a.corner 6) {};

\foreach \x in {0,...,5} { \def\y{Mod(\x+1,6)} \draw (\x) to (\y); } \end{tikzpicture}

  • Basically the same thing as Q251424: TikZ does not “calculate” a node name. Use (node cs: name/.evaluated={int(\x+1)}) or something with mod since you want 5 be connected with 0. But yes, there's a much better approach for this. What's your goal? Just some nodes placed in a circle and connected? – Qrrbrbirlbel Oct 06 '22 at 10:03
  • @Qrrbrbirlbel Instead of using the (node cs: name/.evaluated=...) syntax you can also use \the\numexpr inside the coordinate name. So the following works as well: \draw (\x) to (\the\numexpr\x+1\relax); – Skillmon Oct 06 '22 at 10:08
  • Thanks for the solution, and the mod suggestion as well. I've \draw (\x) to (node cs: name/.evaluated={int({Mod(\x+1,6)})}); at the moment, but for some reason, they're getting recognized as '1.0' instead of '1' for example, even though there's an int(...), any ideas? – J. Schmidt Oct 06 '22 at 10:09
  • @Skillmon Yes, that's the lower level eTeX version of it which gets a bit more complicated if we want to use the mod of something. – Qrrbrbirlbel Oct 06 '22 at 10:09
  • @J.Schmidt No need to put {} around mod/Mod. – Qrrbrbirlbel Oct 06 '22 at 10:19
  • @Qrrbrbirlbel, I've edited my post, but get a new error, any ideas? – J. Schmidt Oct 06 '22 at 10:26
  • @Qrrbrbirlbel no real need to use mod(), it's just a single value that needs special handling, so \ifnum\x=5 0\else\the\numexpr\x+1\relax would suffice (I tend to not know the TikZ niceties, so I stick to the eTeX things :)) – Skillmon Oct 06 '22 at 10:50
  • @Skillmon Sure, but I don't want to confuse new people here with all the pitfalls of \ifnum or similar things if they can't input valid TikZ in the first place and rather teach the solutions that TikZ brings with it and can be applied to much more complex problems. (I tend to now know the niceties of many LaTeX packages and fall back to the most primitive solutions, too. After all, there's probably a simple package where one could just write \eval{\x+1}.) – Qrrbrbirlbel Oct 06 '22 at 11:24
  • @Qrrbrbirlbel \inteval{\x+1} is built into LaTeX nowadays :P Don't understand me wrong, I like seeing your TikZ solutions (and one of the upvotes of your answer was me). – Skillmon Oct 06 '22 at 12:05
  • @Skillmon Don't you scare me with that L3 stuff! :D (Yeah, I didn't know that that exists. That would be even better.) – Qrrbrbirlbel Oct 06 '22 at 13:04

1 Answers1

9

TikZ does not evaluate a node name like it does with coordinates. You're going to have to calculate it before hand and since you're using \foreach you can do it right there with the evaluate key.

\foreach \x[evaluate={\nextX=int(mod(\x+1,6);}] in {0,1,...,5}
  \draw (\x) to (\nextX);

This is not much different than writing \pgfmathsetmacro\nextX{int(mod(\x+1,6))} before the \draw.

You can also do it inline with the .evaluated handler and the explicit version of the node coordinate system

\foreach \x in {0,1,...,5}
  \draw (\x) to (node cs: name/.evaluated={int(mod(\x+1,6))});

The .evaluated handler evalutes the given value with \pgfmathparse and forwards its result to the key. Of course, this is a bit cumbersome.

As noted in the comments by Skillmon, in this rather simple case we can also use eTeX and the primitive \ifnum to calculate the number of the next node:

 \draw (\x) to (\ifnum\x=5 0\else\the\numexpr\x+1\relax\fi);

Both the space and the \relax is there to explicitly delimit the expressions.

You can of course define a macro for that if you need that often, say

 \newcommand*{\nextOutOf}[2]{% next #1 out of #2
   \ifnum#1=#2 0\else\the\numexpr#1+1\relax\fi}

and use it as

 \draw (\x) to (\nextOutOf{\x}{5});

which would then be a sans-PGFmath version of int(mod(\x,5)). (It will also be faster since it doesn't use PGFmath at all.)


That said, there are dozens of ways to place dots around a center and connect them.

You can just place the dots in a loop with polar coordinates:

\foreach \x in {0,...,5}
  \node (\x) [inner sep=2pt, circle, fill, draw=black] at (\x*60+60:1.5cm) {};

You can use the chains library with

\tikzset{start chain=circle placed {at=(\tikzchaincount*60:1.5cm)}}
\foreach \x in {0,...,5}
  \node (\x) [on chain=circle, inner sep=2pt, circle, fill, draw=black] {};

There are also the possibility to automatically have the dots connected by using the various join options that the chains library provides.


Though, maybe the graphs/graphs.standard libraries can replace all that work for you:

\graph[
  nodes={fill, draw, circle, inner sep=2pt},
  radius=1.5cm, typeset=, counterclockwise, phase=60
] {
  subgraph C_n[n=6]
};

In the TikZpicture with the regular polygon I've also replaced the placing of the dots at the corners of the polygon with a loop that similarly needs to evaluate the number of the anchor. It's a little bit more complicated to use the the node cs: with anchor/.evaluated concatenating corner with a number which is why I've used the low-level eTeX version with

\the\numexpr\x+1\relax

which evaluates \x + 1 and leaves the result cleanly for the TikZ parser.

(This is only needed because your nodes start at 0 and the corners start at 1.)

You can't use complicated math here (in fact, only integers will work and the basic arithmetics +, -, * and / as well as ( and )).

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{graphs.standard}% for \graph and subgraph C_n
\usetikzlibrary{shapes.geometric}% for regular polygon

\begin{document}

\begin{tikzpicture} \node[draw=none,minimum size=3cm,regular polygon,regular polygon sides=6] (a) {}; \foreach \x in {0,...,5} \node (\x) [inner sep=2pt, circle, fill, draw=black] at (a.corner \the\numexpr\x+1\relax) {};

\foreach \x[ evaluate={\nextX=int(mod(\x+1,6);} ] in {0,1,...,5} \draw (\x) to (\nextX); \end{tikzpicture}

\begin{tikzpicture} \foreach \x in {0,...,5} \node (\x) [inner sep=2pt, circle, fill, draw=black] at (\x*60+60:1.5cm) {};

\foreach \x in {0,1,...,5} \draw (\x) to (node cs: name/.evaluated={int(mod(\x+1,6))}); \end{tikzpicture}

\begin{tikzpicture} \graph[ nodes={fill, draw, circle, inner sep=2pt}, radius=1.5cm, typeset=, counterclockwise, phase=60 ] { subgraph C_n[n=6] };

% nodes are named 1, …, 6 % \draw (2) -- (4); \end{tikzpicture}

\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • A very complete answer. Would it be possible also to replace the [evaluate={\nextX=int(mod(\x+1,6);}] with a \def\nextX... inside the loop somehow, or have I misunderstood how \def works? – J. Schmidt Oct 06 '22 at 10:37
  • Also, the last example (using graph library) doesn't work for me, I just get one (or a bunch of very close?) node(s). – J. Schmidt Oct 06 '22 at 10:44
  • It looks like, yes, you misunderstood how \def works. \def is the TeX version of LaTeX's \newcommand and just defines \nextX as the literal “string” of whatever you put in the braces after it. It does not evaluate anything. Are you using an uptodate version of your TeX system? Did you try the full example that I posted? You need \usetikzlibrary{graphs.standard} for the subgraph C_n. Only \usetikzlibrary{graphs} will not suffice. – Qrrbrbirlbel Oct 06 '22 at 11:29