2D 유클리드 공간에서 원과 사각형이 교차하는지 어떻게 알 수 있습니까? (예 : 클래식 2D 지오메트리)
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));
}
작동 방식은 다음과 같습니다.
첫 번째 선 쌍은 원의 중심과 사각형의 중심 사이의 x 및 y 차이의 절대 값을 계산합니다. 이렇게하면 4 사분면이 하나로 축소되어 계산을 4 번 수행 할 필요가 없습니다. 이미지는 이제 원의 중심이 놓여 야하는 영역을 보여줍니다. 단일 사분면 만 표시됩니다. 사각형은 회색 영역이고 빨간색 테두리는 사각형의 가장자리에서 정확히 1 반경 떨어진 임계 영역을 나타냅니다. 교차점이 발생하려면 원의 중심이이 빨간색 경계 안에 있어야합니다.
두 번째 선 쌍은 원이 사각형에서 어느 방향으로도 멀리 떨어져있어 교차가 불가능한 쉬운 경우를 제거합니다. 이것은 이미지의 녹색 영역에 해당합니다.
세 번째 선 쌍은 원이 교차가 보장되는 직사각형 (양 방향)에 충분히 가까운 쉬운 경우를 처리합니다. 이것은 이미지의 주황색과 회색 부분에 해당합니다. 이 단계는 논리가 이해되도록 2 단계 이후에 수행되어야합니다.
나머지 선은 원이 사각형의 모서리와 교차 할 수있는 어려운 경우를 계산합니다. 해결하려면 원의 중심과 모서리에서 거리를 계산 한 다음 거리가 원의 반경보다 크지 않은지 확인하십시오. 이 계산은 중심이 빨간색 음영 영역 내에있는 모든 원에 대해 false를 반환하고 중심이 흰색 음영 영역 내에있는 모든 원에 대해 true를 반환합니다.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
구현하기 매우 간단하고 빠른 솔루션도 있습니다. 구가 사각형에 완전히 들어간 경우를 포함하여 모든 교차점을 잡습니다.
// 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 축이 아래를 향하도록하여 세계 왼쪽 상단에서 원점을 가정합니다.
움직이는 원과 사각형 사이의 충돌을 처리하는 솔루션을 원한다면 훨씬 복잡하고 또 다른 대답으로 다루어집니다 .
실제로 이것은 훨씬 간단합니다. 두 가지만 있으면됩니다.
먼저, 원 중심에서 직사각형의 각 선까지 네 개의 직교 거리 를 찾아야 합니다. 그런 다음 세 개가 원 반경보다 큰 경우 원이 사각형과 교차하지 않습니다.
둘째, 원 중심과 직사각형 중심 사이의 거리를 찾아야합니다. 거리가 직사각형 대각선 길이의 절반보다 큰 경우 원은 직사각형 안에 있지 않습니다.
행운을 빕니다!
구와 축이 정렬되지 않은 상자 사이의 충돌을 해결하기위한 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;
}
시각화하려면 키보드의 숫자 키패드를 사용하십시오. 키 '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를 잊어 버리십시오.
이 기능은 Circle과 Rectangle 사이의 충돌 (교차)을 감지합니다. 그는 대답에서 e.James 방법과 같이 작동하지만 오른쪽 모서리뿐만 아니라 모든 사각형 각도의 충돌을 감지합니다.
노트:
aRect.origin.x 및 aRect.origin.y 는 직사각형의 왼쪽 아래 각도 좌표입니다!
aCircle.x 와 aCircle.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;
}
필요하지 않은 경우 비싼 피타고라스를 피하는 방법을 사용했습니다. 사각형의 상자와 원을 경계로 묶을 때.
그리고 그것은 유클리드가 아닌 사람들에게도 효과가 있습니다.
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;
}
}
dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLon
. 물론 당신은 NORMDIST 방법은 당신이를 만들려면 어떻게해야합니까해야한다는 점 사용하는 경우 normedDist = dist*dist;
원에 대한내 GraphHopper 프로젝트 의 전체 BBox 및 Circle 코드를 참조하십시오 .
나는 당신이 즐기기를 희망하는 형태로 일하는 수업을 만들었습니다.
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;
}
}
다음은 수정 된 코드 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)));
}
바삼 알 굴리
다음과 같은 이유로 효율적입니다.
나를 위해 일했습니다 (사각형이 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;
}
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 / 2
과 rect.h / 2
한 번 대신에 세 번까지.
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;
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)));
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
사각형의 네 모서리가 있다고 가정하면 모서리에서 원 중심까지의 거리를 확인하고 반경보다 작 으면 모양이 교차합니다.
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
사각형이 원과 교차하면 사각형의 하나 이상의 모퉁이 점이 원 안에 있어야합니다. 사각형의 네 점이 A, B, C, D라고 가정합니다. 그들 중 적어도 하나는 원과 교차해야합니다. 따라서 한 점에서 원 중심까지의 거리가 원의 반경보다 작 으면 원과 교차해야합니다. 거리를 구하기 위해 피타고라스 정리를 사용할 수 있습니다.
H^2 = A^2 + B^2
이 기술에는 한계가 있습니다. 그러나 게임 개발자에게는 더 효과적입니다. 특히 충돌 감지
Arvo 알고리즘에 대한 좋은 업데이트입니다.