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.
Can you help me about this?
<html>
<head>
<meta charset = "utf-8">
<title>Penrose</title>
</head>
<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>