6

I would like to draw a fractal tree with Tikz lindenmayersystems library. Here is the code I use:

\documentclass[tikz]{standalone}
\usepackage{tikz}

\usetikzlibrary{lindenmayersystems}

\begin{document}
\begin{tikzpicture}
\pgfdeclarelindenmayersystem{starTree}{
  \rule{X -> F--FFFF-X+X+X+X+X-FFFF--F}
  \rule{F -> FF}
}

\draw [draw=blue!50!black]
  [l-system={starTree, step=10pt, right angle=40, left angle=60, axiom=X, order=2}]
  lindenmayer system;
\end{tikzpicture}
\end{document}

It works pretty well for small order value, as order=2: Result at order 2

But when I increase the order, an error become visible and my figure is not symmetric any more (compare horizontal lines at the top). Here is result for 4th order. Result at order 4

It seems that the program preserves the angles but make a small error when evaluating segment lengths. With increasing number of segments, the error become more and more visible.

Does anyone know how to avoid that issue?

Thanks!

Update : Complementary observation

Using only 45-degree angles in a similar figure, branches now are parallel lines and the problem does not show up (despite a greater number of segments than in previous case). Here is the code:

\begin{tikzpicture}
\pgfdeclarelindenmayersystem{starTree}{
  \rule{X -> F--FFFF-X+X+X+X+X+X+X-FFFF--F}
  \rule{F -> FF}
}

\draw [draw=blue!50!black]
  [l-system={starTree, step=10pt, right angle=45, left angle=45, axiom=X, order=4}]
  lindenmayer system;
\end{tikzpicture}

and its result: Result with 45-degree angles at 4th order

4

This is a classical case where the precision of TeX's arithmetics plays a role.

To be specific, maintains a transformation matrix which represents the current shifting/scaling/rotating. If the user demands a rotation of, say, 40 degrees, will first calculates the corresponding rotation matrix (the cos&-sin\\sin&cos one) and then multiply with the current one.

In your case, when you ask for a drawing of order 4, there will be 1250 rotations. Mathematically a lot of them cancel each other out, but numerically the error accumulates significantly. So we have improve the precision of doing rotation.

The good news is, since there are only shifting and rotating involve, we can maintain a global rotating angle. We redefine the low level command as follows.

\makeatletter
\def\pgf@pt@theta{0}  % setup global angle
\def\pgftransformrotate#1{
    % update global angle
    \pgfmathadd{\pgf@pt@theta}{#1}
    \xdef\pgf@pt@theta{\pgfmathresult}
    % calculate cosine
    \pgfmathcos{\pgf@pt@theta}
    \xdef\pgf@pt@aa{\pgfmathresult}
    \xdef\pgf@pt@bb{\pgfmathresult}
    % calculate sine
    \pgfmathsin{\pgf@pt@theta}
    \xdef\pgf@pt@ab{\pgfmathresult}
    \pgf@x=-\pgfmathresult pt%
    \xdef\pgf@pt@ba{\pgf@sys@tonumber{\pgf@x}}
}

Here \pgf@pt@aa and so on are the entries of the transformation matrix. What we do is simply update the global rotating angle and recalculate the matrix by this angle.

As you might have guessed, since there are only 40 degrees and 60 degrees involve, it is very easy for ±40 and ±60 to cancel each other out. The result of it is a high-precision fractal as shown below. (Of order 5, i.e., 6250 rotations.)

# Full working code

\documentclass[tikz,border=99]{standalone}
\usepackage{tikz}

\usetikzlibrary{lindenmayersystems}

\makeatletter
\def\pgf@pt@theta{0}  % setup global angle
\def\pgftransformrotate#1{
    % update global angle
    \pgfmathadd{\pgf@pt@theta}{#1}
    \xdef\pgf@pt@theta{\pgfmathresult}
    % calculate cosine
    \pgfmathcos{\pgf@pt@theta}
    \xdef\pgf@pt@aa{\pgfmathresult}
    \xdef\pgf@pt@bb{\pgfmathresult}
    % calculate sine
    \pgfmathsin{\pgf@pt@theta}
    \xdef\pgf@pt@ab{\pgfmathresult}
    \pgf@x=-\pgfmathresult pt%
    \xdef\pgf@pt@ba{\pgf@sys@tonumber{\pgf@x}}
}
\def\pgftransformxscale#1{\pgferror{Proportional scaling only. sorry!}}
\def\pgftransformyscale#1{\pgferror{Proportional scaling only. sorry!}}

\begin{document}
    \begin{tikzpicture}
        \pgfdeclarelindenmayersystem{starTree}{
            \rule{X -> F--FFFF-X+X+X+X+X-FFFF--F}
            \rule{F -> FF}
        }
        \draw [draw=blue!50!black]
            [l-system={starTree, step=10pt, right angle=40, left angle=60, axiom=X, order=5}]
            lindenmayer system;
    \end{tikzpicture}
\end{document}
  • Transformation matrix belongs to the PS level. TikZ/PGF only modifies it in fixed point precision as you demonstrate. – percusse Mar 5 '17 at 22:57
  • Impressive level of math and pgf knowledge! – Dr. Manuel Kuehner Mar 8 '17 at 6:04
  • @percusse What has PostScript to do with pgf? I thought they are independent systems. – Dr. Manuel Kuehner Mar 8 '17 at 6:06
  • 1
    @Dr.ManuelKuehner See Herbert's answer tex.stackexchange.com/questions/60778/… – percusse Mar 8 '17 at 6:22

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.