如何计算绕左上角旋转的矩形的边界框坐标?
问题描述
几天前我问过相关问题,得到的答案对于绕自身中心旋转的矩形完全适用。
但我现在需要计算绕自身左上角旋转的矩形的边界框。
此前答案中用于计算边界框宽高的代码仍然正确可用:
H = w * Abs(Sin(Fi)) + h * Abs(Cos(Fi)) W = w * Abs(Cos(Fi)) + h * Abs(Sin(Fi))
但原计算边界框x、y坐标的逻辑不适用于绕左上角旋转的场景。最开始我以为沿用下面的逻辑就可以:
x0 = rXCenter - W/2 y0 = rYCenter - H/2
但实际运行发现并不正确。我猜测正确的计算逻辑需要用到sin和cos函数,但始终没有找到正确的组合方式,以下是问题的完整复现代码:
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, rOX, rOY) { rotateCanvas(rAngle, rOX, rOY); drawRectangle(rX, rY, rW, rH); rotateCanvas(-rAngle, rOX, rOY); } function computeBB(x, y, w, h, a) { let sinA = Math.abs(Math.sin(a)); let cosA = Math.abs(Math.cos(a)); let bbH = w * sinA + h * cosA; let bbW = w * cosA + h * sinA; let bbX = x - bbW / 2; let bbY = y - bbH / 2; return { x: bbX, y: bbY, w: bbW, h: bbH }; } let rX = 100; let rY = 100; let rW = 100; let rH = 50; let rA = degreesToRadians(45); let rOX = rX; let rOY = rY; drawRotatedRectangle(rX, rY, rW, rH, rA, rOX, rOY); let bb = computeBB(rX, rY, rW, rH, rA); ctx.strokeStyle = "#ff0000"; drawRectangle(bb.x, bb.y, bb.w, bb.h);
body { margin: 0; overflow: hidden; }
<canvas width="600" height="600"></canvas>
运行代码可见,沿用原逻辑得到的红色边界框无法正确包裹黑色旋转矩形,我需要适配所有rA取值的正确计算方式。
我认为只需要修改代码中以下两行计算bbX、bbY的逻辑即可解决问题,但始终找不到正确的修改方式:
let bbX = x - bbW / 2; let bbY = y - bbH / 2;
请问该如何修改这两行代码,才能正确计算绕左上角旋转的矩形的边界框坐标?
解决方案
直接修改computeBB函数的坐标计算逻辑即可,基于旋转矩阵计算矩形四个顶点旋转后的相对坐标,取偏移最小值作为边界框的左上角:
function computeBB(x, y, w, h, a) { let sinA = Math.sin(a); let cosA = Math.cos(a); let absSinA = Math.abs(sinA); let absCosA = Math.abs(cosA); // 原有宽高计算逻辑保持不变 let bbH = w * absSinA + h * absCosA; let bbW = w * absCosA + h * absSinA; // 计算四个顶点相对于旋转原点的偏移,取最小值作为边界框左上角偏移 let minX = Math.min(0, w * cosA, -h * sinA, w * cosA - h * sinA); let minY = Math.min(0, w * sinA, h * cosA, w * sinA + h * cosA); let bbX = x + minX; let bbY = y + minY; return { x: bbX, y: bbY, w: bbW, h: bbH }; }
该方案对任意正负角度都适配,不需要额外做象限判断。
内容的提问来源于stack exchange,提问作者Ryan Peschel
相关产品推荐
相关产品推荐

