円と長方形の衝突検出(交差)


192

円と長方形が2Dユークリッド空間で交差するかどうかを確認するにはどうすればよいですか?(つまり、クラシック2Dジオメトリ)


1
長方形は常に軸に揃っていますか、それとも任意の角度で回転できますか?
e.James、2008

11
@eJames:それはどういう意味ですか?長方形と円の交差をチェックしています。常に、座標系を変換して、長方形が軸に平行で、円が変化しないようにすることができます:-)
ShreevatsaR

あなたは-Θとすべてを回転答え、...などということを追加する必要があります
AIB

2
@ShreevatsaR:座標変換について心配する必要があるかどうかという点で重要です。@aib:ああ、親愛なる!
e.James、2008

回答:


191

円が長方形と交差するケースは2つだけあります。

  • 円の中心が長方形の内側にあるか、
  • 長方形のエッジの1つが円の中にポイントを持っています。

これは長方形が軸平行である必要がないことに注意してください。

Some different ways a circle and rectangle may intersect

(これを確認する1つの方法:すべてのエッジが完全に円の「外」にある場合)エッジが円の中にポイントを持たない場合、円がポリゴンと完全に交差する唯一の方法は、それが完全に内側にある場合です。ポリゴン。)

円は中心有する場合、その洞察と、次のようなものは、動作するPと半径をR、矩形の頂点を有しABCDその順序(完了していないコード):

def intersect(Circle(P, R), Rectangle(A, B, C, D)):
    S = Circle(P, R)
    return (pointInRectangle(P, Rectangle(A, B, C, D)) or
            intersectCircle(S, (A, B)) or
            intersectCircle(S, (B, C)) or
            intersectCircle(S, (C, D)) or
            intersectCircle(S, (D, A)))

ジオメトリを作成している場合は、おそらく上記の関数がライブラリに既に含まれています。それ以外の場合は、pointInRectangle()いくつかの方法で実装できます。ポリゴンメソッドの一般的なポイントはどれでも機能しますが、長方形の場合は、これが機能するかどうかを確認できます。

0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD

またintersectCircle()、実装も簡単です。1つの方法は、P、線が十分に近く、端点間にあるかどうかを確認し、それ以外の場合は端点を確認することです。

すばらしいことは、同じアイデアが長方形だけでなく、円と単純なポリゴンの交差部分でも機能することです。凸状である必要さえありません。


25
価値があることについては、私はこの答えが私のものより優れていると本当に思います。2つの主な理由:1:長方形が軸に平行でない場合、回転は必要ありません。2:コンセプトはすべてのポリゴンに簡単に拡張できます。
e.James、2011

2
@paniq:まあ、どちらも一定時間です。:-)しかし、はい、これは一般的な解決策としてより有用であり、あらゆる方向の長方形、そして実際にはあらゆる単純な多角形をカバーします。
ShreevatsaR 2011

7
長方形が完全に円の内側にあるが、円の中心が長方形の中にない場合はどうでしょうか?
ericsoco 2013年

2
@ericsoco:良い観察です。:-)私は、「長方形のエッジの1つが円と交差する」で「ディスクと交差する」と言ったはずだったと思います。これは、円の境界ではなく、円自体とポイントを共有することを意味しているためです。とにかく、上記の説明、「P [円の中心]から直線への垂線の足が十分に近く、端点間にあるかどうかを確認し、それ以外の場合は端点を確認する」は引き続き機能します。たとえば、端点は円の内側にあります(ディスク)。
ShreevatsaR 2013年

2
@ DexD.Hunter円の中心が長方形の外側にあるが、その一部が長方形の内側にある場合、必ず長方形のエッジの1つが円と交差します。
ShreevatsaR 2017年

289

ここに私がそれをする方法があります:

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));
}

仕組みは次のとおりです。

illusration

  1. 最初の線のペアは、円の中心と長方形の中心の間のxとyの差の絶対値を計算します。これにより、4つの象限が1つに折りたたまれ、計算を4回行う必要がなくなります。画像は、円の中心が存在する必要がある領域を示しています。単一の象限のみが表示されていることに注意してください。長方形は灰色の領域であり、赤い境界線は長方形の端からちょうど1半径離れた重要な領域の輪郭を描きます。交差が発生するには、円の中心がこの赤い境界線内にある必要があります。

  2. 2番目の線のペアは、円が長方形から(どちらの方向にも)十分に離れており、交差ができないという簡単なケースを排除します。これは画像の緑の領域に対応します。

  3. 3番目の線のペアは、円が長方形に十分近い(どちらの方向でも)交差が保証される簡単なケースを処理します。これは、画像のオレンジとグレーのセクションに対応しています。この手順は、ロジックを理解するために手順2の後に実行する必要があることに注意してください。

  4. 残りの線は、円が長方形の角と交差する場合がある難しいケースを計算します。解決するには、円の中心と角からの距離を計算し、距離が円の半径以下であることを確認します。この計算は、中心が赤の網掛け領域内にあるすべての円に対してfalseを返し、中心が白の網掛け領域内にあるすべての円に対してtrueを返します。


