This is machine translation

Translated by Microsoft
Mouseover text to see original. Click the button below to return to the English version of the page.

Penrose and Fourier Design Playing Cards 2

Posted by Cleve Moler,

MathWorks is creating a deck of playing cards that will be offered as gifts at our trade show booths. The design of the cards is based on Penrose Tilings and plots of the Finite Fourier Transform Matrix.

Contents

Penrose Tilings

This is an example of a Penrose tiling.

Penrose tilings are aperiodic tilings of the plane named after Oxford emeritus physicist Roger Penrose, who studied them in the 1970s. MathWorks' Steve Eddins has written a paper about his MATLAB program for generating Penrose tilings. I will include the paper in this blog post. Steve has also submitted his paper and code to the MATLAB Central File Exchange, Penrose Rhombus Tiling.

The Suits

Look carefully at the example tiling. It's made entirely from two rhombuses, one colored gold and one colored blue. Each rhombus, in turn, is made from two isosceles triangles. These shapes inspired MathWorks designer Gabby Lydon to reimagine the traditional symbols for the four suits in a deck of cards.

Here are the diamonds and hearts.

Here are the clubs and spades.

The Face Cards

The theme of triangles and rhombuses is continued in the face cards.

The Back

The backs of the cards feature two mathematical objects that I have described in this blog. I wrote a five-part series about our logo five years ago, beginning with part one. And, I wrote about the graphic produced by the Finite Fourier Transform matrix.

Start with a prime integer.

   n = 31;

Take a Fourier transform of the columns of the identity matrix.

   F = fft(eye(n,n));

Plot it. Because 31 is prime, this is the complete graph on 31 points. Every point on the circumference is connected to every other point.

   p = plot(F);
   axis square
   axis off

That's too colorful.

   color = get(gca,'colororder');
   set(p,'color',color(1,:))

Now graphic design software takes over and makes room for the logo.

Cleve's Lab

All this has reminded me of two FFT-related apps from Numerical Computing with MATLAB, fftgui and fftmatrix. I've added them to Cleve's Laboratory with Version 3.9. This is the image from fftmatrix for n = 12. Because 12 is not prime, this is not a completely connected graph. You can vary both the matrix order and the columns to be transformed.

Now, here is Steve's paper about Penrose tiling.

Penrose Rhombus Tiling

by Steve Eddins

This story is about creating planar tilings like this:

This is an example of a Penrose tiling. Penrose tilings are aperiodic tilings that named after Roger Penrose, who studied them in the 1970s. This particular form, made from two rhombuses, is called a P3 tiling.

Four Types of Triangles

Construction of a Penrose P3 tiling is based on 4 types of isosceles triangles. The types are labeled A, A', B, and B'. The A and A' triangles have an apex angle of 36 degrees, and the B and B' triangles have an apex angle of 72 degrees.

subplot(2,2,1)
showLabeledTriangles(aTriangle([],-1,1))

subplot(2,2,2)
showLabeledTriangles(apTriangle([],-1,1))

subplot(2,2,3)
showLabeledTriangles(bTriangle([],-1,1))

subplot(2,2,4)
showLabeledTriangles(bpTriangle([],-1,1))
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
vertices =
  -1.0000 + 0.0000i   0.0000 + 0.7265i   1.0000 + 0.0000i
vertices =
  -1.0000 + 0.0000i   0.0000 + 0.7265i   1.0000 + 0.0000i

Triangle Functions and Triangle Representation

Before proceeding further, let's pause to look at what the functions aTriangle, apTriangle, bTriangle, and bpTriangle do.

The function aTriangle takes three arguments: aTriangle(apex,left,right). Each of the three arguments is a point in the complex plane that represents one triangle vertex. You specify any two points, passing in the third point as [], and aTriangle computes the missing vertex for you. Here's an example showing how to compute a type A triangle whose base is on the real axis, extending from -1 to 1.

