我用谷歌搜索直到我脸色发青,除非我遗漏了一些非常明显的东西,否则我找不到任何算法来计算2D扇区的边界框。
考虑到封闭圆的中心点,半径和扇区范围的角度,计算该扇区的轴对齐边界矩形的最佳算法是什么?
答案 0 :(得分:15)
答案 1 :(得分:8)
我要改写yairchu的答案,以便更清楚(对我来说,无论如何)。
暂时忽略中心坐标并在原点绘制圆。说服自己以下内容:
您现在最多可以找到4 + 1 + 2个点。找到绘制矩形的坐标的最大值和最小值。
通过将原始圆心的坐标添加到矩形的坐标,可以很容易地将矩形平移到原始圆。
答案 2 :(得分:3)
首先,如果我犯了错误,我会道歉但英语不是我的第一语言,实际上是西班牙语!
我遇到了这个问题,我认为我找到了一个有效的解决方案。
首先让我们看一下情况的图像
所以我们有一个椭圆(实际上是一个圆圈)和两个点(C
,D
),表示我们的扇区。
我们还有圆圈的中心(B
)和圆弧alpha
的角度。
现在,在这种情况下,我让它通过porpouse上的360º
来查看它是否有效。
让我们说alpha -> -251.1º
(它导致它的顺时针方向为负),让它转换为正值360º - 251.1º = 108.9º
现在我们的目标是找到该角度的二分角,这样我们就能找到最大点对于边界框(图像中的E
),实际上你可能已经意识到,段BE
的长度等于圆的半径但我们必须有角度来获得实际的坐标E
点。
所以108.9º / 2 -> 54.45º
现在我们有了角度。
要找到E的坐标,我们使用极坐标
x = r * Cos(theta)
y = r * Sin(theta)
我们有r
和theta
,因此我们可以计算x和y
在我的例子中r = 2.82
...(实际上它是理性的,但我把前两个十进制数字视为一个简单的问题)
我们知道我们的第一个半径为87.1º
,因此θ为87.1 - 54.45º -> 32.65º
我们知道* theta *是32.65º
所以让我们做一些数学
x = 2.82 * Cos(32.65º) -> 2.37552
y = 2.82 * Sin(32.65º) -> 1.52213
现在我们需要将这些值调整到圆的实际中心,以便
x = x + centerX
y = y + centerY
在示例中,圆圈以(1.86, 4.24)
x -> 4.23552
y -> 5.76213
在这个阶段我们应该使用一些微积分。我们知道边界框的一个边缘将是穿过我们刚刚计算的点的弧的切线,所以让我们找到切线(红线)。
我们知道切线穿过我们的点(4.23, 5.76)
,现在我们需要一个斜率。
如您所见,斜率与通过我们的半径的矩形的斜率相同,因此我们必须找到斜率。
为此,我们需要获取半径的坐标(从极坐标快速转换为卡西斯坐标)。
x = r * Cos(theta)
y = r * Sin(theta)
所以
p0 = (centerX + 2.82 * Cos(87.1º), centerY + 2.82 * Sin(87.1º))
p1 = (centerX + 2.82 * Cos(-21.8º), centerY + 2.82 * Sin(-21.8º))
(21.8º
是从水平轴到它下面的半径顺时针测量的角度,因此我将其置为负值。
p0 (2, 7.06)
p1 (4.48, 3.19)
现在让我们找到斜率:
m = (y - y0) / (x - x0)
...
m = (3.19 - 7.06) / (4.48-2) = -3.87 / 2.48 = -1.56048
...
m = -1.56
具有我们需要计算切线方程的斜率,基本上是一个具有已知斜率(m = -1.56
)的矩形,它通过已经知道的点(E -> (4.23, 5.76)
)
所以我们Y = mx + b
m = -1.56
,y = 5.76
和x = 4.23
所以b
必须
b = 5.76 - (-1.56) * 4.23 = 12.36
现在我们有了切线的完整等式 - > Y = -1.56X + 12.36
我们必须知道的是将点C
和D
投射到该矩形上。
我们需要rects CH
和DI
的等式,所以让我们计算'em
让我们从CH
开始:
我们知道(从tanget方程式)我们的方向向量是(1.56, 1)
我们需要找到一个通过点C -> (2, 7.06)
(x - 2) / 1.56 = (y - 7.06) / 1
做一些代数 - > y = 0.64x + 5.78
我们知道有直方CH
的等式,我们必须计算点H
。
我们必须解决线性系统如下
y = -1.56x + 12.36
y = 1.56x + 5.78
解决这个问题,我们会找到点H (3, 7.69)
我们需要对rect DI
做同样的事情,所以让我们这样做
我们的方向向量再次为(1.56, 1)
D -> (4.48, 3.19)
(x - 4.48) / 1.56 = (y -3.19) / 1
做一些代数 - > y = 0.64x + 0.32
让我们解决线性系统
y = -1.56x + 12.36
y = 0.64x + 0.32
I (5.47, 3.82)
在这个阶段,我们已经有四个点使我们的边界框 - > C, H, D , I
万一你不知道或记得如何用编程语言解决线性系统,我会给你一个小例子
这是纯粹的代数
假设我们有以下系统
Ax + By = C
Dx + Ey = F
然后
Dx = F - Ey
x = (F - Ey) / D
x = F/D - (E/D)y
替换其他等式
A(F/D - (E/D)y) + By = C
AF/D - (AE/D)y + By = C
(AE/D)y + By = C - AF/D
y(-AE/D + B) = C - AF/D
y = (C - AF/D) / (-AE/D + B)
= ( (CD - AF) / D ) / ( (-AE + BD) / D) )
所以
y = (CD - AF) / (BD - AE)
和x
我们做同样的事情
Dx = F - Ey
Dx - F = -Ey
Ey = F - Dx
y = F/E - (D/E)x
替换其他等式
Ax + B(F/E - (D/E)x) = C
Ax + (BF/E - (DB/E)x) = C
Ax - (DB/E)x = C - BF/E
x (A-(DB/E)) = C - BF/E
x = (C - BF/E)/(A-(DB/E))
= ((CE - BF) / E) / ((AE-DB) / E)
x = (CE - BF) / (AE - DB)
我为答案的范围道歉,但我的意思是尽可能清楚,因此,我几乎一步一步地做到了。
答案 3 :(得分:0)
使用C#代码:
/// <summary>
/// The input parameters describe a circular arc going _clockwise_ from E to F.
/// The output is the bounding box.
/// </summary>
public Rect BoundingBox(Point E, Point F, Point C, double radius)
{
// Put the endpoints into the bounding box:
double x1 = E.X;
double y1 = E.Y;
double x2 = x1, y2 = y1;
if (F.X < x1)
x1 = F.X;
if (F.X > x2)
x2 = F.X;
if (F.Y < y1)
y1 = F.Y;
if (F.Y > y2)
y2 = F.Y;
// Now consider the top/bottom/left/right extremities of the circle:
double thetaE = Math.Atan2(E.Y - C.Y, E.X - C.X);
double thetaF = Math.Atan2(F.Y - C.Y, F.X - C.X);
if (AnglesInClockwiseSequence(thetaE, 0/*right*/, thetaF))
{
double x = (C.X + radius);
if (x > x2)
x2 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/2/*bottom*/, thetaF))
{
double y = (C.Y + radius);
if (y > y2)
y2 = y;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/*left*/, thetaF))
{
double x = (C.X - radius);
if (x < x1)
x1 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI*3/2/*top*/, thetaF))
{
double y = (C.Y - radius);
if (y < y1)
y1 = y;
}
return new Rect(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Do these angles go in clockwise sequence?
/// </summary>
private static bool AnglesInClockwiseSequence(double x, double y, double z)
{
return AngularDiffSigned(x, y) + AngularDiffSigned(y, z) < 2*Math.PI;
}
/// <summary>
/// Returns a number between 0 and 360 degrees, as radians, representing the
/// angle required to go clockwise from 'theta1' to 'theta2'. If 'theta2' is
/// 5 degrees clockwise from 'theta1' then return 5 degrees. If it's 5 degrees
/// anticlockwise then return 360-5 degrees.
/// </summary>
public static double AngularDiffSigned(double theta1, double theta2)
{
double dif = theta2 - theta1;
while (dif >= 2 * Math.PI)
dif -= 2 * Math.PI;
while (dif <= 0)
dif += 2 * Math.PI;
return dif;
}