Wie kann ich feststellen, ob sich ein Kreis und ein Rechteck im euklidischen 2D-Raum schneiden? (dh klassische 2D-Geometrie)
Wie kann ich feststellen, ob sich ein Kreis und ein Rechteck im euklidischen 2D-Raum schneiden? (dh klassische 2D-Geometrie)
Antworten:
Es gibt nur zwei Fälle, in denen sich der Kreis mit dem Rechteck schneidet:
Beachten Sie, dass das Rechteck hierfür nicht achsparallel sein muss.
(Eine Möglichkeit, dies zu sehen: Wenn keine der Kanten einen Punkt im Kreis hat (wenn alle Kanten vollständig "außerhalb" des Kreises liegen), kann der Kreis das Polygon nur dann noch schneiden, wenn er vollständig innerhalb des Kreises liegt Polygon.)
Mit dieser Einsicht wird etwa wie folgt arbeiten, wo die Kreismitte hat P
und Radius R
, und das Rechteck hat Ecken A
, B
, C
, D
in dieser Reihenfolge (nicht vollständiger 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)))
Wenn Sie eine Geometrie schreiben, haben Sie wahrscheinlich bereits die oben genannten Funktionen in Ihrer Bibliothek. Andernfalls pointInRectangle()
kann auf verschiedene Arten implementiert werden; Jeder der allgemeinen Punkte in Polygonmethoden funktioniert, aber für ein Rechteck können Sie einfach überprüfen, ob dies funktioniert:
0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD
Und intersectCircle()
ist auch einfach zu implementieren: Eine Möglichkeit wäre zu prüfen, ob der Fuß der Senkrechten vonP
zur Linie nahe genug und zwischen den Endpunkten liegt, und die Endpunkte ansonsten zu überprüfen.
Das Coole ist, dass die gleiche Idee nicht nur für Rechtecke funktioniert, sondern auch für den Schnittpunkt eines Kreises mit einem einfachen Polygon - muss nicht einmal konvex sein!
So würde ich es machen:
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));
}
So funktioniert das:
Das erste Linienpaar berechnet die absoluten Werte der x- und y-Differenz zwischen dem Mittelpunkt des Kreises und dem Mittelpunkt des Rechtecks. Dadurch werden die vier Quadranten zu einem zusammengefasst, sodass die Berechnungen nicht viermal durchgeführt werden müssen. Das Bild zeigt den Bereich, in dem der Mittelpunkt des Kreises liegen muss. Beachten Sie, dass nur der einzelne Quadrant angezeigt wird. Das Rechteck ist der graue Bereich, und der rote Rand umreißt den kritischen Bereich, der genau einen Radius von den Rändern des Rechtecks entfernt ist. Der Mittelpunkt des Kreises muss innerhalb dieses roten Randes liegen, damit der Schnittpunkt auftritt.
Das zweite Linienpaar eliminiert die einfachen Fälle, in denen der Kreis weit genug vom Rechteck entfernt ist (in beide Richtungen), dass kein Schnitt möglich ist. Dies entspricht dem grünen Bereich im Bild.
Das dritte Linienpaar behandelt die einfachen Fälle, in denen der Kreis nahe genug am Rechteck (in beide Richtungen) liegt, sodass ein Schnittpunkt garantiert ist. Dies entspricht den orangefarbenen und grauen Abschnitten im Bild. Beachten Sie, dass dieser Schritt nach Schritt 2 ausgeführt werden muss, damit die Logik sinnvoll ist.
Die verbleibenden Linien berechnen den schwierigen Fall, in dem der Kreis die Ecke des Rechtecks schneiden kann. Berechnen Sie zum Lösen den Abstand vom Mittelpunkt des Kreises und der Ecke und stellen Sie dann sicher, dass der Abstand nicht größer als der Radius des Kreises ist. Diese Berechnung gibt false für alle Kreise zurück, deren Mittelpunkt innerhalb des rot schattierten Bereichs liegt, und gibt true für alle Kreise zurück, deren Mittelpunkt innerhalb des weiß schattierten Bereichs liegt.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
Hier ist eine andere Lösung, die ziemlich einfach zu implementieren ist (und auch ziemlich schnell). Es werden alle Schnittpunkte erfasst, auch wenn die Kugel das Rechteck vollständig betreten hat.
// 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);
Mit jeder anständigen Mathematikbibliothek kann dies auf 3 oder 4 Zeilen gekürzt werden.
Ihre Kugel und Ihr Rechteck schneiden sich IIF ist
der Abstand zwischen dem Kreismittelpunkt und einem Scheitelpunkt Ihres Rechtecks kleiner als der Radius Ihrer Kugel
ODER
der Abstand zwischen dem Kreismittelpunkt und einer Kante Ihres Rechtecks ist kleiner als der Radius Ihrer Kugel ( [ Punkt-Linie-Abstand ])
ODER
der Kreismittelpunkt liegt innerhalb des Rechtecks
Punkt-Punkt-Abstandes:
P1 = [x1, y1] P2 = [x2, y2] Abstand = sqrt (abs (x1 - x2) + abs (y1 - y2))
Punkt-Linie-Abstand:
L1 = [x1, y1], L2 = [x2, y2] (zwei Punkte Ihrer Linie, dh die Scheitelpunkte) P1 = [px, py] irgendwann Abstand d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / Abstand (L1, L2)
Kreismittelpunkt innerhalb von Rect:
Nehmen Sie eine Trennachse: Wenn eine Projektion auf eine Linie vorhanden ist, die das Rechteck vom Punkt trennt, schneiden sie sich nicht
Sie projizieren den Punkt auf Linien parallel zu den Seiten Ihres Rect und können dann leicht feststellen, ob sie sich schneiden. Wenn sie sich nicht auf allen 4 Projektionen schneiden, können sie (der Punkt und das Rechteck) nicht schneiden.
Sie brauchen nur das innere Produkt (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)
Ihr Test würde so aussehen:
// Rechteckkanten: TL (oben links), TR (oben rechts), BL (unten links), BR (unten rechts) // Testpunkt: POI getrennt = falsch für egde in {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}}: // die Kanten D = Kante [0] - Kante [1] innerProd = D * POI Interval_min = min (D * Kante [0], D * Kante [1]) Interval_max = max (D * Kante [0], D * Kante [1]) wenn nicht (Interval_min ≤ innerProd ≤ Interval_max) getrennt = wahr break // end for loop ende wenn Ende für if (getrennt ist wahr) Rückgabe "keine Kreuzung" sonst Rückgabe "Kreuzung" ende wenn
Dies setzt kein achsenausgerichtetes Rechteck voraus und ist zum Testen von Schnittpunkten zwischen konvexen Mengen leicht erweiterbar.
Dies ist die schnellste Lösung:
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;
}
Beachten Sie die Ausführungsreihenfolge, und die Hälfte der Breite / Höhe ist vorberechnet. Auch das Quadrieren erfolgt "manuell", um einige Taktzyklen zu sparen.
Die einfachste Lösung, die ich gefunden habe, ist ziemlich einfach.
Es funktioniert, indem der Punkt im Rechteck gefunden wird, der dem Kreis am nächsten liegt, und dann der Abstand verglichen wird.
All dies können Sie mit wenigen Vorgängen tun und sogar die Funktion sqrt vermeiden.
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;
}
Und das ist es! Die obige Lösung geht von einem Ursprung oben links auf der Welt aus, wobei die x-Achse nach unten zeigt.
Wenn Sie eine Lösung für die Behandlung von Kollisionen zwischen einem sich bewegenden Kreis und einem Rechteck suchen, ist dies weitaus komplizierter und wird in einer anderen Antwort von mir behandelt.
Eigentlich ist das viel einfacher. Sie brauchen nur zwei Dinge.
Zunächst müssen Sie vier orthogonale Abstände vom Kreismittelpunkt zu jeder Linie des Rechtecks ermitteln. Dann schneidet Ihr Kreis das Rechteck nicht, wenn drei davon größer als der Kreisradius sind.
Zweitens müssen Sie den Abstand zwischen dem Kreismittelpunkt und dem Rechteckmittelpunkt ermitteln. Der Kreis befindet sich dann nicht innerhalb des Rechtecks, wenn der Abstand größer als die Hälfte der diagonalen Länge des Rechtecks ist.
Viel Glück!
Hier ist mein C-Code zum Auflösen einer Kollision zwischen einer Kugel und einem nicht achsenausgerichteten Feld. Es basiert auf einigen meiner eigenen Bibliotheksroutinen, kann sich aber für einige als nützlich erweisen. Ich benutze es in einem Spiel und es funktioniert perfekt.
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;
}
Nehmen Sie zur Visualisierung den Nummernblock Ihrer Tastatur. Wenn die Taste '5' Ihr Rechteck darstellt, repräsentieren alle Tasten 1-9 die 9 Quadranten des Raums geteilt durch die Linien, aus denen Ihr Rechteck besteht (wobei 5 die Innenseite ist).
1) Wenn der Mittelpunkt des Kreises im Quadranten 5 liegt (dh innerhalb des Rechtecks), schneiden sich die beiden Formen.
Damit gibt es zwei mögliche Fälle: a) Der Kreis schneidet zwei oder mehr benachbarte Kanten des Rechtecks. b) Der Kreis schneidet mit einer Kante des Rechtecks.
Der erste Fall ist einfach. Wenn sich der Kreis mit zwei benachbarten Kanten des Rechtecks schneidet, muss er die Ecke enthalten, die diese beiden Kanten verbindet. (Das oder sein Zentrum liegt in Quadrant 5, den wir bereits behandelt haben. Beachten Sie auch, dass der Fall, in dem sich der Kreis mit nur zwei gegenüberliegenden Kanten des Rechtecks schneidet, ebenfalls abgedeckt ist.)
2) Wenn eine der Ecken A, B, C, D des Rechtecks innerhalb des Kreises liegt, schneiden sich die beiden Formen.
Der zweite Fall ist schwieriger. Wir sollten beachten, dass dies nur passieren kann, wenn der Mittelpunkt des Kreises in einem der Quadranten 2, 4, 6 oder 8 liegt Die entsprechende Ecke ist der nächstgelegene Punkt.)
Jetzt haben wir den Fall, dass der Mittelpunkt des Kreises in einem der 'Kanten'-Quadranten liegt und sich nur mit der entsprechenden Kante schneidet. Dann muss der Punkt an der Kante, der dem Mittelpunkt des Kreises am nächsten liegt, innerhalb des Kreises liegen.
3) Konstruieren Sie für jede Linie AB, BC, CD, DA senkrechte Linien p (AB, P), p (BC, P), p (CD, P), p (DA, P) durch den Mittelpunkt des Kreises P. For Wenn bei jeder senkrechten Linie der Schnittpunkt mit der ursprünglichen Kante innerhalb des Kreises liegt, schneiden sich die beiden Formen.
Für diesen letzten Schritt gibt es eine Verknüpfung. Wenn der Mittelpunkt des Kreises im Quadranten 8 liegt und die Kante AB die Oberkante ist, hat der Schnittpunkt die y-Koordinate von A und B und die x-Koordinate des Mittelpunkts P.
Sie können die vier Linienschnittpunkte konstruieren und prüfen, ob sie an den entsprechenden Kanten liegen, oder herausfinden, in welchem Quadranten P sich befindet, und den entsprechenden Schnittpunkt überprüfen. Beide sollten sich zu derselben booleschen Gleichung vereinfachen. Seien Sie vorsichtig, dass der obige Schritt 2 nicht ausschloss, dass sich P in einem der 'Eck'-Quadranten befindet. es suchte nur nach einer Kreuzung.
Bearbeiten: Wie sich herausstellt, habe ich die einfache Tatsache übersehen, dass # 2 ein Unterfall von # 3 oben ist. Schließlich sind auch Ecken Punkte an den Kanten. Eine gute Erklärung finden Sie in der Antwort von @ ShreevatsaR unten. Vergessen Sie in der Zwischenzeit die Nummer 2 oben, es sei denn, Sie möchten eine schnelle, aber redundante Überprüfung.
Diese Funktion erkennt Kollisionen (Schnittpunkte) zwischen Kreis und Rechteck. Er arbeitet in seiner Antwort wie die e.James-Methode, aber diese erkennt Kollisionen für alle Winkel des Rechtecks (nicht nur bis zur oberen Ecke).
HINWEIS:
aRect.origin.x und aRect.origin.y sind Koordinaten des linken unteren Rechteckwinkels!
aCircle.x und aCircle.y sind Koordinaten von 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;
}
Ich habe eine Methode, die die teuren Pythagoras vermeidet, wenn sie nicht notwendig sind - dh. wenn sich Begrenzungsrahmen des Rechtecks und des Kreises nicht schneiden.
Und es wird auch für nicht-euklidische funktionieren:
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
. Wenn Sie diese normDist-Methode verwenden, müssen Sie natürlich eine normedDist = dist*dist;
für den Kreis erstellenDen vollständigen BBox- und Circle- Code meines GraphHopper- Projekts anzeigen .
Ich habe eine Klasse für die Arbeit mit Formen geschaffen, die Ihnen Spaß machen
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;
}
}
Hier funktioniert der modifizierte Code zu 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
Hier ist ein schneller einzeiliger Test dafür:
if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
// They intersect.
}
Dies ist der achsenausgerichtete Fall, in dem rect_halves
ein positiver Vektor von der Rechteckmitte zu einer Ecke zeigt. Der Ausdruck im Inneren length()
ist ein Delta-Vektor von center
zu einem nächstgelegenen Punkt im Rechteck. Dies funktioniert in jeder Dimension.
Es ist effizient, weil:
arbeitete für mich (nur arbeiten, wenn der Winkel des Rechtecks 180 ist)
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;
}
Die Antwort von e.James ein wenig verbessern:
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);
Dies subtrahiert rect.w / 2
und rect.h / 2
einmal statt bis zu dreimal.
Für diejenigen, die die Kreis- / Rechteckkollision in geografischen Koordinaten mit SQL berechnen müssen, ist
dies meine Implementierung in Oracle 11 des von e.James vorgeschlagenen Algorithmus .
Für die Eingabe sind Kreiskoordinaten, Kreisradius in km und zwei Eckpunktkoordinaten des Rechtecks erforderlich:
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;
Funktioniert, habe das erst vor einer Woche herausgefunden und muss es jetzt testen.
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
Angenommen, Sie haben die vier Kanten des Rechtecks. Überprüfen Sie den Abstand zwischen den Kanten und dem Mittelpunkt des Kreises. Wenn dieser kleiner als der Radius ist, schneiden sich die Formen.
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
Wenn sich das Rechteck mit dem Kreis schneidet, sollten sich ein oder mehrere Eckpunkte des Rechtecks innerhalb des Kreises befinden. Angenommen, die vier Punkte eines Rechtecks sind A, B, C, D. Mindestens einer von ihnen sollte den Kreis schneiden. Wenn also der Abstand von einem Punkt zum Mittelpunkt des Kreises kleiner als der Radius des Kreises ist, sollte er den Kreis schneiden. Um die Entfernung zu ermitteln, können Sie den Satz von Pythagoras verwenden:
H^2 = A^2 + B^2
Diese Technik hat einige Grenzen. Aber es wird besser für die Spieleentwickler funktionieren. insbesondere Kollisionserkennung
Es ist ein gutes Update des Arvo-Algorithmus