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


192

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


1
사각형이 항상 축과 정렬되어 있습니까, 아니면 임의의 각도로 회전 할 수 있습니까?
e.James

11
@ eJames : 어떻게 중요합니까? 사각형과 원의 교차점을 확인하고 있습니다 . 당신은 항상 사각형 :-) 원의 변화없이 축과 평행이되도록하여 좌표 시스템을 변환 할 수 있습니다
ShreevatsaR

당신은 -Θ 통해 회전, 답변으로 그를 추가하고 모든 ...한다
AIB

2
@ ShreevatsaR : 그것은 그 좌표 변환에 대해 걱정할 필요가 있는지 여부에 관계없이 중요합니다. @aib : 오 이런!
e.James

답변:


191

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

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

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

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

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

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

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


25
가치있는 것에 대해, 나는이 대답이 내 것보다 낫다고 생각합니다. 두 가지 주요 이유 : 1 : 사각형이 축이 평행하지 않은 경우 회전이 필요하지 않으며 2 : 개념이 모든 다각형으로 쉽게 확장됩니다 .
e.James

2
@paniq : 글쎄요, 둘 다 일정한 시간입니다. :-) 그러나 그렇습니다. 이것은 일반적인 솔루션으로 더 유용합니다. 모든 방향으로 사각형을 덮고 실제로는 간단한 다각형입니다.
ShreevatsaR

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

  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
@Tanner : 우리는 간다. 백업 및 OCD를위한 Hooray;)
e.James

11
명확히하기 위해-이 대답은 축 정렬 사각형에만 적용됩니다. 다른 답변에 대한 의견을 읽는 것이 분명하지만이 답변 + 의견만으로는 명확하지 않습니다. (THO 축 정렬의 구형에 좋은 답변을!)
ericsoco

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
당신은 거기에 버그가 있습니다. 상단과 하단이 아닌 왼쪽과 오른쪽으로 가장 가까운 것을 검색하십시오. 그렇지 않으면 멋진 솔루션입니다.
manveru

8
이 답변이 가장 좋습니다. 짧고 이해하기 쉽고 빠릅니다.
John Kurlak

2
사각형이 x 축과 y 축에 비스듬한 경우 솔루션이 실패한다고 생각합니다.
레오

3
@ 레오이 경우를 수용하기 위해이 알고리즘을 수정하는 것이 어렵지 않다고 생각합니다. 원점이 직사각형 중심에 있고 직사각형이 더 이상 비스듬하지 않은 경우 좌표 변환을 적용해야합니다. 변환을 원 중심에만 적용해야합니다.
enobayram

1
이것은 기본적으로 Objective-C로 이식 한 migapro.com/circle-and-rotated-rectangle-collision-detection에 있는 코드와 동일합니다 . 매우 잘 작동합니다. 문제에 대한 좋은 해결책입니다.
PKCLsoft

10

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 // 루프 종료 
    경우 종료
끝나다
경우 (분리 된 경우)    
      "교차 없음"을 반환
그밖에 
      "교차로"를 반환
경우 종료

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


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
가장 비싼 코드 경로에서 5 가지 테스트 / 비교가 증거없이“가장 빠른 솔루션”이라고 주장 할 수 있다고 생각하지 않습니다.
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

2
이것이 실패하게하는 실제 입력을 제공 할 수 있습니까? 원이 안에있을 때 테스트의 왼쪽 부분은 0.0입니다. 반경이 제로가되지 않는 시험의 오른쪽 부분은> 0.0이어야
ClickerMonkey

회전 된 사각형에서도 작동합니까? 그렇지 않다면 그것에 대해 힌트를주십시오 .....
M Abdul Sami

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 사분면 중 하나에있을 때만 발생할 수 있다는 점에 유의해야합니다 (사실, 중심이 사분면 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를 갖습니다.

4 개의 선 교차점을 구성하고 해당 모서리에 있는지 확인하거나 어느 사분면 P가 있는지 확인하고 해당 교차점을 확인할 수 있습니다. 둘 다 동일한 부울 방정식으로 단순화해야합니다. 위의 2 단계에서 '코너'사분면 중 하나에있는 P를 배제하지 않았다는 점에주의하십시오. 방금 교차로를 찾았습니다.

편집 : 결과적으로 # 2가 위 # 3의 하위 사례라는 간단한 사실을 간과했습니다. 결국 모서리도 가장자리의 점입니다. 자세한 설명은 아래 @ShreevatsaR의 답변을 참조하십시오. 그 동안 빠르고 중복 검사를 원하지 않는 한 위의 # 2를 잊어 버리십시오.


2

이 기능은 Circle과 Rectangle 사이의 충돌 (교차)을 감지합니다. 그는 대답에서 e.James 방법과 같이 작동하지만 오른쪽 모서리뿐만 아니라 모든 사각형 각도의 충돌을 감지합니다.

노트:

aRect.origin.xaRect.origin.y 는 직사각형의 왼쪽 아래 각도 좌표입니다!

aCircle.xaCircle.y 는 Circle Center의 좌표입니다!

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는 전체 거리 계산보다 약간 빠른 방법이 아닙니다. 예를 들어, 유클리드 공간에 제곱근이 없거나 (혹은 haversine을위한 다른 많은 것들이없는) : 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)));
}

바삼 알 굴리


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한 번 대신에 세 번까지.


나는 대부분의 최신 컴파일러가 자동으로 중복 계산을 자동으로 최적화 할 것이라고 생각합니다.
martineau

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

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

작은 원이 큰 사각형으로 완전히 둘러싸인 경우는 어떻습니까? 분명히 그것은 교차점 이며이 답변에서 테스트에 실패합니다.
Ken Paul

아 그래, 나는 그것을 생각하지 않았다. sqrt ((rectangleRight.x / 2-circleCenter.x) ^ 2 + (rectangleBottom.y / 2-circleCenter.y) ^ 2) <반경 다음과 같이 더 많은 검사를 추가 할 수 있습니다. 하지만 내 머리 꼭대기에서 내가 얻을 수있는 최선입니다.
ForYourOwnGood

모서리의 임의의 단일 지점에서 교차 할 수 있습니다. 가장자리 중심 거리도 찾아야합니다. (아, 그리고 당신의 코너 '코너를 ": 전화
AIB

이것은 모서리가 원 안에있을 때만 감지하는 것으로 보입니다.
stark

-2

사각형이 원과 교차하면 사각형의 하나 이상의 모퉁이 점이 원 안에 있어야합니다. 사각형의 네 점이 A, B, C, D라고 가정합니다. 그들 중 적어도 하나는 원과 교차해야합니다. 따라서 한 점에서 원 중심까지의 거리가 원의 반경보다 작 으면 원과 교차해야합니다. 거리를 구하기 위해 피타고라스 정리를 사용할 수 있습니다.

H^2 = A^2 + B^2

이 기술에는 한계가 있습니다. 그러나 게임 개발자에게는 더 효과적입니다. 특히 충돌 감지

Arvo 알고리즘에 대한 좋은 업데이트입니다.


사각형이 원의 반지름보다 큰 변을 가질 때 마다이 대답은 엄청나게 잘못되었습니다.
Paul K
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.