# Create a Penrose tiling

I programmed this type of Penrose Tilings in javascript and the algorithm is 'simple':

The cyan pentagon always have to draw the yellow losenge and the grey pentagon almost always have to draw two red pentagons and a half star, that will draw a grey and red pentagons that will draw a star and so on.

Sometimes we'll need to check collisions and the amount of shapes.

I'll plan to expand to draw even more tiles, but would like to know if it's possible to optimize the code to avoid unnecessary calls to sine and cosine functions, less lines and even unnecessary variables and ifs.

  <html>
<meta charset = "utf-8">
<title>Penrose</title>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script>

var x, y, shape;
var angle;
var size = 20;
var tolerance = 0.00001;
var otherShapesAmount;
var sides;

function list()
{
this.begin = null;
this.size = 0;

function no(x, y, angle, shape, otherShapesAmount, sides, next = null)
{
this.x = x;
this.y = y;
this.angle = angle;
this.shape = shape;
this.otherShapesAmount = otherShapesAmount;
this.sides = sides;
this.next = next;
}

this.insert = function(x, y, angle, shape, otherShapesAmount, sides)
{
this.begin = new no(x, y, angle, shape, otherShapesAmount, sides, this.begin);
this.size++;
}

this.differenceBetweenVertex = function(x, y, shape)
{
let atual = this.begin;

while (atual)
{
if (atual.shape == shape) if (Math.abs(atual.x - x) < tolerance && Math.abs(atual.y - y) < tolerance) return true;
atual = atual.next;
}
return false;
}

this.getData = function()
{
if (this.size == 0) return 0;
else if (this.size == 1)
{
x = this.begin.x;
y = this.begin.y;
angle = this.begin.angle;
shape = this.begin.shape;
otherShapesAmount = this.begin.otherShapesAmount;
sides = this.begin.sides;

this.begin = null;
}
else
{
var anterior;
var auxiliar = 0;
let atual = this.begin;
while (auxiliar < this.size - 1)
{
anterior = atual;
atual = atual.next;
auxiliar++;
}
x = atual.x;
y = atual.y;
angle = atual.angle;
shape = atual.shape;
anterior.next = atual.next;
otherShapesAmount = atual.otherShapesAmount;
sides = atual.sides;
}
this.size--;
return 1;
}

this.clean = function()
{
this.begin = null;
this.size = 0;
}
}

class points
{
constructor(x, y)
{
this.x = x;
this.y = y;
}
}

var pentagonVertex = [];
var losangleVertex = [];
var harfStarVertex = [];
var starVertex = [];

function setup()
{
pentagonVertex[0] = new points();
pentagonVertex[1] = new points();
pentagonVertex[2] = new points();
pentagonVertex[3] = new points();
pentagonVertex[4] = new points();

losangleVertex[0] = new points();
losangleVertex[1] = new points();
losangleVertex[2] = new points();
losangleVertex[3] = new points();

harfStarVertex[0] = new points();
harfStarVertex[1] = new points();
harfStarVertex[2] = new points();
harfStarVertex[3] = new points();
harfStarVertex[4] = new points();
harfStarVertex[5] = new points();
harfStarVertex[6] = new points();

starVertex[0] = new points();
starVertex[1] = new points();
starVertex[2] = new points();
starVertex[3] = new points();
starVertex[4] = new points();
starVertex[5] = new points();
starVertex[6] = new points();
starVertex[7] = new points();
starVertex[8] = new points();
starVertex[9] = new points();

createCanvas(800, 800);
}

function drawPentagon(pentagonVertex)
{
beginShape();
vertex(pentagonVertex[0].x, pentagonVertex[0].y);
vertex(pentagonVertex[1].x, pentagonVertex[1].y);
vertex(pentagonVertex[2].x, pentagonVertex[2].y);
vertex(pentagonVertex[3].x, pentagonVertex[3].y);
vertex(pentagonVertex[4].x, pentagonVertex[4].y);
vertex(pentagonVertex[0].x, pentagonVertex[0].y);
endShape();
}

