在 Tikz 中绘制递归多边形

在 Tikz 中绘制递归多边形

为了绘制不同类型的多边形(具有不同的旋转、缩放因子等),我使用以下代码(生成顶部图片),该代码修改自 这个答案这个帖子。最初的代码是为绘制五边形而设计的。

是否可以使用 TikZ 的递归构造方法来生成类似下面的图片同一篇文章的答案。我在理解这个完美的 MetaPost 代码时遇到了一些困难;而且,在修改这个非常有用的代码以生成具有不同边数和旋转度的其他多边形时,我得到了一些不良结果。

\documentclass[12pt,a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{calc, math}
\begin{document}
{
triangle4
\tikzmath{
real \a, \d, \t, \tini;
\a=2;
\t=120;
\d={2*\a*cos(\t/2)};
\tini=30;
}
\tikzset{
pics/triangl/.style 2 args={%
code={%
\draw[#1, line width=0.08cm, fill=#2] (0:\a)
\foreach \i in {0,...,2}{ -- (\i*\t:\a) } -- cycle;
}
}
}
\begin{tikzpicture}[scale=.4, transform shape, opacity=.6]
\foreach \j/\rgb in
{0/violet, 1/red, 2/green!70!black}{%
\path (\tini+\j*\t+\t:\d\tini*2) pic[rotate={\tini*3}]
{triangl={\rgb}{\rgb!30}};
}
\path (0,0) pic[rotate=\tini] {triangl={blue}{blue!35}};
\end{tikzpicture}
}
%
{
tetragon3
\tikzmath{
real \a, \d, \t, \tini;
\a=3;
\t=90;
\d={2*\a*cos(\t/2)};
\tini=22.5;
}
\tikzset{
pics/tetragon/.style 2 args={%
code={%
\draw[#1, line width=0.08cm, fill=#2] (0:\a)
\foreach \i in {0,...,3}{ -- (\i*\t:\a) } -- cycle;
}
}
}
\begin{tikzpicture}[scale=.4, transform shape, rotate=\tini, opacity=.6]
\foreach \j/\rgb in
{0/violet, 1/red, 2/orange, 3/green!70!black}{%
\path (\tini+\j*\t+\t/2:\d*1.25) pic[rotate={\tini*3}]
{tetragon={\rgb}{\rgb!30}};
}
\path (0,0) pic[rotate=\tini] {tetragon={blue}{blue!35}};
\end{tikzpicture}
}
%
{
hexagon4
\tikzmath{
real \a, \d, \t, \tini;
\a=2;
\t=60;
\d={2*\a*cos(\t/2)};
\tini=30;
}
\tikzset{
pics/hexagon/.style 2 args={%
code={%
\draw[#1, line width=0.08cm, fill=#2] (0:\a)
\foreach \i in {0,...,5}{ -- (\i*\t:\a) } -- cycle;
}
}
}
\begin{tikzpicture}[scale=.4, transform shape, opacity=.6]
\foreach \j/\rgb in
{0/violet, 1/red, 2/orange, 3/green!70!black, 4/cyan!70!black, 5/brown!70!black}{%
\path (\tini+\j*\t+\t:\d*1.2) pic[rotate={\tini}]
{hexagon={\rgb}{\rgb!30}};
}
\path[scale=1.12] (0,0) pic[rotate=\tini*2] {hexagon={blue}{blue!35}};
\end{tikzpicture}
}
%
{
octagon1-
\tikzmath{
real \a, \d, \t, \tini;
\a=2;
\t=45;
\d={2*\a*cos(\t/2)};
\tini=11.25;
}
\tikzset{
pics/octagon/.style 2 args={%
code={%
\draw[#1, line width=0.08cm, fill=#2] (0:\a)
\foreach \i in {0,...,7}{ -- (\i*\t:\a) } -- cycle;
}
}
}
\begin{tikzpicture}[scale=.3, transform shape, opacity=.6]
\foreach \j/\rgb in
{0/violet, 1/red, 2/orange, 3/green!70!black, 4/cyan!70!black, 5/brown!70!black, 6/teal, 7/olive}{%
\path (\tini+\j*\t+\t*-.25:\d*1.45) pic[rotate={-\tini*2}]
{octagon={\rgb}{\rgb!30}};
}
\path[scale=1.88] (0,0) pic[rotate=\tini*2] {octagon={blue}{blue!35}};
\end{tikzpicture}
}
{
octagon4-
\tikzmath{
real \a, \d, \t, \tini;
\a=2;
\t=45;
\d={2*\a*cos(\t/2)};
\tini=11.25;
}
\tikzset{
pics/octagon/.style 2 args={%
code={%
\draw[#1, line width=0.08cm, fill=#2] (0:\a)
\foreach \i in {0,...,7}{ -- (\i*\t:\a) } -- cycle;
}
}
}
\begin{tikzpicture}[scale=.3, transform shape, opacity=.6]
\foreach \j/\rgb in
{0/violet, 1/red, 2/orange, 3/green!70!black, 4/cyan!70!black, 5/brown!70!black, 6/teal, 7/olive}{%
\path (\tini+\j*\t+\t*-.25:\d*1.35) pic[rotate={\tini*4}]
{octagon={\rgb}{\rgb!30}};
}
\path[scale=1.45] (0,0) pic[rotate=\tini*4] {octagon={blue}{blue!35}};
\end{tikzpicture}
}
\end{document}

在此处输入图片描述

在此处输入图片描述

答案1

好的,这是概括我对 Sierpinski 地毯的 TikZ/PGFKeys 回答。它不是在一条路径上进行所有操作(这样可以在整个构造中进行着色和裁剪),而是为每个事物

这也是一种递归方法,而不是(我意识到.scoped/.list这只是一种普通方法)我正在使用一个处理程序,它只循环遍历给定的列表并使用每个值与键。 我们可以写成。\foreach.foreach
\pgfkeys{<key>/.foreach={<list>}}\foreach \item in {<list>}{\pgfkeys{<key>=\item}}


第一部分只定义算法、循环和递归。每一级都会评估一次新的绘制和移动距离。

默认情况下,第一部分中实际上没有使用它们。
您需要以使用值和的方式定义/tikz/flakes/stlying和。/tikz/flakes/before do/tikz/flakes/drawDist/tikz/flakes/shiftDist

由于您的示例使用多边形,因此drawDist是圆周的半径(即每个角与中心的距离)。是shiftDist水平中心之间的距离到水平面的中心−1。
两个 s 之间的比率drawDist基本上是每次递归的比例因子,需要在draw to draw密钥中进行配置(PGFmath 以后会用到)。

该函数draw to shift用于评估前一个drawDist到下一个shiftDist


对于多边形,我添加了第二部分,其中有一些数学知识,在这种情况下,我定义了每个多边形的整个配置:

  • 函数draw to drawdraw to shift
  • 中心部分所需的旋转和
  • 所需的移位功能。

其中一些可能可以推广(想想函数isoddiseven),甚至可能是比例因子。

不过,我现在看到您有示例,其中中心的大小与周围的卫星不同。我现在不知道如何处理这个问题。
但是请注意,这before do会将项目编号指定为#1(如果需要,也可以使用/tikz/flakes/item)。因此,您可以使缩放依赖于#1:如果它是 0,则使用缩放A, 否则b

该值/tikz/flakes/level正在倒计时(每个事物将在 level = 0 处绘制),该值/tikz/flakes/level'正在向上计数。

您可以清空before split样式并使用这些级别和项目编号来即时计算所有内容。


我正在使用我的ext.pgfkeys-plus图书馆(需要最新的 TikZ tikz-ext) 获得一些 PGFKeys“好东西”。

我建议将这些图形外部化,因为这不是一个有效的解决方案(从依赖大量使用 PGFKeys 开始)。


薄片的尺寸由初始drawDist(此处1)决定,该值与坐标坐标系并且受任何缩放的影响,而且受矢量x和的影响y

我还添加了一个\flakescreate宏,该宏可对其内容进行分组并自动发出,start以便之后所有值都保持不变。(第一次迭代未分组。)

为了应用不同的颜色,我添加了一个color i代表 0 到 7 之间的整数,应用draw以及fill颜色:

\tikzset{
  /utils/temp/.style args={#1/#2}{color #1/.style={draw=#2, fill=#2!50}},
  /utils/temp/.list={0/violet, 1/red, 2/orange, 3/green!70!black,
                     4/cyan!70!black, 5/brown!70!black, 6/teal, 7/olive}}

\colorlet您可以通过定义新颜色(通过)几乎相同是名字的一部分。


正如评论所说,我添加了一个start item值来确定要完成的第一项,将其设置为start item = 1跳过中间部分。通过交换end itemstart item可以更改绘制顺序,以便将中间部分放置在所有其他部分之上。

对于更复杂的情况,item list可以手动设置该值。

在此最新更新中,我添加了六边形、七边形、八边形和九边形所需的计算,其中后三个使用自定义和估计的中心部分的比例因子。

代码

\documentclass[tikz]{standalone}
\usepgfkeyslibrary{ext.pgfkeys-plus}
\makeatletter
\pgfqkeys{/handlers}{
  .foreach/.code={\let\pgfkeys@exp@call@\pgfkeys@exp@call
    \foreach\pgf@keys@temp in{#1}{\expandafter\pgfkeys@exp@call@\expandafter{\pgf@keys@temp}}}}
\makeatother
\newcommand*\flakesset{\pgfqkeys{/tikz/flakes}}
\pgfmathdeclarefunction{flakesDTS}{1}{%
  \pgfmathparse{\pgfkeysvalueof{/tikz/flakes/draw to shift/.@cmd}#1\pgfeov}}
\pgfmathdeclarefunction{flakesDTD}{1}{%
  \pgfmathparse{\pgfkeysvalueof{/tikz/flakes/draw to draw/.@cmd}#1\pgfeov}}
\newcommand*\flakescreate[1][]{\begingroup\flakesset{#1,start}\endgroup}
\flakesset{
  .code=\flakesset{#1}, tikz/.code=\tikzset{#1},
  draw to shift/.code={#1/2}, draw to draw/.code ={#1/2},
  drawDist/.initial=1, shiftDist/.initial=1,
  level/.initial=0, level'/.initial=0, item/.initial=0,
  start item/.initial=0, end item/.initial=3,
  item list/.initial={\pgfkeysvalueof{/tikz/flakes/start item},...,\pgfkeysvalueof{/tikz/flakes/end item}},
  start/.style={level'=0, item=0, tikz=flakes/at start/.try, __do=0},
  __do/.style={
    /utils/TeX/ifnum={\pgfkeysvalueof{/tikz/flakes/level}=0}{__place={#1}}{
      level/.--, level'/.++, before split={#1},
      __split/.foreach/.expanded={\pgfkeysvalueof{/tikz/flakes/item list}}}},
  __split/.code={%
    \pgfkeyssetevalue{/tikz/flakes/item}{#1}%
    \tikzset{flakes/before do={#1}}%
    \flakesset{__do={#1}}%
    \tikzset{flakes/after do/.try={#1}}},
  __place/.code={\path[flakes/styling={#1}](0,0)to[flakes/path={#1}](0,0);},
  before split/.style={
    shiftDist/.evaluated=flakesDTS(\pgfkeysvalueof{/tikz/flakes/drawDist}),
    drawDist/.evaluated =flakesDTD(\pgfkeysvalueof{/tikz/flakes/drawDist}),
    level \pgfkeysvalueof{/tikz/flakes/level}/.try={#1},
    level \pgfkeysvalueof{/tikz/flakes/level'}'/.try={#1}}}
\tikzset{
  polygon path/.style 2 args={to path={
      plot[sharp cycle, samples at={1,...,#1}] (\x*360/#1:#2)}},
  polygon path*/.style 2 args={to path={
      plot[sharp cycle, samples at={1,...,#1}] ([shift={(\x*360/#1:{-.5*\pgflinewidth/sin((#1-2)/#1*90)})}]\x*360/#1:#2)}}}
\flakesset{
  poly shift/.style={shift=({#1}:\pgfkeysvalueof{/tikz/flakes/shiftDist})},
  if center/.style 2 args={/utils/TeX/ifnum={\pgfkeysvalueof{/tikz/flakes/item}=0}{#1}{#2}},
  set d2s and d2d/.style={draw to shift/.code={##1-#1}, draw to draw/.code={#1}},
  %
  polygon*/.style={polygon={#1}, path/.append style={polygon path*={#1}{\pgfkeysvalueof{/tikz/flakes/drawDist}}}},
  polygon/.style={
    start item = 0, end item/.pgfmath int = {#1},
    path/.style={polygon path={#1}{\pgfkeysvalueof{/tikz/flakes/drawDist}}},
    polygon calculation #1, at start/.append style={rotate=-90-180/#1},
    /utils/exec=\pgfmathsetmacro\valA{360/(#1)}\pgfmathsetmacro\valB{\valA/2},
    before do/.append style/.expanded={
      flakes/if center={\ifodd#1 rotate=\valB\fi}{flakes/poly shift=\valA*####1}}},
  polygon calculation 3/.style={set d2s and d2d = ##1/2},
  polygon calculation 4/.style={set d2s and d2d = ##1/3},
  polygon calculation 5/.style={set d2s and d2d = 0.3819660112501051*##1},
  polygon calculation 6/.style={set d2s and d2d = ##1/3},
  polygon calculation 7/.style={set d2s and d2d = 0.3079785283699041*##1, before do/.append style={flakes/if center={scale=1.5}{}}},
  polygon calculation 8/.style={set d2s and d2d = 0.2928932188134525*##1, before do/.append style={flakes/if center={scale=1.42}{}}},
  polygon calculation 9/.style={set d2s and d2d = 0.2577728010314408*##1, before do/.append style={flakes/if center={scale=2.07}{}}},
  Sierpinski carpet/.style={
    start item = 0, end item = 8,
    path/.style={to path={
                (-\pgfkeysvalueof{/tikz/flakes/drawDist},-\pgfkeysvalueof{/tikz/flakes/drawDist})
      rectangle ( \pgfkeysvalueof{/tikz/flakes/drawDist}, \pgfkeysvalueof{/tikz/flakes/drawDist})}},
    set d2s and d2d = ##1/3, before do/.append style={flakes/Sierpinski carpet/do ##1}},
  Sierpinski carpet/shift/.style 2 args={shift={(#1*\pgfkeysvalueof{/tikz/flakes/shiftDist},#2*\pgfkeysvalueof{/tikz/flakes/shiftDist})}},
  Sierpinski carpet/do 0/.code=,
  Sierpinski carpet/do 1/.style={flakes/Sierpinski carpet/shift=11},       Sierpinski carpet/do 2/.style={flakes/Sierpinski carpet/shift=01},
  Sierpinski carpet/do 3/.style={flakes/Sierpinski carpet/shift={-1}1},    Sierpinski carpet/do 4/.style={flakes/Sierpinski carpet/shift={-1}0},
  Sierpinski carpet/do 5/.style={flakes/Sierpinski carpet/shift={-1}{-1}}, Sierpinski carpet/do 6/.style={flakes/Sierpinski carpet/shift=0{-1}},
  Sierpinski carpet/do 7/.style={flakes/Sierpinski carpet/shift=1{-1}},    Sierpinski carpet/do 8/.style={flakes/Sierpinski carpet/shift=10},
}
\tikzset{
  /utils/temp/.style args={#1/#2}{color #1/.style={draw=#2, fill=#2!50}},
  /utils/temp/.list={0/violet, 1/red, 2/orange, 3/green!70!black,
                     4/cyan!70!black, 5/brown!70!black, 6/teal,
                     7/olive, 8/black, 9/yellow!50!black}}
\begin{document}
\begin{tikzpicture}[
  row sep=+2mm, column sep=+2mm,
  row 5/.style={flakes={level=3, start item=1}},
  cells={
    /utils/exec={\useasboundingbox (-1,-1) rectangle (1,1);},
    flakes={level/.pgfmath int=\the\pgfmatrixcurrentrow-1,
          polygon/.pgfmath int=\the\pgfmatrixcurrentcolumn+2}}]
\matrix (m1) [flakes/styling/.style={fill/.pgfmath wrap={red!##1!blue}{#1/\pgfkeysvalueof{/tikz/flakes/end item}*100}}]{
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
};
\matrix[flakes/styling/.style={color #1}, matrix anchor=west] at (m1.east){
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
  \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate & \flakescreate \\
};
\end{tikzpicture}
\tikz[x=+2cm, y=+2cm, flakes/styling/.style={color #1, draw=none}, row sep=+2mm, column sep=+2mm]\matrix{
  \flakescreate[Sierpinski carpet, level = 3] &
  \flakescreate[Sierpinski carpet, level = 3, start item = 1] &
  \flakescreate[Sierpinski carpet, level = 3, start item = 1, styling/.style={draw=black, fill=gray}] \\
  \flakescreate[level = 3, level 1/.append style={styling/.style=color #1}, polygon = 5] &
  \flakescreate[styling/.append style={draw, ultra thick, line join=round}, level = 1, polygon  = 3, start item = 3, end item = 0] &
  \flakescreate[styling/.append style={draw, ultra thick}, level = 1, polygon* = 3]
  \\};
\end{document}

输出

在此处输入图片描述 在此处输入图片描述

相关内容