11

I want to build a tree which satisfies the following:

  1. It grows to the right.
  2. The first child grows at 0 degrees (subsequent children growing clockwise).
  3. All children nodes are in the same vertical, and the distance between consecutive nodes is always the same.

All in all, what I'd like to achieve is this:

enter image description here

Indeed, an easy way to code that is through

\documentclass{article}
\usepackage{tikz}
\tikzset{bcir/.style={circle,fill=black,
 minimum size=4pt,inner sep=0},every node/.style={bcir}}
\begin{document}
\begin{tikzpicture}[x=3cm,y=2mm]
\node (A) {};
\foreach \i in {0,...,8} \node at (1,-\i) {} edge (A);
\end{tikzpicture}
\end{document}

However, I tried an alternative approach, which I was expecting to produce the same results:

\documentclass{article}
\usepackage{tikz}
\usepackage{fp}
\usetikzlibrary{fixedpointarithmetic}
\tikzset{bcir/.style={circle,fill=black,
minimum size=4pt,inner sep=0},every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[fixed point arithmetic]  
\node {}
  child[grow=\g,level distance=\l cm] 
  foreach \i [evaluate={
     \k=tan(5)*\i;
     \g=-atan(\k);
     \l=3/cos(\g);
  }] in {0,...,13} {node {}};
\end{tikzpicture}  
\end{document}

Note that in the code above I have assumed that 1) the level distance of the first (horizontal) child is 3cm and 2) the second child grows at -5 degrees. With these two conditions and some basic trigonometry, one can calculate first the sibling distance between consecutive nodes and then the angle g and level distance l for any children node.

I believe my parametrization is correct; however some sibling angles/level distances are slightly off,

enter image description here

even though (following other answers) I used the fixed point arithmetic engine to try to improve the accuracy. Even replacing the trigonometric functions by the first terms of their Taylor series,

\begin{tikzpicture}[fixed point arithmetic]  
\node {}
  child[grow=\g,level distance=\l cm] 
  foreach \i [evaluate={
     \k=tan(5)*\i;
     \grad=\k-\k^3/3+\k^5/5-\k^7/7+\k^9/9;
     \g=-deg(\grad);
     \l=3/(1-\grad^2/2!+\grad^4/4!-\grad^6/6!);
  }] in {0,...,8} {node {}};
\end{tikzpicture}

the same problems are present (note that the number of nodes here has been chosen so as to ensure the series for atan does converge):

enter image description here

Is there something I could do to overcome these inaccuracy issues or is it an intrinsic TikZ problem that cannot be avoided? (assuming there is no flaw in my approach, as I think is the case).

  • I heard that Taylor series is not the best method to evaluate a function. By the way, since the math library is probably implemented by Taylor series, it cannot be called as an improvement, right? – Symbol 1 Apr 22 '15 at 9:08
  • Well, I don't know exactly how the math library is implemented (certainly not exclusively with the Taylor series, since then it couldn't e.g. calculate atan(x) for x>1). In any case, you are right in that it doesn't represent an improvement, I just thought it could perhaps be useful information for people thinking about the issue. Indeed if you could think of a better method I could use to evaluate the trigonometric functions, that would be great :) – summer Apr 22 '15 at 9:29
  • Your thinking is theoretically quite correct. However numerically, you cannot implement trig functions with taylor series because the convergence is very slow and the residuals are too big. That's why it is always a pain to implement them. TikZ for example, takes a float and approximates to the nearest angle atan() via a lookup table. We had a discussion about this before but I couldn't find it instead maybe this can offer some insight : stackoverflow.com/questions/23047978/how-is-arctan-implemented – percusse Apr 22 '15 at 12:38
13

I don't quite know why the maths seems to fail in this case (although I suppose I should), but it seems it is not a problem with the way TikZ/PGF does maths, it is something to do with way trees are constructed.

Unfortunately, I haven't been able work out what the problem is exactly, but the fact that the problem is not the accuracy of mathematical calculations per se, can be demonstrated by using the same calculations outside of a tree:

