已知方位参数的旋转矩形中心及顶点坐标计算(Java)技术问询
我来帮你梳理一下这个问题的解法,其实不需要绕到先计算蓝色坐标轴的矩形再旋转,直接通过向量偏移就能算出中心和顶点坐标,逻辑清晰还不容易出错。
解题思路
首先明确几个关键定义(避免歧义):
- 朝向角度
theta:以x轴正方向为0度,逆时针旋转的角度(如果你的角度定义是顺时针,后面代码里调整符号即可) dBow:点p到矩形艏边(前端面)的垂直距离dStern:点p到矩形艉边(后端面)的垂直距离dPort:点p到矩形左舷边的垂直距离dStarboard:点p到矩形右舷边的垂直距离
核心步骤分为两步:计算矩形中心,再推导四个顶点坐标。
1. 计算矩形中心
矩形的中心相对于点p的偏移,由两个方向的分量组成:
- 艏-艉方向:中心到艏边和艉边的距离相等,所以中心在这个方向上离p的距离是
(dStern - dBow)/2,方向与艏方向一致(如果结果为负,说明中心在p的艉侧) - 左-右舷方向:同理,中心在这个方向上离p的距离是
(dPort - dStarboard)/2,方向与右舷方向一致(结果为负则在左舷侧)
接下来把这两个方向转换成笛卡尔坐标系的单位向量:
- 艏方向单位向量:
(cos(thetaRad), sin(thetaRad))(thetaRad是角度转弧度后的值) - 右舷方向单位向量:
(sin(thetaRad), -cos(thetaRad))(垂直于艏方向顺时针转90度,对应船舶右舷的方向)
把偏移量和单位向量相乘,加到p的坐标上,就得到中心坐标。
2. 计算四个顶点
首先算出矩形的半长(中心到艏/艉边的垂直距离)和半宽(中心到左/右舷边的垂直距离):
- 半长:
(dBow + dStern)/2 - 半宽:
(dPort + dStarboard)/2
然后通过中心加上/减去半长的艏方向向量,加上/减去半宽的左舷方向向量(左舷是右舷的反方向),就能得到四个顶点:
- 艏左顶点:中心 + 半长×艏方向 + 半宽×左舷方向
- 艏右顶点:中心 + 半长×艏方向 - 半宽×左舷方向
- 艉左顶点:中心 - 半长×艏方向 + 半宽×左舷方向
- 艉右顶点:中心 - 半长×艏方向 - 半宽×左舷方向
Java实现代码
这里用Java的Point2D.Double来表示坐标,代码里包含完整的计算逻辑,你可以直接复用:
import java.awt.geom.Point2D; public class RotatedRectangleCalculator { /** * 计算旋转矩形的中心和四个顶点 * @param x1 已知点p的x坐标 * @param y1 已知点p的y坐标 * @param dBow p到艏边的垂直距离 * @param dStern p到艉边的垂直距离 * @param dPort p到左舷边的垂直距离 * @param dStarboard p到右舷边的垂直距离 * @param thetaDeg 矩形朝向角度(度,x轴正方向逆时针为正) * @return 返回包含中心和四个顶点的数组,顺序为:中心、艏左、艏右、艉左、艉右 */ public static Point2D.Double[] calculateRectangle(double x1, double y1, double dBow, double dStern, double dPort, double dStarboard, double thetaDeg) { // 角度转弧度 double thetaRad = Math.toRadians(thetaDeg); // 计算单位向量 double bowDirX = Math.cos(thetaRad); double bowDirY = Math.sin(thetaRad); double starboardDirX = Math.sin(thetaRad); double starboardDirY = -Math.cos(thetaRad); // 左舷方向是右舷的反方向 double portDirX = -starboardDirX; double portDirY = -starboardDirY; // 计算中心相对于p的偏移 double deltaBowStern = (dStern - dBow) / 2.0; double deltaPortStarboard = (dPort - dStarboard) / 2.0; // 中心坐标 double cx = x1 + deltaBowStern * bowDirX + deltaPortStarboard * starboardDirX; double cy = y1 + deltaBowStern * bowDirY + deltaPortStarboard * starboardDirY; Point2D.Double center = new Point2D.Double(cx, cy); // 计算半长和半宽 double halfLength = (dBow + dStern) / 2.0; double halfWidth = (dPort + dStarboard) / 2.0; // 计算四个顶点 Point2D.Double bowLeft = new Point2D.Double( cx + halfLength * bowDirX + halfWidth * portDirX, cy + halfLength * bowDirY + halfWidth * portDirY ); Point2D.Double bowRight = new Point2D.Double( cx + halfLength * bowDirX - halfWidth * portDirX, cy + halfLength * bowDirY - halfWidth * portDirY ); Point2D.Double sternLeft = new Point2D.Double( cx - halfLength * bowDirX + halfWidth * portDirX, cy - halfLength * bowDirY + halfWidth * portDirY ); Point2D.Double sternRight = new Point2D.Double( cx - halfLength * bowDirX - halfWidth * portDirX, cy - halfLength * bowDirY - halfWidth * portDirY ); return new Point2D.Double[]{center, bowLeft, bowRight, sternLeft, sternRight}; } // 测试示例 public static void main(String[] args) { // 测试参数:p(0,0),艏距1,艉距3,左舷距1,右舷距1,朝向90度(艏朝上) Point2D.Double[] result = calculateRectangle(0, 0, 1, 3, 1, 1, 90); System.out.println("中心坐标:(" + result[0].x + ", " + result[0].y + ")"); System.out.println("艏左顶点:(" + result[1].x + ", " + result[1].y + ")"); System.out.println("艏右顶点:(" + result[2].x + ", " + result[2].y + ")"); System.out.println("艉左顶点:(" + result[3].x + ", " + result[3].y + ")"); System.out.println("艉右顶点:(" + result[4].x + ", " + result[4].y + ")"); // 预期结果:中心(0, -1),艏左(-1,0),艏右(1,0),艉左(-1,-2),艉右(1,-2) } }
注意事项
- 角度定义:如果你的朝向角度是顺时针为正(比如某些船舶坐标系),只需要把
thetaRad改成Math.toRadians(-thetaDeg)即可调整方向。 - 距离的正负:如果p在矩形外部,距离值会是正的,代码依然能正确计算中心和顶点,因为逻辑是基于垂直距离的相对关系。
- 精度问题:如果需要更高精度,可以用
BigDecimal代替double,但一般场景下double足够。
内容的提问来源于stack exchange,提问作者Braveness
相关产品推荐
相关产品推荐

