我在 javascript / p5.js 中完成了这种类型的彭罗斯平铺 (P1) 的编程,该算法运行得非常好,在具有 8 GB RAM 的 Core i5 – 4590 (3.3 GHz) 上需要 1.549 秒才能生成下图。但我想知道是否可以优化代码,以提高性能并花费更少的时间。
当我开始在另一篇文章中编程时,我什至问过这个问题,并且我使用(并优化)了用户Linda Paiste提出的一些建议,这是一个很好的建议,但我想知道是否可以进一步优化。
这是代码:
<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>
优化彭罗斯平铺 (P1) 算法当然可以提高性能。除了您已经进行的优化之外,以下一些高级技术可能会有所帮助:
优化示例:
const angleCache = {};
function getAngle(angle) {
if (!(angle in angleCache)) {
angleCache[angle] = {
sin: Math.sin(angle * radianFactor),
cos: Math.cos(angle * radianFactor),
};
}
return angleCache[angle];
}
function createVertexForms(formAngles) {
const aux = new Array(formAngles.length);
let prevPoint = { x: formVertex.x, y: formVertex.y };
formAngles.forEach((angle, i) => {
const cached = getAngle(formVertex.angle + angle);
aux[i + 1] = {
x: prevPoint.x + size * cached.cos,
y: prevPoint.y + size * -cached.sin,
};
prevPoint = aux[i + 1];
});
return aux;
}
这种缓存机制减少了冗余的三角计算,提高了createVertexForms函数的效率。