是否可以优化此代码以获得更高的性能?

问题描述 投票:0回答:1

我在 javascript / p5.js 中完成了这种类型的彭罗斯平铺 (P1) 的编程,该算法运行得非常好,在具有 8 GB RAM 的 Core i5 – 4590 (3.3 GHz) 上需要 1.549 秒才能生成下图。但我想知道是否可以优化代码,以提高性能并花费更少的时间。

Penrose Tiling - P1

当我开始在另一篇文章中编程时,我什至问过这个问题,并且我使用(并优化)了用户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>
javascript optimization p5.js tiling
1个回答
0
投票

优化彭罗斯平铺 (P1) 算法当然可以提高性能。除了您已经进行的优化之外,以下一些高级技术可能会有所帮助:

  1. 记忆和缓存 存储结果:缓存常用的值,例如角度和顶点位置,以避免冗余计算。 哈希图:使用哈希图存储以前计算的形式或顶点,以便快速检索而不是重新计算。
  2. 最小化循环和递归 避免深度递归:在可能的情况下,用迭代方法替换递归,以防止性能下降和堆栈溢出。 展开循环:考虑对关键循环展开循环,尽管现代 JavaScript 引擎通常会自动优化它。
  3. 用于并行处理的 Web Worker 卸载计算:使用 Web Workers 卸载繁重的计算,保持 UI 响应能力。 分割处理:将平铺过程分成更小的任务并并行处理。
  4. 优化的数据结构 类型化数组:用类型化数组(例如 Float32Array、Int32Array)替换标准数组,以提高内存使用和计算效率。 减少对象创建:通过重用对象或使用对象池,最大限度地减少性能关键循环中的对象创建。
  5. 优化三角运算 预计算函数:存储常见角度的正弦和余弦值,以避免重新计算它们。 降低精度:如果不需要高精度,则使用较低的精度进行三角计算。
  6. 画布优化 减少画布大小:如果可能,在中间计算期间使用较小的画布,并在最终渲染时放大。 批量绘图操作:将绘图操作分组并批量渲染,以减少画布 API 调用次数。
  7. GPU加速 使用 WebGL:在 p5.js 中切换到 WebGL 以进行 GPU 加速渲染。 利用着色器:如果适用,使用着色器实现顶点变换和其他计算量大的操作。
  8. 简化的几何计算 近似平铺:如果精度并不重要,请简化几何计算以使用更少的计算昂贵的操作。 多边形分解:将复杂的多边形分解为更简单的组件,以加快计算和渲染速度。
  9. 优化的公差检查 整数算术:尽可能用整数计算替换浮点算术,以加快容差检查速度。
  10. 惰性评估 按需计算:仅在需要显示时才生成图块。这可以减少屏幕外元素的不必要的计算。

优化示例:

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函数的效率。

© www.soinside.com 2019 - 2024. All rights reserved.