mathgeometrytrigonometryrotational-matrices

Calculate new X,Y rectangle coords after rotation


trigonometry, rotated triangle

How to correctly calculate X1 after the rectangle was rotated 30 degrees? The X point is 164,83 before the rotation. I used this code:

void rotate_rectangle(double x, double y, double angle, double* new_x, double* new_y) {
  // Convert angle to radians
  double angle_rad = angle * M_PI / 180.0;

  // Calculate coordinates of top-left corner relative to center
  double rel_x = -w / 2;
  double rel_y = -h / 2;

  // Rotate top-left corner around center
  double new_rel_x = rel_x * cos(angle_rad) - rel_y * sin(angle_rad);
  double new_rel_y = rel_x * sin(angle_rad) + rel_y * cos(angle_rad);

  // Calculate new coordinates of top-left corner
  *new_x = x + new_rel_x;
  *new_y = y + new_rel_y;
}

But as you can see from the printed output when the mouse points at X1 the coords are 190,35 but the calculation gives 89 and 11.

Can someone share some light on this? Google didn't help sadly.


Solution

  • Calculate the original coordinate as polar coordinate, relative to the center of the rectangle (x+w/2,y+h/2), i.e. get its representation as an angle and magnitude. Then the updated position has the same magnitude, with an angle that's equal to the original angle plus the rotation you added.

    Getting the Euclidean coordinate is then a matter of x=cos(a) and y=sin(a), rather than using the full rotation matrix:

    void rotate_rectangle(double x, double y, double angle, double* new_x, double* new_y) {
      double dx = -w/2;
      double dy = -h/2;
    
      // Get the center offset
      double cx = x - dx;
      double cy = y - dy;
    
      // Get (x,y) as polar coordinate
      double a = atan2(dy, dx);
      double m = sqrt(dx*dx + dy*dy);
    
      // Then all we need to do is update that angle
      angle = a + angle * M_PI / 180.0;
    
      // And then we convert back to euclidean
      *new_x = cx + m * cos(angle);
      *new_y = cy + m * sin(angle);
    }
    

    With an on-page example:

    const d = gfx.width;
    const ctx = gfx.getContext(`2d`);
    const { atan2, cos, sin, sqrt, PI } = Math;
    const rad = (v) => (v / 180) * PI;
    let angle = 0;
    
    function rotateCoordinate(x, y, w, h, angle) {
      const dx = -w/2;
      const dy = -h/2;
    
      // Get the center offset
      const cx = x - dx;
      const cy = y - dy;
    
      // Get (x,y) as polar coordinate
      const a = atan2(dy, dx);
      const m = sqrt(dx * dx + dy * dy);
    
      // Then all we need to do is update that angle
      angle = a + rad(angle);
    
      // And then we convert back to euclidean
      return {
        x: cx + m * cos(angle),
        y: cy + m * sin(angle),
      };
    }
    
    function drawRectAndPoint(x, y, w, h) {
      ctx.beginPath();
      ctx.rect(x, y, w, h);
      ctx.stroke();
    }
    
    function drawNextFrame() {
      ctx.clearRect(0,0,d,d);
    
      // first the normal rect
      ctx.strokeStyle = `grey`;
      drawRectAndPoint(10, 50, 130, 50);
    
      // then rotated about the center
      ctx.translate(d / 2, d / 2);
      ctx.rotate(rad(angle));
      ctx.translate(-d / 2, -d / 2);
      ctx.strokeStyle = `red`;
      drawRectAndPoint(10, 50, 130, 50);
      ctx.resetTransform();
    
      // And then we draw our "own" rotated point
      const { x, y } = rotateCoordinate(10, 50, 130, 50, angle);
      ctx.strokeStyle = `purple`;
      ctx.beginPath();
      ctx.rect(x - 1, y - 1, 3, 3);
      ctx.stroke();
    
      angle = (angle + 0.25) % 360;
      requestAnimationFrame(drawNextFrame);
    }
    
    drawNextFrame();
    <canvas id="gfx" width="150" height="150"></canvas>