旋转左上角锚定的外层矩形,修正不同尺寸内层矩形定位算法
解决外层矩形旋转后内层矩形左上角位置计算错误问题
问题场景
我有一个以左上角为锚点的外层矩形R,内部包含多个不同宽高的内层矩形。已实现R旋转α角度的算法,但需要计算每个不随R旋转的内层矩形的新左上角位置。当前的rotatePoint函数无法适配不同尺寸的内层矩形,例如旋转90度后,2×2尺寸的内层矩形左上角位置计算错误,结果与1×1尺寸的矩形完全一致。
问题原因
原函数仅直接旋转内层矩形的左上角点,但外层矩形旋转时,内层矩形的相对位置应该以外层矩形的中心为旋转基准。不同尺寸的内层矩形,其左上角到自身中心的偏移量不同,原方法未考虑这个偏移,导致大尺寸矩形的位置计算偏差。
解决方案
修改rotatePoint函数,增加内层矩形的宽高参数,先计算内层矩形的中心坐标,以外层中心为基准旋转该中心点,再根据旋转后的中心点反推出内层矩形的新左上角位置。
修改后的核心代码
1. 调整后的rotatePoint函数
static rotatePoint(x0, y0, xi, yi, innerWidth, innerHeight, degrees) { // 计算内层矩形的中心坐标 const innerCenterX = xi + innerWidth / 2; const innerCenterY = yi + innerHeight / 2; // 计算内层中心相对于外层中心的偏移量 let x = innerCenterX - x0; let y = innerCenterY - y0; const rad = degrees * (Math.PI/180); const cos_rad = Math.cos(rad); const sin_rad = Math.sin(rad); // 旋转内层中心坐标 const rotatedCenterX = x0 + cos_rad * x + sin_rad * y; const rotatedCenterY = y0 + -sin_rad * x + cos_rad * y; // 从旋转后的中心反推内层矩形的新左上角位置 const qx = rotatedCenterX - innerWidth / 2; const qy = rotatedCenterY - innerHeight / 2; return { x: qx, y: qy }; }
2. 对应修改调用逻辑的rotateUnit函数
static rotateUnit(origin, inner, direction) { const angle = origin.rotation + ((direction > 0) ? 45 : -45); const delta_angle = origin.rotation - angle; origin.rotation = angle; const box_x = origin.x + (origin.width / 2); const box_y = origin.y + (origin.height / 2); for (var i of inner) { var new_pos = Rect.rotatePoint(box_x, box_y, i.x, i.y, i.width, // 传入内层矩形宽度 i.height, // 传入内层矩形高度 delta_angle); i.x = new_pos.x; i.y = new_pos.y; } }
逻辑说明
- 计算内层中心:通过内层矩形左上角坐标加上自身宽高的一半,得到其中心位置,确保旋转基准点正确。
- 计算相对偏移:以内层中心与外层中心的偏移量作为旋转向量,保证旋转基于外层中心的相对位置。
- 旋转中心坐标:对偏移向量进行旋转变换,得到旋转后的内层中心绝对坐标。
- 反推左上角位置:从旋转后的中心坐标减去内层宽高的一半,得到适配尺寸的新左上角位置。
完整可视化代码
class Rect { static rotateUnit(origin, inner, direction) { const angle = origin.rotation + ((direction > 0) ? 45 : -45); const delta_angle = origin.rotation - angle; origin.rotation = angle; const box_x = origin.x + (origin.width / 2); const box_y = origin.y + (origin.height / 2); for (var i of inner) { var new_pos = Rect.rotatePoint(box_x, box_y, i.x, i.y, i.width, i.height, delta_angle); i.x = new_pos.x; i.y = new_pos.y; } } static rotatePoint(x0, y0, xi, yi, innerWidth, innerHeight, degrees) { // 计算内层矩形的中心坐标 const innerCenterX = xi + innerWidth / 2; const innerCenterY = yi + innerHeight / 2; // 计算内层中心相对于外层中心的偏移量 let x = innerCenterX - x0; let y = innerCenterY - y0; const rad = degrees * (Math.PI/180); const cos_rad = Math.cos(rad); const sin_rad = Math.sin(rad); // 旋转内层中心坐标 const rotatedCenterX = x0 + cos_rad * x + sin_rad * y; const rotatedCenterY = y0 + -sin_rad * x + cos_rad * y; // 从旋转后的中心反推内层矩形的新左上角位置 const qx = rotatedCenterX - innerWidth / 2; const qy = rotatedCenterY - innerHeight / 2; return { x: qx, y: qy }; } static printCoordinates(origin, inner, offset) { const ratio = 5 var canvas = document.getElementById("canvas"); if (canvas.getContext) { var ctx = canvas.getContext("2d"); ctx.fillStyle = "rgb(0,0,0)"; ctx.fillRect(origin.x/ratio+offset/ratio, origin.y/ratio+offset/ratio, 70*origin.width/ratio, 70*origin.height/ratio); ctx.fillStyle = "rgb(200,0,0)"; for (var i of inner) { ctx.fillRect(i.x/ratio+offset/ratio, i.y/ratio+offset/ratio, 70*i.width/ratio, 70*i.height/ratio); } } } } /* * SIZE (WIDTH AND HEIGHT) CORRELATES TO * PIXELS BY A CONSTANT OF 1:70 */ const rect_origin = { rotation: 0, x: 2302.492125, y: 1700.492125, width: 0.9842500000000001, height: 0.9842500000000001 } const inner = [ { x: 2336, y: 1734, width: 1.9685000000000001, height: 1.9685000000000001 }, { x: 2336, y: 1872, width: 0.9842500000000001, height: 0.9842500000000001 }, { x: 2405, y: 1872, width: 0.9842500000000001, height: 0.9842500000000001 }, { x: 2336, y: 1941, width: 0.9842500000000001, height: 0.9842500000000001 }, { x: 2405, y: 1941, width: 0.9842500000000001, height: 0.9842500000000001 } ] Rect.printCoordinates(rect_origin, inner, -1500); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, -1100); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, -700); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, -300); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, 100); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, 500); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, 900); Rect.rotateUnit(rect_origin, inner, 1); Rect.printCoordinates(rect_origin, inner, 1300);
<canvas id="canvas" width="1000" height="1000"></canvas>
内容的提问来源于stack exchange,提问作者Victor Lundgren
相关产品推荐
相关产品推荐

