Penrose Tiles

Something I’d wanted to do for a long time was create a program that made nice configurable penrose tiles. If you need some background, Penrose Tiles are a aperiodic tiling of the plane, meaning they can be placed to infinity without a simple repeating pattern. Specifically I’ve always been partial to the tiling P3. Somewhere around this text should be an example of the program output. mypenroseOne thing I was particularly interested in was being able to add a border around tiles and configure its thickness. Unfortunately the way I have it now is not uniform. Its actually a ton of annoying trigonometry to get the borders right. But I’ll fix it sometime. The code for this was written in processing and here it is below.

//Program to generate Penrose Tiles (P3) through successive 
PShape fattriangle;
PShape thintriangle;
int sidelength;
float scalefactor;
int maxDepth;
int padding;
float padshift;

void setup() 
{
  size(1500, 1000);  // Size should be the first statement
  stroke(255); // Set line drawing color to black
  strokeWeight(1);
  //fill(255, 0, 0);
  background(255);
  noLoop();
      
  sidelength = 3000;
  scalefactor = 1.0/sqrt(pow(1+cos(radians(72)),2) + pow(sin(radians(72)),2));
  maxDepth = 9;
  padding = 200;
}

void drawFinalThinTriangle(int depth)
{
  int rand = (int)(Math.random()*128);
  int rand2 = (int)(Math.random()*128);
  
  pushMatrix();
  translate(-padding*pow(scalefactor,depth), 0);
  
  float newsidelength = sidelength-(padding)*sqrt(1+tan(radians(72))*tan(radians(72)));
  
  thintriangle = createShape();
  thintriangle.beginShape();

  //fill color
  thintriangle.fill(rand, rand2, 255);
  
  thintriangle.vertex(0, 0);
  thintriangle.vertex(-newsidelength*cos(radians(72)), -newsidelength*sin(radians(72)));
  thintriangle.vertex(-2*newsidelength*cos(radians(72)), 0);
  thintriangle.vertex(-newsidelength*cos(radians(72)), newsidelength*sin(radians(72)));
  thintriangle.vertex(0, 0);
  thintriangle.endShape();
  
  //scale the triangle to the proper depth
  thintriangle.scale(pow(scalefactor,depth));
  shape(thintriangle, 0, 0);
  //unscale the triangle
  //should probably just make a new one
  thintriangle.scale(pow(scalefactor,-depth));
  popMatrix();
}

void drawFinalFatTriangle(int depth)
{
  int rand = (int)(Math.random()*128);
  int rand2 = (int)(Math.random()*128);
  
  pushMatrix();
  translate(-padding*pow(scalefactor,depth)*cos(radians(36)), -padding*pow(scalefactor,depth)*sin(radians(36)));
  
  float newsidelength = sidelength-(padding)*sqrt(1+tan(radians(36))*tan(radians(36)));

  fattriangle = createShape();
  fattriangle.beginShape();
  
  //fill color 
  fattriangle.fill(255, rand, rand2);
  
  fattriangle.vertex(0, 0);
  fattriangle.vertex(-newsidelength, 0);
  fattriangle.vertex(newsidelength*(cos(radians(108))-1), -newsidelength*sin(radians(108)));
  fattriangle.vertex(newsidelength*cos(radians(108)), -newsidelength*sin(radians(108)));
  fattriangle.vertex(0, 0);
  fattriangle.endShape();
  
  //scale the triangle to the proper depth
  fattriangle.scale(pow(scalefactor,depth));
  shape(fattriangle, 0, 0);
  //unscale the triangle
  ///should probably just make a new one
  fattriangle.scale(pow(scalefactor,-depth));
  popMatrix();
}

void drawThinTriangle(int depth)
{
  if(depth >= maxDepth)
  {
    drawFinalThinTriangle(depth);
  }
  else
  {
    //increment depth
    depth += 1;
    
    pushMatrix();
    //fuck this is ugly...
    //padding trig calculation
    translate(-2*sidelength*cos(radians(72))*cos(radians(72))*pow(scalefactor,depth), -2*sidelength*cos(radians(72))*sin(radians(72))*pow(scalefactor,depth));
    rotate(radians(-108));
    drawThinTriangle(depth);
    popMatrix();
    
    pushMatrix();
    translate(sidelength*cos(radians(108))*pow(scalefactor,depth-1), -sidelength*sin(radians(108))*pow(scalefactor,depth-1));
    //translate(sidelength*(cos(radians(108)))*pow(scalefactor,depth), -sidelength*sin(radians(108))*pow(scalefactor,depth));
    //fattriangle.scale(-1,1);
    rotate(radians(-108));
    drawMirroredFatTriangle(depth);
    popMatrix();
  }
}

void drawFatTriangle(int depth)
{
  if(depth >= maxDepth)
  {
    drawFinalFatTriangle(depth);
  }
  else
  {
    //increment depth
    depth += 1;
    
    drawMirroredFatTriangle(depth);
    
    pushMatrix();
    translate(-pow(scalefactor,depth)*sidelength, 0);
    drawThinTriangle(depth);
    popMatrix();
    
    pushMatrix();
    translate(sidelength*pow(scalefactor,depth-1)*(cos(radians(108))-1), -sidelength*pow(scalefactor,depth-1)*sin(radians(108)));
    //fattriangle.scale(-1,1);
    rotate(radians(-144));
    drawFatTriangle(depth);
    popMatrix();
  }
}

void drawMirroredThinTriangle(int depth)
{
  if(depth >= maxDepth)
  {
    drawFinalThinTriangle(depth);
  }
  else
  {
    //increment depth
    depth += 1;
    
    pushMatrix();
    //fuck this is ugly...
    translate(-2*sidelength*cos(radians(72))*pow(scalefactor,depth-1), 0);
    rotate(radians(108));
    drawMirroredThinTriangle(depth);
    popMatrix();
    
    pushMatrix();
    rotate(radians(36));
    drawFatTriangle(depth);
    popMatrix();
  }
}


void drawMirroredFatTriangle(int depth)
{
  if(depth >= maxDepth)
  {
    drawFinalFatTriangle(depth);
  }
  else
  {
    //increment depth
    depth += 1;
    
    pushMatrix();
    translate(-pow(scalefactor,depth-1)*sidelength, 0);
    rotate(radians(144));
    drawMirroredFatTriangle(depth);
    popMatrix();
    
    pushMatrix();
    translate(-pow(scalefactor,depth-1)*sidelength, 0);
    rotate(radians(72));
    drawMirroredThinTriangle(depth);
    popMatrix();
    
    pushMatrix();
    translate(-pow(scalefactor,depth)*sidelength*cos(radians(36)), -pow(scalefactor,depth)*sidelength*sin(radians(36)));
    //fattriangle.scale(-1,1);
    //rotate(radians(-144));
    drawFatTriangle(depth);
    popMatrix();
  }
}

void draw() 
{ 
  translate(width+1500, height);

  //draw first triangle at 0 recursion depth
  drawFatTriangle(0);
  
  save("mypenrose.png");
} 

So this code is super ugly. Why am I even publishing this? Well whatever. The code uses the inflation method to draw the tiles. You can read about this method in the linked page. Essentially the tiles have a scaling symmetry where one tile can be replaced with many. By repeatedly applying this to a single starting tile we can reach a nice tiling of the plane. The depth variable controls the number of inflations before the image is generated. You can play with this parameter, the padding, and the tile colors to make cool images.

Leave a Reply

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