2D Öklid uzayında bir dairenin ve bir dikdörtgenin kesişip kesişmediğini nasıl anlayabilirim? (yani klasik 2D geometrisi)
2D Öklid uzayında bir dairenin ve bir dikdörtgenin kesişip kesişmediğini nasıl anlayabilirim? (yani klasik 2D geometrisi)
Yanıtlar:
Dairenin dikdörtgenle kesiştiği sadece iki durum vardır:
Bunun, dikdörtgenin eksen paralel olmasını gerektirmediğini unutmayın.
(Bunu görmenin bir yolu: kenarlardan hiçbirinin dairede bir noktası yoksa (tüm kenarlar dairenin tamamen dışındaysa), o zaman dairenin çokgene kesebilmesinin tek yolu, kenarın tamamen içinde kalmasıdır. çokgen.)
Daire merkezi nerede anlayış ile, aşağıdaki gibi bir şey, çalışacak P
ve yarıçapı R
ve dikdörtgen kesişim noktası var A
, B
, C
, D
bu sırayla (tam değil 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)))
Herhangi bir geometri yazıyorsanız muhtemelen kütüphanenizde yukarıdaki işlevler zaten vardır. Aksi takdirde, pointInRectangle()
çeşitli şekillerde uygulanabilir; çokgen yöntemlerindeki genel noktalardan herhangi biri çalışır, ancak bir dikdörtgen için bunun işe yarayıp yaramadığını kontrol edebilirsiniz:
0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD
Ve intersectCircle()
uygulanması da kolaydır: bir yol P
, çizgiden dik olan ayağın yeterince yakın ve uç noktalar arasında olup olmadığını kontrol etmek ve aksi takdirde uç noktaları kontrol etmek olacaktır.
Güzel olan şey, aynı fikrin sadece dikdörtgenler için değil, herhangi bir basit çokgenle bir dairenin kesişimi için de işe yaramasıdır - dışbükey bile olmak zorunda değildir!
İşte nasıl yaparım:
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));
}
Şöyle çalışır:
İlk çizgi çifti dairenin merkezi ile dikdörtgenin merkezi arasındaki x ve y farklarının mutlak değerlerini hesaplar. Bu, dört çeyreği bire indirir, böylece hesaplamaların dört kez yapılması gerekmez. Görüntü, dairenin merkezinin şimdi yatması gereken alanı göstermektedir. Yalnızca tek çeyrek daire gösterildiğine dikkat edin. Dikdörtgen gri alandır ve kırmızı kenarlık, dikdörtgenin kenarlarından tam olarak bir yarıçap olan kritik alanı ana hatlarıyla belirtir. Kesişimin meydana gelmesi için dairenin merkezi bu kırmızı kenarlığın içinde olmalıdır.
İkinci çizgi çifti, dairenin kesişmenin mümkün olmadığı dikdörtgenden (her iki yönde) yeterince uzakta olduğu kolay durumları ortadan kaldırır. Bu, görüntüdeki yeşil alana karşılık gelir.
Üçüncü satır çifti, dairenin kesişimin garanti edildiği dikdörtgene (her iki yönde) yeterince yakın olduğu kolay durumları ele alır. Bu, görüntüdeki turuncu ve gri bölümlere karşılık gelir. Bu adımın mantığın anlamlı olması için 2. adımdan sonra yapılması gerektiğini unutmayın.
Kalan çizgiler dairenin dikdörtgenin köşesiyle kesişebileceği zor durumu hesaplar. Çözmek için, dairenin merkezinden ve köşeden uzaklığı hesaplayın ve sonra mesafenin dairenin yarıçapından fazla olmadığını doğrulayın. Bu hesaplama, merkezi kırmızı gölgeli alan içindeki tüm daireler için false değerini ve merkezi beyaz gölgeli alan içindeki tüm daireler için true değerini döndürür.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
İşte uygulanması oldukça basit (ve oldukça hızlı) başka bir çözüm. Kürenin dikdörtgeni tam olarak girmesi de dahil olmak üzere tüm kavşakları yakalayacaktır.
// 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);
Herhangi bir iyi matematik kütüphanesi ile, bu 3 veya 4 satıra kısaltılabilir.
küreniz ve doğrultunuz IIF
ile daire merkezi ve rektinizin bir tepe noktası
arasındaki mesafe kürenizin yarıçapından daha küçüktür
VEYA
daire merkezi ile rektinizin bir kenarı arasındaki mesafe kürenizin yarıçapından daha küçüktür ( [ nokta çizgisi mesafesi ])
VEYA
daire merkezi rektetin içindedir
noktası mesafesinin :
P1 = [x1, y1] P2 = [x2, y2] Mesafe = sqrt (abs (x1 - x2) + abs (y1-y2))
nokta çizgisi mesafesi:
L1 = [x1, y1], L2 = [x2, y2] (çizginizin iki noktası, yani tepe noktaları) P1 = [px, py] bir nokta Mesafe d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / Mesafe (L1, L2)
rect içindeki daire merkezi:
ayırıcı bir eksen yaklaşımı alın: dikdörtgeni noktadan ayıran bir çizgi üzerinde bir projeksiyon varsa, kesişmezler
noktayı rektifinizin kenarlarına paralel çizgiler üzerine yansıtırsınız ve kesişip kesişmediklerini kolayca belirleyebilirsiniz. 4 projeksiyonun hepsinde kesişmezlerse, onlar (nokta ve dikdörtgen) kesişemez.
sadece iç ürüne ihtiyacınız var (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)
testiniz şöyle görünecektir:
// dikdörtgen kenarlar: TL (sol üst), TR (sağ üst), BL (sol alt), BR (sağ alt) // test edilecek nokta: İÇN ayrılmış = yanlış {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}} 'deki egde için: // kenarlar D = kenar [0] - kenar [1] innerProd = D * POI Aralık_dak = dak (D * kenar [0], D * kenar [1]) Aralık_maks = maksimum (D * kenar [0], D * kenar [1]) değilse (Aralık_dakika ≤ içProd ≤ Aralık_maks) ayrılmış = doğru break // end for loop eğer biterse sonu eğer (ayrılmış doğrudur) dönüş "kavşak yok" Başka dönüş "kavşak" eğer biterse
bu, eksene hizalanmış bir dikdörtgeni kabul etmez ve dışbükey kümeler arasındaki kesişimleri test etmek için kolayca genişletilebilir.
Bu en hızlı çözüm:
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;
}
Yürütme sırasına dikkat edin ve genişlik / yüksekliğin yarısı önceden hesaplanır. Ayrıca, bazı saat çevrimlerini kaydetmek için kareleme "manuel olarak" yapılır.
Ortaya koyduğum en basit çözüm oldukça basit.
Dikdörtgendeki daireye en yakın noktayı bularak mesafeyi karşılaştırarak çalışır.
Tüm bunları birkaç işlemle yapabilir ve hatta sqrt işlevinden kaçınabilirsiniz.
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;
}
Ve bu kadar! Yukarıdaki çözüm, x ekseni aşağı bakacak şekilde dünyanın sol üst köşesinde bir başlangıç noktası olduğunu varsaymaktadır.
Hareketli bir daire ile dikdörtgen arasındaki çarpışmaları ele almak için bir çözüm istiyorsanız, çok daha karmaşıktır ve benim başka bir cevabımla kaplıdır .
Aslında bu çok daha basit. Sadece iki şeye ihtiyacınız var.
İlk olarak, daire merkezinden dikdörtgenin her satırına dört dikey mesafe bulmanız gerekir . Üçü daire yarıçapından daha büyükse, daireniz dikdörtgenle kesişmez.
İkincisi, daire merkezi ve dikdörtgen merkezi arasındaki mesafeyi bulmanız gerekir, o zaman mesafe dikdörtgen diyagonal uzunluğun yarısından daha büyükse daire dikdörtgenin içinde olmaz.
İyi şanslar!
İşte bir küre ve eksen olmayan hizalanmış bir kutu arasındaki bir çarpışmayı çözmek için C kodum. Kendi kütüphane rutinlerime dayanıyor, ancak bazıları için yararlı olabilir. Bir oyunda kullanıyorum ve mükemmel çalışıyor.
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;
}
Görselleştirmek için klavyenizin sayısal tuş takımını kullanın. Eğer '5' tuşu dikdörtgeni temsil ediyorsa, 1-9 arasındaki tüm tuşlar 9 çeyrek alanı temsil eder ve dikdörtgeni oluşturan çizgilere bölünür (5 içeride olacak şekilde).
1) Dairenin merkezi 4. çeyrekte ise (yani dikdörtgenin içinde), iki şekil kesişir.
Bu yoldan çekildiğinde, iki olası durum vardır: a) Daire, dikdörtgenin iki veya daha fazla komşu kenarı ile kesişir. b) Daire, dikdörtgenin bir kenarı ile kesişir.
İlk durum basit. Daire, dikdörtgenin iki komşu kenarı ile kesişirse, bu iki kenarı birleştiren köşeyi içermelidir. (Bu ya da merkezi, daha önce kapladığımız kadran 5'te yatıyor. Ayrıca, dairenin dikdörtgenin sadece iki karşıt kenarı ile kesiştiği durumun da kapsanacağına dikkat edin .)
2) Dikdörtgenin A, B, C, D köşelerinden herhangi biri dairenin içinde ise, iki şekil kesişir.
İkinci durum daha hileli. Bunun sadece dairenin merkezi 2, 4, 6 veya 8 çeyreklerden birinde olduğunda meydana gelebileceğini not etmeliyiz (Aslında, merkez 1, 3, 7, 8 çeyreklerden herhangi birinde ise, karşılık gelen köşe ona en yakın nokta olacaktır.)
Şimdi dairenin merkezinin 'kenar' kadranlarından birinde olduğu ve sadece karşılık gelen kenarla kesiştiği durumumuz var. Ardından, kenarda dairenin merkezine en yakın nokta dairenin içinde olmalıdır.
3) Her AB, BC, CD, DA hattı için dairenin merkezinden P dikey çizgiler p (AB, P), p (BC, P), p (CD, P), p (DA, P) oluşturun. her dikey çizgi, eğer orijinal kenarla kesişme dairenin içinde yer alıyorsa, iki şekil kesişir.
Bu son adım için bir kısayol var. Dairenin merkezi kadranda 8 ise ve AB kenarı üst kenarsa, kesişme noktasında A ve B'nin y koordinatı ve P merkezinin x koordinatı olacaktır.
Dört çizgi kavşağını yapılandırabilir ve karşılık gelen kenarlarında olup olmadıklarını kontrol edebilir veya hangi çeyrek daire P'nin olduğunu ve ilgili kavşağı kontrol edebilirsiniz. Her ikisi de aynı boole denklemini basitleştirmelidir. Yukarıdaki 2. adımın P'nin 'köşe' çeyrek çemberlerinden birinde bulunmadığına dikkat etmediğinizden emin olun; sadece bir kavşak aradı.
Düzenleme: Görünüşe göre, # 2 yukarıdaki # 3 bir alt harf olduğu basit gerçeği göz ardı ettik. Sonuçta, köşeler de kenarlardaki noktalardır. Harika bir açıklama için @ ShreevatsaR'ın cevabına bakınız. Ve bu arada, hızlı ama gereksiz bir kontrol istemiyorsanız yukarıdaki # 2'yi unutun.
Bu işlev Daire ve Dikdörtgen arasındaki çarpışmaları (kesişimleri) algılar. Cevabında e.James yöntemi gibi çalışıyor, ancak bu, dikdörtgenin tüm açıları için çarpışmaları tespit ediyor (sadece sağ köşede değil).
NOT:
aRect.origin.x ve aRect.origin.y , dikdörtgenin sol alt açısının koordinatlarıdır!
aCircle.x ve aCircle.y , Circle Center'ın koordinatlarıdır!
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;
}
Gerekli değilse pahalı pisagorlardan kaçınan bir yöntemim var. Dikdörtgenin ve dairenin sınırlayıcı kutuları kesişmediğinde.
Ve öklidyen olmayanlar için de işe yarayacak:
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
. Tabii ki bu normDist yöntemini kullanırsanız normedDist = dist*dist;
, çevre için birTam Bkz BBOX ve Çember benim kodunu GraphHopper projesi.
Şekillerle çalışmak için sınıf oluşturdum umarım beğenirsiniz
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;
}
}
İşte% 100 çalışan değiştirilmiş kod:
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
İşte bunun için hızlı bir satırlık test:
if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
// They intersect.
}
Bu, rect_halves
dikdörtgenin ortasından köşeye doğru pozitif bir vektörün bulunduğu eksene hizalanmış durumdur . İçindeki ifade , dikdörtgenin en yakın noktasına length()
kadar bir delta vektörüdür center
. Bu her boyutta çalışır.
Verimlidir, çünkü:
benim için çalıştı (sadece dikdörtgen açısı 180 olduğunda çalışır)
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;
}
E.James'in cevabını biraz geliştirmek :
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);
Bu toplayıp çıkarmadan rect.w / 2
ve rect.h / 2
bir kez yerine üç kereye kadar.
SQL ile Coğrafi Koordinatlarda Çember / Dikdörtgen çarpışmasını hesaplamak zorunda olanlar için,
bu benim e.James önerilen algoritmasının kehanet 11'inde benim uygulama .
Girişte daire koordinatları, km cinsinden daire yarıçapı ve dikdörtgenin iki köşe koordinatı gerekir:
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;
İşler, bunu bir hafta önce anladım ve şimdi test ettim.
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
Dikdörtgenin dört kenarına sahip olduğunuzu varsayarsak, kenarlardan dairenin merkezine olan mesafeyi kontrol edin, eğer yarıçaptan daha azsa, şekiller kesişmektedir.
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
Dikdörtgen daireyle kesişiyorsa, dikdörtgenin bir veya daha fazla köşe noktasının dairenin içinde olması gerekir. Bir dikdörtgenin dört noktasının A, B, C, D olduğunu varsayalım. bunlardan en az biri daireyle kesişmelidir. böylece bir noktadan dairenin merkezine olan mesafe dairenin yarıçapından daha azsa daireyle kesişmelidir. Mesafeyi elde etmek için Pisagor teoremini kullanabilirsiniz,
H^2 = A^2 + B^2
Bu tekniğin bazı sınırları vardır. Ancak oyun geliştiricileri için daha iyi çalışacaktır. özellikle çarpışma tespiti
Arvo'nun Algoritması için iyi bir güncelleme