圆矩形碰撞检测(相交)


192

如何判断圆和矩形在2D欧几里得空间中是否相交?(即经典的2D几何)


1
矩形是否始终与轴对齐,或者可以将其旋转任意角度?
e.James

11
@eJames:这有什么关系?您正在检查矩形与圆形的交点;您始终可以变换坐标系,以使矩形与轴平行,而圆没有变化:-)
ShreevatsaR

您应该添加它作为答案,并依次旋转-Θ和所有...
aib

2
@ShreevatsaR:不管是否重要,我都需要担心坐标翻译与否。@aib:亲爱的!
e.James

Answers:


190

圆与矩形相交只有两种情况:

  • 圆的中心位于矩形内,或者
  • 矩形的边缘之一在圆中有一个点。

请注意,这不需要矩形与轴平行。

圆形和矩形可能相交的一些不同方式

(一种查看方式:如果没有一条边在圆中有一个点(如果所有边都完全在圆的“外部”),那么圆仍然可以与多边形相交的唯一方法是如果它完全位于圆的内部多边形。)

有了这种洞察力,像下面的工作,其中圆的中心P和半径R,以及矩形的顶点ABCD的顺序(不完整的代码):

def intersect(Circle(P, R), Rectangle(A, B, C, D)):
    S = Circle(P, R)
    return (pointInRectangle(P, Rectangle(A, B, C, D)) or
            intersectCircle(S, (A, B)) or
            intersectCircle(S, (B, C)) or
            intersectCircle(S, (C, D)) or
            intersectCircle(S, (D, A)))

如果要编写任何几何图形,则库中可能已经具有上述功能。否则,pointInRectangle()可以通过多种方式实现;多边形方法中的任何常规都可以使用,但是对于矩形,您只需检查是否可以使用:

0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD

而且intersectCircle()也很容易实现:一种方法是检查从垂直P线到直线的脚是否足够靠近端点,并在端点之间进行检查。

很棒的事情是,相同的想法不仅适用于矩形,而且适用于圆与任何简单多边形的交点-甚至不必是凸形!


24
就其价值而言,我真的认为这个答案比我的要好。主要有两个原因:1:如果矩形不平行于轴,则不需要旋转; 2:概念很容易扩展到所有多边形。
e.James 2011年

2
@paniq:嗯,都是固定时间的。:-)但是,是的,这作为通用解决方案更为有用,它可以覆盖具有任何方向的矩形,甚至可以覆盖任何简单的多边形。
ShreevatsaR 2011年

7
如果矩形完全在圆内,但是圆的中心不在矩形内,该怎么办?
ericsoco

2
@ericsoco:好的观察。:-)我想我应该在“矩形的一个边缘与圆的相交”中说“与圆盘相交”,因为我的意思是说它与圆本身共享一个点,而不一定是圆的边界。无论如何,上面的说明“检查从P [圆的中心]到直线的垂直线的脚是否足够靠近端点,并在端点之间进行检查,否则仍然可以使用-例如,端点位于圆内(光盘)。
ShreevatsaR

2
@ DexD.Hunter如果圆的中心在矩形外部,但是一部分在矩形内部,则矩形的边缘之一必然与圆相交。
ShreevatsaR

289

这是我的方法:

bool intersects(CircleType circle, RectType rect)
{
    circleDistance.x = abs(circle.x - rect.x);
    circleDistance.y = abs(circle.y - rect.y);

    if (circleDistance.x > (rect.width/2 + circle.r)) { return false; }
    if (circleDistance.y > (rect.height/2 + circle.r)) { return false; }

    if (circleDistance.x <= (rect.width/2)) { return true; } 
    if (circleDistance.y <= (rect.height/2)) { return true; }

    cornerDistance_sq = (circleDistance.x - rect.width/2)^2 +
                         (circleDistance.y - rect.height/2)^2;

    return (cornerDistance_sq <= (circle.r^2));
}

运作方式如下:

