如何计算围绕其中心旋转的矩形的边界框?
How to calculate a bounding box for a rectangle rotated around its center?
我想要计算围绕其中心旋转的矩形的边界框。我已阅读 this question,虽然 MarkusQ 的回答一般有效,但效率不足以满足我的需求。我正在尝试让 Troubadour 的答案起作用,但它似乎只在旋转原点位于拐角而不是中心时才起作用。
是否可以调整他的解决方案来处理以中心为中心旋转原点的矩形?
我已经完全重现了以下问题:
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
function drawRectangle(rX, rY, rW, rH) {
ctx.beginPath();
ctx.rect(rX, rY, rW, rH);
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(rX, rY, rW, rH, rAngle) {
let rXCenter = rX + rW / 2;
let rYCenter = rY + rH / 2;
rotateCanvas(rAngle, rXCenter, rYCenter);
drawRectangle(rX, rY, rW, rH);
rotateCanvas(-rAngle, rXCenter, rYCenter);
}
function computeAABBCenter(x, y, w, h, theta) {
const ux = Math.cos(theta) * 0.5; // half unit vector along w
const uy = Math.sin(theta) * 0.5;
const wx = w * ux;
const wy = w * uy; // vector along w
const hx = h * -uy;
const hy = h * ux; // vector along h
// all point from top left CW
const x1 = x - wx - hx;
const y1 = y - wy - hy;
const x2 = x + wx - hx;
const y2 = y + wy - hy;
const x3 = x + wx + hx;
const y3 = y + wy + hy;
const x4 = x - wx + hx;
const y4 = y - wy + hy;
return {
x1: Math.min(x1, x2, x3, x4),
y1: Math.min(y1, y2, y3, y4),
x2: Math.max(x1, x2, x3, x4),
y2: Math.max(y1, y2, y3, y4),
};
}
let rX = 100;
let rY = 100;
let rW = 100;
let rH = 50;
let rA = 0.707;
drawRotatedRectangle(rX, rY, rW, rH, rA);
let bb = computeAABBCenter(rX, rY, rW, rH, rA);
drawRectangle(bb.x1, bb.y1, bb.x2 - bb.x1, bb.y2 - bb.y1);
body { margin: 0; overflow: hidden; }
<canvas width="800" height="800"></canvas>
如您所见,边界框矩形不正确。这是它目前的样子,它应该是这样的:
边界框的大小()
H = w * Abs(Sin(Fi)) + h * Abs(Cos(Fi))
W = w * Abs(Cos(Fi)) + h * Abs(Sin(Fi))
有了rXCenter, rYCenter
,你可以找到获取边界坐标
x0 = rXCenter - W/2
y0 = rYCenter - H/2
感觉你想多了。在这里,我展示了如何绘制三个围绕我制作的蓝色中心点旋转的矩形。
const wrapper = document.getElementById('wrapper');
const canvas = document.getElementById('canvas');
const wCS = window.getComputedStyle(wrapper, null)
const pl = parseFloat(wCS.getPropertyValue('padding-left'));
const pt = parseFloat(wCS.getPropertyValue('padding-top'));
const pr = parseFloat(wCS.getPropertyValue('padding-right'));
const pb = parseFloat(wCS.getPropertyValue('padding-bottom'));
canvas.width = wrapper.clientWidth - pl - pr;
canvas.height = wrapper.clientHeight - pt - pb;
const ctx = canvas.getContext('2d');
// setup a rectangle
let cornerLeft = 70;
let cornerTop = 70;
let width = 180;
let height = 60;
let horizontalCenter = cornerTop + (width / 2);
let verticalCenter = cornerTop + (height / 2);
let centerPoint = {
horz: horizontalCenter,
vert: verticalCenter
};
let colorBlue = '#0000FF';
let colorLime = '#40FF40';
let colorPurple = '#4040FF';
let colorOrange = '#FFA500';
ctx.fillStyle = colorBlue;
ctx.fillRect(centerPoint.horz, centerPoint.vert, 8, 8); // fill in the pixels at center point
ctx.strokeStyle = colorPurple;
ctx.strokeRect(cornerTop, cornerLeft, width, height); // horizontal purple box
ctx.strokeStyle = colorOrange;
ctx.translate(centerPoint.horz, centerPoint.vert); // center point note positive
ctx.rotate(Math.PI / 2); // vertical
ctx.translate(-centerPoint.horz, -centerPoint.vert); // center point - note negative
ctx.strokeRect(cornerTop, cornerLeft, width, height); // vertical orange box
ctx.strokeStyle = colorLime;
ctx.translate(-centerPoint.horz * (1.375), -centerPoint.vert * (0.5)); // center point - note negative
ctx.rotate((Math.PI / 2) / -2); //back left by 45 deg left
ctx.translate(centerPoint.horz * (.025), centerPoint.vert * (2.75)); // center point note positive
ctx.strokeRect(cornerTop, cornerLeft, width, height);
#wrapper {
width: 400px;
height: 400px;
padding: 2rem;
border: 1px solid blue;
}
#canvas {
border: 1px solid orange;
}
<div id="wrapper"><canvas id="canvas"></canvas></div>
我想要计算围绕其中心旋转的矩形的边界框。我已阅读 this question,虽然 MarkusQ 的回答一般有效,但效率不足以满足我的需求。我正在尝试让 Troubadour 的答案起作用,但它似乎只在旋转原点位于拐角而不是中心时才起作用。
是否可以调整他的解决方案来处理以中心为中心旋转原点的矩形?
我已经完全重现了以下问题:
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
function drawRectangle(rX, rY, rW, rH) {
ctx.beginPath();
ctx.rect(rX, rY, rW, rH);
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(rX, rY, rW, rH, rAngle) {
let rXCenter = rX + rW / 2;
let rYCenter = rY + rH / 2;
rotateCanvas(rAngle, rXCenter, rYCenter);
drawRectangle(rX, rY, rW, rH);
rotateCanvas(-rAngle, rXCenter, rYCenter);
}
function computeAABBCenter(x, y, w, h, theta) {
const ux = Math.cos(theta) * 0.5; // half unit vector along w
const uy = Math.sin(theta) * 0.5;
const wx = w * ux;
const wy = w * uy; // vector along w
const hx = h * -uy;
const hy = h * ux; // vector along h
// all point from top left CW
const x1 = x - wx - hx;
const y1 = y - wy - hy;
const x2 = x + wx - hx;
const y2 = y + wy - hy;
const x3 = x + wx + hx;
const y3 = y + wy + hy;
const x4 = x - wx + hx;
const y4 = y - wy + hy;
return {
x1: Math.min(x1, x2, x3, x4),
y1: Math.min(y1, y2, y3, y4),
x2: Math.max(x1, x2, x3, x4),
y2: Math.max(y1, y2, y3, y4),
};
}
let rX = 100;
let rY = 100;
let rW = 100;
let rH = 50;
let rA = 0.707;
drawRotatedRectangle(rX, rY, rW, rH, rA);
let bb = computeAABBCenter(rX, rY, rW, rH, rA);
drawRectangle(bb.x1, bb.y1, bb.x2 - bb.x1, bb.y2 - bb.y1);
body { margin: 0; overflow: hidden; }
<canvas width="800" height="800"></canvas>
如您所见,边界框矩形不正确。这是它目前的样子,它应该是这样的:
边界框的大小(
H = w * Abs(Sin(Fi)) + h * Abs(Cos(Fi))
W = w * Abs(Cos(Fi)) + h * Abs(Sin(Fi))
有了rXCenter, rYCenter
,你可以找到获取边界坐标
x0 = rXCenter - W/2
y0 = rYCenter - H/2
感觉你想多了。在这里,我展示了如何绘制三个围绕我制作的蓝色中心点旋转的矩形。
const wrapper = document.getElementById('wrapper');
const canvas = document.getElementById('canvas');
const wCS = window.getComputedStyle(wrapper, null)
const pl = parseFloat(wCS.getPropertyValue('padding-left'));
const pt = parseFloat(wCS.getPropertyValue('padding-top'));
const pr = parseFloat(wCS.getPropertyValue('padding-right'));
const pb = parseFloat(wCS.getPropertyValue('padding-bottom'));
canvas.width = wrapper.clientWidth - pl - pr;
canvas.height = wrapper.clientHeight - pt - pb;
const ctx = canvas.getContext('2d');
// setup a rectangle
let cornerLeft = 70;
let cornerTop = 70;
let width = 180;
let height = 60;
let horizontalCenter = cornerTop + (width / 2);
let verticalCenter = cornerTop + (height / 2);
let centerPoint = {
horz: horizontalCenter,
vert: verticalCenter
};
let colorBlue = '#0000FF';
let colorLime = '#40FF40';
let colorPurple = '#4040FF';
let colorOrange = '#FFA500';
ctx.fillStyle = colorBlue;
ctx.fillRect(centerPoint.horz, centerPoint.vert, 8, 8); // fill in the pixels at center point
ctx.strokeStyle = colorPurple;
ctx.strokeRect(cornerTop, cornerLeft, width, height); // horizontal purple box
ctx.strokeStyle = colorOrange;
ctx.translate(centerPoint.horz, centerPoint.vert); // center point note positive
ctx.rotate(Math.PI / 2); // vertical
ctx.translate(-centerPoint.horz, -centerPoint.vert); // center point - note negative
ctx.strokeRect(cornerTop, cornerLeft, width, height); // vertical orange box
ctx.strokeStyle = colorLime;
ctx.translate(-centerPoint.horz * (1.375), -centerPoint.vert * (0.5)); // center point - note negative
ctx.rotate((Math.PI / 2) / -2); //back left by 45 deg left
ctx.translate(centerPoint.horz * (.025), centerPoint.vert * (2.75)); // center point note positive
ctx.strokeRect(cornerTop, cornerLeft, width, height);
#wrapper {
width: 400px;
height: 400px;
padding: 2rem;
border: 1px solid blue;
}
#canvas {
border: 1px solid orange;
}
<div id="wrapper"><canvas id="canvas"></canvas></div>