[geometry] 원-사각 충돌 탐지 (교차)

2D 유클리드 공간에서 원과 사각형이 교차하는지 어떻게 알 수 있습니까? (예 : 클래식 2D 지오메트리)



답변

원이 사각형과 교차하는 경우는 두 가지뿐입니다.

  • 원의 중심이 사각형 안에 있거나
  • 사각형 가장자리 중 하나의 원에 점이 있습니다.

이것은 사각형이 축과 평행 할 필요는 없습니다.

원과 사각형이 교차 할 수있는 몇 가지 다른 방법

(이를 보는 한 가지 방법 : 원에 점이없는 모서리가없는 경우 (모든 모서리가 원의 “바깥 쪽”에있는 경우) 원이 다각형을 완전히 교차 할 수있는 유일한 방법은 다각형.)

원이 중심이 곳 통찰력으로, 다음과 같은 무언가가, 작동 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선 에서 수직선의 발이 충분히 가깝고 끝점 사이에 있는지 확인하고 그렇지 않으면 끝점을 확인하는 것입니다.

멋진 것은 같은 생각이 직사각형 그러나 어떤과 원의 교차점에 대한뿐만 아니라 작동하는 간단한 다각형 – 심지어 볼록 될 필요가 없습니다!


답변

내가하는 방법은 다음과 같습니다.

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 차이의 절대 값을 계산합니다. 이렇게하면 4 사분면이 하나로 축소되어 계산을 4 번 수행 할 필요가 없습니다. 이미지는 이제 원의 중심이 놓여 야하는 영역을 보여줍니다. 단일 사분면 만 표시됩니다. 사각형은 회색 영역이고 빨간색 테두리는 사각형의 가장자리에서 정확히 1 반경 떨어진 임계 영역을 나타냅니다. 교차점이 발생하려면 원의 중심이이 빨간색 경계 안에 있어야합니다.

  2. 두 번째 선 쌍은 원이 사각형에서 어느 방향으로도 멀리 떨어져있어 교차가 불가능한 쉬운 경우를 제거합니다. 이것은 이미지의 녹색 영역에 해당합니다.

  3. 세 번째 선 쌍은 원이 교차가 보장되는 직사각형 (양 방향)에 충분히 가까운 쉬운 경우를 처리합니다. 이것은 이미지의 주황색과 회색 부분에 해당합니다. 이 단계는 논리가 이해되도록 2 단계 이후에 수행되어야합니다.

  4. 나머지 선은 원이 사각형의 모서리와 교차 할 수있는 어려운 경우를 계산합니다. 해결하려면 원의 중심과 모서리에서 거리를 계산 한 다음 거리가 원의 반경보다 크지 않은지 확인하십시오. 이 계산은 중심이 빨간색 음영 영역 내에있는 모든 원에 대해 false를 반환하고 중심이 흰색 음영 영역 내에있는 모든 원에 대해 true를 반환합니다.


답변

구현하기 매우 간단하고 빠른 솔루션도 있습니다. 구가 사각형에 완전히 들어간 경우를 포함하여 모든 교차점을 잡습니다.

// 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 줄로 단축 할 수 있습니다.


답변

IIF 교차하여 구와 RECT는
원 센터와 RECT의 한 정점 사이의 거리가 당신의 구체의 반경보다 작은
또는
원 센터와 RECT의 한쪽 끝이 구체의 반경보다 작은 사이의 거리 ( [ 포인트-라인 거리 ])
또는
원 중심이

정점 포인트 거리 내에
있습니다 :

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)

rect 내부의 원 중심 :
분리 축 aproach를 취하십시오 : 점에서 직사각형을 분리하는 선에 투영이 있으면 교차하지 않습니다.

점의 측면과 평행 한 선에 점을 투영 한 다음 교차점을 쉽게 결정할 수 있습니다. 4 개의 투영에서 모두 교차하지 않으면 점과 사각형이 교차 할 수 없습니다.

내부 제품이 필요합니다 (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}}에서 // 가장자리
    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
           break // 루프 종료
    경우 종료
끝나다
경우 (분리 된 경우)
      "교차 없음"을 반환
그밖에
      "교차로"를 반환
경우 종료

이것은 축 정렬 사각형을 가정하지 않으며 볼록 세트 사이의 교차점을 테스트하기 위해 쉽게 확장 할 수 있습니다.


답변

이것이 가장 빠른 해결책입니다.

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;
}

실행 순서와 너비 / 높이의 절반이 미리 계산됩니다. 또한 제곱은 “수동으로”수행되어 일부 클럭주기를 절약합니다.


답변

내가 생각해 낸 가장 간단한 해결책은 매우 간단합니다.

원에서 가장 가까운 사각형의 점을 찾은 다음 거리를 비교하여 작동합니다.

몇 가지 조작으로이 모든 작업을 수행 할 수 있으며 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 축이 아래를 향하도록하여 세계 왼쪽 상단에서 원점을 가정합니다.

움직이는 원과 사각형 사이의 충돌을 처리하는 솔루션을 원한다면 훨씬 복잡하고 또 다른 대답으로 다루어집니다 .


답변

실제로 이것은 훨씬 간단합니다. 두 가지만 있으면됩니다.

먼저, 원 중심에서 직사각형의 각 선까지 네 개의 직교 거리 를 찾아야 합니다. 그런 다음 세 개가 원 반경보다 큰 경우 원이 사각형과 교차하지 않습니다.

둘째, 원 중심과 직사각형 중심 사이의 거리를 찾아야합니다. 거리가 직사각형 대각선 길이의 절반보다 큰 경우 원은 직사각형 안에 있지 않습니다.

행운을 빕니다!