幻觉

  1. 第一条线计算圆心和矩形中心之间的x和y差的绝对值。这会将四个象限折叠成一个象限,因此不必进行四次计算。该图显示了圆心现在必须位于的区域。注意,仅显示单个象限。矩形是灰色区域,红色边框勾勒出关键区域,该区域与矩形的边缘正好相距一个半径。圆的中心必须在此红色边框内,以使相交发生。

  2. 第二对线消除了简单的情况,即圆与矩形(沿任一方向)相距足够远而无法相交。这对应于图像中的绿色区域。

  3. 第三对线处理简单的情况,即圆与矩形(沿任一方向)足够近,从而确保了相交。这对应于图像中的橙色和灰色部分。注意,为了使逻辑有意义,必须在步骤2之后执行此步骤。

  4. 其余的线计算出圆形可能与矩形的角相交的困难情况。要解决该问题,请计算到圆心和角的距离,然后验证该距离不超过圆心的半径。对于中心在红色阴影区域内的所有圆,此计算均返回false;对于中心在白色阴影区域内的所有圆,此计算均返回true。


4
非常好!注意:显然,这里rect.x / y在矩形的右上角。您也可以通过比较半径的平方来消除昂贵的平方根。
luqui

2
哦,不,我的坏。rect.x / y在矩形的左下方。我会这样写:circleDistance.x = abs(circle.x-(rect.x + rect.width / 2));
luqui

2
@坦纳:我们去了。万岁备份和OCD;)
e.James 2011年

