59

How can I auto-generate a Penrose tiling by TikZ? Here's my code for Mathematica:

GG = (Sqrt[5]+1)/2;
standardthin = {1/2+Sqrt[GG^2-(1/2)^2] I , 0, 1};
standardthick = {GG/2+Sqrt[1-(GG/2)^2] I, 0, GG};
sectthin[n_, t_] := 
         If[n > 0, 
             Join[sectthin[n-2, {t[[2]], t[[3]], (GG*t[[3]]+t[[1]])/(GG+1)}], 
                  sectthick[n-1, {(GG*t[[3]]+t[[1]])/(GG+1), t[[1]], t[[2]]}]],
                  {thin @@ N[t]}];
sectthick[n_, t_] := 
        If[n > 0, 
            Join[sectthick[n-2, {(GG*t[[2]]+t[[3]])/(GoldenRatio+1),t[[1]],t[[2]]}], 
                 sectthin[n-1, {t[[3]], (GG*t[[2]]+t[[3]])/(GG+1), t[[1]]}]],
                 {thick @@ N[t]}];
addtothick[n_, t_, d_] := 
        If[d == 0, {}, 
        Join[{frame @@ t}, 
           If[EvenQ[d], 
              Join[addtothick[n+2, {t[[2]], t[[3]], ((GG+1)*t[[1]]-GG*t[[3]])},d/2], 
                   sectthin[n+1, {((GG+1)*t[[1]]-GG*t[[3]]), t[[1]], t[[2]]}]], 
              Join[addtothin[n+1, {t[[2]], t[[3]], ((GG+1)*t[[1]]-t[[2]])/GG}, (d-1)/2], 
                   sectthin[n-1, {t[[3]], ((GG+1)*t[[1]]-t[[2]])/GG, t[[1]]}]]]]];

addtothin[n_, t_, d_] := 
        If[d == 0, {}, 
        Join[{frame @@ t}, 
          If[EvenQ[d], 
             Join[addtothin[n+2, {((GG+1)*t[[3]]-GG*t[[2]]), t[[1]], t[[2]]}, d/2], 
                  sectthick[n+1, {t[[3]], ((GG+1)*t[[3]]-GG*t[[2]]), t[[1]]}]], 
             Join[addtothick[n+1, {t[[3]], ((GG+1)*t[[2]]-t[[1]])/GG, t[[1]]}, (d -1)/2], 
                  sectthick[n-1, {t[[2]], t[[3]], ((GG+1)*t[[2]]-t[[1]])/GG}]]]]];

