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}
10
  • 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? Oct 6, 2022 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 6, 2022 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 6, 2022 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. Oct 6, 2022 at 10:09
  • @J.Schmidt No need to put {} around mod/Mod. Oct 6, 2022 at 10:19

1 Answer 1

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

3
  • 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 6, 2022 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 6, 2022 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. Oct 6, 2022 at 11:29

You must log in to answer this question.

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