Adron
Adron

Reputation: 37

Rotate a triangle in the cente of itself

I want to rotate a triangle in the center of itself. I have this script:

  var ctx = canvas.getContext('2d');
  var angle = 30;
  setInterval(rotate, 50);
  function rotate() {
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.save();
    ctx.translate(150, 150); // x, y
    ctx.rotate(angle * Math.PI / 180)
    ctx.fillStyle = "yellow";
    var path=new Path2D();
    path.moveTo(-50+50,-25);
    path.lineTo(-50,-50-25);
    path.lineTo(-50-50,-25);
    ctx.fill(path);
    ctx.restore();
    angle++;
  }
<canvas id="canvas" width="1800" height="700"></canvas>

It rotates it, but not in the center. I want it to look like this:

  var ctx = canvas.getContext('2d'); 
  setInterval(rotate, 50);
  var angle = 30;
  function rotate() {
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.translate(50, 50);
    ctx.rotate(angle * Math.PI / 180)

    ctx.fillStyle = "green";
    ctx.fillRect(-25, -25, 50, 50);
    ctx.restore();
    angle++;
    }
<canvas id="canvas" width="1800" height="700"></canvas>
I think, I just have to get the width and hight of the triangle and devive it by 2, but I don't know, how to do that. Thx for every answer!

Upvotes: 1

Views: 399

Answers (2)

Blindman67
Blindman67

Reputation: 54109

Create the Path once

You are using a Path2D object which is reusable.

If you create the triangle already centered on its origin (or any path for that matter) it is then trivial to rotate it.

Reusing the path object is also a lot quicker if you have a lot to render.

The function to creates a path from a set of points. It automatically centers the path to its own origin (defined by the mean of its points)

const point = (x, y) => ({x, y});
function createPath(...points) {
    var cx = 0; cy = 0;
    for (const p of points) {
        cx += p.x;
        cy += p.y;
    }
    cx /= points.length;
    cy /= points.length;

    const path = new Path2d;
    for (const p of points) { path.lineTo(p.x - cx, p.y - cy); }
    path.closePath();
    return path;
}

To create the triangle

const triangle = createPath(point(0,-25), point(-50,-75), point(-100,-25));

Then you can render it rotated about its own origin with

function drawPath(path, x, y, angle) {
     ctx.setTransform(1, 0, 0, 1, x, y);
     ctx.rotate(angle);
     ctx.stroke(path);
}

Example

Shows how to create various shapes centered on their means. Each shape is a path created once and then rendered as needed.

const point = (x, y) => ({x, y});
const triangle = createPath(point(0,-25), point(-50,-75), point(-100,-25));
const rectangle = createPath(point(0,-25), point(-50,-25), point(-50,-125), point(0,-125));
const thing = createPath(point(0,-12), point(-25,-12), point(-25,-62), point(0,-62), point(22,-35));

function drawPath(path, x, y, angle) {
    ctx.setTransform(1, 0, 0, 1, x, y);
    ctx.rotate(angle);
    ctx.stroke(path);
}

function drawPath_V2(path, x, y, scale, angle, strokeStyle, fillStyle) {
    ctx.setTransform(scale, 0, 0, scale, x, y);
    ctx.rotate(angle);
    fillStyle && (ctx.fillStyle = fillStyle, ctx.fill(path));
    strokeStyle && (ctx.strokeStyle = strokeStyle, ctx.stroke(path));
}


function renderLoop(time) {
    ctx.clearRect(0, 0, can.width, can.height);
    const scale = Math.sin(time / 500) * 0.2 + 1.0;
    const scale2 = Math.cos(time / 1000) * 0.4 + 1.0;

    drawPath(triangle, 75, 74, time / 1000 * Math.PI); //360 every 2 second

    // scale path
    drawPath_V2(rectangle, 125, 125, scale, time / 2000 * Math.PI, "black"); //360 every 4 second

    // fill scale path
    drawPath_V2(thing, 125, 100, scale2, time / 3000 * Math.PI, "", "black"); 
    
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
    requestAnimationFrame(renderLoop);
}

requestAnimationFrame(renderLoop);
const can = Object.assign(document.createElement("canvas"), {width: 200, height: 200});
document.body.appendChild(can);
const ctx = can.getContext("2d");

function createPath(...points) {
    var cx = 0; cy = 0;
    for (const p of points) {
        cx += p.x;
        cy += p.y;
    }
    cx /= points.length;
    cy /= points.length;

    const path = new Path2D;
    for (const p of points) {
        path.lineTo(p.x - cx , p.y - cy);
    }
    path.closePath();
    return path;
}

Upvotes: 1

Kaiido
Kaiido

Reputation: 137133

What you want is the centroid of your shape.

var ctx = canvas.getContext('2d');
var angle = 30;
var points = [
  {x:0, y:-25},
  {x:-50, y:-75},
  {x:-100, y:-25}
];
// first sum it all
var sums = points.reduce( (sum, point) => {
  sum.x += point.x;
  sum.y += point.y;
  return sum;
}, {x:0, y:0});
// we want the mean
var centroid = {
  x: sums.x / points.length,
  y: sums.y / points.length
};

rotate();
function rotate() {
  ctx.setTransform(1,0,0,1,0,0);
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  // general position in canvas
  ctx.translate(100, 100);
  // move to centroid of our triangle
  ctx.translate(centroid.x, centroid.y); // x, y
  // rotate
  ctx.rotate(angle * Math.PI / 180)
  // go back to our initial position
  ctx.translate(-centroid.x, -centroid.y); // x, y

  ctx.fillStyle = "yellow";
  var path=new Path2D();
  path.moveTo(points[0].x, points[0].y);
  path.lineTo(points[1].x, points[1].y);
  path.lineTo(points[2].x, points[2].y);
  ctx.fill(path);

  // demo only
  ctx.beginPath();
  ctx.arc(centroid.x, centroid.y, 50, 0, Math.PI*2)
  ctx.stroke();
  
  angle++;
  requestAnimationFrame( rotate );
}
<canvas id="canvas" width="1800" height="700"></canvas>

Upvotes: 1

Related Questions