绘制龙曲线

绘制龙曲线

我想画龙曲线(或折纸曲线)以编程方式绘制。例如,创建一个表示曲线的序列,然后解析该序列以绘制曲线。

这个序列在整数序列在线百科全书/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 的图片环境的解决方案(增强了图片包裹)。

更新添加了带圆角的变体(参见答案末尾)。

龍曲

这次从右开始(动画已更新):

龍軌2

底部有圆角的变体。

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

龍軌3

答案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。这可能会稍微加快编译时间。

相关内容