Tikz l 系统中的段长度评估错误

Tikz l 系统中的段长度评估错误

我想用 Tikzlindenmayersystems库绘制一棵分形树。这是我使用的代码:

\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}

对于小订单价值,它效果很好,例如 order=2: 订单 2 的结果

但是当我增加阶数时,错误变得明显,我的图形不再对称(比较顶部的水平线)。这是 4 阶的结果。 订单 4 的结果

看起来程序保留了角度,但在评估线段长度时犯了一个小错误。随着线段数量的增加,错误变得越来越明显。

有人知道如何避免这个问题吗?

谢谢!

更新:补充观察

只使用类似图形中的 45 度角,分支现在是平行线,问题没有出现(尽管段数比前一种情况要多)。代码如下:

\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}

及其结果: 45 度角四阶结果

答案1

这是一个典型的 TeX 算法精度发挥作用的案例。

再具体一点,保持变换矩阵表示当前的平移/缩放/旋转。如果用户要求旋转 40 度,将首先计算相应的旋转矩阵(一个cos&-sin\\sin&cos),然后与当前的旋转矩阵相乘。

在您的例子中,当您要求绘制 4 阶图形时,将进行 1250 次旋转。从数学上讲,许多旋转相互抵消,但从数值上讲,误差会显著累积。因此,我们必须提高旋转的精度。

好消息是,由于只涉及移动和旋转,我们可以保持全局旋转角度.我们重新定义低级命令如下。

\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}}
}

这里\pgf@pt@aa等是变换矩阵的条目。我们所做的只是更新全局旋转角度并根据此角度重新计算矩阵。

您可能已经猜到了,由于只涉及 40 度和 60 度,因此 ±40 和 ±60 很容易相互抵消。其结果是一个高精度分形,如下所示。(阶数为 5,即 6250 次旋转。)

# 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}

相关内容