I have a list of (angles/value) pairs. I'd like to use TikZ to

  1. draw nodes with those values at these angles (easy),
  2. then draw an edge from node i to i+1, with the value of node i as a label (not at all easy).

My idea so far: store the values in pgfkeys, with keys the same as the node names. Then, values could be easily accessed. Problem is, I cannot get the pgfkeyssetvalue to evalute the node name first.

Example:

\documentclass[tikz]{standalone}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
  \draw [dotted] (0,0)  circle  (2cm);

  % draw nodes on a circle, remembering their value fails: 
  \foreach \angle/\value [count = \i] in {0/17, 45/22, 80/5, 140/12} {
    \node [circle, draw, fill=white] (p\i) at (\angle:2cm) {\value}; 
    \pgfkeyssetvalue{/nodevalues/p\i}{\value}
  }

  % to illustrate that it works with explicit node names: 
  \pgfkeyssetvalue{/nodevalues/p1}{17}
  \pgfkeyssetvalue{/nodevalues/p2}{22}

  % edges: 
  \foreach  [evaluate = {\j=int(mod(\i, 4)+1)}] \i in {1,...,4}  
      \draw [->] (p\i) to [bend right=45] node[midway] {\pgfkeysvalueof{/nodevalues/p\i}} (p\j) ; 
\end{tikzpicture}

\end{document}

This draws the edges for the manually set keys /nodevalues/p1 and /nodevalues/p2 correctly. But pgfkeyssetkeys does not do what I like. Expected result would be that 5->12 arc is label with 5, and 12->17 with 12.

Example ring with two missing labels

  • Are you by chance looking for an array? BTW, the node names can be numbers, so you could just give the nodes the same names as their contents. – marmot Aug 21 at 15:50
  • Numbers or p\i makes no difference in MWE. Node names are actually not explicitly used in the keys; could be arbitrary; just the same for convenience. Not sure what you have in mind with array? – Holger Karl Aug 21 at 15:52
  • I use an array in my answer below. BTW Welcome to TeX.SE! – marmot Aug 21 at 16:01
up vote 6 down vote accepted

Here is how I would do it: build up a list and reuse its entries.

\documentclass[tikz,border=3.14mm]{standalone}
\begin{document}
\begin{tikzpicture}
  \draw [dotted] (0,0)  circle  (2cm);
  % draw nodes on a circle, remembering their value no longer fails: 
  \foreach \angle/\value [count = \i] in {0/17, 45/22, 80/5, 140/12} {
    \node [circle, draw, fill=white] (p\i) at (\angle:2cm) {\value}; 
    \ifnum\i=1
    \xdef\Lst{\value}
    \else
    \xdef\Lst{\Lst,\value}
    \fi
  }
  % edges: 
  \foreach  [evaluate = {\j=int(mod(\i, 4)+1)}] \i in {1,...,4} 
    \pgfmathsetmacro{\X}{{\Lst}[\i-1]} 
    \typeout{\X}
      \draw [->] (p\i) to [bend right=45] node[midway,auto,swap] 
      {\X} (p\j) ; 
\end{tikzpicture}
\end{document}

enter image description here

EDIT: I think this is a nice question which also deserves an explanation why it fails. Essentially the loop does the keys locally, and you need to make them global. This globalization has been achieved by @percusse in this nice answer. Once one employs it, your original approach can also be made work.

\documentclass[tikz,border=3.14mm]{standalone}
\begin{document}
\begin{tikzpicture}
  \draw [dotted] (0,0)  circle  (2cm);

  % draw nodes on a circle, remembering their value no longer fails: 
  \foreach \angle/\value [count = \i] in {0/17, 45/22, 80/5, 140/12} {
    \node [circle, draw, fill=white] (p\i) at (\angle:2cm) {\value}; 
    \begingroup\globaldefs=1\relax
    \edef\temp{\noexpand\pgfkeyssetvalue{/nodevalues/p\i}{\value}}
    \temp
    \endgroup
  }

  % edges: 
  \foreach  [evaluate = {\j=int(mod(\i, 4)+1)}] \i in {1,...,4}  
      \draw [->] (p\i) to [bend right=45] node[midway] {\pgfkeysvalueof{/nodevalues/p\i}} (p\j) ; 
\end{tikzpicture}
\end{document}

enter image description here

Yet another possibility would be to employ Henri Menke's trick to remember node contents, I'd be happy to spell that out if you ask me to.

  • marmot: Your edited answer is enlightening, thanks. I had seen @percusse comment in the other thread, but hadn't understood its relevance here. A bit counterintuitive to have to use \noexpand to expand \i, though :-). The approach via macro seems quite heavy-weight? – Holger Karl Aug 22 at 6:30
  • @HolgerKarl This expansion thingy is something that one has sometimes to do in \foreach loops and more often in pgfplots. It ensures here that when \pgfkeyssetvalue gets "executed" \i and \value are already expanded. Whether or not this is "heavy weight" is a matter of taste. If you look into the codes in packages, you will often see tons of \expanafter and \noexpand statements. But yes, it is "nicer" if one can do without those. ;-) – marmot Aug 22 at 10:11

Just for fun, a single \foreach that does what you want.

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

\usepackage{tikz}

\begin{document}

    \begin{tikzpicture}
        \draw [dotted] (0,0)  circle  (2cm);

        \foreach \angle/\value [count = \i, count=\j from 0, remember=\value as \oldvalue] in {0/17, 45/22, 80/5, 140/12} {
            \node [circle, draw, fill=white] (p\i) at (\angle:2cm) {\value};
            \ifnum\j>0
                \draw[->] (p\j) to [bend right=45] node[midway,auto,swap] {\oldvalue} (p\i);
            \fi
            \ifnum\i=4
                \draw[->] (p\i) to [bend right=45] node[midway,auto,swap] {\value} (p1);
            \fi
        }
    \end{tikzpicture}

\end{document}

enter image description here

  • 1
    Oops, I spent the evening looking to do like you and you were faster. I had not thought at all to use two different counts, one starting at 0, the other at 1, your solution is more beautiful than mine :) – AndréC Aug 21 at 21:16
  • 1
    @AndréC Thanks! I think your remember=\i as \lasti (initially 4) is also very clever, saves you to do the int(mod(...)) stuff. I will try to remember that :) – Max Aug 22 at 6:19
  • 1
    Unfortunately, a single loop does not work for me - I am drawing the base graph and then different annotations on top of it (not shown in my question). But yes, that is also nice with the \lasti - didnt know about that either :-) – Holger Karl Aug 22 at 6:26

It is possible to solve your problem without using pgf keys but only using the native capabilities of \foreach operation.

Here, I used a first loop to draw the nodes and a second to create the arrows between the nodes with the edge operation

\documentclass[tikz]{standalone}

\begin{document}
\begin{tikzpicture}
  \draw [dotted] (0,0)  circle  (2cm);

  % create the node
  \foreach \angle/\value [count=\i,remember=\angle as \lastangle (initially 140),remember=\i as \lasti (initially 4)] in {0/17, 45/22, 80/5, 140/12} {
     \node (\i) at (\angle:2cm) {\value};
  }
  % draw arrow with edge operation
  \foreach \value [count=\i,remember =\i as \lasti (initially 4),remember=\value as \lvalue (initially 12)] in {17,22,5,12}{
  \path[->](\lasti) edge[bend right=45]  node[midway,auto,swap]{\lvalue}(\i);
  }
\end{tikzpicture}

\end{document}

noeuds

  • 1
    See above; answer to @Max – Holger Karl Aug 22 at 6:26

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.