【问题标题】:How to determine whether two circular sectors overlap with each other如何判断两个圆形扇区是否相互重叠
【发布时间】:2015-11-13 09:34:56
【问题描述】:

每个扇区可以表示为(x,y,r,a,d),其中x,y是位置,r是半径,d是方向,a是角度。给定两个圆形扇区的这些信息,如何判断它们是否相互重叠? 有没有有效的算法来解决它?谢谢!

【问题讨论】:

  • 你不需要两个角来完全指定一个圆段吗?开始和结束角度?
  • 起始角为(d-a/2),结束角为(d+a/2)
  • 啊,这样更好。所以d 是一个角度(线段的“中心”),a 是线段的“展开”。从描述来看,d 似乎是一个简单的顺时针/逆时针说明符。
  • 如果没有人想出其他任何东西,最坏的情况是:只有一个完全在另一个内部,或者它们的边缘相交时,它们才会相交。因此,您可以计算一堆“扇区中某个点的成员资格”和“直线段和/或圆弧段之间的交点”。所有这些都很乏味。
  • 如果我们可以确定引导给定点的函数应该移动到哪里才能进入扇区,我们可以使用二进制搜索来确定两个扇区中是否存在公共点。我已经尝试了一段时间,但没有运气。也许三点行列式可能是一个好方法

标签: java algorithm


【解决方案1】:

我知道一种非常快速的方法来降低这种可能性,因为我以前用它来进行圆形碰撞。

计算出两个中心之间的距离,然后,如果该距离大于半径之和,则不会发生碰撞。为了效率,不要使用平方根,直接在平方值上工作:

if (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) > (r1 + r2) * (r1 + r2):
    # No chance of collision.

计算圆段会有点困难。


您选择的方法取决于您需要的准确度。如果你正在做实际的数学,你可能需要高精度。但是,例如,如果您正在为计算机游戏之类的东西这样做,那么足够接近可能就足够了。

如果是这种情况,我会考虑将弧线转换为一系列直线(其数量可能取决于a,即弧线的“展开” - 你可能会侥幸逃脱几条线用于展开一个弧度,但对于 180 度来说效果不太好)。

直线碰撞检测是一种更广为人知的方法,尽管您必须处理比较次数可能会迅速增加的事实。


如果您不想使用线段,请遵循以下流程。它使用圆碰撞算法来找出整个圆的零、一个或两个碰撞点,然后检查这些点是否在两个圆弧内。

首先,运行上面的检查以检测不可能发生碰撞的情况。如果圆之间不可能发生碰撞,那么弧也不会发生碰撞。

其次,检查圆是否有一个碰撞点。如果是这样的话:

(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) == (r1 + r2) * (r1 + r2)

当然是在合适的误差范围内。现在我们都应该知道,比较浮点数是否相等应该使用某种 delta 比较。

如果是这种情况,您有一点需要检查,您可以轻松找出该点。它是从(x1,y1)(x2,y2) 的直线上的点r1 单位,或者,将其视为沿该直线移动一些分数:

(x1 + (x2-x1) * (r1+r2) / r1, y1 + (y2-y1) * (r1+r2) / r1)

否则,有两点需要检查,您可以使用this one 等问题的答案来确定这两点是什么。

一旦你有一些碰撞点,它是一个much simpler method 来确定这些点是否在一个弧上,请记住,候选点需要在 两个 弧上才能使它们碰撞,而不仅仅是一个。

【讨论】:

  • 是的,弦线比弧线容易。好吧,我还是更喜欢用数学来证明它。非常感谢
  • 这个答案是否适用于所有情况?例如,当一个扇区完全在另一个扇区内时。或者当两个扇区的弧不接触,但从它们的“锥尖”到“弧端”的线段确实接触。这个答案似乎取决于弧上的碰撞点。如果不是这种情况,部门可以重叠。
  • @Scott,我从游戏的角度出发,假设物体是分开的,然后相互碰撞。您是正确的,这种方法不会检测到完全遏制。关于“仅检测弧线碰撞”,我也许应该更清楚地检查中心到边缘的线,而不仅仅是弧线。我会澄清的。
【解决方案2】:

有两个步骤。首先是确定两个中心是否足够接近以允许发生碰撞,这可以通过将它们之间的距离与其半径之和进行比较来完成:

if (((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) > ((r1 + r2) * (r1 + r2)))
    // No collision.

然后您需要检查中心之间的线是否在您的各个角度定义的弧内:

float angle1to2 = Math.atan2(y2 - y1, x2 - x1);
if (angle1to2 < (d1 - a1/2) || angle1to2 > (d1 + a1/2))
    // No collision
float angle2to1 = angle1to2 + Math.PI;
if (angle2to1 < (d2 - a2/2) || angle2to1 > (d2 + a2/2))
    // No collision

如果您通过这些检查而没有排除碰撞的可能性,那么您已成功检测到碰撞。

警告:此代码根本未经测试。特别是,atan2 调用可能需要根据您的坐标系进行一些调整。

编辑: 刚刚意识到这错过了一个重要的极端情况,即弧线不是“指向”彼此但仍然重叠。会对此深思熟虑并返回...

【讨论】:

  • 不确定这是否正确,但这个想法很好。谢谢!我会试着在这个方向思考问题
  • 扩展我的编辑后,我开始意识到我的方法可能根本无法正常工作。我有一种感觉,这两个中心之间的界线可能根本不相关——如果我有新的想法,我会回复你,否则请暂时忽略我的回答。
【解决方案3】:

由于我们有圆形扇区,因此如果您实时执行此操作,角度和方向并不重要。以下内容仅适用于全圆扇区,或者两个扇区相互指向的情况。

您可以按照以下步骤操作:

1) 求每个扇区之间的距离, 2)将两个半径减去该距离, 3)如果结果是否定的,则两个扇区之间发生了冲突。否则,它的碰撞距离。

例如,我们有两个扇区,两个扇区的半径均为 50 个单位。它们的中心点之间的距离是 80。减去 80-50-50 = -20,所以你知道发生了 20 个单位距离的碰撞。

否则,如果距离为 500,500-50-50 = 400,一个正值,现在您知道这两个扇区相距 400 个单位。

现在,如果圆圈太近,比如相隔 1 个单位,1-50-50 = -99,这意味着它们几乎完全重叠。

对于您在 cmets 上指定的真正分段圆形扇区,您应该使用 paxdiablos 或 Macs 答案。

【讨论】:

  • 这适用于完整的圆圈,但不一定适用于细分。想象两个相隔一个单位的十单位圆。尽管圆本身会发生碰撞,但外边缘(距另一个圆的半径最远)上的线段不会发生碰撞。
  • True.but 问题说明圆形段,并检查它们是否重叠。顺便说一句,你的答案有一个很好的实现。
  • 如果他们面对相反的方向会发生什么。即使它们的中心点接近 1 个单位,它们也不能重叠
  • 这个解决方案适用于圈子,但不适用于部门,对吧?
  • true,仅当扇区是一个完整的圆时。
猜你喜欢
  • 2010-09-23
  • 2011-04-06
  • 2015-04-18
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 2012-08-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多