t_a = aTriangle([],-1,1)
t_a =
  1×4 table
      Apex       Left    Right    Type
    _________    ____    _____    ____
    0+3.0777i     -1       1       A  

The result is returned as a table, which is convenient because we'll be creating large collections of these triangles, and it is helpful to be able to refer to the different vertices of the triangles using the notation t.Apex, t.Left, and t.Right.

The function showLabeledTriangles takes a table of triangles and displays them all, with the triangle types and their sides labeled. (We'll talk more about the labeling of the sides below.)

t_b = bTriangle(t_a.Apex,t_a.Right,[]);
T = [t_a ; t_b]
clf
showLabeledTriangles(T)
T =
  2×4 table
      Apex       Left        Right        Type
    _________    ____    _____________    ____
    0+3.0777i     -1         1+0i          A  
    0+3.0777i      1     2.618+4.9798i     B  
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
   1.0000 + 0.0000i   0.0000 + 3.0777i   2.6180 + 4.9798i

Triangle Side Labels

The markers on the sides of the triangles help us to distinguish between A and A' triangles, as well as between B and B' triangles. For example, an A triangle has the circle marker on the left side (assuming the apex is oriented at the top), whereas the A' triangle has the circle marker on the right side.

The side labels also help us confirm whether we have a correct arrangement of triangles in our Penrose tiling. Triangles are only allowed to share an edge in the Penrose P3 tiling if the side markers align together and are the same. For example, in the two triangles shown above, the square marker on the right edge of the triangle lines up with the square marker on the left edge of the B triangle. If we had used a B' triangle instead, the markers would not have been identical.

t_bp = bpTriangle(t_a.Apex,t_a.Right,[]);
T = [t_a ; t_bp];
showLabeledTriangles(T)
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
   1.0000 + 0.0000i   0.0000 + 3.0777i   2.6180 + 4.9798i

Making the Two Types of Rhombus

In the Penrose P3 tiling, one rhombus is made from an A and an A' triangle, and the other is made from a B and a B' triangle.

subplot(1,2,1)
r1 = [ ...
    aTriangle([],-1,1)
    apTriangle([],1,-1)];
showLabeledTriangles(r1)

subplot(1,2,2)
r2 = [ ...
    bTriangle([],-1,1)
    bpTriangle([],1,-1)];
showLabeledTriangles(r2)
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
   1.0000 + 0.0000i   0.0000 - 3.0777i  -1.0000 + 0.0000i
vertices =
  -1.0000 + 0.0000i   0.0000 + 0.7265i   1.0000 + 0.0000i
   1.0000 + 0.0000i   0.0000 - 0.7265i  -1.0000 + 0.0000i

Triangle Decomposition

Construction of the P3 tiling proceeds by starting with one triangle and then successively decomposing it. Each of the four types of triangles has a different rule for decomposition.

An A triangle decomposes into an A triangle and a B' triangle.

t_a = aTriangle([],-1,1);
t_a_d = decomposeATriangle(t_a)

clf
subplot(1,2,1)
showLabeledTriangles(t_a)
lims = axis;
subplot(1,2,2)
showLabeledTriangles(t_a_d)
axis(lims)
t_a_d =
  2×4 table
         Apex            Left            Right         Type
    _______________    _________    _______________    ____
         -1+0i         1+0i         0.61803+1.1756i     A  
    0.61803+1.1756i    0+3.0777i         -1+0i          Bp 
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
vertices =
   1.0000 + 0.0000i  -1.0000 + 0.0000i   0.6180 + 1.1756i
   0.0000 + 3.0777i   0.6180 + 1.1756i  -1.0000 + 0.0000i

You can look at the very short implementation of decomposeATriangle to see how this is done.

function out = decomposeATriangle(in)
out = [ ...
    aTriangle(in.Left,in.Right,[])
    bpTriangle([],in.Apex,in.Left) ];

The smaller A triangle is determined by placing its apex at the left vertex of the input triangle and placing its left vertex at the right vertex of the input triangle.

The smaller B' triangle is determined by placing its left vertex at the apex of the input triangle and placing its right vertex at the left vertex of the input triangle.

There are similar rules for decomposing the other three types of triangles.

t_ap = apTriangle([],-1,1);
t_ap_d = decomposeApTriangle(t_ap)

clf
subplot(1,2,1)
showLabeledTriangles(t_ap)
lims = axis;
subplot(1,2,2)
showLabeledTriangles(t_ap_d)
axis(lims)
t_ap_d =
  2×4 table
          Apex                Left            Right       Type
    ________________    ________________    __________    ____
           1+0i         -0.61803+1.1756i    -1+0i          Ap 
    -0.61803+1.1756i           1+0i          0+3.0777i     B  
vertices =
  -1.0000 + 0.0000i   0.0000 + 3.0777i   1.0000 + 0.0000i
vertices =
  -0.6180 + 1.1756i   1.0000 + 0.0000i  -1.0000 + 0.0000i
   1.0000 + 0.0000i  -0.6180 + 1.1756i   0.0000 + 3.0777i
t_b = bTriangle([],-1,1);
t_b_d = decomposeBTriangle(t_b)

clf
subplot(2,1,1)
showLabeledTriangles(t_b)
lims = axis;
subplot(2,1,2)
showLabeledTriangles(t_b_d)
axis(lims)
t_b_d =
  3×4 table
          Apex              Left              Right          Type
    _________________    ___________    _________________    ____
     0.23607+0i           1+0i                 0+0.72654i     B  
     0.23607+0i           0+0.72654i    -0.38197+0.44903i     A  
    -0.38197+0.44903i    -1+0i           0.23607+0i           Bp 
vertices =
  -1.0000 + 0.0000i   0.0000 + 0.7265i   1.0000 + 0.0000i
vertices =
   1.0000 + 0.0000i   0.2361 + 0.0000i   0.0000 + 0.7265i
   0.0000 + 0.7265i   0.2361 + 0.0000i  -0.3820 + 0.4490i
  -1.0000 + 0.0000i  -0.3820 + 0.4490i   0.2361 + 0.0000i
t_bp = bpTriangle([],-1,1);
t_bp_d = decomposeBpTriangle(t_bp)

clf
subplot(2,1,1)
showLabeledTriangles(t_bp)
lims = axis;
subplot(2,1,2)
showLabeledTriangles(t_bp_d)
axis(lims)
t_bp_d =
  3×4 table
          Apex                 Left              Right       Type
    _________________    _________________    ___________    ____
    -0.23607+0i                 0+0.72654i    -1+0i           Bp 
    -0.23607+0i           0.38197+0.44903i     0+0.72654i     Ap 
     0.38197+0.44903i    -0.23607+0i           1+0i           B  
vertices =
  -1.0000 + 0.0000i   0.0000 + 0.7265i   1.0000 + 0.0000i
vertices =
   0.0000 + 0.7265i  -0.2361 + 0.0000i  -1.0000 + 0.0000i
   0.3820 + 0.4490i  -0.2361 + 0.0000i   0.0000 + 0.7265i
  -0.2361 + 0.0000i   0.3820 + 0.4490i   1.0000 + 0.0000i

From Triangles to Rhombuses

Let's start with a B triangle and decompose it three times successively.

t = bTriangle([],-1,1);
t = decomposeTriangles(t)
t = decomposeTriangles(t)
t = decomposeTriangles(t)
t =
  3×4 table
          Apex              Left              Right          Type
    _________________    ___________    _________________    ____
     0.23607+0i           1+0i                 0+0.72654i     B  
     0.23607+0i           0+0.72654i    -0.38197+0.44903i     A  
    -0.38197+0.44903i    -1+0i           0.23607+0i           Bp 
t =
  8×4 table
          Apex                 Left                 Right          Type
    _________________    _________________    _________________    ____
     0.38197+0.44903i           0+0.72654i     0.23607+0i           B  
     0.38197+0.44903i     0.23607+0i           0.52786+0i           A  
     0.52786+0i                 1+0i           0.38197+0.44903i     Bp 
           0+0.72654i    -0.38197+0.44903i     -0.1459+0.27751i     A  
     -0.1459+0.27751i     0.23607+0i                 0+0.72654i     Bp 
    -0.52786+0i          -0.38197+0.44903i          -1+0i           Bp 
    -0.52786+0i           -0.1459+0.27751i    -0.38197+0.44903i     Ap 
     -0.1459+0.27751i    -0.52786+0i           0.23607+0i           B  
t =
  21×4 table
           Apex                 Left                 Right           Type
    __________________    _________________    __________________    ____
       0.1459+0.27751i     0.23607+0i            0.38197+0.44903i     B  
       0.1459+0.27751i     0.38197+0.44903i      0.23607+0.55503i     A  
      0.23607+0.55503i           0+0.72654i       0.1459+0.27751i     Bp 
      0.23607+0i           0.52786+0i            0.47214+0.17151i     A  
      0.47214+0.17151i     0.38197+0.44903i      0.23607+0i           Bp 
      0.76393+0.17151i     0.52786+0i                  1+0i           Bp 
      0.76393+0.17151i     0.47214+0.17151i      0.52786+0i           Ap 
      0.47214+0.17151i     0.76393+0.17151i      0.38197+0.44903i     B  
     -0.38197+0.44903i     -0.1459+0.27751i     -0.09017+0.44903i     A  
     -0.09017+0.44903i           0+0.72654i     -0.38197+0.44903i     Bp 
       0.1459+0.27751i     -0.1459+0.27751i      0.23607+0i           Bp 
       0.1459+0.27751i    -0.09017+0.44903i      -0.1459+0.27751i     Ap 
     -0.09017+0.44903i      0.1459+0.27751i            0+0.72654i     B  
     -0.61803+0.27751i    -0.52786+0i           -0.38197+0.44903i     Bp 
     -0.61803+0.27751i     -0.7082+0i           -0.52786+0i           Ap 
      -0.7082+0i          -0.61803+0.27751i           -1+0i           B  
     -0.38197+0.44903i     -0.2918+0.17151i      -0.1459+0.27751i     Ap 
      -0.2918+0.17151i    -0.38197+0.44903i     -0.52786+0i           B  
    -0.055728+0i           0.23607+0i            -0.1459+0.27751i     B  
    -0.055728+0i           -0.1459+0.27751i      -0.2918+0.17151i     A  
      -0.2918+0.17151i    -0.52786+0i          -0.055728+0i           Bp 
clf
showLabeledTriangles(t)
vertices =
   0.2361 + 0.0000i   0.1459 + 0.2775i   0.3820 + 0.4490i
   0.3820 + 0.4490i   0.1459 + 0.2775i   0.2361 + 0.5550i
   0.0000 + 0.7265i   0.2361 + 0.5550i   0.1459 + 0.2775i
   0.5279 + 0.0000i   0.2361 + 0.0000i   0.4721 + 0.1715i
   0.3820 + 0.4490i   0.4721 + 0.1715i   0.2361 + 0.0000i
   0.5279 + 0.0000i   0.7639 + 0.1715i   1.0000 + 0.0000i
   0.4721 + 0.1715i   0.7639 + 0.1715i   0.5279 + 0.0000i
   0.7639 + 0.1715i   0.4721 + 0.1715i   0.3820 + 0.4490i
  -0.1459 + 0.2775i  -0.3820 + 0.4490i  -0.0902 + 0.4490i
   0.0000 + 0.7265i  -0.0902 + 0.4490i  -0.3820 + 0.4490i
  -0.1459 + 0.2775i   0.1459 + 0.2775i   0.2361 + 0.0000i
  -0.0902 + 0.4490i   0.1459 + 0.2775i  -0.1459 + 0.2775i
   0.1459 + 0.2775i  -0.0902 + 0.4490i   0.0000 + 0.7265i
  -0.5279 + 0.0000i  -0.6180 + 0.2775i  -0.3820 + 0.4490i
  -0.7082 + 0.0000i  -0.6180 + 0.2775i  -0.5279 + 0.0000i
  -0.6180 + 0.2775i  -0.7082 + 0.0000i  -1.0000 + 0.0000i
  -0.2918 + 0.1715i  -0.3820 + 0.4490i  -0.1459 + 0.2775i
  -0.3820 + 0.4490i  -0.2918 + 0.1715i  -0.5279 + 0.0000i
   0.2361 + 0.0000i  -0.0557 + 0.0000i  -0.1459 + 0.2775i
  -0.1459 + 0.2775i  -0.0557 + 0.0000i  -0.2918 + 0.1715i
  -0.5279 + 0.0000i  -0.2918 + 0.1715i  -0.0557 + 0.0000i

Now the labels are getting in the way.

showTriangles(t)

That's better, but it's hard to visualize the rhombuses because the triangle bases are being drawn. Let's switch to a different visualization function that draws the rhombuses with colored shading and without the triangle bases.

showTiles(t)

Let's do five more levels of decomposition.

for k = 1:5
    t = decomposeTriangles(t);
end

num_triangles = height(t)
num_triangles =
        2584

Now we have lots of triangles. Let's take a look.

clf
showTiles(t)

Zoom in.

axis([-0.3 0.2 0.1 0.5])

For fun, you can add to the visualization by inserting arcs or other shapes in the triangles. You just need to make everything match up across the different types of triangles. The showDecoratedTiles function shows one possible variation.

clf
showDecoratedTiles(t)
axis([-0.2 0.1 0.2 0.4])

Starting from Multiple Triangles

Another interesting thing to try is to start from a pattern of multiple triangles instead of just one. You just to arrange the initial triangles so that they satisfy the side matching rules. Let's use a circular pattern of alternating A and A' triangles sharing a common apex.

t = table;
for k = 1:5
    thetad = 72*(k-1);
    t_a = aTriangle(0,cosd(thetad) + 1i*sind(thetad),[]);
    t_ap = apTriangle(0,t_a.Right,[]);
    t = [t ; t_a ; t_ap];
end
showLabeledTriangles(t)
vertices =
   1.0000 + 0.0000i   0.0000 + 0.0000i   0.8090 + 0.5878i
   0.8090 + 0.5878i   0.0000 + 0.0000i   0.3090 + 0.9511i
   0.3090 + 0.9511i   0.0000 + 0.0000i  -0.3090 + 0.9511i
  -0.3090 + 0.9511i   0.0000 + 0.0000i  -0.8090 + 0.5878i
  -0.8090 + 0.5878i   0.0000 + 0.0000i  -1.0000 + 0.0000i
  -1.0000 + 0.0000i   0.0000 + 0.0000i  -0.8090 - 0.5878i
  -0.8090 - 0.5878i   0.0000 + 0.0000i  -0.3090 - 0.9511i
  -0.3090 - 0.9511i   0.0000 + 0.0000i   0.3090 - 0.9511i
   0.3090 - 0.9511i   0.0000 + 0.0000i   0.8090 - 0.5878i
   0.8090 - 0.5878i   0.0000 + 0.0000i   1.0000 + 0.0000i
t2 = t;
for k = 1:4
    t2 = decomposeTriangles(t2);
end
clf
showDecoratedTiles(t2)


Get the MATLAB code

Published with MATLAB® R2018a

2 CommentsOldest to Newest

Damir replied on : 1 of 2
Dear Professor Moler, great, interesting, relaxing, seasonal appropriate, etc. ... blog post. Thank You! BR, Damir
Anu replied on : 2 of 2
I tried the fourier transform plot code myself with different numbers, and i thought it was super cool. Made my day. thanks.

Add A Comment

Your email address will not be published. Required fields are marked *

Preview: hide