方形填充曲线和 Lindenmayer 系统绘图库

方形填充曲线和 Lindenmayer 系统绘图库

前两次迭代和基本修改

我正在尝试获取曲线序列的第一项,这些曲线收敛到众所周知的曲线家族中的一条曲线平面(或空间)填充曲线这是我在James Munkres的《拓扑学》一书中找到的一个变体(见上图)。

图 44.1 和图 44.2 描述了前两条曲线(函数图像GG'分别)以及从一步到下一步的操作。例如,下一条曲线将通过对四个部分(位于四个小方块中)中的每一个应用相同的操作来获得G'该操作尊重每个部分的几何方向;见图 44.3。

我曾尝试使用 pgf 和 tikz 来绘制它们,但由于对 Lindenmayer 系统不太熟悉,我很难获得它们。以下是我迄今为止尝试过的代码。

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary {lindenmayersystems}
\begin{document}

\pgfdeclarelindenmayersystem{curve 1} {
    \symbol{X}{\pgflsystemdrawforward}
    \symbol{+}{\pgflsystemturnright}
    \symbol{-}{\pgflsystemturnleft}
    \rule{A -> -AXB--XA++XB++XA--XB++XA++XB--XA}
    \rule{B -> +BXA++XB--XA--XB++XA--XB--XA++XB}
}

\foreach \x in {1,2,3} {
\tikz\draw[lindenmayer system={curve 1, axiom=A, order=\x, angle=45}, scale=1.5] lindenmayer system;
\vskip .5in
}

\end{document}

答案1

前两个学期

为了定义沿曲线的方向,左边它的一侧是彩色的。在上图中,我们看到了序列的前两项在从一个项传递到下一个项时,对当前曲线的每个部分执行基本运算(称为小学部分在续篇中)。 基本部件有四种类型,向上正确的向下, 和左边。它们在每个步骤中被替换。例如,正确的得到下面的曲线。

正确的

每个基本部分的下一步:

下一步

序列的前四个项。

前四个学期

我不知道如何使用 Lindenmayer 系统。因此,代码基于四个pic元素的构造,每个元素对应一个基本部分。这些元素是递归定义的,每个元素都带有两个参数:递归步骤和递归界限。还有一个\r常数必须定义一开始;它控制缩放因子。

评论。该序列的曲线并不简单(它们自动相交,即相应的函数不是单射)。可以稍微修改代码(参见希尔伯特曲线)以获得简单曲线。

代码

\documentclass[11pt, margin=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{math, calc}
\usetikzlibrary{decorations.pathreplacing} % for show control points
\begin{document}

\tikzset{
  pics/up/.style 2 args={% recursion step, end
    code={%
      \tikzmath{
        integer \i;
        \i = #1+1;
        \tmp = {\r/pow(2, \i)};
        if \i<#2 then {%
          {%
            \path (-\tmp, -\tmp) pic {right={\i}{#2}};
            \path (-\tmp, \tmp) pic {up={\i}{#2}};
            \path (\tmp, \tmp) pic {up={\i}{#2}};
            \path (\tmp, -\tmp) pic {left={\i}{#2}};
          };
        } else {%
          {%
            \fill[opacity=.2] (-2*\tmp, -2*\tmp) -- (0, 0)
            -- (2*\tmp, -2*\tmp) -- ++(0, 4*\tmp) -| cycle;
            \draw (-2*\tmp, -2*\tmp) -- (0, 0) -- (2*\tmp, -2*\tmp);
          };
        };
      }
    }
  },
  pics/right/.style 2 args={% recursion step, end
    code={%
      \tikzmath{
        integer \i;
        \i = #1+1;
        \tmp = {\r/pow(2, \i)};
        if \i<#2 then {%
          {%
            \path (-\tmp, -\tmp) pic {up={\i}{#2}};
            \path (\tmp, -\tmp) pic {right={\i}{#2}};
            \path (\tmp, \tmp) pic {right={\i}{#2}};
            \path (-\tmp, \tmp) pic {down={\i}{#2}};
          };
        } else {%
          {%
            \fill[opacity=.2] (-2*\tmp, -2*\tmp)
            -- (0, 0) -- (-2*\tmp, 2*\tmp) -- cycle;
            \draw (-2*\tmp, -2*\tmp) -- (0, 0) -- (-2*\tmp, 2*\tmp);
          };
        };
      }
    }
  },
  pics/down/.style 2 args={% recursion step, end
    code={%
      \tikzmath{
        integer \i;
        \i = #1+1;
        \tmp = {\r/pow(2, \i)};
        if \i<#2 then {%
          {%
            \path (\tmp, \tmp) pic {left={\i}{#2}};
            \path (\tmp, -\tmp) pic {down={\i}{#2}};
            \path (-\tmp, -\tmp) pic {down={\i}{#2}};
            \path (-\tmp, \tmp) pic {right={\i}{#2}};
          };
        } else {%
          {%
            \fill[opacity=.2] (2*\tmp, 2*\tmp) -- (0, 0)
            -- (-2*\tmp, 2*\tmp) -- ++(0, -4*\tmp) -| cycle;
            \draw (2*\tmp, 2*\tmp) -- (0, 0) -- (-2*\tmp, 2*\tmp);
          };
        };
      }
    }
  },  
  pics/left/.style 2 args={% recursion step, end
    code={%
      \tikzmath{
        integer \i;
        \i = #1+1;
        \tmp = {\r/pow(2, \i)};
        if \i<#2 then {%
          {%
            \path (\tmp, \tmp) pic {down={\i}{#2}};
            \path (-\tmp, \tmp) pic {left={\i}{#2}};
            \path (-\tmp, -\tmp) pic {left={\i}{#2}};
            \path (\tmp, -\tmp) pic {up={\i}{#2}};
          };
        } else {%
          {%
            \fill[opacity=.2] (2*\tmp, 2*\tmp) -- (0, 0)
            -- (2*\tmp, -2*\tmp) -- cycle;
            \draw (2*\tmp, 2*\tmp) -- (0, 0) -- (2*\tmp, -2*\tmp);
          };
        };
      }
    }
  },
  pics/context1/.style 2 args={% color, function's name
    code={%
      \draw[thin, ->] (-.5*\r, 0) -- ++(2.5*\r, 0);
      \draw[#1, very thick] (0, 0) -- ++(1.5*\r, 0);
      \foreach \i in {0, .5, 1}{
        \draw[thick, #1] (\i*1.5*\r, 0) -- +(0, 1ex);
      }
      \foreach \i in {0, 1}{
        \draw[thick, #1] (\i*1.5*\r, 0) -- +(0, -1ex) 
         node[below, scale=.7] {$\i$};
      }
      
      \draw[->] (2.5*\r, .5*\r) to[out=20, in=160] ++(\r, 0);
      \path (2.5*\r, 0) +(.5*\r, .75*\r) node[scale=.7] {$#2_1$}; 
    }
  },
  pics/context2/.style 2 args={% color, function's name
    code={%
      \draw[thin, ->] (-.5*\r, 0) -- ++(2.5*\r, 0);
      \draw[#1, very thick] (0, 0) -- ++(1.5*\r, 0);
      \foreach \i [evaluate=\i as \k using {1.5*\r/8*\i}] in {0, ..., 8}{
        \draw[thick, #1] (\k, 0) -- +(0, 1ex);
      }
      \foreach \i [evaluate=\i as \k using {1.5*\r/4*\i}] in {0, ..., 4}{
        \draw[thick, #1] (\k, 0) -- +(0, -1ex);
      }
      \foreach \i in {0, 1}{
        \path[#1] (\i*1.5*\r, 0) -- +(0, -1ex) node[below, scale=.7] {$\i$};
      }
      
      \draw[->] (2.5*\r, .5*\r) to[out=20, in=160] ++(\r, 0);
      \path (2.5*\r, 0) +(.5*\r, .75*\r) node[scale=.7] {$#2_2$};
    }
  }
}

% \begin{tikzpicture}[evaluate={\r=1.2;}]
%   \path (-5*\r, -.5*\r) pic {context1={red}{u}};  % u_1
%   \draw[gray, thin] (-\r, -\r) rectangle (\r, \r);
%   \path (0, 0) pic[red, thick, fill=gray] {up={0}{1}};

%   \begin{scope}[yshift={-2.5*\r cm}]  % u_2
%     \path (-5*\r, -.5*\r) pic {context2={red}{u}};    
%     \draw[gray, thin] (-\r, -\r) rectangle (\r, \r);
%     \draw[gray, thin] (-\r, 0) -- (\r, 0);
%     \draw[gray, thin] (0, -\r) -- (0, \r);
%     \path (0, 0) pic[red, thick, fill=gray] {up={0}{2}};
%   \end{scope}
% \end{tikzpicture}

\begin{tikzpicture}[evaluate={\r=1.7;}]
  \foreach \s in {1, ..., 4}{%
    \begin{scope}[xshift={(\s-1)*2.3*\r cm}]
      \draw[gray, thin] (-\r, -\r) rectangle (\r, \r);
      \path (0, 0) pic[red, thick, fill=gray] {up={0}{\s}};
    \end{scope}
  }
\end{tikzpicture}

\end{document}

答案2

在等待 Lindenmayer 系统帮助时,可以使用以下不同的方法元帖子

在此处输入图片描述

我们从以原点为中心的直角路径开始gene。然后在循环中制作该路径的 1/2 比例副本 ( gg),然后 gene用适当旋转和移位的四个副本替换gg。路径的长度每次增加四倍,因此在第五次循环之后(如上所示),它的长度为 2048 点。

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibnumbersystem{double}
\begin{mplibcode}
beginfig(1);
    path gene; gene = (dir 225 -- origin -- dir 315) scaled 256;
    for i = 1 upto 5:
        path gg; gg = gene scaled 1/2; numeric n; n = length gg;
        gene := reverse gg rotated -90 shifted  point 0 of gg &
                        gg             shifted -point n of gg &
                        gg             shifted -point 0 of gg &
                reverse gg rotated +90 shifted  point n of gg;
    endfor
    draw gene;
    label.lrt(decimal length gene, llcorner currentpicture);
endfig;
\end{mplibcode}
\end{document}

如果要进一步操作,则需要添加\mplibnumbersystem{double}如示例中所示。然后,您可以再增加一点。这是第 8 级的情况(在我的旧 MacBook 上需要 3.1 秒才能完成),路径中有 131072 步。

在此处输入图片描述

相关内容