求圆弧特定包围盒算法:以起止点连线为边计算矩形另外两点
嘿,这个需求确实比常规轴对齐包围盒要绕一点——毕竟要求一条边固定是圆弧的起点终点连线,核心就是找到另外两个能刚好包住圆弧的顶点对吧?我给你梳理一套清晰的计算步骤,连伪代码示例都准备好了:
以圆弧起点-终点连线为边的包围盒计算方案
首先我们明确已知的核心参数:
- 圆弧起点:
A(x₁, y₁) - 圆弧终点:
B(x₂, y₂) - 圆弧所在圆的圆心:
O(x₀, y₀) - 圆的半径:
r
步骤1:确定AB的方向与垂直方向
- 先计算向量
AB = (B.x - A.x, B.y - A.y),记为(dx, dy),再算出AB的长度L = √(dx² + dy²) - 得到AB的单位向量
u_AB = (dx/L, dy/L),以及两个垂直方向的单位向量:u_perp1 = (-dy/L, dx/L)u_perp2 = (dy/L, -dx/L)
接下来我们要确定哪个方向是圆弧凸侧的方向——也就是圆弧向外鼓出的那一侧。
步骤2:计算圆弧到AB的最大距离
这是关键一步,我们需要找到圆弧上离直线AB最远的点到AB的距离:
- 先写出直线AB的一般式:
a*x + b*y + c = 0,其中:a = y₂ - y₁b = x₁ - x₂c = x₂*y₁ - x₁*y₂
- 计算圆心O到直线AB的距离:
d = |a*x₀ + b*y₀ + c| / √(a² + b²) - 判断圆弧是劣弧还是优弧:
- 计算向量OA(O到A)和OB(O到B)的点积,得到圆心角θ:
θ = arccos( (OA·OB)/(r²) ) - 如果θ < π(180°),是劣弧;θ > π则是优弧(注意如果圆弧是顺时针方向,需要用2π-θ来判断)
- 计算向量OA(O到A)和OB(O到B)的点积,得到圆心角θ:
- 计算最大距离D:
- 劣弧:圆弧凸侧朝向AB,最远点是弧中点,距离为
D = r - d - 优弧:圆弧凸侧远离AB,最远点是圆上与弧中点相对的点,距离为
D = r + d
- 劣弧:圆弧凸侧朝向AB,最远点是弧中点,距离为
步骤3:确定凸侧的垂直方向
根据圆心在AB的哪一侧,结合圆弧类型(劣弧/优弧)确定正确的垂直方向:
- 先计算
side = a*x₀ + b*y₀ + c,这个值的正负表示圆心在直线AB的哪一侧 - 劣弧:凸侧是远离圆心的一侧,所以垂直方向与
side的符号相反 - 优弧:凸侧是朝向圆心的一侧,所以垂直方向与
side的符号相同
步骤4:计算两个紫色顶点
有了正确的垂直单位向量u_perp和最大距离D,直接计算:
- 从A点沿
u_perp移动D距离,得到第一个紫色点:P1 = A + u_perp * D - 从B点沿
u_perp移动D距离,得到第二个紫色点:P2 = B + u_perp * D
这样P1、A、B、P2就构成了刚好包围圆弧的矩形,AB是指定的那条边。
伪代码示例
import math def calculate_arc_bounding_box(A, B, O, r): # 解析坐标 x1, y1 = A x2, y2 = B x0, y0 = O # 计算AB向量与长度 dx = x2 - x1 dy = y2 - y1 L = math.hypot(dx, dy) if L == 0: raise ValueError("起点A和终点B不能重合") # 直线AB的一般式参数 a = y2 - y1 b = x1 - x2 c = x2 * y1 - x1 * y2 # 圆心到AB的距离 numerator = abs(a * x0 + b * y0 + c) d = numerator / math.hypot(a, b) # 计算圆心角,判断劣弧/优弧 OA_x = x1 - x0 OA_y = y1 - y0 OB_x = x2 - x0 OB_y = y2 - y0 dot_product = OA_x * OB_x + OA_y * OB_y cos_theta = dot_product / (r * r) cos_theta = max(min(cos_theta, 1.0), -1.0) # 修正浮点误差 theta = math.acos(cos_theta) # 处理顺时针圆弧的情况 cross_product = OA_x * OB_y - OA_y * OB_x if cross_product < 0: theta = 2 * math.pi - theta # 确定最大距离D if theta < math.pi: D = r - d # 劣弧 else: D = r + d # 优弧 # 确定凸侧的垂直单位向量 side = a * x0 + b * y0 + c perp_length = math.hypot(a, b) if theta < math.pi: # 劣弧:凸侧远离圆心,方向与side相反 u_perp_x = (-a / perp_length) if side > 0 else (a / perp_length) u_perp_y = (-b / perp_length) if side > 0 else (b / perp_length) else: # 优弧:凸侧朝向圆心,方向与side相同 u_perp_x = (a / perp_length) if side > 0 else (-a / perp_length) u_perp_y = (b / perp_length) if side > 0 else (-b / perp_length) # 计算两个紫色顶点 P1 = (x1 + u_perp_x * D, y1 + u_perp_y * D) P2 = (x2 + u_perp_x * D, y2 + u_perp_y * D) return (P1, P2)
内容的提问来源于stack exchange,提问作者philm
相关产品推荐
相关产品推荐