function drawLosangle(losangleVertex)
{
beginShape()
vertex(losangleVertex[0].x, losangleVertex[0].y);
vertex(losangleVertex[1].x, losangleVertex[1].y);
vertex(losangleVertex[2].x, losangleVertex[2].y);
vertex(losangleVertex[3].x, losangleVertex[3].y);
vertex(losangleVertex[0].x, losangleVertex[0].y);
endShape();
}

function drawHalfStar(harfStarVertex)
{
beginShape();
vertex(harfStarVertex[0].x, harfStarVertex[0].y);
vertex(harfStarVertex[1].x, harfStarVertex[1].y);
vertex(harfStarVertex[2].x, harfStarVertex[2].y);
vertex(harfStarVertex[3].x, harfStarVertex[3].y);
vertex(harfStarVertex[4].x, harfStarVertex[4].y);
vertex(harfStarVertex[5].x, harfStarVertex[5].y);
vertex(harfStarVertex[6].x, harfStarVertex[6].y);
vertex(harfStarVertex[0].x, harfStarVertex[0].y);
endShape();
}

function drawStar(starVertex)
{
beginShape();
vertex(starVertex[0].x, starVertex[0].y);
vertex(starVertex[1].x, starVertex[1].y);
vertex(starVertex[2].x, starVertex[2].y);
vertex(starVertex[3].x, starVertex[3].y);
vertex(starVertex[4].x, starVertex[4].y);
vertex(starVertex[5].x, starVertex[5].y);
vertex(starVertex[6].x, starVertex[6].y);
vertex(starVertex[7].x, starVertex[7].y);
vertex(starVertex[8].x, starVertex[8].y);
vertex(starVertex[9].x, starVertex[9].y);
vertex(starVertex[0].x, starVertex[0].y);
endShape();
}

var mainList = new list();
var vertexList = new list();
var start = true;

function draw()
{
translate (800 / 2, 800 / 2);
if (start == true) //beginning - the cyan pentagon will nedd to be draw using the hypotenuse to be draw in the center of the translated point
{
angle = 18;
let hypotenuse = size / (2 * sin((36 * PI) / 180));
pentagonVertex[0].x = hypotenuse * cos((angle * PI) / 180);
pentagonVertex[0].y = hypotenuse * (-sin((angle * PI) / 180));
mainList.insert(pentagonVertex[0].x, pentagonVertex[0].y, angle - 18, 1, 1, undefined);
mainList.insert(pentagonVertex[0].x, pentagonVertex[0].y, angle - 54, 2, 10, undefined);
angle += 72;
pentagonVertex[1].x = hypotenuse * cos((angle * PI) / 180);
pentagonVertex[1].y = hypotenuse * (-sin((angle * PI) / 180));
mainList.insert(pentagonVertex[1].x, pentagonVertex[1].y, angle - 18, 1, 1, undefined);
mainList.insert(pentagonVertex[1].x, pentagonVertex[1].y, angle - 54, 2, 10, undefined);
angle += 72;
pentagonVertex[2].x = hypotenuse * cos((angle * PI) / 180);
pentagonVertex[2].y = hypotenuse * (-sin((angle * PI) / 180));
mainList.insert(pentagonVertex[2].x, pentagonVertex[2].y, angle - 18, 1, 1, undefined);
mainList.insert(pentagonVertex[2].x, pentagonVertex[2].y, angle - 54, 2, 10, undefined);
angle += 72;
pentagonVertex[3].x = hypotenuse * cos((angle * PI) / 180);
pentagonVertex[3].y = hypotenuse * (-sin((angle * PI) / 180));
mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle - 18, 1, 1, undefined);
mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle - 54, 2, 10, undefined);
angle += 72;
pentagonVertex[4].x = hypotenuse * cos((angle * PI) / 180);
pentagonVertex[4].y = hypotenuse * (-sin((angle * PI) / 180));
mainList.insert(pentagonVertex[4].x, pentagonVertex[4].y, angle - 18, 1, 1);
mainList.insert(pentagonVertex[4].x, pentagonVertex[4].y, angle - 54, 2, 10, undefined);