4
非常に素晴らしい!注:明らかにここでは、rect.x / yは長方形の右上隅にあります。また、代わりに半径の2乗と比較することで、高価な平方根を排除できます。
ルキ2009年

2
ああ、いけない。rect.x / yは長方形の左下にあります。私は書いたでしょう:circleDistance.x = abs(circle.x-(rect.x + rect.width / 2));
ルキ2009年

2
@タナー:いよいよ。Hooray、バックアップとOCD;)
e.James、2011

11
ただ明確にするために-この答えは、軸が揃った長方形にのみ適用されます。他の回答についてのコメントを読んでも明らかですが、この回答とコメントだけでは明らかではありません。(軸合わせの四角形のすばらしい答えです!)
ericsoco 2013年

3
すごい!読者がここで、rectの定義はrect.xであり、rect.yはrectの中心であることを知っていることが重要です。私の世界では、rectのxyはrectの上/左で、0,0は画面の上/左なので、次を使用しました:circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
erco

123

これは、実装が非常に簡単な(そして非常に高速な)別のソリューションです。球が完全に長方形に入ったときを含め、すべての交差をキャッチします。

// clamp(value, min, max) - limits value to the range min..max

// Find the closest point to the circle within the rectangle
float closestX = clamp(circle.X, rectangle.Left, rectangle.Right);
float closestY = clamp(circle.Y, rectangle.Top, rectangle.Bottom);

// Calculate the distance between the circle's center and this closest point
float distanceX = circle.X - closestX;
float distanceY = circle.Y - closestY;

// If the distance is less than the circle's radius, an intersection occurs
float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < (circle.Radius * circle.Radius);

適切な数学ライブラリを使用すると、3行または4行に短縮できます。


3
あなたはそこにバグがあります、あなたは上と下ではなく、左と右で近いYを検索します、そうでなければ素敵な解決策です。
マンベル2010年

8
私はこの答えが一番好きです。短く、理解しやすく、高速です。
John Kurlak、2011年

2
長方形がx軸とy軸に対して傾いている場合、ソリューションは失敗すると思います。
2013年

3
@Leoその場合に対応するためにこのアルゴリズムを変更することは難しくないと思います。原点が長方形の中心にあり、長方形がもう斜めではない座標変換を適用するだけです。円の中心のみに変換を適用する必要があります。
enobayram 2013

1
これは基本的に、migapro.com / circle - and - rotated - rectangle - collision - detectionにあるコードと同じですが、これもObjective-Cに移植しました。非常にうまく機能します。それは問題に対する素晴らしい解決策です。
PKCLsoft 2014年

10

球と四角形がIIFと交差
する円の中心と四角形の1つの頂点の 間の距離が球の半径よりも小さいか、
または
円の中心と四角形の1つのエッジの間の距離が球の半径よりも小さい( [ point-line distance ])
または
、円の中心が長方形の

点間距離の内側にあり ます。

P1 = [x1、y1]
P2 = [x2、y2]
距離= sqrt(abs(x1-x2)+ abs(y1-y2))

ポイントライン距離:

L1 = [x1、y1]、L2 = [x2、y2](ラインの2つのポイント、つまり頂点ポイント)
P1 = [px、py]ある点

距離d = abs((x2-x1)(y1-py)-(x1-px)(y2-y1))/距離(L1、L2)



四角形の内側の円の中心:分離軸にアプローチします:ポイントから長方形を分離する線への投影が存在する場合、それらは交差しません

長方形の側面に平行な線上に点を投影すると、それらが交差するかどうかを簡単に判断できます。4つすべての投影で交差しない場合、それら(点と長方形)は交差できません。

あなただけの内積が必要です(x = [x1、x2]、y = [y1、y2]、x * y = x1 * y1 + x2 * y2)

テストは次のようになります。

//長方形のエッジ:TL(左上)、TR(右上)、BL(左下)、BR(右下)
//テストするポイント:POI

分離= false
{{TL、TR}、{BL、BR}、{TL、BL}、{TR-BR}}のegdeの場合://エッジ
    D =エッジ[0]-エッジ[1]
    innerProd = D * POI
    Interval_min = min(D * edge [0]、D * edge [1])
    Interval_max = max(D * edge [0]、D * edge [1])
    そうでない場合(Interval_min≤innerProd≤Interval_max) 
           分離= true
           break //ループの終了 
    終われば
終わり
if(seperatedがtrue)    
      「交差なし」を返す
そうしないと 
      「交差点」を返す
終われば

これは、軸に沿った長方形を想定しておらず、凸集合間の交差をテストするために簡単に拡張できます。


1
ポイントツーポイントの距離は、腹筋ではなく正方形を使用するべきではありませんか?
トーマス

6

これが最速のソリューションです。

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;
}

実行順序に注意してください。幅と高さの半分は事前に計算されています。また、二乗は「手動」で行われ、いくつかのクロックサイクルを節約します。


3
最も高価なコードパスでの5つのテスト/比較が、なんら証明のない「最速のソリューション」であると主張することはできないと思います。
sam hocevar 2013年


1
この方法での私の経験では、衝突はほとんどの場合発生しません。したがって、テストはほとんどのコードが実行される前に終了します。
intrepidis

6

私が思いついた最も単純な解決策は、かなり単純です。

円に最も近い長方形の点を見つけ、距離を比較することで機能します。

いくつかの操作でこれらすべてを行うことができ、sqrt関数を回避することもできます。

public boolean intersects(float cx, float cy, float radius, float left, float top, float right, float bottom)
{
   float closestX = (cx < left ? left : (cx > right ? right : cx));
   float closestY = (cy < top ? top : (cy > bottom ? bottom : cy));
   float dx = closestX - cx;
   float dy = closestY - cy;

   return ( dx * dx + dy * dy ) <= radius * radius;
}

以上です!上記のソリューションは、x軸が下を向いた状態で、ワールドの左上を原点と仮定しています。

移動する円と長方形の間の衝突を処理するためのソリューションが必要な場合は、それははるかに複雑であり、私の別の答えでカバーされています。


円の半径が小さすぎて、中心が長方形の内側にある場合、交差の検出に失敗します。
Yoav

2
これを失敗させる実際の入力を提供できますか?円が内側にある場合、テストの左側は0.0です。半径がゼロでない限り、テストの右側の部分は0.0以上でなければなりません
ClickerMonkey

これは回転した長方形でも機能しますか?そうでない場合は、それについて私にヒントを教えてください.....
M Abdul Sami

4

実際、これははるかに簡単です。必要なのは2つだけです。

最初に、円の中心から長方形の各線までの4つの直交距離を見つける必要があります。その場合、いずれか3つが円の半径より大きい場合、円は長方形と交差しません。

次に、円の中心と長方形の中心の間の距離を見つける必要があります。距離が長方形の対角線の長さの半分より大きい場合、円は長方形の内側にありません。

幸運を!


3

これは、球体と非軸整列ボックスの間の衝突を解決するための私のCコードです。それは私自身のいくつかのライブラリルーチンに依存していますが、一部にとっては有用であると判明するかもしれません。私はそれをゲームで使用しており、完全に機能します。

float physicsProcessCollisionBetweenSelfAndActorRect(SPhysics *self, SPhysics *actor)
{
    float diff = 99999;

    SVector relative_position_of_circle = getDifference2DBetweenVectors(&self->worldPosition, &actor->worldPosition);
    rotateVector2DBy(&relative_position_of_circle, -actor->axis.angleZ); // This aligns the coord system so the rect becomes an AABB

    float x_clamped_within_rectangle = relative_position_of_circle.x;
    float y_clamped_within_rectangle = relative_position_of_circle.y;
    LIMIT(x_clamped_within_rectangle, actor->physicsRect.l, actor->physicsRect.r);
    LIMIT(y_clamped_within_rectangle, actor->physicsRect.b, actor->physicsRect.t);

    // Calculate the distance between the circle's center and this closest point
    float distance_to_nearest_edge_x = relative_position_of_circle.x - x_clamped_within_rectangle;
    float distance_to_nearest_edge_y = relative_position_of_circle.y - y_clamped_within_rectangle;

    // If the distance is less than the circle's radius, an intersection occurs
    float distance_sq_x = SQUARE(distance_to_nearest_edge_x);
    float distance_sq_y = SQUARE(distance_to_nearest_edge_y);
    float radius_sq = SQUARE(self->physicsRadius);
    if(distance_sq_x + distance_sq_y < radius_sq)   
    {
        float half_rect_w = (actor->physicsRect.r - actor->physicsRect.l) * 0.5f;
        float half_rect_h = (actor->physicsRect.t - actor->physicsRect.b) * 0.5f;

        CREATE_VECTOR(push_vector);         

        // If we're at one of the corners of this object, treat this as a circular/circular collision
        if(fabs(relative_position_of_circle.x) > half_rect_w && fabs(relative_position_of_circle.y) > half_rect_h)
        {
            SVector edges;
            if(relative_position_of_circle.x > 0) edges.x = half_rect_w; else edges.x = -half_rect_w;
            if(relative_position_of_circle.y > 0) edges.y = half_rect_h; else edges.y = -half_rect_h;   

            push_vector = relative_position_of_circle;
            moveVectorByInverseVector2D(&push_vector, &edges);

            // We now have the vector from the corner of the rect to the point.
            float delta_length = getVector2DMagnitude(&push_vector);
            float diff = self->physicsRadius - delta_length; // Find out how far away we are from our ideal distance

            // Normalise the vector
            push_vector.x /= delta_length;
            push_vector.y /= delta_length;
            scaleVector2DBy(&push_vector, diff); // Now multiply it by the difference
            push_vector.z = 0;
        }
        else // Nope - just bouncing against one of the edges
        {
            if(relative_position_of_circle.x > 0) // Ball is to the right
                push_vector.x = (half_rect_w + self->physicsRadius) - relative_position_of_circle.x;
            else
                push_vector.x = -((half_rect_w + self->physicsRadius) + relative_position_of_circle.x);

            if(relative_position_of_circle.y > 0) // Ball is above
                push_vector.y = (half_rect_h + self->physicsRadius) - relative_position_of_circle.y;
            else
                push_vector.y = -((half_rect_h + self->physicsRadius) + relative_position_of_circle.y);

            if(fabs(push_vector.x) < fabs(push_vector.y))
                push_vector.y = 0;
            else
                push_vector.x = 0;
        }

        diff = 0; // Cheat, since we don't do anything with the value anyway
        rotateVector2DBy(&push_vector, actor->axis.angleZ);
        SVector *from = &self->worldPosition;       
        moveVectorBy2D(from, push_vector.x, push_vector.y);
    }   
    return diff;
}

2

視覚化するには、キーボードのテンキーを使用します。キー「5」が長方形を表す場合、1〜9のすべてのキーは、長方形を構成する線で区切られた9象限のスペースを表します(5が内側)。

1)円の中心が第4象限(つまり、長方形の内側)にある場合、2つの形状が交差します。

それが邪魔にならない場合、次の2つのケースが考えられます。a)円が長方形の2つ以上の隣接するエッジと交差する。b)円は長方形の1つのエッジと交差します。

最初のケースは単純です。円が長方形の隣接する2つのエッジと交差する場合、その2つのエッジを接続するコーナーが含まれている必要があります。(それ、またはその中心は、すでにカバーした象限5にあります。また、円が長方形の2つの対向するエッジのみと交差する場合もカバーされることに注意してください。)

2)長方形のコーナーA、B、C、Dのいずれかが円の内側にある場合、2つの形状が交差します。

2番目のケースはトリッキーです。円の中心が象限2、4、6、8のいずれかにある場合にのみ発生する可能性があることに注意してください(実際、中心が象限1、3、7、8のいずれかにある場合、対応するコーナーがそれに最も近いポイントになります。)

これで、円の中心が「エッジ」象限の1つにあり、対応するエッジとのみ交差する場合があります。次に、円の中心に最も近いエッジ上の点が円の内側になければなりません。

3)各線AB、BC、CD、DAについて、円の中心Pを通る垂直線p(AB、P)、p(BC、P)、p(CD、P)、p(DA、P)を作成します。各垂直線。元のエッジとの交差が円の内側にある場合、2つの形状が交差します。

この最後のステップのショートカットがあります。円の中心が象限8にあり、エッジABが上端である場合、交点は、AとBのy座標と中心Pのx座標になります。

4つの線の交点を作成し、それらが対応するエッジ上にあるかどうかを確認するか、どの象限Pが存在するかを確認して、対応する交点を確認できます。どちらも同じブール式に単純化する必要があります。上記のステップ2でPが「コーナー」象限の1つにあることを除外しなかったことに注意してください。交差点を探しただけです。

編集:結局のところ、私は#2が上記の#3のサブケースであるという単純な事実を見過ごしてきました。結局のところ、コーナーもエッジ上のポイントです。優れた説明については、以下の@ShreevatsaRの回答を参照してください。その間、迅速で冗長なチェックが必要でない限り、上記の2は忘れてください。


2

この関数は、CircleとRectangleの衝突(交差)を検出します。彼の答えはe.Jamesメソッドのように機能しますが、これは長方形のすべての角度(右上隅だけでなく)の衝突を検出します。

注意:

aRect.origin.xaRect.origin.yは、長方形の左下角の座標です!

aCircle.xaCircle.yは、サークルセンターの座標です。

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;
}

1

私は必要がなければ高価なピタゴラスを回避する方法を持っています-すなわち。長方形と円の境界ボックスが交差しない場合。

そして、それは非ユークリッドにも機能します:

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;
  }
}
  • minLat、maxLatは、minY、maxYに置き換えることができます。minLon、maxLonも同じです。minX、maxXに置き換えます。
  • normDistは、完全な距離の計算よりも少し速い方法ではありません。ユークリッド空間での平方根のない(または半正矢のための他の多くのものなし)例:dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLon。もちろん、そのnormDistメソッドを使用する場合normedDist = dist*dist;は、円のを作成する必要があります

GraphHopperプロジェクトの完全なBBoxおよびCircleコードを参照してください。


1

シェイプを使った作業用のクラスを作成しました。

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;
  } 
}

1

以下は、修正されたコードが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)));
}

バッサムアルギリ


1

このための高速な1行のテストを次に示します。

if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
  // They intersect.
}

これは、軸に揃えられた場合でrect_halvesあり、は長方形の中央からコーナーを指す正のベクトルです。内部の式は、長方形の最も近い点length()からのデルタベクトルcenterです。これはどの次元でも機能します。


1
  • 最初に、長方形と円に接する正方形が重なっている(簡単に)かどうかを確認します。それらが重ならない場合、それらは衝突しません。
  • 円の中心が長方形の中にあるかどうかを確認します(簡単)。中にあると衝突します。
  • 長方形の辺から円の中心までの最小二乗距離を計算します(少し難しい)。半径の2乗よりも小さい場合は衝突し、そうでない場合は衝突しません。

次の理由により、効率的です。

  • まず、最も一般的なシナリオを安価なアルゴリズムでチェックし、衝突しないことが確実な場合は終了します。
  • 次に、安価なアルゴリズムで次の最も一般的なシナリオをチェックし(平方根を計算せず、2乗値を使用します)、衝突が確実な場合は終了します。
  • 次に、より高価なアルゴリズムを実行して、四角形の境界との衝突をチェックします。

1

私のために働いた(長方形の角度が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;
}

うーん...私はこれに賛成票を投じましたが、適切にテストされたので、たとえば、コーナーでは機能しないと思います。2つの長方形に対して機能します。
Dan Zen

1

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);

これは、最大3回ではなくrect.w / 2rect.h / 21回減算します。


0

SQLを使用して地理座標での円/長方形の衝突を計算する必要がある場合、
これはe.James推奨アルゴリズムの oracle 11での私の実装です

入力では、円座標、km単位の円半径、および長方形の2つの頂点座標が必要です。

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;    

0

動作します。1週間前にこれを理解し、今すぐテストしました。

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)));

Circle-Squareで機能する可能性がありますが、問題はCircle-Rectangleについてです。
martineau

0
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

-2

四角形の4つのエッジがあると仮定して、エッジから円の中心までの距離を確認します。半径よりも小さい場合、形状は交差しています。

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

小さな円が大きな長方形で完全に囲まれている場合はどうですか?確かにそれは交差点であり、この答えのテストに失敗するでしょう。
ケン・ポール・

ああ、そうは思いませんでした。sqrt((rectangleRight.x / 2-circleCenter.x)^ 2 +(rectangleBottom.y / 2-circleCenter.y)^ 2)<radiusが交差する場合のように、さらにチェックを追加することができます。でも頭のてっぺんからは思いつく限りです。
ForYourOwnGood 2008

それらは、任意のエッジ上の任意の[単一の]ポイントで交差できます。エッジと中心の距離も調べる必要があります。(ああ、あなたのコーナーを「コーナー」と呼んでください:)
aib '31 / 12/31

これは、コーナーが円の内側にあるときのみ検出するように見えます。
スターク

-2

長方形が円と交差する場合、長方形の1つ以上のコーナーポイントが円の内側にある必要があります。長方形の4つの点がA、B、C、Dであるとします。それらの少なくとも1つが円と交差する必要があります。したがって、1つの点から円の中心までの距離が円の半径よりも小さい場合は、円と交差する必要があります。距離を求めるには、ピタゴラスの定理を使用できます。

H^2 = A^2 + B^2

この手法にはいくつかの制限があります。しかし、ゲーム開発者にとってはうまく機能します。特に衝突検出

これは、Arvoのアルゴリズムの良いアップデートです。


長方形の辺が円の半径よりも大きい場合、この答えは信じられないほど間違っています。
ポールK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.