11
只是为了澄清-此答案仅适用于轴对齐的矩形。通过阅读其他答案的注释可以清楚地看出来,但仅凭此答案+注释就不明显了。(对轴对齐的
rect的

3
大!重要的是要让读者知道,在这里我相信rect的定义是rect.x和rect.y是rect 的中心。在我的世界中,rect的xy是rect的顶部/左侧,而0,0是屏幕的顶部/左侧,所以我使用了:circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
erco

123

这是另一个实现起来非常简单(也非常快)的解决方案。它会捕捉所有相交点,包括当球体完全进入矩形时。

// clamp(value, min, max) - limits value to the range min..max

// Find the closest point to the circle within the rectangle
float closestX = clamp(circle.X, rectangle.Left, rectangle.Right);
float closestY = clamp(circle.Y, rectangle.Top, rectangle.Bottom);

// Calculate the distance between the circle's center and this closest point
float distanceX = circle.X - closestX;
float distanceY = circle.Y - closestY;

// If the distance is less than the circle's radius, an intersection occurs
float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < (circle.Radius * circle.Radius);

使用任何体面的数学库,可以将其缩短为3或4行。


3
您那里有一个错误,您使用“左”和“右”而不是“顶部”和“底部”来搜索closestY,否则该解决方案很不错。
manveru 2010年

8
我最喜欢这个答案。它简短,易懂且快速。
约翰·库拉克

2
我认为,如果矩形相对于x轴和y轴倾斜,则您的解决方案将失败。
狮子座

3
@Leo我认为修改该算法以适应这种情况并不难,应该简单地应用坐标变换,其中原点位于矩形的中心并且矩形不再倾斜。您仅需要将变换应​​用于圆心。
enobayram 2013年

1
这基本上与migapro.com/circle-and-rotated-rectangle-collision-detection中找到的代码相同,我也将其移植到了Objective-C。效果很好;这是解决该问题的好方法。
PKCLsoft 2014年

10

您的球体和rect相交
,如果圆心与rect的一个顶点之间的距离小于球体的半径,
或者
圆心与rect的一个边缘之间的距离小于球体的半径( [ point-line distance ])

圆心在rect

point-point距离内:

P1 = [x1,y1]
P2 = [x2,y2]
距离= sqrt(abs(x1-x2)+ abs(y1-y2))

点线距离:

L1 = [x1,y1],L2 = [x2,y2](直线的两个点,即顶点)
P1 = [px,py]某点

距离d = abs((x2-x1)(y1-py)-(x1-px)(y2-y1))/距离(L1,L2)


矩形内部的圆心:
采用单独的轴方法:如果在将矩形与该点分开的线上存在投影,则它们不相交

您可以将点投影在与矩形侧面平行的线上,然后可以轻松确定它们是否相交。如果它们不在所有四个投影上相交,则它们(点和矩形)不能相交。

您只需要内积(x = [x1,x2],y = [y1,y2],x * y = x1 * y1 + x2 * y2)

您的测试如下所示:

//矩形边缘:TL(左上),TR(右上),BL(左下),BR(右下)
//要测试的点:POI

分隔=假
对于{{TL,TR},{BL,BR},{TL,BL},{TR-BR}}中的egde://边缘
    D =边缘[0]-边缘[1]
    innerProd = D * POI
    Interval_min = min(D * edge [0],D * edge [1])
    Interval_max = max(D * edge [0],D * edge [1])
    如果不是(Interval_min≤innerProd≤Interval_max) 
           分隔=真
           中断//循环结束 
    万一
结束于
如果(分隔为true)    
      返回“无路口”
其他 
      返回“交集”
万一

这不假定轴对齐的矩形,并且可以很容易地扩展以测试凸集之间的相交。


1
点对点距离不应该使用正方形而不是绝对值吗?
托马斯

6

这是最快的解决方案:

public static boolean intersect(Rectangle r, Circle c)
{
    float cx = Math.abs(c.x - r.x - r.halfWidth);
    float xDist = r.halfWidth + c.radius;
    if (cx > xDist)
        return false;
    float cy = Math.abs(c.y - r.y - r.halfHeight);
    float yDist = r.halfHeight + c.radius;
    if (cy > yDist)
        return false;
    if (cx <= r.halfWidth || cy <= r.halfHeight)
        return true;
    float xCornerDist = cx - r.halfWidth;
    float yCornerDist = cy - r.halfHeight;
    float xCornerDistSq = xCornerDist * xCornerDist;
    float yCornerDistSq = yCornerDist * yCornerDist;
    float maxCornerDistSq = c.radius * c.radius;
    return xCornerDistSq + yCornerDistSq <= maxCornerDistSq;
}

请注意执行顺序,并且宽度/高度的一半已预先计算。同样,平方是“手动”完成的,以节省一些时钟周期。


3
我认为您不能声称没有最充分证据的情况下,最昂贵的代码路径中的五个测试/比较是“最快的解决方案”。
sam hocevar


1
以我对这种方法的经验,碰撞通常不会发生。因此,测试将导致在执行大多数代码之前退出。
intrepidis

6

我想出的最简单的解决方案非常简单。

它的工作原理是在矩形中找到最接近圆的点,然后比较距离。

您可以通过一些操作来完成所有这些操作,甚至可以避免使用sqrt函数。

public boolean intersects(float cx, float cy, float radius, float left, float top, float right, float bottom)
{
   float closestX = (cx < left ? left : (cx > right ? right : cx));
   float closestY = (cy < top ? top : (cy > bottom ? bottom : cy));
   float dx = closestX - cx;
   float dy = closestY - cy;

   return ( dx * dx + dy * dy ) <= radius * radius;
}

就是这样!上面的解决方案假设x轴指向下在世界的左上方。

如果您想找到一种解决方案来处理移动的圆形和矩形之间的碰撞,则它要复杂得多,这也是我的另一个答案。


如果圆半径太小且圆心在矩形内,将无法检测到相交!
Yoav 2014年

2
您能否提供使失败的实际输入?当圆圈在内部时,测试的左侧部分为0.0。除非半径为零,否则测试的右侧部分应> 0.0
ClickerMonkey

这对旋转的矩形也适用吗?如果不是的话,请给我一个提示.....
M Abdul Sami 2015年

4

实际上,这要简单得多。您只需要两件事。

首先,您需要找到从圆心到矩形的每条线的四个正交距离。如果您的圆中的任何三个大于圆半径,则您的圆将不会与该矩形相交。

其次,您需要找到圆心和矩形中心之间的距离,然后如果该距离大于矩形对角线长度的一半,则您的圆将不在矩形内。

祝好运!


3

这是我的C代码,用于解决球体与无轴对齐框之间的碰撞。它依赖于我自己的几个库例程,但对某些人可能有用。我在游戏中使用它,效果很好。

float physicsProcessCollisionBetweenSelfAndActorRect(SPhysics *self, SPhysics *actor)
{
    float diff = 99999;

    SVector relative_position_of_circle = getDifference2DBetweenVectors(&self->worldPosition, &actor->worldPosition);
    rotateVector2DBy(&relative_position_of_circle, -actor->axis.angleZ); // This aligns the coord system so the rect becomes an AABB

    float x_clamped_within_rectangle = relative_position_of_circle.x;
    float y_clamped_within_rectangle = relative_position_of_circle.y;
    LIMIT(x_clamped_within_rectangle, actor->physicsRect.l, actor->physicsRect.r);
    LIMIT(y_clamped_within_rectangle, actor->physicsRect.b, actor->physicsRect.t);

    // Calculate the distance between the circle's center and this closest point
    float distance_to_nearest_edge_x = relative_position_of_circle.x - x_clamped_within_rectangle;
    float distance_to_nearest_edge_y = relative_position_of_circle.y - y_clamped_within_rectangle;

    // If the distance is less than the circle's radius, an intersection occurs
    float distance_sq_x = SQUARE(distance_to_nearest_edge_x);
    float distance_sq_y = SQUARE(distance_to_nearest_edge_y);
    float radius_sq = SQUARE(self->physicsRadius);
    if(distance_sq_x + distance_sq_y < radius_sq)   
    {
        float half_rect_w = (actor->physicsRect.r - actor->physicsRect.l) * 0.5f;
        float half_rect_h = (actor->physicsRect.t - actor->physicsRect.b) * 0.5f;

        CREATE_VECTOR(push_vector);         

        // If we're at one of the corners of this object, treat this as a circular/circular collision
        if(fabs(relative_position_of_circle.x) > half_rect_w && fabs(relative_position_of_circle.y) > half_rect_h)
        {
            SVector edges;
            if(relative_position_of_circle.x > 0) edges.x = half_rect_w; else edges.x = -half_rect_w;
            if(relative_position_of_circle.y > 0) edges.y = half_rect_h; else edges.y = -half_rect_h;   

            push_vector = relative_position_of_circle;
            moveVectorByInverseVector2D(&push_vector, &edges);

            // We now have the vector from the corner of the rect to the point.
            float delta_length = getVector2DMagnitude(&push_vector);
            float diff = self->physicsRadius - delta_length; // Find out how far away we are from our ideal distance

            // Normalise the vector
            push_vector.x /= delta_length;
            push_vector.y /= delta_length;
            scaleVector2DBy(&push_vector, diff); // Now multiply it by the difference
            push_vector.z = 0;
        }
        else // Nope - just bouncing against one of the edges
        {
            if(relative_position_of_circle.x > 0) // Ball is to the right
                push_vector.x = (half_rect_w + self->physicsRadius) - relative_position_of_circle.x;
            else
                push_vector.x = -((half_rect_w + self->physicsRadius) + relative_position_of_circle.x);

            if(relative_position_of_circle.y > 0) // Ball is above
                push_vector.y = (half_rect_h + self->physicsRadius) - relative_position_of_circle.y;
            else
                push_vector.y = -((half_rect_h + self->physicsRadius) + relative_position_of_circle.y);

            if(fabs(push_vector.x) < fabs(push_vector.y))
                push_vector.y = 0;
            else
                push_vector.x = 0;
        }

        diff = 0; // Cheat, since we don't do anything with the value anyway
        rotateVector2DBy(&push_vector, actor->axis.angleZ);
        SVector *from = &self->worldPosition;       
        moveVectorBy2D(from, push_vector.x, push_vector.y);
    }   
    return diff;
}

2

为了可视化,请使用键盘的小键盘。如果键“ 5”代表您的矩形,则所有键1-9代表9个象限的空间,除以构成矩形的线(其中5个是内部)。

1)如果圆心在第5象限内(即矩形内),则两个形状相交。

因此,有两种可能的情况:a)圆与矩形的两个或更多相邻边缘相交。b)圆与矩形的一条边相交。

第一种情况很简单。如果圆与矩形的两个相邻边相交,则它必须包含连接这两个边的角。(那个或它的中心位于我们已经覆盖的象限5中。另外请注意,也覆盖了圆仅与矩形的两个相对边缘相交的情况。)

2)如果矩形的任意角A,B,C,D位于圆内,则两个形状相交。

第二种情况比较棘手。我们应该注意,只有当圆心位于第2、4、6或8个象限之一时,才会发生这种情况。相应的角将是最接近它的点。)

现在,我们有了圆的中心位于“边缘”象限之一的情况,并且它仅与相应的边缘相交。然后,最靠近圆心的边上的点必须位于圆内。

3)对于AB,BC,CD,DA的每条线,通过圆心P构造垂直线p(AB,P),p(BC,P),p(CD,P),p(DA,P)。每条垂直线,如果与原始边缘的交点位于圆内,则两个形状相交。

这是最后一步的快捷方式。如果圆的中心在象限8中,并且边缘AB是上边缘,则交点将具有A和B的y坐标,以及中心P的x坐标。

您可以构造四个线相交,并检查它们是否位于其相应的边上,或者找出哪个象限P位于并检查相应的相交。两者都应简化为相同的布尔方程。请注意,上面的步骤2并不排除P在“角”象限之一中;它只是在寻找路口。

编辑:事实证明,我忽略了一个简单的事实,即#2是上面#3的子情况。毕竟,拐角也是边缘上的点。有关详细说明,请参见下面的@ShreevatsaR答案。同时,除非您想要快速但多余的检查,否则请忘记上面的#2。


2

此功能检测圆形和矩形之间的碰撞(相交)。他的回答类似于e.James方法,但是这一方法检测矩形所有角度(不仅是右上角)的碰撞。

注意:

aRect.origin.xaRect.origin.y是矩形左下角的坐标!

aCircle.xaCircle.y圆心的坐标!

static inline BOOL RectIntersectsCircle(CGRect aRect, Circle aCircle) {

    float testX = aCircle.x;
    float testY = aCircle.y;

    if (testX < aRect.origin.x)
        testX = aRect.origin.x;
    if (testX > (aRect.origin.x + aRect.size.width))
        testX = (aRect.origin.x + aRect.size.width);
    if (testY < aRect.origin.y)
        testY = aRect.origin.y;
    if (testY > (aRect.origin.y + aRect.size.height))
        testY = (aRect.origin.y + aRect.size.height);

    return ((aCircle.x - testX) * (aCircle.x - testX) + (aCircle.y - testY) * (aCircle.y - testY)) < aCircle.radius * aCircle.radius;
}

1

我有一种避免不必要的昂贵毕达哥拉斯的方法-即。当矩形和圆形的边界框不相交时。

它也适用于非欧几里得:

class Circle {
 // create the bounding box of the circle only once
 BBox bbox;

 public boolean intersect(BBox b) {
    // test top intersect
    if (lat > b.maxLat) {
        if (lon < b.minLon)
            return normDist(b.maxLat, b.minLon) <= normedDist;
        if (lon > b.maxLon)
            return normDist(b.maxLat, b.maxLon) <= normedDist;
        return b.maxLat - bbox.minLat > 0;
    }

    // test bottom intersect
    if (lat < b.minLat) {
        if (lon < b.minLon)
            return normDist(b.minLat, b.minLon) <= normedDist;
        if (lon > b.maxLon)
            return normDist(b.minLat, b.maxLon) <= normedDist;
        return bbox.maxLat - b.minLat > 0;
    }

    // test middle intersect
    if (lon < b.minLon)
        return bbox.maxLon - b.minLon > 0;
    if (lon > b.maxLon)
        return b.maxLon - bbox.minLon > 0;
    return true;
  }
}
  • minLat,maxLat可以用minY,maxY替换,minLon,maxLon相同:用minX,maxX替换
  • normDist ist方法仅比全距离计算快一点。例如,没有在欧几里德空间平方根(或不具有其他很多东西为半正矢): dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLon。当然,如果您使用normDist方法,则需要normedDist = dist*dist;为圆创建一个

请参阅我的GraphHopper项目的完整BBoxCircle代码。


1

我开设了形状课,希望您喜欢

public class Geomethry {
  public static boolean intersectionCircleAndRectangle(int circleX, int circleY, int circleR, int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight){
    boolean result = false;

    float rectHalfWidth = rectangleWidth/2.0f;
    float rectHalfHeight = rectangleHeight/2.0f;

    float rectCenterX = rectangleX + rectHalfWidth;
    float rectCenterY = rectangleY + rectHalfHeight;

    float deltax = Math.abs(rectCenterX - circleX);
    float deltay = Math.abs(rectCenterY - circleY);

    float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;

    do{
        // check that distance between the centerse is more than the distance between the circumcircle of rectangle and circle
        if(lengthHypotenuseSqure > ((rectHalfWidth+circleR)*(rectHalfWidth+circleR) + (rectHalfHeight+circleR)*(rectHalfHeight+circleR))){
            //System.out.println("distance between the centerse is more than the distance between the circumcircle of rectangle and circle");
            break;
        }

        // check that distance between the centerse is less than the distance between the inscribed circle
        float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
        if(lengthHypotenuseSqure < ((rectMinHalfSide+circleR)*(rectMinHalfSide+circleR))){
            //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
            result=true;
            break;
        }

        // check that the squares relate to angles
        if((deltax > (rectHalfWidth+circleR)*0.9) && (deltay > (rectHalfHeight+circleR)*0.9)){
            //System.out.println("squares relate to angles");
            result=true;
        }
    }while(false);

    return result;
}

public static boolean intersectionRectangleAndRectangle(int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight, int rectangleX2, int rectangleY2, int rectangleWidth2, int rectangleHeight2){
    boolean result = false;

    float rectHalfWidth = rectangleWidth/2.0f;
    float rectHalfHeight = rectangleHeight/2.0f;
    float rectHalfWidth2 = rectangleWidth2/2.0f;
    float rectHalfHeight2 = rectangleHeight2/2.0f;

    float deltax = Math.abs((rectangleX + rectHalfWidth) - (rectangleX2 + rectHalfWidth2));
    float deltay = Math.abs((rectangleY + rectHalfHeight) - (rectangleY2 + rectHalfHeight2));

    float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;

    do{
        // check that distance between the centerse is more than the distance between the circumcircle
        if(lengthHypotenuseSqure > ((rectHalfWidth+rectHalfWidth2)*(rectHalfWidth+rectHalfWidth2) + (rectHalfHeight+rectHalfHeight2)*(rectHalfHeight+rectHalfHeight2))){
            //System.out.println("distance between the centerse is more than the distance between the circumcircle");
            break;
        }

        // check that distance between the centerse is less than the distance between the inscribed circle
        float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
        float rectMinHalfSide2 = Math.min(rectHalfWidth2, rectHalfHeight2);
        if(lengthHypotenuseSqure < ((rectMinHalfSide+rectMinHalfSide2)*(rectMinHalfSide+rectMinHalfSide2))){
            //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
            result=true;
            break;
        }

        // check that the squares relate to angles
        if((deltax > (rectHalfWidth+rectHalfWidth2)*0.9) && (deltay > (rectHalfHeight+rectHalfHeight2)*0.9)){
            //System.out.println("squares relate to angles");
            result=true;
        }
    }while(false);

    return result;
  } 
}

1

这是修改后的代码100%有效的代码:

public static bool IsIntersected(PointF circle, float radius, RectangleF rectangle)
{
    var rectangleCenter = new PointF((rectangle.X +  rectangle.Width / 2),
                                     (rectangle.Y + rectangle.Height / 2));

    var w = rectangle.Width  / 2;
    var h = rectangle.Height / 2;

    var dx = Math.Abs(circle.X - rectangleCenter.X);
    var dy = Math.Abs(circle.Y - rectangleCenter.Y);

    if (dx > (radius + w) || dy > (radius + h)) return false;

    var circleDistance = new PointF
                             {
                                 X = Math.Abs(circle.X - rectangle.X - w),
                                 Y = Math.Abs(circle.Y - rectangle.Y - h)
                             };

    if (circleDistance.X <= (w))
    {
        return true;
    }

    if (circleDistance.Y <= (h))
    {
        return true;
    }

    var cornerDistanceSq = Math.Pow(circleDistance.X - w, 2) + 
                                    Math.Pow(circleDistance.Y - h, 2);

    return (cornerDistanceSq <= (Math.Pow(radius, 2)));
}

巴萨姆·阿鲁吉利(Bassam Alugili)


1

这是一个快速的单行测试:

if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
  // They intersect.
}

这是与轴对齐的情况,其中rect_halves是从矩形的中间指向角的正矢量。内部的表达式length()是从三角形center最接近的点的增量向量。这适用于任何维度。


1
  • 首先检查矩形和与圆相切的正方形是否重叠(容易)。如果它们不重叠,则它们不会碰撞。
  • 检查圆的中心是否在矩形内(简单)。如果在里面,它们会发生碰撞。
  • 计算从矩形边到圆心的最小平方距离(很难)。如果它小于半径的平方,则它们会发生碰撞,否则不会发生碰撞。

这是有效的,因为:

  • 首先,它使用便宜的算法检查最常见的场景,并确保它们不会冲突,然后结束。
  • 然后,它使用便宜的算法检查下一个最常见的情况(不计算平方根,使用平方值),并确定它们发生冲突时结束。
  • 然后,它执行更昂贵的算法来检查与矩形边框的碰撞。

1

为我工作(仅当矩形角度为180时工作)

function intersects(circle, rect) {
  let left = rect.x + rect.width > circle.x - circle.radius;
  let right = rect.x < circle.x + circle.radius;
  let top = rect.y < circle.y + circle.radius;
  let bottom = rect.y + rect.height > circle.y - circle.radius;
  return left && right && bottom && top;
}

嗯...我投了赞成票,但随后进行了适当的测试,例如,我认为它在弯道上不起作用。它适用于两个矩形。
Dan Zen:

1

改善e.James的答案:

double dx = abs(circle.x - rect.x) - rect.w / 2,
       dy = abs(circle.y - rect.y) - rect.h / 2;

if (dx > circle.r || dy > circle.r) { return false; }
if (dx <= 0 || dy <= 0) { return true; }

return (dx * dx + dy * dy <= circle.r * circle.r);

该减去rect.w / 2rect.h / 2一次,而不是三倍。


0

对于那些必须使用SQL在地理坐标中计算圆/矩形碰撞的人,
这是我在e.James建议算法的 oracle 11中的实现

在输入中,它需要圆坐标,以km为单位的圆半径和矩形的两个顶点坐标:

CREATE OR REPLACE FUNCTION "DETECT_CIRC_RECT_COLLISION"
(
    circleCenterLat     IN NUMBER,      -- circle Center Latitude
    circleCenterLon     IN NUMBER,      -- circle Center Longitude
    circleRadius        IN NUMBER,      -- circle Radius in KM
    rectSWLat           IN NUMBER,      -- rectangle South West Latitude
    rectSWLon           IN NUMBER,      -- rectangle South West Longitude
    rectNELat           IN NUMBER,      -- rectangle North Est Latitude
    rectNELon           IN NUMBER       -- rectangle North Est Longitude
)
RETURN NUMBER
AS
    -- converts km to degrees (use 69 if miles)
    kmToDegreeConst     NUMBER := 111.045;

    -- Remaining rectangle vertices 
    rectNWLat   NUMBER;
    rectNWLon   NUMBER;
    rectSELat   NUMBER;
    rectSELon   NUMBER;

    rectHeight  NUMBER;
    rectWIdth   NUMBER;

    circleDistanceLat   NUMBER;
    circleDistanceLon   NUMBER;
    cornerDistanceSQ    NUMBER;

BEGIN
    -- Initialization of remaining rectangle vertices  
    rectNWLat := rectNELat;
    rectNWLon := rectSWLon;
    rectSELat := rectSWLat;
    rectSELon := rectNELon;

    -- Rectangle sides length calculation
    rectHeight := calc_distance(rectSWLat, rectSWLon, rectNWLat, rectNWLon);
    rectWidth := calc_distance(rectSWLat, rectSWLon, rectSELat, rectSELon);

    circleDistanceLat := abs( (circleCenterLat * kmToDegreeConst) - ((rectSWLat * kmToDegreeConst) + (rectHeight/2)) );
    circleDistanceLon := abs( (circleCenterLon * kmToDegreeConst) - ((rectSWLon * kmToDegreeConst) + (rectWidth/2)) );

    IF circleDistanceLon > ((rectWidth/2) + circleRadius) THEN
        RETURN -1;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLat > ((rectHeight/2) + circleRadius) THEN
        RETURN -1;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLon <= (rectWidth/2) THEN
        RETURN 0;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLat <= (rectHeight/2) THEN
        RETURN 0;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;


    cornerDistanceSQ := POWER(circleDistanceLon - (rectWidth/2), 2) + POWER(circleDistanceLat - (rectHeight/2), 2);

    IF cornerDistanceSQ <=  POWER(circleRadius, 2) THEN
        RETURN 0;  --  -1 => NO Collision ; 0 => Collision Detected
    ELSE
        RETURN -1;  --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    RETURN -1;  --  -1 => NO Collision ; 0 => Collision Detected
END;    

0

Works,一个星期前才发现,现在要进行测试。

double theta = Math.atan2(cir.getX()-sqr.getX()*1.0,
                          cir.getY()-sqr.getY()*1.0); //radians of the angle
double dBox; //distance from box to edge of box in direction of the circle

if((theta >  Math.PI/4 && theta <  3*Math.PI / 4) ||
   (theta < -Math.PI/4 && theta > -3*Math.PI / 4)) {
    dBox = sqr.getS() / (2*Math.sin(theta));
} else {
    dBox = sqr.getS() / (2*Math.cos(theta));
}
boolean touching = (Math.abs(dBox) >=
                    Math.sqrt(Math.pow(sqr.getX()-cir.getX(), 2) +
                              Math.pow(sqr.getY()-cir.getY(), 2)));

可能对Circle-Square有用,但是问题是关于Circle-Rectangle。
martineau 2015年

0
def colision(rect, circle):
dx = rect.x - circle.x
dy = rect.y - circle.y
distance = (dy**2 + dx**2)**0.5
angle_to = (rect.angle + math.atan2(dx, dy)/3.1415*180.0) % 360
if((angle_to>135 and angle_to<225) or (angle_to>0 and angle_to<45) or (angle_to>315 and angle_to<360)):
    if distance <= circle.rad/2.+((rect.height/2.0)*(1.+0.5*abs(math.sin(angle_to*math.pi/180.)))):
        return True
else:
    if distance <= circle.rad/2.+((rect.width/2.0)*(1.+0.5*abs(math.cos(angle_to*math.pi/180.)))):
        return True
return False

-2

假设您具有矩形的四个边缘,请检查从边缘到圆心的距离,如果圆角小于半径,则形状是相交的。

if sqrt((rectangleRight.x - circleCenter.x)^2 +
        (rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleRight.x - circleCenter.x)^2 +
        (rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleLeft.x - circleCenter.x)^2 +
        (rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleLeft.x - circleCenter.x)^2 +
        (rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect

如果一个小圆圈完全被一个大矩形包围,该怎么办?当然,这是一个交叉点,并且在此答案中的测试将失败。
肯保罗,

啊,是的,我没想到。您可以添加更多检查,例如sqrt((rectangleRight.x / 2-circleCenter.x)^ 2 +(rectangleBottom.y / 2-circleCenter.y)^ 2)<radius然后它们相交这将是又长又慢,但是从我的头顶上跳出来,这就是我能想到的最好的。
ForYourOwnGood

它们可以在任何边缘的任何[单个]点上相交。您还应该找到边缘中心距离。(哦,把你的角落叫做“角落” :)
aib

这似乎仅能检测到圆角是否在圆内。
形成鲜明

-2

如果矩形与圆相交,则矩形的一个或多个角点应位于圆的内部。假设矩形的四个点是A,B,C,D。其中至少一个应与圆相交。因此,如果从一个点到圆心的距离小于圆的半径,则它应与圆相交。要获得距离,您可以使用勾股定理,

H^2 = A^2 + B^2

此技术有一些限制。但这对于游戏开发人员来说会更好。特别是碰撞检测

这是对Arvo算法的一个很好的更新


每当矩形的边大于圆的半径时,此答案都是非常错误的。
Paul K
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.