告诉 TikZ 不要在剪切区域之外绘制至少一个坐标的路径

告诉 TikZ 不要在剪切区域之外绘制至少一个坐标的路径

使用 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):我认为这个代码更具可读性。

代码生成:

在此处输入图片描述

相关内容