3

I'm trying to write a code that will draw a tree consisting of 2 children for each node up to a specified depth. The drawing of the tree itself works but applying the styles (which are numbered consecutively in the order the children are drawn) doesn't. I'm incrementing a counter for every child and if I manually write out the code of the tree, all is fine. However there seem to be some subtelties with the foreach operation that I don't understand. Here is an example not very "minimal", since I can't pinpoint the problem. The first bit works as expected, only the specified node is drawn in a different color. But if I request that child to be missing, then it's sibling also disappears. I don't know if it's related to the unexpected result of the third part of the example, which increments another counter only on the second execution of each of the nested foreach loops, yet the counter seems to step by 2, not 1. Any insight into this is much appreciated.

\documentclass{article}
\usepackage{tikz}
\parindent=0pt\relax

\begin{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcount\foo
\tikzset{
  level distance=2cm,
  level/.style={sibling distance=4cm/#1},
  inc foo/.code=\global\advance\foo by 1\relax,
  left/.style={label=left:\the\foo,inc foo},
  right/.style={label=right:\the\foo,inc foo},
  s0/.style=blue,
  s1/.style=blue,
  s2/.style=blue,
  s3/.style=blue,
  s4/.style=green,
  s5/.style=blue,
}

only node 5 should be green\par
\foo=0\relax
\begin{tikzpicture}[grow=up]
  \node {A} child[s\the\foo] foreach \x in {right,left} {
    node[\x] {} child[s\the\foo] foreach \y in {right,left} {node[\y] {}
    }
};
\end{tikzpicture}

\tikzset{s4/.style=missing}
only node 5 should be missing\par
\foo=0\relax
\begin{tikzpicture}[grow=up]
  \node {A} child[s\the\foo] foreach \x in {right,left} {
    node[\x] {} child[s\the\foo] foreach \y in {right,left} {node[\y] {}
    }
  };
\end{tikzpicture}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcount\foobar
\tikzset{
  s4/.style=missing,
  foo/.code args={#1}{\global\advance\foobar by #1\relax},
  left/.style={label={left:\the\foo, \the\foobar},inc foo},
  right/.style={label={right:\the\foo, \the\foobar},inc foo},
}

second number should go up by 0 on every right child and 1 on every left child\par
\foo=0\relax
\foobar=1\relax
\begin{tikzpicture}[grow=up]
  \node {A} child[foo=\n] foreach \x/\n in {right/0,left/1} {
    node[\x] {} child[foo=\m] foreach \y/\m in {right/0,left/1} {node[\y] {}
    }
  };
\end{tikzpicture}

\end{document}

@cfr suggested I use Forest: I will have a look at it. However, I'm not sure to what extent it is useful in my case or how long it would take me to rewrite my code using it. Since the actual example is way more complicated--I have 500 lines of code (almost done) that takes input of the form ::!::!...., and for each path specification of the form rrllrlrl (for move right/left) it sets the lable of the first edge to and the color of all edges to ; if a new path specification is encountered after !, then continue from where the previous path ended (applying label and color to the new path); if a new path is after a coma, then start at the root again. Furthermore, every time a change in direction is encountered, such as when I say rl or r::!l:..., then continue the previous path in the default color. ALSO, unless one of color or label is given, or the edge style has never been set (missing), do not override previous values. Finally, the last path specification (before a coma) should be continued until the top of the tree (i.e. the color should be applied until the end of the edge, on its corresponding side, left or right)... So for example saying rl:a:green!r:s:red,l:a:blue!r:c:red should give (but with the labels midway next to the edge, I just drew that "by hand" now quickly):

enter image description here

  • May I suggest that Forest would make this much easier? Dynamic tree creation is something of its speciality. – cfr Apr 6 '16 at 1:18
  • child[foo=\n] isn't within the scope of the foreach. – cfr Apr 6 '16 at 1:30
  • @cfr Thanks for the suggestion, I replied by editing my OP, since it's too long for a comment. As for the foo=\n, nice spotting it! But I'm not sure if it's related to the problem: i.e. why does setting one of the styles to green work, but setting it to missing, applies it to the sibling as well? – Aayla Secura Apr 6 '16 at 1:53
  • Now you've edited, Forest would definitely be easier! Maybe not easier from 500 lines of existing code, but certainly easier for that job. I don't know how missing is implemented. I just noticed the \n in the last case where you use it. – cfr Apr 6 '16 at 1:55
  • Taking the stepping of the two counters inside the relevant loops solves the problem with the last tree for me. – cfr Apr 6 '16 at 2:06
2

Not really a solution, but I found a workaround: redefine the missing style to simply not draw the edge and hide any text given to the node:

\tikzset{missing/.style={edge from parent macro=\missing, nodes={text opacity=0}}}
\def\missing#1#2{}

This works :)

1

For the last tree, ensuring that foo=\n and foo=\m within the scope of the relevant foreach gives the expected result:

\begin{tikzpicture}[grow=up]
  \node {A} child foreach \x/\n in {right/0,left/1} {
    node[\x, foo=\n] {} child foreach \y/\m in {right/0,left/1} {node[\y, foo=\m] {}
    }
  };
\end{tikzpicture}

modified loops

  • I see; so the counter is incremented, but the nodes, which display its value don't see the change? – Aayla Secura Apr 6 '16 at 2:30
  • I'm not sure it is incremented when foo=\n is outside the loop defining \n, for example. But I'm not sure quite what happens in that it clearly gets incremented by something at some point. I'm not altogether certain in what order exactly TikZ/PGF is parsing everything since the missing case is quite odd. (And you can get the number of children to 6 if you practise, without adding any more of them!) – cfr Apr 6 '16 at 2:36
  • But certainly moving foo=\n and foo=\m into the node options - i.e. within the relevant loops - seems to ensure that the counter is incremented and used in the right place. – cfr Apr 6 '16 at 2:37
  • Bottom of page 316 from the manual gives a similar (working) example: "writing \node {root} child[\pos] foreach \name/\pos in {1/left, 2/right} { node[\pos] {\name}}; the effect will be the same as: \node {root} child[left] {node[left] {1}} child[right] {node[right] {2}};" which implies that child does see the loop's variables. – Aayla Secura Apr 6 '16 at 2:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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