I'm trying to rotate two rectangles the same amount around the same point. The point is arbitrary, so for simplicity, I'm using the top-left (0, 0)
Unfortunately, the result seems slightly off, and I'm not sure what's causing it. Here is a full reproduction of the issue:
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
class Rectangle {
constructor(x, y, w, h, theta) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.theta = theta;
}
}
function drawRectangle(r) {
ctx.beginPath();
ctx.rect(r.x, r.y, r.w, r.h);
ctx.stroke();
}
function degreesToRadians(degrees) { return degrees * (Math.PI / 180); }
function rotateCanvas(radians, centerX, centerY) {
ctx.translate(centerX, centerY);
ctx.rotate(radians);
ctx.translate(-centerX, -centerY);
}
function drawRotatedRectangle(r) {
let rXCenter = r.x + (r.w / 2);
let rYCenter = r.y + (r.h / 2);
alert(rXCenter);
rotateCanvas(r.theta, rXCenter, rYCenter);
drawRectangle(r);
rotateCanvas(-r.theta, rXCenter, rYCenter);
}
let r1 = new Rectangle(100, 52, 90, 30, degreesToRadians(-20));
let r2 = new Rectangle(140, 80, 25, 25, degreesToRadians(10));
function simpleRotate(r, theta) {
let transX = Math.cos(theta) * r.x - Math.sin(theta) * r.y;
let transY = Math.sin(theta) * r.x + Math.cos(theta) * r.y;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
drawRotatedRectangle(r1);
drawRotatedRectangle(r2);
let r1AABB = simpleRotate(r1, -r1.theta);
let r2Rotate = simpleRotate(r2, -r1.theta);
ctx.strokeStyle = "#ff0000";
drawRotatedRectangle(r1AABB);
drawRotatedRectangle(r2Rotate);
body { margin: 0; overflow: hidden; }
<canvas width="600" height="600"></canvas>
The black rectangles are the two rectangles before being rotated, and the red rectangles are the two rectangles after being rotated.
As you can see, the two black rectangles are touching (colliding) before being rotated. Then, I rotate them both by the same amount around the same point (0, 0). However, afterwards they are no longer touching (as you can see the red rectangles are no longer colliding.
Why is this? I followed this code for rotating a point, but I seem to be getting inaccurate results.
If I take a screenshot of the black rectangles, open it up an image editor, box select them, and rotate them, then they stay together (colliding). How can I emulate this in my code example posted above?
This may let it work as you expect.
function simpleRotate(r, theta) {
let transX = Math.cos(theta) * (r.x + r.w / 2) - Math.sin(theta) * (r.y + r.h / 2) - r.w / 2;
let transY = Math.sin(theta) * (r.x + r.w / 2) + Math.cos(theta) * (r.y + r.h / 2) - r.h / 2;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
Other otherwise, if you'd like to change center, use following form.
function simpleRotate(r, theta, centerX, centerY) {
let transX = Math.cos(theta) * (r.x + r.w / 2 - centerX) - Math.sin(theta) * (r.y + r.h / 2 - centerY) - r.w / 2 + centerX;
let transY = Math.sin(theta) * (r.x + r.w / 2 - centerX) + Math.cos(theta) * (r.y + r.h / 2 - centerY) - r.h / 2 + centerY;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
Then
let r1AABB = simpleRotate(r1, -r1.theta, 145, 67);
let r2Rotate = simpleRotate(r2, -r1.theta, 145, 67);
In general, 2d rotational transform with offset consists of translation and rotation. Then, in that setup rotation is to be applied via drawRotatedRectangle() afterwards. Therefore all what you have to do is to compose appropriate translation. Inspecting simpleRotate() function carefully, you'll notice what's actually done there is calculation of translation (transX, transY ... though sin/cos are being used).
Then, as you know, since drawRotatedRectangle() rotates the rectangle around its center, by composing translation to move the center to the appropriate position, you can obtain rotational transform you need.
Initial state → Translate each item by simpleRotate() → Rotate each item by drawRotatedRectangle() → Final state.