I finished programming this type of Penrose Tiling (P1) in javascript / p5.js and the algorithm works very fine and takes 1.549 seconds on a Core i5 – 4590 (3.3 gHz) with 8 GB of RAM to generate the image below. But I would like to know if it’s possible to optimize the code so that there is more performance and takes even less time.
I even asked this when I was starting to program in another post and I used (and optimized) some suggestions made by user Linda Paiste, which was a great suggestion, but I would like to know if it is possible to optimize it further.
Here’s the code:
<html>
<head>
<meta charset = "utf-8">
<title>Penrose Tiling - P1</title>
</head>
<body>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script>
var size = 10;
const tolerance = 0.00001;
const radianFactor = 3.141592653589793 / 180;
var formVertex = {};
var mainList = [];
var forms = [];
var amount = 0;
//Each "cyan" pentagon with five rhombus (type 0) will generate two more of the same type forming a "layer" (with the exception of the first). Each time a layer is formed, the quantity is increased until the maximum quantity is reached. You can change the "maxLayerAmount" variable and higher values will produce larger images, but it will take more time.
var limit = 0;
var layerAmount = 0;
var maxLayerAmount = 10;
var pentagonVertex = [];
var rhombusVertex = [];
var boatVextex = [];
var starVertex = [];
class points
{
constructor(x, y)
{
this.x = x;
this.y = y;
}
}
function setup()
{
let i;
for (i = 0; i < 5; i++) pentagonVertex[i] = new points();
for (i = 0; i < 4; i++) rhombusVertex[i] = new points();
for (i = 0; i < 7; i++) boatVextex[i] = new points();
for (i = 0; i < 10; i++) starVertex[i] = new points();
createCanvas(2000, 2000);
}
function createVertexForms(formAngles)
{
let auxiliar = new Array(formAngles.length);
let i;
let previousPoint;
auxiliar[0] = {x: formVertex.x, y: formVertex.y};
formAngles.map((formAngles, i) => {
formVertex.angle += formAngles;
previousPoint = auxiliar[i];
auxiliar[i + 1] = {x: previousPoint.x + size * cos(formVertex.angle * radianFactor), y: previousPoint.y + size * (-sin(formVertex.angle * radianFactor))};
});
return auxiliar;
}
function drawForm(formVertex)
{
beginShape();
for (let i in formVertex)
vertex(formVertex[i].x, formVertex[i].y);
endShape(CLOSE);
}
function draw()
{
var startTime = performance.now();
translate (width / 2, height / 2);
let startAngle = 18;
let hipotenuse = size / (2 * sin(36 * radianFactor));
for (let i = 0; i < 5; i++)
{
pentagonVertex[i].x = hipotenuse * cos(startAngle * radianFactor);
pentagonVertex[i].y = hipotenuse * (-sin(startAngle * radianFactor));
mainList.unshift({x: pentagonVertex[i].x, y: pentagonVertex[i].y, angle: startAngle - 18, form: 0, type: 1});
mainList.unshift({x: pentagonVertex[i].x, y: pentagonVertex[i].y, angle: startAngle + 162, form: 2, type: 0});
startAngle += 72;
}
fill('cyan');
drawForm(pentagonVertex);
while ((formVertex = mainList.pop()) !== undefined)
{
switch (formVertex.form)
{
case 0: if (forms.findIndex(item => {return item.identificator === 0 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance);}) >= 0) break;
rhombusVertex = createVertexForms([0, 36, 144]);
if (formVertex.type > 1)
{
let auxiliar = forms.findIndex(item => {return item.identificator === 0 && item.form.some(form => Math.abs(form.x - rhombusVertex[1].x) < tolerance && Math.abs(form.y - rhombusVertex[1].y) < tolerance)});
if (auxiliar >= 0)
{
forms.splice(auxiliar, 1);
continue;
}
else
{
auxiliar = forms.findIndex(item => {return item.identificator === 0 && item.form.some(form => Math.abs(form.x - rhombusVertex[3].x) < tolerance && Math.abs(form.y - rhombusVertex[3].y) < tolerance)});
if (auxiliar >= 0)
{
forms.splice(auxiliar, 1);
continue;
}
}
}
forms.push({form: rhombusVertex, identificator: 0});
mainList.unshift({x: rhombusVertex[2].x, y: rhombusVertex[2].y, angle: formVertex.angle + 72, form: 1, type: formVertex.type});
fill('yellow');
drawForm(rhombusVertex);
break;
case 1: pentagonVertex = createVertexForms([72, 72, 72, 72]);
if (formVertex.type === 0)
{
if (forms.findIndex(item => {return item.identificator === 1 && item.type === 0 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) >= 0)
{
mainList.unshift({x: pentagonVertex[1].x, y: pentagonVertex[1].y, angle: formVertex.angle + 72, form: 0, type: 1});
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 144, form: 0, type: 1});
mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 216, form: 0, type: 1});
mainList.unshift({x: pentagonVertex[4].x, y: pentagonVertex[4].y, angle: formVertex.angle + 288, form: 0, type: 1});
continue;
}
else
{
if (amount === limit)
{
layerAmount++;
if (layerAmount > maxLayerAmount)
{
if (forms.findIndex(item => {return item.identificator === 1 && item.type === 4 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) < 0)
{
fill('cyan');
drawForm(pentagonVertex);
}
else
{
mainList.unshift({x: formVertex.x, y: formVertex.y, angle: formVertex.angle - 36, form: 2, type: 3});
mainList.unshift({x: pentagonVertex[4].x, y: pentagonVertex[4].y, angle: formVertex.angle + 252, form: 2, type: 3});
}
forms.push({form: pentagonVertex, identificator: 1, type: 4});
break;
}
amount = 0;
limit += 5;
}
amount++;
mainList.unshift({x: pentagonVertex[0].x, y: pentagonVertex[0].y, angle: formVertex.angle + 180, form: 2, type: 0});
mainList.unshift({x: pentagonVertex[1].x, y: pentagonVertex[1].y, angle: formVertex.angle + 252, form: 2, type: 0});
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 324, form: 2, type: 0});
mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 36, form: 2, type: 0});
mainList.unshift({x: pentagonVertex[4].x, y: pentagonVertex[4].y, angle: formVertex.angle + 108, form: 2, type: 0});
}
}
else if (formVertex.type === 1)
{
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 144, form: 0, type: 3});
mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 216, form: 0, type: 2});
}
else if (formVertex.type === 2)
{
if ((forms.findIndex(item => {return item.identificator === 1 && item.type === 3 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) >= 0)
|| (forms.findIndex(item => {return item.identificator === 1 && item.type === 1 && item.form.some(form => Math.abs(form.x - pentagonVertex[1].x) < tolerance && Math.abs(form.y - pentagonVertex[1].y) < tolerance)}) >= 0)) continue;
mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 216, form: 0, type: 0});
}
else
{
if ((forms.findIndex(item => {return item.identificator === 1 && item.type === 2 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) >= 0)
|| (forms.findIndex(item => {return item.identificator === 1 && item.type === 1 && item.form.some(form => Math.abs(form.x - pentagonVertex[3].x) < tolerance && Math.abs(form.y - pentagonVertex[3].y) < tolerance)}) >= 0)) continue;
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 144, form: 0, type: 0});
}
forms.push({form: pentagonVertex, identificator: 1, type: formVertex.type});
fill('cyan');
drawForm(pentagonVertex);
break;
case 2: pentagonVertex = createVertexForms([72, 72, 72, 72]);
if (formVertex.type < 4)
if (formVertex.type === 0)
{
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 108, form: 3, type: 3});
mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 252, form: 4, type: 0});
mainList.unshift({x: pentagonVertex[4].x, y: pentagonVertex[4].y, angle: formVertex.angle + 108, form: 3, type: 3});
}
else
{
if (formVertex.type == 3)
{
if (forms.findIndex(item => {return item.identificator === 5 && item.form.some(form => Math.abs(form.x - pentagonVertex[2].x) < tolerance && Math.abs(form.y - pentagonVertex[2].y) < tolerance)}) < 0) continue;
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle - 36, form: 3, type: 3});
}
mainList.unshift({x: pentagonVertex[2].x, y: pentagonVertex[2].y, angle: formVertex.angle + 108, form: 3, type: formVertex.type});
}
fill('gray');
drawForm(pentagonVertex);
break;
case 3: pentagonVertex = createVertexForms([72, 72, 72, 72]);
if (formVertex.type < 3)
if (formVertex.type === 0) mainList.unshift({x: pentagonVertex[1].x, y: pentagonVertex[1].y, angle: formVertex.angle + 108, form: 5, type: 1});
else if (formVertex.type === 1) mainList.unshift({x: formVertex.x, y: formVertex.y, angle: formVertex.angle + 324, form: 4, type: 1});
else mainList.unshift({x: pentagonVertex[3].x, y: pentagonVertex[3].y, angle: formVertex.angle + 180, form: 4, type: 1});
fill('red');
drawForm(pentagonVertex);
break;
case 4: if (formVertex.type === 0)
{
if (forms.findIndex(item => {return item.identificator === 5 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) >= 0) continue;
boatVextex = createVertexForms([-36, -72, 144, 36, 36, 144]);
mainList.unshift({x: boatVextex[2].x, y: boatVextex[2].y, angle: formVertex.angle + 324, form: 2, type: 1});
mainList.unshift({x: boatVextex[3].x, y: boatVextex[3].y, angle: formVertex.angle, form: 3, type: 0});
mainList.unshift({x: boatVextex[5].x, y: boatVextex[5].y, angle: formVertex.angle + 324, form: 2, type: 2});
}
else
{
if (forms.findIndex(item => {return item.identificator === 0 && item.form.some(form => Math.abs(form.x - formVertex.x) < tolerance && Math.abs(form.y - formVertex.y) < tolerance)}) >= 0) continue;
boatVextex = createVertexForms([36, 144, -72, 144, -72, 144]);
mainList.unshift({x: boatVextex[1].x, y: boatVextex[1].y, angle: formVertex.angle + 180, form: 2, type: 4});
mainList.unshift({x: boatVextex[2].x, y: boatVextex[2].y, angle: formVertex.angle + 324, form: 3, type: 3});
mainList.unshift({x: boatVextex[3].x, y: boatVextex[3].y, angle: formVertex.angle, form: 2, type: 4});
mainList.unshift({x: boatVextex[4].x, y: boatVextex[4].y, angle: formVertex.angle + 36, form: 3, type: 3});
}
fill('green');
drawForm(boatVextex);
break;
default: if (forms.findIndex(item => {return item.identificator === 5 && item.form.some(form => Math.abs(form.x - (formVertex.x + size * cos((formVertex.angle + 72) * radianFactor))) < tolerance && Math.abs(form.y - (formVertex.y + size * (-sin((formVertex.angle + 72) * radianFactor)))) < tolerance)}) >= 0) continue;
starVertex = createVertexForms([72, -72, 144, -72, 144, -72, 144, -72, 144]);
forms.push({form: starVertex, identificator: 5});
fill('orange');
drawForm(starVertex);
}
}
var finalTime = performance.now();
console.log("Time spent: " + ((finalTime - startTime) / 1000) + " seconds.");
forms.splice(0, forms.length);
noLoop();
}
</script>
</body>
</html>