旋转后包围盒的数学本质是求旋转后四个顶点在原始坐标系下的x、y坐标极值,即左=min(x)、右=max(x)、下=min(y)、上=max(y),需经平移→旋转→反向平移确保坐标正确,忽略平移会导致整体偏移。

旋转后包围盒的数学本质是什么
长方形绕某点旋转后,四个顶点坐标发生变化,最小外接矩形(轴对齐包围盒,AABB)必须覆盖所有顶点。关键不是“画个框”,而是求这四个顶点在原始坐标系下的 x 和 y 坐标极值:
- 新包围盒左边界 = min(四个顶点的
x) - 右边界 = max(四个顶点的
x) - 下边界 = min(四个顶点的
y) - 上边界 = max(四个顶点的
y)
旋转中心影响结果:若绕自身中心旋转,顶点对称;若绕原点或任意点旋转,需先平移→旋转→再反向平移。漏掉平移步骤是常见错误,会导致包围盒整体偏移。
用 std::sin 和 std::cos 手动计算顶点
假设长方形中心在 (cx, cy),宽 w、高 h,逆时针旋转角度为 theta(单位:弧度):
- 四个局部顶点(以中心为原点)为:
(w/2, h/2),(-w/2, h/2),(-w/2, -h/2),(w/2, -h/2) - 对每个点
(x0, y0),应用旋转公式:x1 = x0 <em> cos(theta) - y0 </em> sin(theta)y1 = x0 <em> sin(theta) + y0 </em> cos(theta) - 再加上中心偏移:
world_x = cx + x1,world_y = cy + y1
注意:theta 必须是弧度;用 std::sin/std::cos 前要 #include <cmath>;浮点精度误差可能导致极值判断失准,建议用 std::numeric_limits<double>::epsilon() 辅助比较(仅当需要严格相等判断时)。
立即学习“C++免费学习笔记(深入)”;
避免重复计算的优化技巧
四个顶点旋转后,x 坐标实际只有两种表达式组合(因符号对称):
-
cx ± (w/2 <em> cos(theta) - h/2 </em> sin(theta)) -
cx ± (w/2 <em> cos(theta) + h/2 </em> sin(theta))
所以左右边界可直接算:left = cx - std::abs(w/2 <em> cos(theta) - h/2 </em> sin(theta)) - std::abs(w/2 <em> cos(theta) + h/2 </em> sin(theta))?不对——这是常见误解。
正确简化方式是:
-
min_x = cx + std::min({ w/2<em>c - h/2</em>s, -w/2<em>c - h/2</em>s, -w/2<em>c + h/2</em>s, w/2<em>c + h/2</em>s })
其中c = cos(theta),s = sin(theta)。
实际编码中,不建议强行合并,4 次计算开销极小,且可读性远高于“优化”后的混乱表达式。CPU 流水线和编译器常数折叠已足够高效。
用 cv::RotatedRect 和 cv::boundingRect 快速验证(OpenCV 场景)
如果项目已依赖 OpenCV,可快速交叉验证手算结果:
- 构造
cv::RotatedRect(center, cv::Size2f(w, h), angle_in_degrees) - 调用
cv::boundingRect(rotated_rect)得到cv::Rect(即轴对齐包围盒)
注意:angle_in_degrees是顺时针为正,且定义为“长边与 x 轴夹角”,和你手动旋转的语义可能不一致;若长方形w < h,OpenCV 可能自动交换宽高并调整角度 ±90°,导致boundingRect结果看似“错位”。最稳妥的方式仍是手算四个顶点——它不依赖库行为,逻辑透明,调试时一眼能看出哪一步出问题。
旋转包围盒本身不难,难的是在不同坐标系约定(中心/顶点锚点、角度正方向、度/弧度)之间保持清醒。每次换一个库或引擎,先花 30 秒确认它的旋转定义,比事后调半天 sin 符号更省时间。


















