Luke Haas
Luke Haas

Reputation: 692

Rotating Polygons on HTML Canvas

I'm trying to rotate an N sided polygon on the canvas and I'm having issues with the coordinates. The shapes seem to be rotating about an origin which is outside of themselves (I'd like the origin to be the centre of the shape). Any tips would be much appreciated.

var x = 50;
var y = 50;
var tranx;
var trany;

x -= tranx = x + shape.radius;
y -= trany = y + shape.radius;

elem.translate(tranx,trany);
elem.rotate(90 * radian);

var k = 0,
angle = 360/shape.sides;

elem.moveTo(x,y);

for (; k <shape.sides; k++) {
    elem.lineTo(x+=Math.cos( ( angle * k )* radian) * shape.radius, y+=Math.sin( ( angle * k )* radian) * shape.radius);
}

Upvotes: 2

Views: 2593

Answers (2)

worc
worc

Reputation: 3792

I put together a couple of functions to rotate regular polygons. The first step is generating a list of (x, y) coordinates that's separate from <canvas> entirely. It's important to note that to get a phase shift (which will ultimately cause the polygon to rotate), you have to add your rotation to the statement within the cosine and sine functions:

Math.cos(rotation + (i * 2 * Math.PI / numberOfSides))
Math.sin(rotation + (i * 2 * Math.PI / numberOfSides))

Here's a working example of generating a canvas with a regular polygon, and rotating the polygon around its own origin:

var canvas = document.querySelector('canvas');
var polygon = {
  sides: 3,
  radius: 50,
  phase: 0
};

document.addEventListener('click', function(e) {
  switch (e.target.id) {
    case "-side":
      polygon.sides--;
      break;
    case "+side":
      polygon.sides++;
      break;
    case "-phase":
      polygon.phase -= Math.PI / 12;
      break;
    case "+phase":
      polygon.phase += Math.PI / 12;
      break;
  }

  drawPolygon(canvas, polygon.sides, polygon.radius, polygon.phase);
});

function generateCoordinates(centerX, centerY, numberOfSides, radius, rotation) {
  var coordinates = [];
  for (var i = 0; i < numberOfSides; i++) {
    coordinates.push({
      x: parseFloat((centerX + radius * Math.cos(rotation + (i * 2 * Math.PI / numberOfSides))).toFixed(4)),
      y: parseFloat((centerY + radius * Math.sin(rotation + (i * 2 * Math.PI / numberOfSides))).toFixed(4))
    })
  }
  return coordinates;
};

function drawPolygon(canvas, numberOfSides, radius, rotation) {
  var context = canvas.getContext('2d');
  canvas.height = radius * 2;
  canvas.width = radius * 2;

  var coordinates = generateCoordinates(radius, radius, numberOfSides, radius, rotation);

  context.strokeStyle = "black";
  context.beginPath();

  coordinates.forEach(function(coordinate, index) {
    if (index === 0) {
      context.moveTo(coordinate.x, coordinate.y);
    } else {
      context.lineTo(coordinate.x, coordinate.y);
    }
  });
  context.closePath();
  context.stroke();
}

drawPolygon(canvas, polygon.sides, polygon.radius, polygon.phase);
<canvas></canvas>

<div>
  <button id="-side">fewer sides</button>
  <button id="+side">more sides</button>
</div>

<div>
  <button id="-phase">minus phase shift</button>
  <button id="+phase">plus phase shift</button>
</div>

Upvotes: 3

begemotv2718
begemotv2718

Reputation: 868

Well, the first solution, somewhat a hack, would be the following: add a parameter rotation_angle to the shape object. Then your loop should change in the following way:

k=0;
elem.moveTo(
     x+=Math.cos( ( angle * k +shape.rotation_angle)* radian) * shape.radius,
     y+=Math.sin( ( angle * k +shape.rotation_angle)* radian) * shape.radius);
for(k=1;k<shape.sides;k++){
     elem.lineTo(
          x+=Math.cos( ( angle * k +shape.rotation_angle)* radian) * shape.radius,
          y+=Math.sin( ( angle * k +shape.rotation_angle)* radian) * shape.radius);
}

The second solution rely on assumption that elem is a canvas context and the center of the polygon should be at the coordinate (x,y).

Then I guess, the correct sequence will be the following:

elem.translate(x,y); //Translate the origin to the center of polygon.
elem.rotate(rotation_angle); // Rotate the context around the origin
var k=0;
elem.moveTo(shape.radius,0);
for(k=1;k<shape.sides;k++){
   elem.lineTo(Math.cos(k*angle*radian)*shape.radius,
               Math.sin(k*angle*radian)*shape.radius);
}

Upvotes: 1

Related Questions