triangle[a_, b_, c_] := Graphics[Line[{Re[#], Im[#]} & /@ {a, b, c, a}]];
        thin := triangle;
        thick := triangle;

frame[a_, b_, c_] := Graphics[{Thickness[0.005], Line[{Re[#], Im[#]} & /@ {a, b, c, a}]}]

Show[addtothin[0, standardthin, 200], AspectRatio -> Automatic]

which creates something like this:

this

6
  • Can you provide the atoms that you want to use in the generation?
    – percusse
    Jun 27, 2012 at 16:11
  • 3
    Short answer: by implementing the construction algorithm. ;-) -- While this is an interesting topic (I'd like to see a real answer myself) I think you can improve your question by elaborating a bit. For example: What are your efforts so far? Which penrose tiling (P1, P2 or P3) do you want?
    – Fritz
    Jun 27, 2012 at 16:12
  • 5
    PSTricks can do that ...
    – user2478
    Jun 27, 2012 at 16:21
  • @Fritz: I have a code I wrote in Mathematica, which does P2 :p Does it help posting the code here?
    – Yuji
    Jun 27, 2012 at 16:48
  • @Herbert Can you please undelete your answer to my recent question? I like the code; it leaves something mathematical to think about.
    – kan
    Jun 27, 2012 at 17:16

5 Answers 5

93

Third edit: a faster and more customizable variation!

Features of this version:

  • builds Penrose tilings with Robinson's triangles (semikites and semidarts) or kite and dart.
  • uses TikZ styles to specify filling and drawing.
  • uses TikZ styles to design shapes of each pieces.
  • provides TikZ styles to use Penrose tilings as pattern.

My (long) code is divided in several parts.

Here's the longest part (preamble, some default styles and the macros to build and to draw/fill tilings by recursion):

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

% phi = golden ratio = (1+sqrt(5))/2
% invphi = 1/phi
% n: number of decompositions left to do
% ver: starting vertex
% angle: direction of first edge
% len: lenght of first edge
% rot: sense of rotation (0 => anticlockwise, 1 => clockwise)

\pgfmathsetmacro{\invphi}{2/(1+sqrt(5))}

% default styles
\tikzset{
  % borders style
  penrose line/.style={draw=black,line join=round},
  % semikites and semidarts styles
  penrose clockwise semikite/.style={penrose line},
  penrose anticlockwise semikite/.style={penrose line},
  penrose clockwise semidart/.style={penrose line},
  penrose anticlockwise semidart/.style={penrose line},
  % kites and darts styles
  penrose kite/.style={penrose line},
  penrose dart/.style={penrose line},
  % the three paths (and the three corresponding reverse paths)
  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\penrosedrawclockwisesemikite[3]{% ver, angle, len
  % semikite (clockwise)
  \path (#1) +(#2:#3) coordinate (#1-b) +(#2-36:#3) coordinate (#1-c);
  \path[penrose anticlockwise semikite] (#1)
  to[penrose path 1] (#1-b)
  to[penrose path 2] (#1-c)
  to[penrose path 3] (#1);
}

\newcommand\penrosedrawanticlockwisesemikite[3]{% ver, angle, len
  % semikite (anticlockwise)
  \path (#1) +(#2:#3) coordinate (#1-b) +(#2+36:#3) coordinate (#1-c);
  \path[penrose clockwise semikite] (#1)
  to[penrose path 1] (#1-b)
  to[penrose path 2] (#1-c)
  to[penrose path 3] (#1);
}

\newcommand\penrosesemikite[5]{% n, ver, angle, len, rot
  \ifnum#1=0 % draw or recursive decomposition ?
  \ifnum#5=1 % anticlockwise or clockwise ?
  \penrosedrawclockwisesemikite{#2}{#3}{#4}
  \else
  \penrosedrawanticlockwisesemikite{#2}{#3}{#4}
  \fi
  \else
  {
    % decomposition (semikite => 2 semikites and 1 semidart)
    \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 % anticlockwise or clockwise ?
    \path (\ver) ++(\angle-36:\len) coordinate (\namex);
    \pgfmathtruncatemacro{\newanglea}{mod(\angle+108,360)}
    \penrosesemikite{\n}{\namex}{\newanglea}{\newlen}{1}
    \penrosesemikite{\n}{\namex}{\newanglea}{\newlen}{0}
    \penrosesemidart{\n}{\ver}{\angle}{\newlen}{1}
    \else
    \path (\ver) ++(\angle+36:\len) coordinate (\namex);
    \pgfmathtruncatemacro{\newanglea}{mod(\angle-108,360)}
    \penrosesemikite{\n}{\namex}{\newanglea}{\newlen}{0}
    \penrosesemikite{\n}{\namex}{\newanglea}{\newlen}{1}
    \penrosesemidart{\n}{\ver}{\angle}{\newlen}{0}
    \fi
  }
  \fi
}

\newcommand\penrosedrawkite[3]{% ver, angle, len
  % kite (current clockwise semikite + opposite anticlockwise semikite)
  \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, ver, angle, len, rot
  \ifnum#1=0 % draw or recursive decomposition ?
  \ifnum#5=1 % draw kite if current semikite is clockwise
  \penrosedrawkite{#2}{#3}{#4}
  \fi
  \else
  {
    % decomposition (semikite => 2 semikites and 1 semidart)
    \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 % anticlockwise or clockwise ?
    \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\penrosedrawclockwisesemidart[3]{
  % semidart (clockwise)
  \path (#1)
  +(#2:#3)            coordinate (#1-b)
  +(#2-36:#3*\invphi) coordinate (#1-c);
  \path[penrose anticlockwise semidart] (#1)
  to[penrose path 3] (#1-b)
  to[penrose path 2] (#1-c)
  to[penrose path 1] (#1);
}

\newcommand\penrosedrawanticlockwisesemidart[3]{
  % semidart (anticlockwise)
 \path (#1)
  +(#2:#3)            coordinate (#1-b)
  +(#2+36:#3*\invphi) coordinate (#1-c);
  \path[penrose clockwise semidart] (#1)
  to[penrose path 3] (#1-b)
  to[penrose path 2] (#1-c)
  to[penrose path 1] (#1);
 }

\newcommand\penrosesemidart[5]{% n, ver, angle, len, rot
  \ifnum#1=0 % draw or recursive decomposition ?
  \ifnum#5=1 % anticlockwise or clockwise ?
  \penrosedrawclockwisesemidart{#2}{#3}{#4}
  \else
  \penrosedrawanticlockwisesemidart{#2}{#3}{#4}
  \fi
  \else
  {
    % decomposition (semidart => 1 semikite and 1 semidart)
    \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 % anticlockwise or clockwise
    \pgfmathsetmacro{\newanglea}{mod(\angle-144,360)}
    \pgfmathsetmacro{\newangleb}{mod(\angle-36,360)}
    \penrosesemidart{\n}{\namex}{\newanglea}{\newlen}{1}
    \penrosesemikite{\n}{\ver}{\newangleb}{\newlen}{0}
    \else
    \pgfmathtruncatemacro{\newanglea}{mod(\angle+144,360)}
    \pgfmathtruncatemacro{\newangleb}{mod(\angle+36,360)}
    \penrosesemidart{\n}{\namex}{\newanglea}{\newlen}{0}
    \penrosesemikite{\n}{\ver}{\newangleb}{\newlen}{1}
    \fi
  }
  \fi
}

\newcommand\penrosedrawdart[3]{
  % dart (current clockwise semidart + opposite anticlockwise semidart)
  \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 % draw or recursive decomposition ?
  \ifnum#5=1 % draw dart if current semidart is clockwise
  \penrosedrawdart{#2}{#3}{#4}
  \fi
  \else
  {
    % decomposition (semidart => 1 semikite and 1 semidart)
    \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 % anticlockwise or clockwise
    \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
}

The second part provides four styles to use Penrose tilings as fill pattern of arbitrary path:

\newcommand\setveclength[5]{%
  %\typeout{-#1-#2-#3-#4-}%
  \pgfpointanchor{#2}{#3}
  \pgfgetlastxy{\penrosexa}{\penroseya}
  \pgfpointanchor{#4}{#5}
  \pgfgetlastxy{\penrosexb}{\penroseyb}
  \pgfmathparse{veclen(\penrosexb-\penrosexa,\penroseyb-\penroseya)}
  \edef#1{\pgfmathresult}
}

\tikzset{
  semipenrose sym fill/.style={
    path picture={
      \setveclength{\mylen}%
      {path picture bounding box}{center}%
      {path picture bounding box}{north west}
      \pgfmathsetmacro{\len}{1.17*\mylen}
      \begin{scope}[shift={(path picture bounding box.center)}]
        \foreach \level in {0,...,4}{
          \begin{scope}[rotate=\level*72+9]
            \coordinate (a) at (0,0);
            \penrosesemikite{#1}{a}{0}{\len pt}{0}
            \penrosesemikite{#1}{a}{0}{\len pt}{1}
          \end{scope}
        }
      \end{scope}
    }
  },
  semipenrose fill/.style={
    path picture={
      \setveclength{\mylen}%
      {path picture bounding box}{center}%
      {path picture bounding box}{west}
      \pgfmathsetmacro{\len}{1.33*\mylen}
      \begin{scope}[shift={(path picture bounding box.center)},rotate=9]
        \coordinate (a) at (-\len pt,0);
        \path (a) +(36:2*\len pt) coordinate (b);
        \path (a) +(-36:2*\len pt) coordinate (c);
        \penrosesemidart{#1}{b}{36-180}{2*\len pt}{1}
        \penrosesemikite{#1}{a}{0}{2*\len pt}{0}
        \penrosesemikite{#1}{a}{0}{2*\len pt}{1}
        \penrosesemidart{#1}{c}{180-36}{2*\len pt}{0}
      \end{scope}
    }
  },
  penrose sym fill/.style={
    path picture={
      \setveclength{\mylen}%
      {path picture bounding box}{center}%
      {path picture bounding box}{north west}
      \pgfmathsetmacro{\len}{1.23*\mylen}
      \begin{scope}[shift={(path picture bounding box.center)}]
        \foreach \level in {0,...,4}{
          \begin{scope}[rotate=\level*72+9]
            \coordinate (a) at (0,0);
            \penrosekite{#1}{a}{18}{\len pt}{0}
            \penrosekite{#1}{a}{18}{\len pt}{1}
          \end{scope}
        }
      \end{scope}
    }
  },
  penrose fill/.style={
    path picture={
      \setveclength{\mylen}%
      {path picture bounding box}{center}%
      {path picture bounding box}{west}
      \pgfmathsetmacro{\len}{2.38*\mylen}
      \begin{scope}[shift={(path picture bounding box.center)},rotate=9]
        \coordinate (a) at (-\len pt,0);
        \path (a) +(36:2*\len pt) coordinate (b);
        \path (a) +(-36:2*\len pt) coordinate (c);
        \penrosedart{#1}{b}{36-180}{2*\len pt}{1}
        \penrosekite{#1}{a}{0}{2*\len pt}{0}
        \penrosekite{#1}{a}{0}{2*\len pt}{1}
        \penrosedart{#1}{c}{180-36}{2*\len pt}{0}
      \end{scope}
    }
  },
}

...then some examples.

First example (with default values):

\usetikzlibrary{shapes.arrows}
\pgfmathsetlengthmacro{\len}{4cm}
\pgfmathtruncatemacro{\recurs}{5}
\begin{document}
\begin{tikzpicture}
  \tikzset{
    gray arrow/.style={
      fill=gray,minimum height=.5cm,rotate=-90,
      single arrow,single arrow head extend=1mm,
    },
  }
  % explanations of building process
  \begin{scope}
    \path (-4cm,0)   coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemikite{0}{a}{0}{1.5cm}{0}
    \penrosesemikite{1}{b}{0}{1.5cm}{0}
    \node[gray arrow] at (arr){};

    \path (-2cm,0)   coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemikite{0}{a}{0}{1.5cm}{1}
    \penrosesemikite{1}{b}{0}{1.5cm}{1}
    \node[gray arrow] at (arr){};

    \path (0cm,0)    coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemidart{0}{a}{0}{1.5cm}{0}
    \penrosesemidart{1}{b}{0}{1.5cm}{0}
    \node[gray arrow] at (arr){};

    \path (2cm,0)    coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemidart{0}{a}{0}{1.5cm}{1}
    \penrosesemidart{1}{b}{0}{1.5cm}{1}
    \node[gray arrow] at (arr){};
  \end{scope}
  % example of semidarts and semikites
  \begin{scope}[yshift=-2*\len]
    \foreach \level in {0,...,4}{
      \begin{scope}[rotate=\level*72]
        \coordinate (a) at (0,0);
        \penrosesemikite{\recurs}{a}{0}{\len}{0}
        \penrosesemikite{\recurs}{a}{0}{\len}{1}
      \end{scope}
    }
  \end{scope}
  % explanations of building process
  \begin{scope}[yshift=-3.3*\len]
    \path (-3cm,0)   coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemikite{0}{a}{0}{1.5cm}{1}
    \penrosesemikite{0}{a}{0}{1.5cm}{0}
    \penrosekite{0}{b}{0}{1.5cm}{1}
    \penrosekite{0}{b}{0}{1.5cm}{0}
    \node[gray arrow] at (arr){};

    \path (1cm,0)    coordinate (a)
    +(.75cm,-1.25cm) coordinate (arr)
    ++(0,-2.5cm)     coordinate (b);
    \penrosesemidart{0}{a}{36}{1.5cm}{1}
    \penrosesemidart{0}{a}{-36}{1.5cm}{0}
    \penrosedart{0}{b}{36}{1.5cm}{1}
    \penrosedart{0}{b}{-36}{1.5cm}{0}
    \node[gray arrow] at (arr){};
  \end{scope}
  % example of darts and kites
  \begin{scope}[yshift=-5.2*\len]
    \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}
    }
  \end{scope}
\end{tikzpicture}
\end{document}

first example (with default values)

The same example by appending the following lines :

  \tikzset{
    % style for borders
    line/.style={draw=white,rounded corners=0pt},
    % styles for semikites and semidarts
    penrose clockwise semikite/.style={fill=orange,line},
    penrose anticlockwise semikite/.style={fill=orange!50!black,line},
    penrose clockwise semidart/.style={fill=cyan!80,line},
    penrose anticlockwise semidart/.style={fill=cyan!50,line},
    % styles for kites and darts
    penrose kite/.style={fill=red!75!black,line},
    penrose dart/.style={fill=olive!75!white,line},
  }

second example with colors

Third example with shape variations (by appending the following lines):

  % set the three paths (and the three reverse paths)
  \pgfmathtruncatemacro{\defang}{36}
  \tikzset{
    common/.style={relative,looseness=1},
    penrose path 1/.style={out=\defang,in=180-\defang,common},
    penrose path 2/.style={out=\defang,in=180-\defang,common},
    penrose path 3/.style={out=-\defang,in=180+\defang,common},
    penrose rev path 1/.style={out=-\defang,in=180+\defang,common},
    penrose rev path 2/.style={out=-\defang,in=180+\defang,common},
    penrose rev path 3/.style={out=\defang,in=180-\defang,common},
  }

third example with shape variations

Last example: using Penrose tiling as pattern.

\begin{document}
\begin{tikzpicture}
  % some colors
  \tikzset{
    penrose line/.style={draw=white,line join=round,
      line width=.1pt,rounded corners=2pt},
    penrose kite/.style={left color=blue,right color=cyan,penrose line},
    penrose dart/.style={left color=yellow,right color=red,penrose line},
  }
  \begin{scope}
    % four styles exists: 'penrose fill', 'penrose sym fill',
    % 'semipenrose fill' and 'semipenrose sym fill'.
    % (argument is depth of recursion)
    \path[penrose fill=7,rounded corners=0mm]
    (0,.2) -- (4,0) -- (3,3) -- (2,2) -- (1,2) -- cycle;
  \end{scope}
\end{tikzpicture}
\end{document}

Penrose pattern

7
  • Is that really a penrose tiling? Interesing nonetheless.
    – Fritz
    Jun 29, 2012 at 6:30
  • 1
    @Fritz Each of the ten triangles are a Penrose tiling. If you are in doubt, follow the first link I gave. Jun 29, 2012 at 6:35
  • Great addition!! Do you think that it can be transformed into a custom fill pattern as in this one : tex.stackexchange.com/questions/54358/… ? Then Herbert's answer is truly replicated. By the way here is an interesting lecture from Peter Lu if you want to kill time: Harvard Colloquium Lecture
    – percusse
    Jun 30, 2012 at 18:24
  • @percusse: Good question! A fill pattern (as defined by TikZ/pgf) is periodic. Penrose tiling is non-periodic. How to transform a non-periodic tiling into a periodic pattern ? Jun 30, 2012 at 19:37
  • 3
    Fantastic. I hope this finds its way to the official version!
    – percusse
    Jul 16, 2012 at 23:35
52

run with xelatex or pdflatex -shell-escape or pdflatex -enable-write18 (for Windows)

\documentclass{article}
\usepackage[pdf]{pstricks}
\begin{document}

\begin{pspicture}(10,8)
\pspolygon[fillstyle=penrose](0,4)(5,8)(10,4)
\pspolygon[fillstyle=penrose,psscale=0.2](0,0)(5,4)(10,0)
\end{pspicture}

\begin{pspicture}(10,8)
\pspolygon[fillstyle=penroseA,hatchcolor=white](0,4)(5,8)(10,4)
\pspolygon[fillstyle=penroseA,psscale=0.5,
  kitecolor=yellow,dartcolor=cyan,hatchcolor=blue](0,0)(5,4)(10,0)
\end{pspicture}
\end{document}

enter image description here

3
  • 1
    What pstricks documentation should I read to learn more? Jun 30, 2012 at 21:45
  • May be a bug with \pspolygon[fillstyle=penrose,psscale=0.2](0,0)(0,15)(15,15)(15,0)? Jun 30, 2012 at 21:54
  • you need the pstricks.tex and pstricks.pro from texnik.dante.de The example code can be found in texnik.dante.de/doc/generic/pstricks/pst-news12.pdf With the fillstyle penrose an extremely fast algoithm is used the reason why it can be a problem with small scaling values because the predefined area then can be smaller than the clipping area. However, I'll improve the code. It doesn't happened with the fill style penroseA which uses an other algorithm.
    – user2478
    Jul 2, 2012 at 9:11
42

This is my first attempt with TikZ. I try to make this : Penrose_tiling(P1). I agree with Herbert that the better method is to use a recursion but for the moment, I don't know how to begin a recursion in this case. So here I try only to reproduce the drawing that you can see in the link.

version 7

Remarks :

1) Now with the same methods, it's easy to get the complete figure given in the link.

2) Now it's interesting to find another way ...

3) I changed some names for options like penta instead of hexa. I added the green stars surrounded by five pentagons. I added the code for the little part at the end of my answer.

Some explanations. First I draw a pentagone (s) then I draw inside (s) five pentagons. I used \l=0.382 cm because I have not had the courage to make an effort to calculate the ratio between the lengths of pentagons ( but it's not a real great problem).

enter image description here

\documentclass{scrartcl}
\usepackage[dvipsnames]{xcolor}  
\usepackage{tikz}
\usetikzlibrary{shapes}

\begin{document} 
\def\l{0.382cm} 

\begin{tikzpicture} [scale=2,
                     transform shape,
                     penta/.style= {shape                 =  regular polygon,
                                   regular polygon sides = 5,
                                   minimum size          = #1,
                                   inner sep             = 0,
                                   outer sep             = 0,
                                   anchor                = south,
                                   fill                  = lightgray}] 
\node[penta=1cm,fill=yellow] (s) {};
\foreach \i in {1,...,5} {\node[draw,penta=\l,anchor=corner \i]  at (s.corner \i) {};}
\node[penta=\l,anchor=center,fill=MidnightBlue,rotate=36]  at (s.center) {}; 

\begin{scope}[rotate=108,sub penta/.style= {draw,penta=\l,anchor=corner \j}]
         \foreach \i in {1,...,5}{%
      \node[penta=1cm,fill=yellow,anchor=corner \i] (s\i) at (s.corner \i){};
      \foreach \j in {1,...,5}{%
         \pgfmathtruncatemacro\k{mod(\i,5)+1}
         \ifnum\j=\i \tikzset{sub penta/.append style={fill=red}}         \fi
         \ifnum\j=\k \tikzset{sub penta/.append style={fill=red}}         \fi
         \node[sub penta] (s\i\j) at (s\i.corner \j){};
         }
   \node[penta=\l,anchor=center,fill=MidnightBlue,rotate=36]  at (s\i.center) {};
     \pgfmathtruncatemacro\ii{mod(\i+1,5)+1} 
     \pgfmathtruncatemacro\iii{mod(\i+3,5)+1} 
   \node[penta=\l,anchor=corner \ii,fill=red,rotate=36,draw] (ss\i)  at (s\i.corner \iii) {};
                     }  
    \foreach \i in {1,...,5}{%
      \pgfmathtruncatemacro\ii{mod(\i,5)+1}
      \pgfmathtruncatemacro\j{mod(\i+1,5)+1}
      \pgfmathtruncatemacro\k{mod(\i+2,5)+1}
      \pgfmathtruncatemacro\h{mod(\i+2,5)+1} 
      \pgfmathtruncatemacro\l{mod(\i+3,5)+1} 
      \fill[MidnightBlue!40,draw=black] (s.corner \i) 
                                     -- (s\i\i.corner \l) 
                                     -- (s\i\i.corner \k) 
                                     -- (ss\i.corner \ii)  
                                     -- (ss\i.corner \i)
                                     -- (s\ii\j.corner \l) 
                                     -- (s\ii\j.corner \h) 
                                     -- cycle;
    }   
   \foreach \i in {1,...,5}{%
   \pgfmathtruncatemacro\j{mod(\i+2,5)+1}  
     \node[penta=1cm,fill=yellow,anchor=corner \i,rotate=-36] (t\i) at (s\i\j.corner \j) {t}; 
     \node[penta=1cm,fill=yellow,anchor=corner \i,rotate=108] (u\i) at (s\i\j.corner \j) {};
      \foreach \k in {1,...,5} {%
      \node[draw,penta=\l,anchor=corner \k,rotate=-36,fill=red] (t\i\k)  at (t\i.corner \k) {};}
      \foreach \k in {1,...,5} {%
      \node[draw,penta=\l,anchor=corner \k,rotate=108,fill=red]
            (u\i\k) at  (u\i.corner \k) {};}  
           \node[penta=\l,anchor=center,fill=MidnightBlue]  at (t\i.center) {}; 
           \node[penta=\l,anchor=center,fill=MidnightBlue]  at (u\i.center) {}; 
           } 
           \foreach \i in {1,...,5} {%
           \pgfmathtruncatemacro\j{mod(\i+3,5)+1}
           \node[sub penta,fill=lightgray,anchor=center,rotate=36]  at (t\i\j){}; 
           \node[sub penta,fill=lightgray,anchor=center,rotate=36]  at (u\j\i){};     
           }   

   \foreach \i in {1,...,5} {%
            \pgfmathtruncatemacro\j{mod(\i+2,5)+1}
            \pgfmathtruncatemacro\k{mod(\i+3,5)+1}
            \pgfmathtruncatemacro\h{mod(\i+1,5)+1} 
            \pgfmathtruncatemacro\l{mod(\i,5)+1} 
      \fill[green] (ss\i.corner \h)  -- (t\l\h.corner \k) --
                   (u\i\j.corner \i) -- (ss\i.corner \k)  --
                   (u\i\j.corner \j) -- cycle;
      }   
\end{scope}
 \foreach \j in {1,...,5}{%
      \pgfmathtruncatemacro\k{mod(\j+1,5)+1} 
  \begin{scope}[rotate=108]
    \node[penta=1cm,fill=yellow,anchor=corner \k] (xs\j) at (u\j.corner \k) {};
     \foreach \i in {1,...,5} {\node[draw,penta=\l,anchor=corner \i]  at 
                              (xs\j.corner \i) {};}
   \node[penta=\l,anchor=center,fill=MidnightBlue,rotate=36]  at (xs\j.center) {}; 
  \end{scope}  
   }     
  \foreach \i in {1,...,5}{% 
   \pgfmathtruncatemacro\j{mod(\i,5)+1}
   \pgfmathtruncatemacro\k{mod(\i+1,5)+1} \pgfmathtruncatemacro\h{mod(\i+2,5)+1}
  \begin{scope}[sub penta/.style= {draw,penta=\l},rotate=36+(\i-1)*72]
  \node[sub penta,red,anchor=corner 4,draw=black] (p1) at (t\j\i.corner \j) {};
  \node[sub penta,red,anchor=corner 3,draw=black] (p2) at (p1.corner 5) {};
  \node[sub penta,red,anchor=corner 4,draw=black] (p3) at (p2.corner 1) {};
  \node[sub penta,red,anchor=corner 5,draw=black] (p4) at (p3.corner 2) {};
  \node[sub penta,red,anchor=corner 1,draw=black] (p5) at (p4.corner 3) {}; 
  \fill[green] (p1.corner 5) -- (p3.corner 2) -- (p5.corner 4) -- (p2.corner 1) -- 
   (p4.corner 3) -- cycle;
  \fill[MidnightBlue!40,draw=black] (p1.corner 3) -- (p1.corner 4) -- 
                                    (t\j\i.corner \k)  --  
                                    (t\j\j.corner \i)  -- 
                                    (t\j\j.corner \j) -- 
                                    (u\j\j.corner \k)  -- (u\j\j.corner \h) -- cycle;        
  \end{scope}}  
    \end{tikzpicture}  

\end{document}   

Green star surrounded by five pentagons

   \begin{tikzpicture} [scale=2,
                         transform shape,
                         penta/.style= {shape                 =  regular polygon,
                                       regular polygon sides = 5,
                                       minimum size          = #1,
                                       inner sep             = 0,
                                       outer sep             = 0,
                                       anchor                = south,
                                       fill                  = lightgray}] 
      \begin{scope}[sub penta/.style= {draw,penta=\l}]
      \node[sub penta,red,anchor=corner 4,draw=black] (p1)  {};
      \node[sub penta,red,anchor=corner 3,draw=black] (p2) at (p1.corner 5) {};
      \node[sub penta,red,anchor=corner 4,draw=black] (p3) at (p2.corner 1) {};
      \node[sub penta,red,anchor=corner 5,draw=black] (p4) at (p3.corner 2) {};
      \node[sub penta,red,anchor=corner 1,draw=black] (p5) at (p4.corner 3) {}; 
      \fill[green] (p1.corner 5) -- (p3.corner 2) -- (p5.corner 4) -- 
                   (p2.corner 1) -- (p4.corner 3) -- cycle;      
      \end{scope}

 \end{tikzpicture}

enter image description here

With

  \def\r{2.31}
  \draw[shift=(s.center),clip] (-54:\r) -- (18:\r) -- (90:\r) -- (162:\r) -- (234:\r) -- cycle;  

we get :

enter image description here

2
  • without a recursion you don't get the correct Penrose tiling
    – user2478
    Jun 28, 2012 at 12:19
  • I agree that a recursion is the better way but I think that it's possible to reproduce Penrose_Tiling_(P1).svg en.wikipedia.org/wiki/File%3aPenrose_Tiling_%28P1%29.svg . The only difficulty is to add the colors. Is it possible to make this drawing with pstricks ? Jun 28, 2012 at 12:41
15

Strongly inspired by Paul Gaborit's answer, a while ago I put together my own version of a Penrose tiling generator. In clearing out a few bits of code, I decided I may as well upload it to CTAN. (It should make its way into the official repositories in due time.)

Here's an example, more can be found in the package documentation. This example demonstrates manual placement by aligning tiles, together with tile deformation.

\documentclass{article}
\usepackage[scale=.91]{geometry}
\usepackage[svgnames]{xcolor}
\usepackage{tikz}
\usetikzlibrary{penrose}

\colorlet{thinRhombus}{DarkOrchid}
\colorlet{thickRhombus}{DarkSlateGray}
\colorlet{circleArc}{RosyBrown}
\colorlet{longArc}{LawnGreen}

\colorlet{kite}{HotPink}
\colorlet{dart}{Fuchsia}

\colorlet{goldenTriangle}{Gold}
\colorlet{reverseGoldenTriangle}{Magenta}
\colorlet{goldenGnomon}{Cyan}
\colorlet{reverseGoldenGnomon}{LimeGreen}

\begin{document}
\begin{tikzpicture}
\path[save Penrose path=a] (0,0) to[out=-30,in=100] (1,0);
\path[save Penrose path=b] (0,0) to[out=0,in=140] (1,0);
\path[save Penrose path=c] (0,0) to[out=-40,in=140] (1,0);
\MakePenroseTile{thin rhombus}
\MakePenroseTile{thick rhombus}
\MakePenroseTile{golden triangle}
\MakePenroseTile{reverse golden triangle}
\MakePenroseTile{golden gnomon}
\MakePenroseTile{reverse golden gnomon}
\MakePenroseTile{kite}
\MakePenroseTile{dart}
\end{tikzpicture}

\begin{tikzpicture}[
  scale=3,
  every Penrose pic/.style={
    transform shape,
    draw=black,
    ultra thick,
  },
  every kite/.style={
    fill=kite,
  },
  every dart/.style={
    fill=dart,
  },
  every circle arc/.style={
    line width=1mm,
    draw=circleArc
  },
  every long arc/.style={
    line width=2mm,
    draw=longArc
  }
]
\pic[dart,name=a0];
\foreach[evaluate=\k as \kmo using int(\k-1)] \k in {1,...,4} {
  \pic[dart,name=a\k,align with={a\kmo} along a];
}
\foreach \k in {0,...,4} {
  \foreach \l/\e/\ee in {0/c/a,1/C/A} {
    \pic[kite,name=b\l\k,align with={a\k} along \e];
    \pic[dart,name=c\l\k,align with={b\l\k} along \ee];
    \pic[kite,name=d\l\k,align with={c\l\k} along \e];
  }
  \pic[kite,name=e\k,align with={c0\k} along C];
  \pic[dart,name=f\k,align with={c0\k} along a];
  \foreach \e in {c,C} {
    \pic[kite,name=g\k,align with={f\k} along \e];
  }
}
\end{tikzpicture}
\end{document}

Result

Penrose tiling

6

Altermundus' lovely green star, like much of his code, can do with a simple loop.

\documentclass{scrartcl}
\usepackage[dvipsnames]{xcolor}
\usepackage{tikz}
\usetikzlibrary{shapes}
\def\ydo#1,#2,#3,#4;{%
  \ifx\ydo#1\else
    \noexpand\node[sub penta,red,anchor=corner #1,draw=black]%
      (p#2)\if0#3\else at(p#3.corner #4)\fi{};
    \expandafter\ydo
  \fi
}
\begin{document}
\def\alterl{0.382cm}
\begin{tikzpicture}[
  scale=2,transform shape,
  penta/.style={%
    shape=regular polygon,regular polygon sides=5,minimum size=#1,
    inner sep=0pt,outer sep=0pt,anchor=south,fill=lightgray
  }
]
  \begin{scope}[sub penta/.style={draw,penta=\alterl}]
    \pgfextra{%
      \edef\x{\ydo 4,1,0,0;3,2,1,5;4,3,2,1;5,4,3,2;1,5,4,3;\ydo,,,;}\x
    }%
    \fill[green](p1.corner 5)--(p3.corner 2)--(p5.corner 4)
      --(p2.corner 1)--(p4.corner 3)--cycle;
  \end{scope}
\end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .