Làm thế nào tôi có thể biết liệu một hình tròn và hình chữ nhật có giao nhau trong không gian Euclide 2D không? (tức là hình học 2D cổ điển)
Làm thế nào tôi có thể biết liệu một hình tròn và hình chữ nhật có giao nhau trong không gian Euclide 2D không? (tức là hình học 2D cổ điển)
Câu trả lời:
Chỉ có hai trường hợp khi vòng tròn giao nhau với hình chữ nhật:
Lưu ý rằng điều này không yêu cầu hình chữ nhật phải song song trục.
(Một cách để thấy điều này: nếu không có cạnh nào có một điểm trong vòng tròn (nếu tất cả các cạnh hoàn toàn "nằm ngoài" vòng tròn), thì cách duy nhất mà vòng tròn vẫn có thể giao nhau với đa giác là nếu nó nằm hoàn toàn bên trong đa giác.)
Với cái nhìn sâu sắc rằng, một cái gì đó như sau sẽ làm việc, nơi mà các vòng tròn có tâm P
và bán kính R
, và hình chữ nhật có các đỉnh A
, B
, C
, D
theo thứ tự đó (không hoàn thành code):
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)))
Nếu bạn đang viết bất kỳ hình học nào, có lẽ bạn đã có các chức năng trên trong thư viện của mình. Mặt khác, pointInRectangle()
có thể được thực hiện theo nhiều cách; bất kỳ điểm chung nào trong các phương thức đa giác đều hoạt động, nhưng đối với hình chữ nhật, bạn chỉ cần kiểm tra xem điều này có hoạt động không:
0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD
Và intersectCircle()
cũng dễ thực hiện: một cách sẽ là kiểm tra xem chân vuông góc P
với đường thẳng có đủ gần và giữa các điểm cuối hay không, và kiểm tra các điểm cuối khác.
Điều thú vị là cùng một ý tưởng hoạt động không chỉ cho hình chữ nhật mà còn cho giao điểm của một vòng tròn với bất kỳ đa giác đơn giản nào - thậm chí không cần phải lồi!
Đây là cách tôi sẽ làm điều đó:
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));
}
Đây là cách nó hoạt động:
Cặp đường đầu tiên tính giá trị tuyệt đối của chênh lệch x và y giữa tâm của đường tròn và tâm của hình chữ nhật. Điều này thu gọn bốn góc phần tư thành một, để các phép tính không phải thực hiện bốn lần. Hình ảnh cho thấy khu vực mà trung tâm của vòng tròn phải nằm. Lưu ý rằng chỉ có góc phần tư duy nhất được hiển thị. Hình chữ nhật là khu vực màu xám và đường viền màu đỏ phác thảo khu vực quan trọng cách chính xác một bán kính so với các cạnh của hình chữ nhật. Tâm của vòng tròn phải nằm trong đường viền màu đỏ này để xảy ra giao lộ.
Cặp đường thứ hai loại bỏ các trường hợp dễ dàng trong đó hình tròn đủ xa hình chữ nhật (theo một trong hai hướng) mà không có giao điểm nào có thể. Điều này tương ứng với khu vực màu xanh lá cây trong hình ảnh.
Cặp đường thứ ba xử lý các trường hợp dễ dàng trong đó vòng tròn đủ gần với hình chữ nhật (theo một trong hai hướng) mà một giao lộ được đảm bảo. Điều này tương ứng với các phần màu cam và màu xám trong hình ảnh. Lưu ý rằng bước này phải được thực hiện sau bước 2 để logic có ý nghĩa.
Các dòng còn lại tính toán trường hợp khó trong đó hình tròn có thể cắt nhau ở góc của hình chữ nhật. Để giải quyết, hãy tính khoảng cách từ tâm của vòng tròn và góc, sau đó xác minh rằng khoảng cách không lớn hơn bán kính của vòng tròn. Tính toán này trả về sai cho tất cả các vòng tròn có tâm nằm trong vùng bóng mờ màu đỏ và trả về true cho tất cả các vòng tròn có tâm nằm trong vùng bóng trắng.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
Đây là một giải pháp khác khá đơn giản để thực hiện (và cũng khá nhanh). Nó sẽ bắt tất cả các giao điểm, bao gồm cả khi hình cầu đã hoàn toàn đi vào hình chữ nhật.
// 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);
Với bất kỳ thư viện toán học tử tế nào, có thể rút ngắn xuống còn 3 hoặc 4 dòng.
hình cầu và trực tràng của bạn giao nhau IIF
khoảng cách giữa tâm vòng tròn và một đỉnh của trực tràng của bạn nhỏ hơn bán kính của hình cầu
HOẶC
khoảng cách giữa tâm vòng tròn và một cạnh của trực tràng của bạn nhỏ hơn bán kính của hình cầu của bạn ( [ khoảng cách điểm dòng ])
HOẶC
tâm vòng tròn nằm trong
khoảng cách điểm trực tràng:
P1 = [x1, y1] P2 = [x2, y2] Khoảng cách = sqrt (abs (x1 - x2) + abs (y1-y2))
khoảng cách điểm:
L1 = [x1, y1], L2 = [x2, y2] (hai điểm trên đường thẳng của bạn, tức là các điểm đỉnh) P1 = [px, py] một số điểm Khoảng cách d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / Khoảng cách (L1, L2)
tâm vòng tròn bên trong trực tràng:
lấy một trục aproach tách biệt: nếu có một hình chiếu lên một đường phân cách hình chữ nhật từ điểm đó, chúng không giao nhau
bạn chiếu điểm trên các đường thẳng song song với các cạnh của trực tràng và sau đó có thể dễ dàng xác định xem chúng có giao nhau không. nếu chúng không giao nhau trên cả 4 hình chiếu thì chúng (điểm và hình chữ nhật) không thể giao nhau.
bạn chỉ cần sản phẩm bên trong (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)
bài kiểm tra của bạn sẽ như thế:
// các cạnh hình chữ nhật: TL (trên cùng bên trái), TR (trên cùng bên phải), BL (dưới cùng bên trái), BR (dưới cùng bên phải) // điểm để kiểm tra: POI tách biệt = sai for egde in {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}}: // các cạnh D = cạnh [0] - cạnh [1] InternalProd = D * POI Interval_min = min (D * edge [0], D * edge [1]) Interval_max = max (D * edge [0], D * edge [1]) nếu không (Interval_min InternalProd ≤ Interval_max) tách biệt = đúng ngắt // kết thúc vòng lặp kết thúc nếu kết thúc cho nếu (tách biệt là đúng) trả lại "không có giao lộ" khác trả lại "ngã tư" kết thúc nếu
điều này không giả sử một hình chữ nhật được căn chỉnh theo trục và có thể dễ dàng mở rộng để kiểm tra các giao điểm giữa các bộ lồi.
Đây là giải pháp nhanh nhất:
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;
}
Lưu ý thứ tự thực hiện và một nửa chiều rộng / chiều cao được tính toán trước. Ngoài ra bình phương được thực hiện "thủ công" để lưu một số chu kỳ đồng hồ.
Giải pháp đơn giản nhất mà tôi nghĩ ra là khá đơn giản.
Nó hoạt động bằng cách tìm điểm trong hình chữ nhật gần nhất với vòng tròn, sau đó so sánh khoảng cách.
Bạn có thể thực hiện tất cả điều này với một vài thao tác và thậm chí tránh chức năng 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;
}
Và đó là nó! Giải pháp trên giả định nguồn gốc ở phía trên bên trái của thế giới với trục x chỉ xuống.
Nếu bạn muốn một giải pháp để xử lý các va chạm giữa một vòng tròn chuyển động và hình chữ nhật, thì nó phức tạp hơn nhiều và nằm trong một câu trả lời khác của tôi.
Trên thực tế, điều này đơn giản hơn nhiều. Bạn chỉ cần hai điều.
Đầu tiên, bạn cần tìm bốn khoảng cách trực giao từ tâm vòng tròn đến từng đường của hình chữ nhật. Sau đó, vòng tròn của bạn sẽ không giao nhau với hình chữ nhật nếu bất kỳ ba trong số chúng lớn hơn bán kính vòng tròn.
Thứ hai, bạn cần tìm khoảng cách giữa tâm hình tròn và tâm hình chữ nhật, sau đó bạn khoanh tròn sẽ không nằm trong hình chữ nhật nếu khoảng cách lớn hơn một nửa chiều dài đường chéo hình chữ nhật.
Chúc may mắn!
Đây là mã C của tôi để giải quyết xung đột giữa hình cầu và hộp không khớp trục. Nó dựa vào một vài thói quen thư viện của riêng tôi, nhưng nó có thể hữu ích với một số người. Tôi đang sử dụng nó trong một trò chơi và nó hoạt động hoàn hảo.
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;
}
Để hình dung, hãy lấy bàn phím của bàn phím của bạn. Nếu khóa '5' đại diện cho hình chữ nhật của bạn, thì tất cả các phím 1-9 đại diện cho 9 góc phần tư chia cho các đường tạo nên hình chữ nhật của bạn (với 5 là bên trong.)
1) Nếu tâm của hình tròn nằm trong góc phần tư 5 (tức là bên trong hình chữ nhật) thì hai hình dạng giao nhau.
Ngoài ra, có hai trường hợp có thể xảy ra: a) Vòng tròn giao nhau với hai hoặc nhiều cạnh lân cận của hình chữ nhật. b) Đường tròn cắt với một cạnh của hình chữ nhật.
Trường hợp đầu tiên là đơn giản. Nếu hình tròn giao nhau với hai cạnh lân cận của hình chữ nhật, nó phải chứa góc nối hai cạnh đó. (Điều đó, hoặc trung tâm của nó nằm ở góc phần tư 5, mà chúng ta đã che. Cũng lưu ý rằng trường hợp đường tròn giao nhau chỉ có hai cạnh đối diện của hình chữ nhật cũng được che phủ.)
2) Nếu bất kỳ góc A, B, C, D nào của hình chữ nhật nằm bên trong đường tròn thì hai hình giao nhau.
Trường hợp thứ hai là khó khăn hơn. Chúng ta nên lưu ý rằng điều đó chỉ có thể xảy ra khi tâm của vòng tròn nằm ở một trong các góc phần tư 2, 4, 6 hoặc 8. (Trên thực tế, nếu tâm nằm trên bất kỳ góc phần tư 1, 3, 7, 8, góc tương ứng sẽ là điểm gần nhất với nó.)
Bây giờ chúng ta có trường hợp rằng tâm của vòng tròn nằm trong một trong các góc phần tư 'cạnh' và nó chỉ giao nhau với cạnh tương ứng. Sau đó, điểm trên cạnh gần nhất với tâm của vòng tròn, phải nằm trong vòng tròn.
3) Với mỗi đường thẳng AB, BC, CD, DA, xây dựng các đường vuông góc p (AB, P), p (BC, P), p (CD, P), p (DA, P) qua tâm của đường tròn P. For mỗi đường vuông góc, nếu giao điểm với cạnh ban đầu nằm bên trong đường tròn thì hai hình sẽ giao nhau.
Có một lối tắt cho bước cuối cùng này. Nếu tâm của đường tròn nằm trong góc phần tư 8 và cạnh AB là cạnh trên, điểm giao nhau sẽ có tọa độ y của A và B và tọa độ x của tâm P.
Bạn có thể xây dựng bốn giao điểm đường và kiểm tra xem chúng có nằm trên các cạnh tương ứng của chúng không, hoặc tìm ra góc phần tư P nằm trong và kiểm tra giao điểm tương ứng. Cả hai nên đơn giản hóa để cùng một phương trình boolean. Hãy cảnh giác rằng bước 2 ở trên không loại trừ P nằm trong một trong các góc phần tư 'góc'; nó chỉ tìm một ngã tư.
Chỉnh sửa: Hóa ra, tôi đã bỏ qua một thực tế đơn giản rằng # 2 là một chữ con của số 3 ở trên. Rốt cuộc, các góc cũng là điểm trên các cạnh. Xem câu trả lời của @ ShreevatsaR dưới đây để được giải thích tuyệt vời. Và trong khi đó, hãy quên # 2 ở trên trừ khi bạn muốn kiểm tra nhanh nhưng không cần thiết.
Hàm này phát hiện các va chạm (giao điểm) giữa Vòng tròn và Hình chữ nhật. Anh ta làm việc như phương pháp e.James trong câu trả lời của mình, nhưng điều này phát hiện ra sự va chạm cho tất cả các góc của hình chữ nhật (không chỉ góc phải lên).
GHI CHÚ:
aRect.origin.x và aRect.origin.y là tọa độ của góc dưới bên trái của hình chữ nhật!
aCircle.x và aCircle.y là tọa độ của 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;
}
Tôi có một phương pháp tránh các pythagoras đắt tiền nếu không cần thiết - tức là. khi các hộp giới hạn của hình chữ nhật và hình tròn không giao nhau.
Và nó cũng sẽ hoạt động cho cả phi hạt nhân:
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
. Tất nhiên, nếu bạn sử dụng phương thức NormDist đó, bạn sẽ cần tạo một normedDist = dist*dist;
vòng trònXem mã BBox và Circle đầy đủ của dự án GraphHopper của tôi .
Tôi đã tạo lớp cho công việc với hình dạng hy vọng bạn thích
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;
}
}
Đây là mã modfied hoạt động 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)));
}
Bassam Alugili
Đây là một thử nghiệm nhanh một dòng cho việc này:
if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
// They intersect.
}
Đây là trường hợp căn chỉnh trục trong đó rect_halves
là một vectơ dương chỉ từ hình chữ nhật ở giữa đến một góc. Biểu thức bên trong length()
là một vectơ delta từ center
đến một điểm gần nhất trong hình chữ nhật. Điều này hoạt động trong bất kỳ chiều.
Nó hiệu quả, bởi vì:
làm việc cho tôi (chỉ hoạt động khi góc của hình chữ nhật là 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;
}
Cải thiện một chút câu trả lời của 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);
Điều này trừ rect.w / 2
và rect.h / 2
một lần thay vì lên đến ba lần.
Đối với những người phải tính toán xung đột Vòng tròn / Hình chữ nhật trong Tọa độ địa lý với SQL,
đây là triển khai của tôi trong thuật toán đề xuất 11 của e.James .
Trong đầu vào, nó yêu cầu tọa độ hình tròn, bán kính hình tròn tính bằng km và hai tọa độ đỉnh của hình chữ nhật:
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;
Hoạt động, chỉ cần tìm ra điều này một tuần trước, và bây giờ phải thử nghiệm nó.
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
Giả sử bạn có bốn cạnh của hình chữ nhật kiểm tra khoảng cách từ các cạnh đến tâm của vòng tròn, nếu nó nhỏ hơn bán kính, thì các hình dạng được giao nhau.
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
Nếu hình chữ nhật cắt với hình tròn, một hoặc nhiều điểm góc của hình chữ nhật sẽ nằm trong hình tròn. Giả sử bốn điểm của hình chữ nhật là A, B, C, D. ít nhất một trong số chúng nên giao nhau trong vòng tròn. do đó, nếu khoảng cách từ một điểm đến tâm của vòng tròn nhỏ hơn bán kính của vòng tròn thì nó sẽ cắt đường tròn. Để có được khoảng cách, bạn có thể sử dụng định lý Pythagore,
H^2 = A^2 + B^2
Kỹ thuật này có một số giới hạn. Nhưng nó sẽ làm việc tốt hơn cho các nhà phát triển trò chơi. đặc biệt là phát hiện va chạm
Đây là một bản cập nhật tốt cho Thuật toán của Arvo