我想画龙曲线(或折纸曲线)以编程方式绘制。例如,创建一个表示曲线的序列,然后解析该序列以绘制曲线。
这个序列在整数序列在线百科全书/OEIS 中有详细记载,A014577,这里是一定义(通过字符串替换生成):
开始:L 规则: 左-->左1右 右-->L0R 0 --> 0 1 --> 1 ------------- 0:(#= 1) 大号 1:(#= 3) L1R 2:(#=7) 左1右1 3:(#= 15) L1R1L0R1L1R0L0R 4:(#=31) L1R1L0R1L1R0L0R1L1R1L0R0L1R0L0R 5:(#=63) L1R1L0R1L1R0L0R1L1R1L0R0L1R0L0R1L1R1L0R1L1R0L0R0L1R1L0R0L1R1L0R0L1R0L0R 删除所有 L 和 R 以获得 1101100111001001110110001100100
显示 n 折叠的完整序列将有 2^n-1 个元素。以下是绘制序列的递归视图:
我怎样才能做到这一点?
答案1
使用 Plain TeX 宏和 LaTeX 的图片环境的解决方案(增强了图片包裹)。
更新添加了带圆角的变体(参见答案末尾)。
这次从右开始(动画已更新):
底部有圆角的变体。
\documentclass[multi=picture,ignorerest=false]{standalone}
% convert -density 150 -verbose -delay 40 -dispose None DG/dragoncurve.* -delay 200 DG/dragoncurve.13.png -loop 0 dragoncurve.gif
\usepackage{pict2e}
\usepackage{color}
\usepackage{picture}
\newcount\X % integer horizontal coordinate
\newcount\Y % integer vertical coordinate
\newdimen\E % initial scale
\E 4cm
\newcount\Iter % iteration level, for displaying
\Iter = 1
\newcount\DeltaX
\newcount\DeltaY
% initial direction for first drawn Dragon curve (has two segments)
\DeltaX = -1
\DeltaY = 1
\let\LL\relax
\let\RR\relax
\def\Dragon {\L}
\def\IterateDragon {\advance\Iter 1
% adjust initial direction, rotating 45 degrees clockwise
\count255 = \DeltaX
\advance\DeltaX by \DeltaY
\advance\DeltaY by -\count255
% adjust scale
\E = 0.5\E
% apply rules
\def\L{\noexpand\L\LL\noexpand\R}%
\def\R{\noexpand\L\RR\noexpand\R}%
\edef\Dragon{\Dragon}%
}
% draw one segment in given direction and with current scale
\def\DrawSegment {\advance\X\DeltaX
\advance\Y\DeltaY
\lineto(\X,\Y)}
\def\DrawDragon {%
\setlength{\unitlength}{\E}%
\begin{picture}(13cm,9cm)(-9.5cm,-3cm)
\linethickness{1.5pt}%
\def\L {\count255 = \DeltaX
\DeltaX = -\DeltaY
\DeltaY = \count255
\DrawSegment }%
\def\R {\count255 = \DeltaX
\DeltaX = \DeltaY
\DeltaY = -\count255
\DrawSegment }%
\let\LL\L
\let\RR\R
\X = 0
\Y = 0
\put(0,0){\textcolor{blue}{\phantom{x}\the\Iter}}
\moveto(0,0)
\DrawSegment
\Dragon
\strokepath
\end{picture}}
\begin{document}
\ttfamily
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
\IterateDragon
\DrawDragon
% twelfth .. slow
\IterateDragon
\DrawDragon
% thirteenth .... sloooww
\IterateDragon
\DrawDragon
\end{document}
圆角的代码变体:
\documentclass[multi=picture,ignorerest=false]{standalone}
% convert -density 75 -verbose -dispose none -delay 100 -- DG3/dragoncurve3.* -delay 200 DG3/dragoncurve3.12.png -loop 0 dragoncurve3.gif
\usepackage{pict2e}
\usepackage{color}
\usepackage{picture}
\newcount\X % integer horizontal coordinate
\newcount\Y % integer vertical coordinate
% for convenience another pair
\newcount\x
\newcount\y
\newdimen\E % initial scale
\E 1cm
\newcount\Iter % iteration level, for displaying
\Iter = 1
\newcount\DeltaX
\newcount\DeltaY
% initial direction for first drawn Dragon curve (has two segments)
% this version goes rightward
\DeltaX = 1
\DeltaY = -1
% for convenience another pair
\newcount\deltax
\newcount\deltay
% (the first iterate goes down then up, thus turning left).
\def\Dragon {\L}
\def\IterMode {%
\let\LL\relax
\let\RR\relax
%
\def\L{\noexpand\L\LL\noexpand\R}%
\def\R{\noexpand\L\RR\noexpand\R}%
}
\def\DrawMode {%
\def\L {\deltax = -\DeltaY
\deltay = \DeltaX
\DrawArc
}%
\def\R {\deltax = \DeltaY
\deltay = -\DeltaX
\DrawArc
}%
\let\LL\L
\let\RR\R
}
\def\DrawArc {%
\x\numexpr \X + 2*\DeltaX + 2*\deltax\relax
\y\numexpr \Y + 2*\DeltaY + 2*\deltay\relax
\curveto
(\numexpr\X+\DeltaX\relax,\numexpr\Y+\DeltaY\relax)%
(\numexpr\x-\deltax\relax,\numexpr\y-\deltay\relax)%
(\x,\y)%
\X\x
\Y\y
\DeltaX\deltax
\DeltaY\deltay
}%
\def\IterateDragon {%
\global\advance\Iter 1
% adjust initial direction, rotating 45 degrees clockwise
\count255 = \DeltaX
\global\advance\DeltaX by \DeltaY
\global\advance\DeltaY by -\count255
% adjust scale
\global\E = 0.5\E
% apply rules and modify \Dragon globally
\IterMode
\xdef\Dragon{\Dragon}%
}
\def\DrawDragonPath #1{%
\linethickness{#1}%
\ifodd\Iter\color{red}\else\color{blue}\fi
\moveto(0,0)
\X\numexpr2*\DeltaX\relax
\Y\numexpr2*\DeltaY\relax
\lineto(\X,\Y)
\DrawMode
\Dragon
\X\numexpr\X+2*\DeltaX\relax
\Y\numexpr\Y+2*\DeltaY\relax
\lineto(\X,\Y)
\strokepath
}%
\def\DrawOneDragon #1{%
\setlength{\unitlength}{\E}%
\begin{picture}(12.35cm,8.6cm)(-2.85cm,-5.6cm)
\DrawDragonPath {#1}%
\put(0,0){\llap{\the\Iter\phantom{x}}}%
\end{picture}%
}
\def\DrawTwoDragons {% draws AND iterates once to get next curve too.
\setlength{\unitlength}{\E}%
\begin{picture}(12.35cm,8.6cm)(-2.85cm,-5.6cm)
% je fais ça vite fait, car avec convert je n'ai pas vu comment avoir deux
% rémanences, donc on fait deux dessins ici.
% we store initial direction:
\count2=\DeltaX
\count4=\DeltaY
\DrawDragonPath {1pt}%
% restore initial direction (which will be rotated 45° by \IterateDragon)
\DeltaX \count2
\DeltaY \count4
\IterateDragon % does \IterMode, makes global changes to \Dragon etc...
% compensate (only in this picture) for scale being left the same.
\divide\DeltaX by 2
\divide\DeltaY by 2
\DrawDragonPath {1.5pt}%
\put(0,0){\llap{\the\Iter\phantom{x}}}%
\end{picture}%
}
\begin{document}
\ttfamily
\DrawOneDragon {1.5pt}%1
\DrawTwoDragons %2
\DrawTwoDragons %3
\DrawTwoDragons %4
\DrawTwoDragons %5
\DrawTwoDragons %6
\DrawTwoDragons %7
\DrawTwoDragons %8
\DrawTwoDragons %9
\DrawTwoDragons %10
\DrawTwoDragons %11
\DrawOneDragon {1pt}%
\end{document}
答案2
作为 Lindenmayer 系统,龙曲线可以表示为
angle 90°
initial string FX
string rewriting rules
X ↦ X+YF+
Y ↦ −FX−Y.
因此我们有了一个使用库的简单 TikZ 解决方案lindenmayersystems
:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\begin{document}
\fbox{%
\tikz[rotate=65]
\draw[green!60!black]
l-system
[l-system={
rule set={X -> X+YF+,Y->-FX-Y},
axiom=FX,
angle=90,
order=12,
step=5pt
}
];
}
\end{document}
改为order=14
并将步骤减少为2pt
可得:
我的电脑报告的时间也相当不错:
real 0m48.379s
user 0m46.404s
sys 0m0.120s
然而,order=15
已经发生可怕的TeX capacity exceeded!
错误。
beamer
直到第 12 阶的小动画:
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\begin{document}
\begin{frame}
\centering
\tikz
\foreach \Valor in {1,2,...,12}
\draw<\Valor>[green!60!black]
l-system
[l-system={
rule set={X -> X+YF+,Y->-FX-Y},
axiom=FX,
angle=90,
order=\Valor,
step=3pt
}
];
\end{frame}
\end{document}
圆形版本
rounded corners=<length>
只需添加到选项即可获得舍入版本\draw
;11 阶的小示例:
\documentclass[border=3pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\begin{document}
\tikz
\draw[green!60!black,rounded corners=4pt]
l-system
[l-system={
rule set={X -> X+YF+,Y->-FX-Y},
axiom=FX,
angle=90,
order=11,
step=10pt
}
];
\end{document}
结果:
双龙
这戴维斯-克努斯龙也可以轻松获得:
\documentclass[tikz,border=3pt]{standalone}
\usetikzlibrary{lindenmayersystems}
\begin{document}
\tikz\draw[line width=1pt,green!60!black,rounded corners]
l-system
[l-system={
rule set={X -> X+YF,Y->FX-Y},
axiom=FX+FX+,
angle=90,
order=12,
step=10pt
}
];
\end{document}
答案3
这是一个实现,使用技巧。
该序列是通过重复字符串替换生成的xstring
的\StrSubstitute
:
\documentclass{article}
\usepackage[paper=a3paper,landscape,margin=0pt]{geometry}
\usepackage{etoolbox,pstricks,xstring,multido}
\pagestyle{empty}
\begin{document}
\begin{pspicture}(-25cm,-10cm)(10cm,7cm)
\psset{unit=5mm}
\SpecialCoor
\def\dragoncurve{L,1R}% Starting fold
\multido{\i=0+1}{10}{% Add 10 more folds
% Add fold
\StrSubstitute{\dragoncurve}{L}{L,1P}[\dragoncurve]% L -> L1P
\StrSubstitute{\dragoncurve}{R}{L,0R}[\dragoncurve]% R -> L0R
\StrSubstitute{\dragoncurve}{P}{R}[\dragoncurve]% P -> R
\xdef\dragoncurve{\dragoncurve}% Make definition global
}
\StrSubstitute{\dragoncurve}{L}{}[\dragoncurve]% Drop L
\StrSubstitute{\dragoncurve}{R}{}[\dragoncurve]% Drop R
\StrSubstitute[1]{\dragoncurve}{,}{}[\dragoncurve]% Drop first ,
\def\nextangle{0}% Starting angle
\pscustom[linewidth=.1pt]{
\psline(0,0)% Initial node
\renewcommand{\do}[1]{
\rlineto(1;\nextangle)% Draw next line
\xdef\nextangle{\number\numexpr\nextangle+\ifnum#1=1 (-90)\else (90)\fi}
}%
\expandafter\docsvlist\expandafter{\dragoncurve}% Process dragon curve
\rlineto(1;\nextangle)% Draw final line
}
\end{pspicture}
\end{document}
产量仅受 TeX 内存的限制。使用默认设置,大概可以进行 12 次折叠(编译时间很长,并且必须使用非常大的纸张尺寸或调整 and unit
/or runit
)。
答案4
LuaLaTeX 程序内部的 MetaPost 解决方案。
\documentclass{standalone}
\usepackage{luamplib}
\mplibnumbersystem{double}
\begin{document}
\begin{mplibcode}
vardef dragon(expr A, B, n) =
if n = 0: draw A--B;
else: save C; pair C; C = A rotatedaround (.5[A,B], 90);
dragon(A, C, n-1);
dragon(B, C, n-1); fi
enddef;
beginfig(1);
dragon(origin, (12cm, 0), 18);
endfig;
\end{mplibcode}
\end{document}
对于 14 级递归:
现在进行 18 级递归。对于我的旧笔记本电脑(2008 年),它只需不到半分钟。正在进行进一步的测试,以测试 MetaPost 的极限,但无论如何它不会对图表本身产生太大影响 :-)
编辑:21 个级别的结果,在不到 3 分钟的时间内完成。如您所见,该图有点“平滑”。我想 MetaPost 可以走得更远,但它在此过程中会大大降低我的旧机器的速度。我想我会在这里停下来 :-)
编辑:递归已经很多简化。另外,按照 Thruston 的示例,我使用了更简单的新点计算(C = A rotatedaround (.5[A,B], 90)
而不是)C = B + .5sqrt2*(A-B) rotated 45
。这可能会稍微加快编译时间。