使用 Paul Gaborit 的代码为了绘制彭罗斯拼贴的子区域,我创建了以下图像:
产生这个结果的代码是:
\documentclass{article}
\usepackage[active,tightpage]{preview}
\usepackage{tikz}
\PreviewEnvironment{tikzpicture}
\usetikzlibrary{calc}
\pgfmathsetlengthmacro{\len}{10cm}
\pgfmathtruncatemacro{\recurs}{5}
\pgfmathsetmacro{\invphi}{2/(1+sqrt(5))} % phi = golden ratio = (1+sqrt(5))/2
\tikzset{
penrose line/.style={draw=black,line join=round},
penrose kite/.style={fill=teal,penrose line},
penrose dart/.style={fill=yellow,penrose line},
penrose common/.style={},
penrose path 1/.style={penrose common},
penrose path 2/.style={penrose common},
penrose path 3/.style={penrose common},
penrose rev path 1/.style={penrose common},
penrose rev path 2/.style={penrose common},
penrose rev path 3/.style={penrose common},
}
\newcommand\penrosedrawkite[3]{% ver = starting vertex, angle = direction of first edge, len = length of first edge
\path (#1)
+(#2+36:#3) coordinate (#1-b)
+(#2:#3) coordinate (#1-c)
+(#2-36:#3) coordinate (#1-d);
\path[penrose kite] (#1)
to[penrose path 1] (#1-b)
to[penrose rev path 2] (#1-c)
to[penrose path 2] (#1-d)
to[penrose rev path 1] (#1);
}
\newcommand\penrosekite[5]{% n = number of decompositions remaining, ver, angle, len, rot = sense of rotation (0 means anticlockwise, 1 means clockwise)
\ifnum#1=0 % i.e. if no decomposition cycles remain
\ifnum#5=1
\penrosedrawkite{#2}{#3}{#4}
\fi
\else % i.e. if at least one decomposition cycle remains
{
\edef\dep{#1}
\edef\ver{#2}
\edef\angle{#3}
\edef\len{#4}
\edef\rot{#5}
\pgfmathtruncatemacro{\n}{\dep-1}
\edef\namex{\ver\n}
\pgfmathsetlengthmacro{\newlen}{\len*\invphi}
\ifnum#5=1
\path (\ver) ++(\angle-36:\len) coordinate (\namex);
\pgfmathtruncatemacro{\newanglea}{mod(\angle+108,360)}
\penrosekite{\n}{\namex}{\newanglea}{\newlen}{1}
\penrosekite{\n}{\namex}{\newanglea}{\newlen}{0}
\penrosedart{\n}{\ver}{\angle}{\newlen}{1}
\else
\path (\ver) ++(\angle+36:\len) coordinate (\namex);
\pgfmathtruncatemacro{\newanglea}{mod(\angle-108,360)}
\penrosekite{\n}{\namex}{\newanglea}{\newlen}{0}
\penrosekite{\n}{\namex}{\newanglea}{\newlen}{1}
\penrosedart{\n}{\ver}{\angle}{\newlen}{0}
\fi
}
\fi
}
\newcommand\penrosedrawdart[3]{% ver, angle, len
\path (#1)
+(#2:#3) coordinate (#1-b)
+(#2-36:#3*\invphi) coordinate (#1-c)
+(#2-72:#3) coordinate (#1-d);
\path[penrose dart] (#1)
to[penrose path 3] (#1-b)
to[penrose path 2] (#1-c)
to[penrose rev path 2] (#1-d)
to[penrose rev path 3] (#1);
}
\newcommand\penrosedart[5]{% n, ver, angle, len, rot
\ifnum#1=0 % i.e. if no decomposition cycles remain
\ifnum#5=1
\penrosedrawdart{#2}{#3}{#4}
\fi
\else % i.e. if at least one decomposition cycle remains
{
\edef\dep{#1}
\edef\ver{#2}
\edef\angle{#3}
\edef\len{#4}
\edef\rot{#5}
\pgfmathtruncatemacro{\n}{\dep-1}
\edef\namex{\ver\n}
\pgfmathsetlengthmacro{\newlen}{\len*\invphi}
\path (\ver) ++(\angle:\len) coordinate (\namex);
\ifnum#5=1
\pgfmathsetmacro{\newanglea}{mod(\angle-144,360)}
\pgfmathsetmacro{\newangleb}{mod(\angle-36,360)}
\penrosedart{\n}{\namex}{\newanglea}{\newlen}{1}
\penrosekite{\n}{\ver}{\newangleb}{\newlen}{0}
\else
\pgfmathtruncatemacro{\newanglea}{mod(\angle+144,360)}
\pgfmathtruncatemacro{\newangleb}{mod(\angle+36,360)}
\penrosedart{\n}{\namex}{\newanglea}{\newlen}{0}
\penrosekite{\n}{\ver}{\newangleb}{\newlen}{1}
\fi
}
\fi
}
\begin{document}
\begin{tikzpicture}
\begin{scope}[yshift=-5.2*\len, rotate=342]
\foreach \level in {0,...,4}{
\begin{scope}[rotate=\level*72]
\coordinate (a) at (0,0);
\penrosekite{\recurs}{a}{0}{\len}{0}
\penrosekite{\recurs}{a}{0}{\len}{1}
\end{scope}
}
\draw[ultra thick,white] (0,0) circle (2.125in);
\end{scope}
\end{tikzpicture}
\end{document}
现在我想修改一下,以便只绘制那些顶点都位于原点 2.125 英寸范围内的图块。(上图中出现了一个半径为该半径的白色圆圈。)要做到这一点,自然的想法是修改命令的定义,将下面\penrosekite
这行替换为
\penrosedrawkite{#2}{#3}{#4}
包含以下几行内容:“如果您要绘制的风筝的每个顶点距离原点 2.125 英寸以内,则绘制该风筝,否则不要绘制它。” 命令也是如此\penrosedart
。我尝试过以各种方式实现这个想法,但都失败了。有没有简单的方法?
答案1
\penrosedrawkite
如果将(和类似的\penrosedrawdart
)的定义更改为
\makeatletter
\newcommand\penrosedrawkite[3]{% ver = starting vertex, angle = direction of first edge, len = length of first edge
\pgfinterruptboundingbox % don't mess with my bounding box
\path (#1)
+(#2+36:#3) coordinate (#1-b)
+(#2:#3) coordinate (#1-c)
+(#2-36:#3) coordinate (#1-d);
\endpgfinterruptboundingbox
\def\maxVeclen{0pt}%
\foreach \suffix in {,-b,-c,-d} {
\pgfextractx\pgf@xb{\pgfpointanchor{#1\suffix}{center}}%
\pgfextracty\pgf@yb{\pgfpointanchor{#1\suffix}{center}}%
\pgfmathparse{veclen(+\the\pgf@xb,+\the\pgf@yb)}%
\ifdim\pgfmathresult pt>\maxVeclen\xdef\maxVeclen{\pgfmathresult pt}\fi
}
\ifdim\maxVeclen<2.125in\relax
\path[penrose kite] (#1)
to[penrose path 1] (#1-b)
to[penrose rev path 2] (#1-c)
to[penrose path 2] (#1-d)
to[penrose rev path 1] (#1);
\fi
}
\makeatother
代码生成:
笔记
- 不要忘记
\makeatletter
和\makeatother
。 - TikZ 的
calc
库可能提供了一种更易于阅读和理解的方法。
答案2
就像 Qrrbrbirlbel 的回答一样,我建议将其定义更改\penrosedrawkite
为:
\makeatletter
\newcommand\penrosedrawkite[3]{% ver = starting vertex, angle = direction of first edge, len = length of first edge
\pgfinterruptboundingbox % don't mess with my bounding box
\path (#1)
+(#2+36:#3) coordinate (#1-b)
+(#2:#3) coordinate (#1-c)
+(#2-36:#3) coordinate (#1-d);
\endpgfinterruptboundingbox
\gdef\maxVeclen{0pt}%
\foreach \suffix in {,-b,-c,-d} {
\path let
\p1 = (#1\suffix.center),
\n1 = {veclen(\x1,\y1)},
\n{res} = {\n1 > \maxVeclen ? \n1 : \maxVeclen}
in
\pgfextra{
\xdef\maxVeclen{\n{res}}
};
}
\ifdim\maxVeclen<2.125in\relax
\path[penrose kite] (#1)
to[penrose path 1] (#1-b)
to[penrose rev path 2] (#1-c)
to[penrose path 2] (#1-d)
to[penrose rev path 1] (#1);
\fi
}
\makeatother
在这个版本中,我没有使用 PGF 宏,而只使用了 TikZ 宏(let
):我认为这个代码更具可读性。
代码生成: