如何分辨圆和矩形在二维欧几里得空间中是否相交?(即经典二维几何)


当前回答

假设你有矩形的四条边,检查从这些边到圆心的距离,如果小于半径,那么这些形状是相交的。

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

其他回答

稍微改进一下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);

这就减去了一次,而不是最多减去三次。

如果你对一个更图形化的解决方案感兴趣,甚至在(平面上)旋转的矩形..

演示:https://jsfiddle.net/exodus4d/94mxLvqh/2691/

这个想法是:

将场景转换为原点[0,0] 如果矩形不在平面上,则旋转中心应在 (0,0) 将场景旋转回平面 计算交点

const hasIntersection = ({x: cx, y: cy, r: cr}, {x, y, width, height}) => { const distX = Math.abs(cx - x - width / 2); const distY = Math.abs(cy - y - height / 2); if (distX > (width / 2 + cr)) { return false; } if (distY > (height / 2 + cr)) { return false; } if (distX <= (width / 2)) { return true; } if (distY <= (height / 2)) { return true; } const Δx = distX - width / 2; const Δy = distY - height / 2; return Δx * Δx + Δy * Δy <= cr * cr; }; const rect = new DOMRect(50, 20, 100, 50); const circ1 = new DOMPoint(160, 80); circ1.r = 20; const circ2 = new DOMPoint(80, 95); circ2.r = 20; const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.strokeRect(rect.x, rect.y, rect.width, rect.height); ctx.beginPath(); ctx.strokeStyle = hasIntersection(circ1, rect) ? 'red' : 'green'; ctx.arc(circ1.x, circ1.y, circ1.r, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = hasIntersection(circ2, rect) ? 'red' : 'green'; ctx.arc(circ2.x, circ2.y, circ2.r, 0, 2 * Math.PI); ctx.stroke(); <canvas id="canvas"></canvas>

提示:而不是旋转矩形(4点)。你可以向相反的方向旋转圆(1点)。

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

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

圆的中心在矩形的内部,或者 矩形的一条边在圆上有一个点。

注意,这并不要求矩形与轴平行。

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

有了这样的见解,就可以像下面这样工作,其中圆的中心是P,半径是R,矩形的顶点是A, B, C, D(不完整的代码):

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到直线的垂线的脚是否足够近并且在端点之间,否则检查端点。

最酷的是,同样的想法不仅适用于矩形,而且适用于一个圆与任何简单多边形的交点——甚至不必是凸多边形!

为了可视化,拿你的键盘的numpad。如果键“5”代表你的矩形,那么所有的键1-9代表空间的9个象限除以构成矩形的线(5是里面的线)。

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

这里有两种可能的情况: a)圆与矩形的两条或多条相邻边相交。 b)圆与矩形的一条边相交。

第一种情况很简单。如果圆与矩形的两条相邻边相交,则它必须包含连接这两条边的角。(或者说它的中心在象限5,我们已经讲过了。还要注意,圆只与矩形的两条相对边相交的情况也被覆盖了。)

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

第二种情况比较棘手。我们应该注意到,只有当圆的中心位于2、4、6或8象限中的一个象限时,才会发生这种情况。(事实上,如果中心在1、3、7、8象限中的任何一个象限上,则相应的角将是离它最近的点。)

现在我们有了圆的中心在一个“边”象限内的情况,它只与相应的边相交。那么,边缘上最接近圆中心的点必须在圆内。

3)对于每条直线AB, BC, CD, DA,构造经过圆中心p的垂直线p(AB, p), p(BC, p), p(CD, p), p(DA, p),对于每条垂直线,如果与原边的交点在圆内,则两个图形相交。

最后一步有一个捷径。如果圆的圆心在象限8,边AB是上边,交点的y坐标是A和B, x坐标是P。

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

编辑:事实证明,我忽略了一个简单的事实,即#2是#3的子情况。毕竟,角也是边缘上的点。请看下面@ShreevatsaR的回答,你会得到很好的解释。与此同时,忘记上面的第二条,除非你想要一个快速但冗余的检查。