fill('cyan');
drawPentagon(pentagonVertex);
start = false;
}
else
{
while (mainList.getData())
{
switch (shape)
{
case 0: if (vertexList.differenceBetweenVertex(x, y, 0) == false) //cyan pentagon
{
pentagonVertex[0].x = x;
pentagonVertex[0].y = y;

angle += 72;
pentagonVertex[1].x = x + size * cos((angle * PI) / 180);
pentagonVertex[1].y = y + (size * (-sin((angle * PI) / 180)));

angle += 72;
pentagonVertex[2].x = pentagonVertex[1].x + size * cos((angle * PI) / 180);
pentagonVertex[2].y = pentagonVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[3].x = pentagonVertex[2].x + size * cos((angle * PI) / 180);
pentagonVertex[3].y = pentagonVertex[2].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[4].x = pentagonVertex[3].x + size * cos((angle * PI) / 180);
pentagonVertex[4].y = pentagonVertex[3].y + size * (-sin((angle * PI) / 180));

fill('cyan');
drawPentagon(pentagonVertex);
mainList.insert(pentagonVertex[2].x, pentagonVertex[2].y, angle + 144, 1, 1, sides);
vertexList.insert(pentagonVertex[1].x, pentagonVertex[1].y, null, 0, undefined, undefined);
vertexList.insert(pentagonVertex[3].x, pentagonVertex[3].y, null, 0, undefined, undefined);
vertexList.insert(pentagonVertex[4].x, pentagonVertex[4].y, null, 0, undefined, undefined);
}
else
{
let auxiliar = 0;
pentagonVertex[0].x = x;
pentagonVertex[0].y = y;

if (vertexList.differenceBetweenVertex(x, y, 1) == true) auxiliar++;
angle += 180;

pentagonVertex[1].x = x + size * cos((angle * PI) / 180);
pentagonVertex[1].y = y + size * (-sin((angle * PI) / 180));
if (vertexList.differenceBetweenVertex(pentagonVertex[1].x, pentagonVertex[1].y, 1) == true) auxiliar++;

angle -= 72;
pentagonVertex[2].x = pentagonVertex[1].x + size * cos((angle * PI) / 180);
pentagonVertex[2].y = pentagonVertex[1].y + size * (-sin((angle * PI) / 180));
if (vertexList.differenceBetweenVertex(pentagonVertex[2].x, pentagonVertex[2].y, 1) == true) auxiliar++;

angle -= 72;
pentagonVertex[3].x = pentagonVertex[2].x + size * cos((angle * PI) / 180);
pentagonVertex[3].y = pentagonVertex[2].y + size * (-sin((angle * PI) / 180));

if (vertexList.differenceBetweenVertex(pentagonVertex[3].x, pentagonVertex[3].y, 1) == false)
{
mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle + 36, 1,1);
vertexList.insert(pentagonVertex[3].x, pentagonVertex[3].y, null,1);
}
auxiliar++;

angle -= 72;
pentagonVertex[4].x = pentagonVertex[3].x + size * cos((angle * PI) / 180);
pentagonVertex[4].y = pentagonVertex[3].y + size * (-sin((angle * PI) / 180));
if (vertexList.differenceBetweenVertex(pentagonVertex[4].x, pentagonVertex[4].y, 1) == true) auxiliar++;

if (auxiliar == 3) mainList.insert(pentagonVertex[1].x, pentagonVertex[1].y, angle + 216, 2, 10, undefined);
else
{
mainList.insert(pentagonVertex[0].x, pentagonVertex[0].y, angle - 72, 2, 7, 1);
mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle + 72, 2, 7, 0);
mainList.insert(pentagonVertex[4].x, pentagonVertex[4].y, angle, 2, 3, undefined);
}
}
break;
case 1: losangleVertex[0].x = x; //losangle
losangleVertex[0].y = y;

losangleVertex[1].x = x + (size * cos((angle * PI) / 180));
losangleVertex[1].y = y + (size * (-sin((angle * PI) / 180)));

angle += 36;
losangleVertex[2].x = losangleVertex[1].x + size * cos((angle * PI) / 180);
losangleVertex[2].y = losangleVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 144;
losangleVertex[3].x = losangleVertex[2].x + size * cos((angle * PI) / 180);
losangleVertex[3].y = losangleVertex[2].y + size * (-sin((angle * PI) / 180));

fill('yellow');
drawLosangle(losangleVertex);
mainList.insert(losangleVertex[2].x, losangleVertex[2].y, angle - 288, 0, 1, undefined);
vertexList.insert(x, y, null, 1, 0, undefined);
vertexList.insert(losangleVertex[2].x, losangleVertex[2].y, null, 1, 0, undefined);
break;
case 2: otherShapesAmount--; //gray pentagon
pentagonVertex[0].x = x;
pentagonVertex[0].y = y;

angle += 72;
pentagonVertex[1].x = x + size * cos((angle * PI) / 180);
pentagonVertex[1].y = y + (size * (-sin((angle * PI) / 180)));

angle += 72;
pentagonVertex[2].x = pentagonVertex[1].x + size * cos((angle * PI) / 180);
pentagonVertex[2].y = pentagonVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[3].x = pentagonVertex[2].x + size * cos((angle * PI) / 180);
pentagonVertex[3].y = pentagonVertex[2].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[4].x = pentagonVertex[3].x + size * cos((angle * PI) / 180);
pentagonVertex[4].y = pentagonVertex[3].y + size * (-sin((angle * PI) / 180));

fill('gray');
drawPentagon(pentagonVertex);
if (otherShapesAmount > 1)
{
mainList.insert(pentagonVertex[1].x, pentagonVertex[1].y, angle + 36, 3, 1, sides);
if (otherShapesAmount > 5) mainList.insert(pentagonVertex[2].x, pentagonVertex[2].y, angle + 180, 4, otherShapesAmount - 2, sides);
mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle - 324, 3, 1, sides);
}
else mainList.insert(pentagonVertex[2].x, pentagonVertex[2].y, angle + 108, 3, 1, sides);
break;
case 3: otherShapesAmount--; //red pentagon
pentagonVertex[0].x = x;
pentagonVertex[0].y = y;
angle += 72;
pentagonVertex[1].x = x + size * cos((angle * PI) / 180);
pentagonVertex[1].y = y + (size * (-sin((angle * PI) / 180)));

angle += 72;
pentagonVertex[2].x = pentagonVertex[1].x + size * cos((angle * PI) / 180);
pentagonVertex[2].y = pentagonVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[3].x = pentagonVertex[2].x + size * cos((angle * PI) / 180);
pentagonVertex[3].y = pentagonVertex[2].y + size * (-sin((angle * PI) / 180));

angle += 72;
pentagonVertex[4].x = pentagonVertex[3].x + size * cos((angle * PI) / 180);
pentagonVertex[4].y = pentagonVertex[3].y + size * (-sin((angle * PI) / 180));

fill('red');
drawPentagon(pentagonVertex);
if (otherShapesAmount > 0) mainList.insert(pentagonVertex[3].x, pentagonVertex[3].y, angle - 324, 5, otherShapesAmount, sides);
break;
case 4: otherShapesAmount--; //half star
harfStarVertex[0].x = x;
harfStarVertex[0].y = y;

angle -= 36;
harfStarVertex[1].x = x + size * cos((angle * PI) / 180);
harfStarVertex[1].y = y + size * (-sin((angle * PI) / 180));

angle -= 72;
harfStarVertex[2].x = harfStarVertex[1].x + size * cos((angle * PI) / 180);
harfStarVertex[2].y = harfStarVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 144;
harfStarVertex[3].x = harfStarVertex[2].x + size * cos((angle * PI) / 180);
harfStarVertex[3].y = harfStarVertex[2].y + size * (-sin((angle * PI) / 180));

