我想用 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:
但是当我增加阶数时,错误变得明显,我的图形不再对称(比较顶部的水平线)。这是 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}
及其结果:
答案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}