6

I am trying to draw a tree using tikzpicture like this:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
    \tikzstyle{node}=[circle, fill=blue!25, minimum size=0.1 cm];
    \foreach \ilayer in {0,...,3} {
        \tikzmath {\nnodes = 3 ^ \ilayer; }
        \tikzmath {\leftnum = 1 - floor(\nnodes / 2) - 1; }
        \tikzmath {\rightnum = \nnodes - floor(\nnodes / 2) - 1; }
        \foreach \isibling in {\leftnum,...,\rightnum} {                
            \tikzmath {\d = 3 ^ (- \ilayer) * 15; }
            \tikzmath {\x = \isibling * \d; }
            \tikzmath {\y = - \ilayer * 2; }
            \node[node] (node_\ilayer_\isibling) at (\x cm, \y cm) {\isibling};
        }
    }
\end{tikzpicture}
\end{document}

I get the tree like the following picture. The texts are nodes' \isibling within each layer. Most nodes are integers, but all leftmost nodes are not.

enter image description here

  • 1
    Welcome to TeX.SX. When you post a question, please provide a "Minimal Working Example" (MWE) that starts with \documentclass, includes all relevant \usepackage commands, ends with \end{document} and compiles without errors, even if it does not produce your desired output. – Sandy G Mar 27 '19 at 14:32
9

You could just tell TikZ explicitly that you want an integer.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
    \tikzset{mynode/.style={circle, fill=blue!25, minimum size=0.1 cm}}
    \foreach \ilayer in {0,...,3} {
        \tikzmath {\nnodes = 3 ^ \ilayer; }
        \tikzmath {\leftnum = int(1 - floor(\nnodes / 2) - 1); }
        \tikzmath {\rightnum = \nnodes - floor(\nnodes / 2) - 1; }
        \foreach \isibling in {\leftnum,...,\rightnum} {                
            \tikzmath {\d = 3 ^ (- \ilayer) * 15; }
            \tikzmath {\x = \isibling * \d; }
            \tikzmath {\y = - \ilayer * 2; }
            \node[mynode] (node_\ilayer_\isibling) at (\x cm, \y cm) {\isibling};
        }
    }
\end{tikzpicture}
\end{document}

enter image description here

Or

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
    \tikzset{mynode/.style={circle, fill=blue!25, minimum size=0.1 cm}}
    \foreach \ilayer in {0,...,3} {
        \tikzmath {int \nnodes,\leftnum,\rightnum;
            \nnodes = 3 ^ \ilayer;
            \leftnum = 1 - floor(\nnodes / 2) - 1; 
        \rightnum = \nnodes - floor(\nnodes / 2) - 1; }
        \foreach \isibling in {\leftnum,...,\rightnum} {                
            \tikzmath {\d = 3 ^ (- \ilayer) * 15; 
            \x = \isibling * \d; 
            \y = - \ilayer * 2; }
            \node[mynode] (node_\ilayer_\isibling) at (\x cm, \y cm) {\isibling};
        }
    }
\end{tikzpicture}
\end{document}

In principle you do not need the math library here.

\documentclass[tikz,border=3.14mm]{standalone}
\begin{document}
\begin{tikzpicture}
    \tikzset{mynode/.style={circle, fill=blue!25, minimum size=0.1 cm}}
    \foreach \ilayer [evaluate=\ilayer as \nnodes using {int(3 ^ \ilayer)},
    evaluate=\nnodes as \leftnum using {int(1 - floor(\nnodes / 2) - 1)},
    evaluate=\nnodes as \rightnum using {int(\nnodes - floor(\nnodes / 2) - 1)}]
    in {0,...,3} {
        \foreach \isibling 
        [evaluate=\ilayer as \d using {3 ^ (- \ilayer) * 15},
        evaluate=\isibling as \x using {\isibling * \d},
        evaluate=\ilayer as \y using {- \ilayer * 2}]
        in {\leftnum,...,\rightnum} {                
            \node[mynode] (node_\ilayer_\isibling) at (\x cm, \y cm) 
            {\isibling};
        }
    }
\end{tikzpicture}
\end{document}

enter image description here

  • Thanks a lot. I finally get where the problem starts. Why 1 - floor(\nnodes / 2) - 1 can be non-integer? Even 1 - int(\nnodes / 2) - 1 is problematic. – landings Mar 27 '19 at 4:27
  • 1
    @landings It is due to the way \foreach is implemented, internally TikZ computes with dimensions and this can lead to slight inconsistencies. So it is better to wrap the full expression into int. – user121799 Mar 27 '19 at 4:33
7

As @marmot said you do not need tikzmath here, but if you use it you can do it in more efficient way :

  • You can have a single \tikzmath command with loops inside it.
  • You can declare your integer variables as int so you do not need to do int() afterward.
  • As \nnodes is odd you do not need separate \rightnum and \leftnum as \rightnum = - \leftnum;
  • Why you use 1-floor(\nnodes/2)-1 in place of -floor(\nnodes/2) ?
  • The value \d can be calculated in the outer loop.
  • Instead of using \x=\isibling*\d you can say [x=\d cm] and then use \isibling as\x. And in the same way \y can be replaced by \ilayer using [y=-2cm].

So here is my proposal :

\documentclass[tikz,border=7pt]{standalone}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
  \tikzstyle{node}=[circle, fill=blue!25, minimum size=0.1 cm];
  \tikzmath{
    int \ilayer,\nnodes,\rightnum,\isibling;
    \nnodes = 1;
    for \ilayer in {0,...,3}{
      \rightnum = (\nnodes-1)/2;
      \d = 15/\nnodes;
      for \isibling in {-\rightnum,...,\rightnum}{
        {
          \path[x=\d cm,y=-2cm]
            node[node] (node_\ilayer_\isibling) at (\isibling, \ilayer) {\isibling};
        };
      };
      \nnodes = 3*\nnodes;
    };
  }
\end{tikzpicture}
\end{document}

enter image description here

  • 1
    Thanks for your help. I am new to LaTeX and need more practice. I didn't know I could loop and draw things just inside tikzmath code. – landings Mar 27 '19 at 8:03
  • 2
    Don't worry, even some experts don't know how to use \tikzmath ;) – Kpym Mar 27 '19 at 9:34

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.