angle += 36;
harfStarVertex[4].x = harfStarVertex[3].x + size * cos((angle * PI) / 180);
harfStarVertex[4].y = harfStarVertex[3].y + size * (-sin((angle * PI) / 180));

angle += 36;
harfStarVertex[5].x = harfStarVertex[4].x + size * cos((angle * PI) / 180);
harfStarVertex[5].y = harfStarVertex[4].y + size * (-sin((angle * PI) / 180));

angle += 144;
harfStarVertex[6].x = harfStarVertex[5].x + size * cos((angle * PI) / 180);
harfStarVertex[6].y = harfStarVertex[5].y + size * (-sin((angle * PI) / 180));
fill('green');
drawHalfStar(harfStarVertex);
if (otherShapesAmount > 5)
{
mainList.insert(harfStarVertex[2].x, harfStarVertex[2].y, angle - 36, 2, 2, undefined);
mainList.insert(harfStarVertex[3].x, harfStarVertex[3].y, angle, 3, 2, undefined);
mainList.insert(harfStarVertex[5].x, harfStarVertex[5].y, angle- 36, 2, 2, undefined);
}
else if (sides == 0) mainList.insert(harfStarVertex[5].x, harfStarVertex[5].y, angle - 36, 2, 2, sides);
else mainList.insert(harfStarVertex[2].x, harfStarVertex[2].y, angle - 36, 2, 2, sides);
break;
default: starVertex[0].x = x; //star
starVertex[0].y = y;

angle += 72;
starVertex[1].x = x + size * cos((angle * PI) / 180);
starVertex[1].y = y + size * (-sin((angle * PI) / 180));

angle -= 72;
starVertex[2].x = starVertex[1].x + size * cos((angle * PI) / 180);
starVertex[2].y = starVertex[1].y + size * (-sin((angle * PI) / 180));

angle += 144;
starVertex[3].x = starVertex[2].x + size * cos((angle * PI) / 180);
starVertex[3].y = starVertex[2].y + size * (-sin((angle * PI) / 180));

angle -= 72;
starVertex[4].x = starVertex[3].x + size * cos((angle * PI) / 180);
starVertex[4].y = starVertex[3].y + size * (-sin((angle * PI) / 180));

angle += 144;
starVertex[5].x = starVertex[4].x + size * cos((angle * PI) / 180);
starVertex[5].y = starVertex[4].y + size * (-sin((angle * PI) / 180));

angle -= 72;
starVertex[6].x = starVertex[5].x + size * cos((angle * PI) / 180);
starVertex[6].y = starVertex[5].y + size * (-sin((angle * PI) / 180));

angle += 144;
starVertex[7].x = starVertex[6].x + size * cos((angle * PI) / 180);
starVertex[7].y = starVertex[6].y + size * (-sin((angle * PI) / 180));

angle -= 72;
starVertex[8].x = starVertex[7].x + size * cos((angle * PI) / 180);
starVertex[8].y = starVertex[7].y + size * (-sin((angle * PI) / 180));

angle += 144;
starVertex[9].x = starVertex[8].x + size * cos((angle * PI) / 180);
starVertex[9].y = starVertex[8].y + size * (-sin((angle * PI) / 180));

fill('orange');
drawStar(starVertex);
}
}
noLoop();
}
}
</script>
</body>
</html>

• A link to, or a description of this particular Penrose tiling would be very helpful.
– vnp
Aug 19 at 0:06
• You take trig functions of 18° + k × 72°, and a handful of other angles. So memoize those values, and move on to larger concerns.
– J_H
Aug 19 at 1:01
• Welcome to Code Review! I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have. Aug 19 at 8:45