2
\$\begingroup\$

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.

Penrose tiling

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>
\$\endgroup\$
3
  • 1
    \$\begingroup\$ A link to, or a description of this particular Penrose tiling would be very helpful. \$\endgroup\$
    – vnp
    Commented Aug 19, 2023 at 0:06
  • 2
    \$\begingroup\$ You take trig functions of 18° + k × 72°, and a handful of other angles. So memoize those values, and move on to larger concerns. \$\endgroup\$
    – J_H
    Commented Aug 19, 2023 at 1:01
  • 2
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 19, 2023 at 8:45

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.