\documentclass[tikz, border=5]{standalone}
\tikzset{bcir/.style={circle,fill=black,
  minimum size=4pt,inner sep=0}, every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[every node/.style={bcir, anchor=center}]
\node {} child [grow=\g, level distance=\l cm] foreach \i [evaluate={%
  \k=tan(5)*\i; \g=-atan(\k); \l=3/cos(\g);}]
  in {0,...,13} { node {} };

\foreach \i [evaluate={%
  \k=tan(5)*\i; \g=-atan(\k); \l=3/cos(\g);}]
  in {0,...,13} { \draw [red] (0,0) -- (\g:\l) node [bcir,fill=red]{}; }
\end{tikzpicture}  
\end{document}

enter image description here

So there must be something else going on.

In any event I think you are making a lot of work out of something that could be done much more simply with a custom growth function.

The following shows an example of the grow via three points growth function from the trees library.

\documentclass[tikz, border=5]{standalone}
\usetikzlibrary{trees}
\tikzset{bcir/.style={circle,fill=black,
  minimum size=4pt,inner sep=0}, every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[grow via three points={%
  one child at (3,0) and two children at (3,0) and (3,-1/4)
}]
\node {} child foreach \i in {0,...,13} { node {} };
\end{tikzpicture}  
\end{document}

enter image description here

8

A PSTricks solution:

\documentclass{article}

\usepackage{multido}
\usepackage{pstricks}
\usepackage{xfp}

% parameters
\def\NoDots{9}
\def\Hori{4}
\def\Vert{3}

\begin{document}

\begin{pspicture}(\Hori,\Vert)
  \psdot(0,\Vert)
  \multido{\r = \Vert+-\fpeval{\Vert/(\NoDots-1)}}{\NoDots}{%
    \psline(0,\Vert)(\Hori,\r)
    \psdot(\Hori,\r)}
\end{pspicture}

\end{document}

output

All you have to do is change the values of the parameters (\NoDots, \Hori, and \Vert) and the drawing will be adjusted accordingly.

  • 4
    To the downvoter: It is generally accepted and encouraged to answer questions using a different approach or package than the one asked for in the question. The answers are for the benefit of the entire community (nay, the world!), not just the OP. Please see meta.tex.stackexchange.com/questions/3452/… for details. – Jake Apr 22 '15 at 12:06
  • @Jake probably because OP, and potential visitors, is questioning about the accuracy of arithmetics. I mean in the most extreme case, saying type coordinates by hands deserves downvotes. On the other hand, propose that pstricks do better math is what you say encouraged. And also, the calculation is simplified so I cannot see the real power of pstricks, too sad : | – Symbol 1 Apr 22 '15 at 15:35
  • @SvendTveskæg I have to say that I know nothing about PSTricks. I just want to figure out what OP is asking and how your answer answered the question. Is typing coordinates by hands a good answer concerning Jake's comment? If not, then what is a different approach or package? – Symbol 1 Apr 22 '15 at 16:03
  • 1
    @Symbol1: In any case, the downvoter should have left a comment in addition to downvoting, in order to give Svend a chance to respond or improve his answer. Now we're just poking around in the dark and have no idea why the answer was downvoted. – Jake Apr 22 '15 at 16:14
  • 1
    Upvoted to counteract downvote... – 1010011010 Apr 22 '15 at 18:43
5

Your problem made me curious. MetaPost also uses fixed-point arithmetic by default: a “numeric” can not be smaller than 2^(-16) nor greater than 2^12, although internally MetaPost can handle values up to 2^15. (Since quite recently it is possible to switch to several floating-point arithmetic modes, but I will not use them here.)

So I decided to make a MetaPost version of your second graph, with the same functions, and observe the alignment. (To be processed with LuaLaTeX.)

\documentclass[border=2mm]{standalone}
\usepackage{luamplib}
  \mplibsetformat{metafun}  
\begin{document}
  \begin{mplibcode}
    u := cm;
    def dot(expr c) = drawdot c withpen pencircle scaled 3bp enddef;
    beginfig(1);
      dot(origin); 
      for i = 0 upto 13:
        k := i*tand5; % or i*sind5/cosd5 with Plain MetaPost
        g := -atan k ; % or -angle(1, k) with Plain MetaPost; 
        l := 3u/cosd g;
        draw origin -- l*dir g;
        dot(l*dir g);
      endfor
    endfig;
  \end{mplibcode}
\end{document}

Well, as far as I can see they seem correctly aligned:

enter image description here

So in this case there seems to be a problem, not with fixed-point arithmetic by itself, but with the fixed point library of tikz (two few bits used to represent the numbers?). Or with the way the trigonometric functions are defined.

Anyway, if it is possible to switch to floating-point arithmetic for tikz (with LuaTeX maybe?), it would drastically improve accuracy.

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.