Bagaimana saya bisa tahu apakah lingkaran dan persegi panjang berpotongan dalam ruang Euclidean 2D? (yaitu geometri 2D klasik)
Bagaimana saya bisa tahu apakah lingkaran dan persegi panjang berpotongan dalam ruang Euclidean 2D? (yaitu geometri 2D klasik)
Jawaban:
Hanya ada dua kasus ketika lingkaran bersinggungan dengan persegi panjang:
Perhatikan bahwa ini tidak memerlukan persegi panjang untuk menjadi sumbu-paralel.
(Salah satu cara untuk melihat ini: jika tidak ada sisi yang memiliki titik di dalam lingkaran (jika semua ujungnya benar-benar "di luar" lingkaran), maka satu-satunya cara lingkaran masih dapat memotong poligon adalah jika itu terletak di dalam poligon.)
Dengan wawasan itu, sesuatu seperti berikut ini akan bekerja, di mana lingkaran memiliki pusat P
dan jari-jari R
, dan persegi panjang memiliki simpul A
, B
, C
,D
agar (kode tidak lengkap):
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)))
Jika Anda sedang menulis geometri apa pun, Anda mungkin sudah memiliki fungsi-fungsi di atas di perpustakaan Anda. Kalau tidak, pointInRectangle()
dapat diimplementasikan dalam beberapa cara; salah satu titik umum dalam poligon metode akan bekerja, tetapi untuk persegi panjang Anda bisa memeriksa apakah ini bekerja:
0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD
Dan intersectCircle()
juga mudah diterapkan: salah satu caranya adalah dengan memeriksa apakah kaki tegak lurus dari P
ke garis cukup dekat dan di antara titik-titik akhir, dan periksa titik-titik ujung sebaliknya.
Yang keren adalah bahwa ide yang sama bekerja tidak hanya untuk persegi panjang tetapi untuk persimpangan lingkaran dengan poligon sederhana - bahkan tidak harus cembung!
Inilah cara saya akan melakukannya:
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));
}
Begini cara kerjanya:
Pasangan garis pertama menghitung nilai absolut dari perbedaan x dan y antara pusat lingkaran dan pusat persegi panjang. Ini meruntuhkan keempat kuadran menjadi satu, sehingga perhitungan tidak harus dilakukan empat kali. Gambar menunjukkan area di mana pusat lingkaran sekarang harus berada. Perhatikan bahwa hanya kuadran tunggal yang ditampilkan. Persegi panjang adalah area abu-abu, dan batas merah menguraikan area kritis yang persis satu radius dari tepi persegi panjang. Pusat lingkaran harus berada dalam batas merah ini agar persimpangan dapat terjadi.
Pasangan kedua garis menghilangkan kasus mudah di mana lingkaran cukup jauh dari persegi panjang (di kedua arah) sehingga tidak ada persimpangan yang mungkin. Ini sesuai dengan area hijau pada gambar.
Pasangan garis ketiga menangani kotak yang mudah di mana lingkaran cukup dekat dengan persegi panjang (di kedua arah) yang dijamin persimpangan. Ini sesuai dengan bagian oranye dan abu-abu pada gambar. Perhatikan bahwa langkah ini harus dilakukan setelah langkah 2 agar logika masuk akal.
Baris yang tersisa menghitung case yang sulit di mana lingkaran dapat memotong sudut persegi panjang. Untuk menyelesaikan, hitung jarak dari pusat lingkaran dan sudut, lalu verifikasi bahwa jaraknya tidak lebih dari jari-jari lingkaran. Perhitungan ini menghasilkan false untuk semua lingkaran yang pusatnya ada di dalam area berbayang merah dan mengembalikan true untuk semua lingkaran yang pusatnya ada di dalam area berbayang putih.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
Berikut adalah solusi lain yang cukup sederhana untuk diimplementasikan (dan juga cukup cepat). Ini akan menangkap semua persimpangan, termasuk ketika bola telah sepenuhnya memasuki persegi panjang.
// 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);
Dengan perpustakaan matematika yang layak, itu dapat disingkat menjadi 3 atau 4 baris.
bola Anda dan rectect berpotongan IIF
jarak antara pusat lingkaran dan satu simpul persegi Anda lebih kecil dari jari-jari bola Anda
ATAU
jarak antara pusat lingkaran dan satu ujung persegi Anda lebih kecil dari jari-jari bola Anda ( [ jarak titik-garis ])
ATAU
pusat lingkaran berada di dalam
jarak titik-titik persegi:
P1 = [x1, y1] P2 = [x2, y2] Jarak = sqrt (abs (x1 - x2) + abs (y1-y2))
jarak titik-garis:
L1 = [x1, y1], L2 = [x2, y2] (dua titik dari garis Anda, yaitu titik simpul) P1 = [px, py] suatu titik Jarak d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / Jarak (L1, L2)
lingkaran pusat di
dalam persegi : ambil pendekatan sumbu pemisah: jika ada proyeksi ke garis yang memisahkan persegi panjang dari titik, mereka tidak berpotongan
Anda memproyeksikan titik pada garis sejajar dengan sisi persegi Anda dan kemudian dapat dengan mudah menentukan apakah mereka berpotongan. jika mereka berpotongan tidak pada semua 4 proyeksi, mereka (titik dan persegi panjang) tidak dapat berpotongan.
Anda hanya perlu produk dalam (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)
tes Anda akan terlihat seperti itu:
// tepi persegi panjang: TL (kiri atas), TR (kanan atas), BL (kiri bawah), BR (kanan bawah) // arahkan ke tes: POI dipisahkan = salah untuk egde dalam {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}}: // the edge D = tepi [0] - tepi [1] innerProd = D * POI Interval_min = min (D * edge [0], D * edge [1]) Interval_max = maks (D * tepi [0], D * tepi [1]) jika tidak (Interval_min ≤ innerProd ≤ Interval_max) dipisahkan = benar break // end for loop berakhir jika berakhir untuk jika (dipisahkan benar) kembali "tidak ada persimpangan" lain kembali "persimpangan" berakhir jika
ini tidak mengasumsikan persegi panjang sejajar sumbu dan mudah diperpanjang untuk menguji persimpangan antara set cembung.
Ini adalah solusi tercepat:
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;
}
Perhatikan urutan eksekusi, dan setengah lebar / tinggi sudah dihitung sebelumnya. Juga squaring dilakukan "secara manual" untuk menghemat beberapa siklus jam.
Solusi paling sederhana yang saya buat adalah cukup mudah.
Ia bekerja dengan menemukan titik dalam persegi panjang yang paling dekat dengan lingkaran, lalu membandingkan jarak.
Anda dapat melakukan semua ini dengan beberapa operasi, dan bahkan menghindari fungsi 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;
}
Dan itu dia! Solusi di atas mengasumsikan asal di kiri atas dunia dengan sumbu x mengarah ke bawah.
Jika Anda menginginkan solusi untuk menangani tabrakan antara lingkaran bergerak dan persegi panjang, itu jauh lebih rumit dan tercakup dalam jawaban saya yang lain.
Sebenarnya, ini jauh lebih sederhana. Anda hanya membutuhkan dua hal.
Pertama, Anda perlu menemukan empat jarak ortogonal dari pusat lingkaran ke setiap garis persegi panjang. Maka lingkaran Anda tidak akan memotong persegi panjang jika ketiganya lebih besar dari jari-jari lingkaran.
Kedua, Anda perlu menemukan jarak antara pusat lingkaran dan pusat persegi panjang, maka Anda lingkaran tidak akan berada di dalam persegi panjang jika jaraknya lebih besar dari setengah panjang diagonal persegi panjang.
Semoga berhasil!
Inilah kode C saya untuk menyelesaikan tabrakan antara bola dan kotak yang tidak disejajarkan. Itu bergantung pada beberapa rutinitas perpustakaan saya sendiri, tetapi mungkin terbukti bermanfaat bagi sebagian orang. Saya menggunakannya dalam sebuah game dan bekerja dengan sempurna.
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;
}
Untuk memvisualisasikan, ambil numpad keyboard Anda. Jika kunci '5' mewakili persegi panjang Anda, maka semua kunci 1-9 mewakili 9 kuadran ruang dibagi dengan garis-garis yang membentuk persegi panjang Anda (dengan 5 menjadi bagian dalam.)
1) Jika pusat lingkaran berada di kuadran 5 (yaitu di dalam persegi panjang) maka kedua bentuk berpotongan.
Dengan keluar dari jalan, ada dua kasus yang mungkin: a) Lingkaran berpotongan dengan dua atau lebih tepi berdekatan dari persegi panjang. b) Lingkaran bersinggungan dengan satu ujung persegi panjang.
Kasus pertama sederhana. Jika lingkaran berpotongan dengan dua sisi persegi yang berdekatan, itu harus berisi sudut yang menghubungkan kedua tepi. (Itu, atau pusatnya terletak pada kuadran 5, yang telah kita bahas. Juga perhatikan bahwa kasus di mana lingkaran bersilangan dengan hanya dua sisi yang berlawanan dari persegi panjang juga tercakup.)
2) Jika salah satu sudut A, B, C, D dari persegi panjang terletak di dalam lingkaran, maka kedua bentuk berpotongan.
Kasus kedua lebih rumit. Kita harus mencatat bahwa itu hanya dapat terjadi ketika pusat lingkaran terletak di salah satu kuadran 2, 4, 6 atau 8. (Faktanya, jika pusatnya berada di salah satu kuadran 1, 3, 7, 8, sudut yang sesuai akan menjadi titik terdekat dengan itu.)
Sekarang kita memiliki kasus bahwa pusat lingkaran berada di salah satu kuadran 'tepi', dan hanya memotong dengan tepi yang sesuai. Kemudian, titik di tepi yang paling dekat dengan pusat lingkaran, harus berada di dalam lingkaran.
3) Untuk setiap garis AB, BC, CD, DA, buat garis tegak lurus p (AB, P), p (BC, P), p (CD, P), p (DA, P) melalui pusat lingkaran P. Untuk setiap garis tegak lurus, jika persimpangan dengan tepi asli terletak di dalam lingkaran, maka kedua bentuk tersebut bersilangan.
Ada jalan pintas untuk langkah terakhir ini. Jika pusat lingkaran berada di kuadran 8 dan tepi AB adalah tepi atas, titik persimpangan akan memiliki koordinat y dari A dan B, dan koordinat x dari pusat P.
Anda bisa membuat empat persimpangan garis dan memeriksa apakah mereka terletak di tepi yang sesuai, atau mencari tahu di kuadran P mana dan memeriksa persimpangan yang sesuai. Keduanya harus menyederhanakan persamaan boolean yang sama. Berhati-hatilah karena langkah 2 di atas tidak mengesampingkan P berada di salah satu kuadran 'sudut'; itu hanya mencari persimpangan.
Sunting: Ternyata, saya telah mengabaikan fakta sederhana bahwa # 2 adalah subkotak dari # 3 di atas. Lagi pula, sudut juga merupakan titik di tepinya. Lihat jawaban @ ShreevatsaR di bawah ini untuk penjelasan yang bagus. Dan sementara itu, lupakan # 2 di atas kecuali Anda ingin cek cepat tapi berlebihan.
Fungsi ini mendeteksi tabrakan (persimpangan) antara Circle dan Rectangle. Dia bekerja seperti metode e.James dalam jawabannya, tetapi yang ini mendeteksi tabrakan untuk semua sudut persegi panjang (tidak hanya sudut kanan atas).
CATATAN:
aRect.origin.x dan aRect.origin.y adalah koordinat sudut kiri bawah persegi panjang!
aCircle.x dan aCircle.y adalah koordinat dari 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;
}
Saya punya metode yang menghindari pythagoras yang mahal jika tidak perlu - yaitu. ketika kotak kotak persegi panjang dan lingkaran tidak berpotongan.
Dan itu akan bekerja untuk non-euclidean juga:
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
. Tentu saja jika Anda menggunakan metode normDist Anda harus membuat buat normedDist = dist*dist;
untuk lingkaranLihat kode BBox dan Lingkaran penuh dari proyek GraphHopper saya .
Saya membuat kelas untuk bekerja dengan bentuk yang Anda harap dapat Anda nikmati
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;
}
}
Berikut adalah kode yang dimodifikasi 100% berfungsi:
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
Inilah tes satu garis cepat untuk ini:
if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
// They intersect.
}
Ini adalah kasus selaras sumbu di mana rect_halves
vektor positif menunjuk dari persegi panjang ke sudut. Ekspresi di dalam length()
adalah vektor delta dari center
ke titik terdekat di kotak. Ini berfungsi dalam dimensi apa pun.
Ini efisien, karena:
bekerja untuk saya (hanya bekerja ketika sudut persegi panjang adalah 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;
}
Meningkatkan sedikit jawaban dari 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);
Ini mengurangi rect.w / 2
dan rect.h / 2
sekali bukannya tiga kali.
Bagi mereka yang harus menghitung tabrakan Circle / Rectangle di Geographic Coordinates dengan SQL,
ini adalah implementasi saya di oracle 11 dari algoritma yang disarankan e.James .
Dalam input itu membutuhkan koordinat lingkaran, jari-jari lingkaran dalam km dan dua koordinat titik persegi:
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;
Bekerja, baru saja mengetahui hal ini seminggu yang lalu, dan baru saja mengujinya.
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
Dengan asumsi Anda memiliki empat tepi persegi panjang periksa jarak dari tepi ke pusat lingkaran, jika kurang dari jari-jari, maka bentuknya berpotongan.
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
Jika persegi panjang berpotongan dengan lingkaran, satu atau lebih titik sudut persegi panjang harus berada di dalam lingkaran. Misalkan empat titik persegi panjang adalah A, B, C, D. setidaknya salah satu dari mereka harus memotong lingkaran. jadi jika jarak dari satu titik ke pusat lingkaran kurang dari jari-jari lingkaran itu harus memotong lingkaran. Untuk mendapatkan jarak Anda dapat menggunakan teorema Pythagoras,
H^2 = A^2 + B^2
Teknik ini memiliki beberapa batasan. Tapi itu akan bekerja lebih baik untuk para pengembang game. terutama deteksi tabrakan
Ini adalah pembaruan yang bagus untuk Algoritma Arvo