각 Box 객체에는 x, y, width, height 속성이 있고 가운데에 원점이 있으며 객체와 경계 상자가 회전하지 않는다고 가정합니다.
각 Box 객체에는 x, y, width, height 속성이 있고 가운데에 원점이 있으며 객체와 경계 상자가 회전하지 않는다고 가정합니다.
답변:
(C-ish 의사 코드-언어 최적화를 적절하게 조정)
bool DoBoxesIntersect(Box a, Box b) {
return (abs(a.x - b.x) * 2 < (a.width + b.width)) &&
(abs(a.y - b.y) * 2 < (a.height + b.height));
}
영어 : 각 축에서 상자의 중심이 교차 할만큼 충분히 가까운 지 확인하십시오. 두 축이 서로 교차하면 상자가 교차합니다. 그렇지 않으면 그렇지 않습니다.
가장자리 터치를 교차로 계산하려면 <를 <=로 변경할 수 있습니다. 특정 가장자리 터치 전용 수식을 원하면 ==를 사용할 수 없습니다. 가장자리가 닿으면 모서리가 닿는 지 여부를 알려줍니다. 논리적으로에 해당하는 작업을 수행하려고합니다 return DoBoxesIntersectOrTouch(a, b) && !DoBoxesIntersect(a, b)
.
전체 너비와 전체 높이에 추가하여 (또는 대신에) 절반 너비와 절반 높이를 저장하면 작지만 상당한 속도 증가를 얻을 수 있습니다. 반면에 2D 바운딩 박스 교차로가 성능 병목 현상을 일으키는 경우는 거의 없습니다.
abs(5 - 10) * 2 < (10 + 4)
=>로 끝납니다 10 < 14
. 왼쪽 위 모서리와 크기로 작동하려면 간단한 조정이 필요합니다.
이것은 X 및 Y 축과 정렬 된 두 개의 사각형에 적용됩니다.
각 사각형에는
"왼쪽", 왼쪽의 x 좌표,
"상단", 위쪽의 y 좌표,
"오른쪽", 오른쪽의 x 좌표,
"하단", y 좌표의 속성이 있습니다. 바닥면
function IntersectRect(r1:Rectangle, r2:Rectangle):Boolean {
return !(r2.left > r1.right
|| r2.right < r1.left
|| r2.top > r1.bottom
|| r2.bottom < r1.top);
}
이것은 + y 축이 아래를 향하고 + x 축이 오른쪽을 향하는 좌표계 (일반적인 화면 / 픽셀 좌표)를 위해 설계되었습니다. + y가 위쪽을 향한 일반적인 직교 시스템에 이것을 적용하기 위해 수직 축을 따라 비교는 반대로됩니다.
return !(r2.left > r1.right
|| r2.right < r1.left
|| r2.top < r1.bottom
|| r2.bottom > r1.top);
아이디어는 사각형이되는시 가능한 모든 조건을 캡처하는 것입니다 하지 중복, 그리고 그들이 있는지 답을 부정 하는 중복을. 축의 방향에 관계없이 다음과 같은 경우 두 사각형이 겹치지 않는 것을 쉽게 알 수 있습니다 .
r2의 왼쪽 가장자리는 r1의 오른쪽 가장자리보다 더 오른쪽입니다.
________ ________
| | | |
| r1 | | r2 |
| | | |
|________| |________|
또는 r2의 오른쪽 가장자리가 r1의 왼쪽 가장자리보다 더 왼쪽
________ ________
| | | |
| r2 | | r1 |
| | | |
|________| |________|
또는 r2의 상단 가장자리가 r1의 하단 가장자리 아래에 있음
________
| |
| r1 |
| |
|________|
________
| |
| r2 |
| |
|________|
또는 r2의 하단 가장자리가 r1의 상단 가장자리 위에 있음
________
| |
| r2 |
| |
|________|
________
| |
| r1 |
| |
|________|
원래 기능 및 작동 이유에 대한 대체 설명은 여기에서 찾을 수 있습니다. http://tekpool.wordpress.com/2006/10/11/rectangle-intersection-determine-if-two-given-rectangles-intersect 서로 또는하지 /
객체 정렬 경계 상자를 원하는 경우 메타넷을 기준으로 분리 축 정리 에서이 자습서를 시도하십시오 . http://www.metanetsoftware.com/technique/tutorialA.html
SAT는 가장 빠른 솔루션은 아니지만 비교적 간단합니다. 객체를 분리하는 단일 선 (또는 3D면 평면)을 찾으려고합니다. 이 선이 있으면 상자 중 하나의 가장자리에 평행하게 표시되도록 보증되므로 모든 가장자리 테스트를 반복하여 상자가 분리되는지 확인하십시오.
이것은 x / y 축으로 만 제한하여 축 정렬 상자에서도 작동합니다.
위의 DoBoxesIntersect는 좋은 쌍별 솔루션입니다. 그러나 상자가 많은 경우에도 여전히 O (N ^ 2) 문제가 발생하며 Kaj가 말하는 것과 같은 것 외에 무언가를 수행해야 할 수도 있습니다. (3D 충돌 감지 문헌에서 이것은 넓은 위상 및 좁은 위상 알고리즘을 모두 사용하는 것으로 알려져 있습니다. 가능한 모든 중첩 쌍을 찾기 위해 실제로 빠른 작업을 수행 한 다음 가능한 경우 더 비싼 작업을 수행합니다 쌍은 실제 쌍입니다.)
이전에 사용했던 광대역 알고리즘은 "스윕 앤 프룬"입니다. 2D의 경우 각 상자의 시작과 끝의 두 가지 정렬 된 목록을 유지해야합니다. 박스 이동이 프레임마다 >> 박스 스케일이 아닌 한,이 목록의 순서는 크게 변하지 않으므로 버블 또는 삽입 정렬을 사용하여 유지할 수 있습니다. "Real-Time Rendering"이라는 책은 여러분이 할 수있는 최적화에 대한 훌륭한 글을 가지고 있지만, K가 겹치는 N 상자에 대해 광범위한 단계에서 O (N + K) 시간으로 요약됩니다. 프레임 간 교차하는 상자 쌍을 추적하기 위해 N ^ 2 부울을 감당할 수있는 경우 성능. 그런 다음 전체 상자에 O (N + K ^ 2) 시간이 있습니다. 상자가 많지만 겹치는 부분이 많으면 << O (N ^ 2)입니다.
매우 간단한 문제에 대한 많은 수학, 우리는 rect, top, left, bottom, right에 대해 4 점이 결정되었다고 가정합니다.
2 개의 rect가 충돌하는지 여부를 결정하는 경우 충돌을 방지 할 수있는 가능한 모든 극단 만 확인하면됩니다. 만약 충돌이 발생하지 않으면 2 개의 rect가 충돌해야합니다. 경계 충돌을 포함하려면> 및 < 적절한> = 및 = <.
struct aRect{
float top;
float left;
float bottom;
float right;
};
bool rectCollision(rect a, rect b)
{
return ! ( b.left > a.right || b.right < a.left || b.top < a.bottom || b.bottom > a.top);
}
ZorbaTHut의 답변의 대체 버전 :
bool DoBoxesIntersect(Box a, Box b) {
return (abs(a.x - b.x) < (a.width + b.width) / 2) &&
(abs(a.y - b.y) < (a.height + b.height) / 2);
}
해결하려는 문제에 따라 객체를 이동하는 동안 추적하는 것이 좋습니다. 즉, 정렬 된 x 시작 및 종료 위치 목록과 시작 및 종료 y 위치에 대한 목록을 유지하는 것이 좋습니다. 많은 중복 검사를 수행해야하므로 최적화해야하는 경우, 왼쪽에 가까운 결말을 찾는 사람을 즉시 찾을 수 있으므로 끝까지가는 모든 사람은 정리할 수 있습니다. 바로. 위, 아래 및 오른쪽에도 동일하게 적용됩니다.
부기 비용은 물론 시간이 많이 걸리므로 움직이는 물체는 적지 만 겹침 검사가 많은 상황에 더 적합합니다.
또 다른 옵션은 공간 해싱으로, 대략적인 위치 (크기는 여러 버킷에 넣을 수 있음)를 기준으로 객체를 버킷 화하지만, 예약 유지 비용으로 인해 반복 당 이동하는 객체가 거의없는 많은 객체가있는 경우에만 다시 나타납니다.
기본적으로 (n * n) / 2를 피하는 것은 (객체 a를 b에 대해 확인하면 b를 명백하게 확인하지 않아도 됨) 경계 상자 확인을 최적화하는 것 이상을 도와줍니다. 경계 상자 검사에 병목 현상이 발생하면 문제에 대한 대체 솔루션을 찾아 보는 것이 좋습니다.
중심 사이의 거리는 모퉁이 사이의 거리와 같지 않기 때문에 (예를 들어 한 상자가 다른 상자 안에있을 때) 일반적으로이 솔루션이 맞습니다 (생각합니다).
중심 간 거리 (예 : x) : abs(x1+1/2*w1 - x2+1/2*w2)
또는1/2 * abs(2*(x1-x2)+(w1-w2)
최소 거리는입니다 1/2 w1 + 1/2 w2 or 1/2 (w1+w2)
. 반쪽도 취소됩니다 ..
return
ABS(2*(x1 - x2) + (w1-w2) ) < (w1+w2)) &&
ABS(2*(y1 - y2) + (h1-h2) ) < (h1+h2));
다음 은 2 보완 아키텍처를 가정하여 Java로 구현 한 것입니다 . two-complement가 아닌 경우 표준 Math.abs 함수 호출을 대신 사용하십시오.
boolean intersects(IntAxisAlignedBox left, IntAxisAlignedBox right) {
return
(
lineDeltaFactor(left.min.x, left.max.x, right.min.x, right.max.x) |
lineDeltaFactor(left.min.y, left.max.y, right.min.y, right.max.y) |
lineDeltaFactor(left.min.z, left.max.z, right.min.z, right.max.z)
) == 0;
}
int lineDeltaFactor(int leftMin, int leftMax, int rightMin, int rightMax) {
final int
leftWidth = leftMax - leftMin,
rightWidth = rightMax - rightMin,
leftMid = leftMin + ((leftMax - leftMin) >> 1),
rightMid = rightMin + ((rightMax - rightMin) >> 1);
return (abs(leftMid - rightMid) << 1) / (leftWidth + rightWidth + 1);
}
int abs(int value) {
final int mask = value >> (Integer.SIZE - 1);
value ^= mask;
value += mask & 1;
return value;
}
절반 정도의 컴파일러 / LLVM 인라인을 가정하면 이러한 기능이 확장되어 값 비싼 스택 저글링 및 v- 테이블 조회를 피할 수 있습니다. 이것은 실패 32 비트 극단 (즉, 부근에있는 입력 값 Integer.MAX_VALUE
과 Integer.MIN_VALUE
).
가장 빠른 방법은 단일 벡터 레지스터에서 4 개의 값을 모두 결합하는 것입니다.
상자를 다음 값을 가진 벡터에 저장하십시오 [ min.x, min.y, -max.x, -max.y ]
. 이와 같은 상자를 저장하면 교차 테스트는 3 개의 CPU 명령 만 수행합니다.
_mm_shuffle_ps
최소 및 최대 절반을 뒤집어 두 번째 상자를 재정렬합니다.
_mm_xor_ps
_mm_set1_ps(-0.0f)
두 번째 상자에서 4 개의 값을 모두 뒤집을 수있는 매직 넘버 .
_mm_cmple_ps
다음 두 레지스터를 비교하여 4 개의 모든 값을 서로 비교합니다.
[ a.min.x, a.min.y, -a.max.x, -a.max.y ] < [ b.max.x, b.max.y, -b.min.x, -b.min.y ]
마지막으로 필요한 경우 _mm_movemask_ps
벡터 단위에서 스칼라 레지스터로 결과를 가져옵니다. 값 0은 상자가 교차 함을 의미합니다. 또는 상자가 두 개 이상인 경우 필요하지 않은 경우 벡터 레지스터에 값을 그대로두고 비트 단위 연산을 사용하여 여러 상자의 결과를 결합하십시오.
언어 나 플랫폼을 지정하지 않았지만 이와 유사한 SIMD에 대한 지원은 모든 플랫폼과 언어에서 사용할 수 있습니다. 모바일에서 ARM은 매우 유사한 기능을 가진 NEON SIMD를 보유하고 있습니다. .NET은 System.Runtime.Intrinsics 네임 스페이스 등에 Vector128이 있습니다.