2Dバウンディングボックスの交差を処理す​​る最も速い方法は何ですか?


62

各Boxオブジェクトにはプロパティx、y、width、heightがあり、中心は原点にあり、オブジェクトも境界ボックスも回転しないと仮定します。


これらの軸またはオブジェクトに合わせた境界ボックスはありますか?
tenpn

3
この質問をするときは、将来的に他のタイプの交差点を必ずテストする必要があります;)。したがって、オブジェクト/オブジェクトの交差に関するリストを提案します。この表は、静的な状況と動的な状況のすべての一般的なオブジェクトタイプ(ボックス、球、三角形、サイクリンダー、コーンなど)の共通部分を提供します。
デイブO.

2
質問をバウンディング四角形に言い換えてください。私の視点から見ると、3Dオブジェクトを意味します。
デイブO.

回答:


55

(C風の擬似コード-必要に応じて言語の最適化を調整します)

bool DoBoxesIntersect(Box a, Box b) {
  return (abs(a.x - b.x) * 2 < (a.width + b.width)) &&
         (abs(a.y - b.y) * 2 < (a.height + b.height));
}

英語:各軸で、ボックスの中心が交差するのに十分近いかどうかを確認します。両方の軸で交差する場合、ボックスは交差します。そうでない場合は、そうではありません。

エッジタッチを交差としてカウントする場合は、<を<=に変更できます。特定のエッジタッチのみの数式が必要な場合は、==を使用できません。これは、エッジが触れているかどうかではなく、コーナーが触れているかどうかを示します。論理的にに相当することをしたいと思うでしょうreturn DoBoxesIntersectOrTouch(a, b) && !DoBoxesIntersect(a, b)

全幅と全高に加えて(またはその代わりに)半幅と半高を保存することで、わずかではあるが大幅な速度の向上が得られることに注意してください。一方、2Dバウンディングボックスの交差がパフォーマンスのボトルネックになることはまれです。


9
これは、ボックスが軸に沿っていることを前提としています。
tenpn

1
Absは特に遅くなるべきではありません-少なくとも条件付きより遅くはありません、そして、absなしでそれを行う唯一の方法(私が知っている)は余分な条件付きです。
ZorbaTHut

4
はい、軸に沿ったボックスを想定しています。説明した構造には回転を示す方法がありませんが、安全だと感じました。
ZorbaTHut

3
ここでのActionScript(主に整数CALC)でcalulationsをスピードアップするためのいくつかの良いヒントがあります:lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math私はこれを掲示しています、それはまた、より速く含まれているため、 Math.abs()に置き換わるもので、実際にActionscriptの速度を低下させる傾向があります(もちろん、パフォーマンスが重要なものと言えば)。
bummzack

2
左端ではなく中央に原点があることを忘れています。0から10まで実行されるボックスには実際には「x = 5」があり、8から12まで実行されるボックスには「x = 10」があります。最終的にabs(5 - 10) * 2 < (10 + 4)=>になり10 < 14ます。topleft-corner-and-sizeで動作させるには、簡単な調整が必要です。
-ZorbaTHut

37

これは、X軸とY軸に揃えられた2つの長方形に対して機能します。
各長方形には、
「左」、その左側のx座標、
「上部」、その上部のy座標、
「右」、その右側のx座標、
「下部」、y座標のプロパティがあります。その下側、

function IntersectRect(r1:Rectangle, r2:Rectangle):Boolean {
    return !(r2.left > r1.right
        || r2.right < r1.left
        || r2.top > r1.bottom
        || r2.bottom < r1.top);
}

これは、+ y軸がを向き、+ x軸が右を向く座標系(つまり、一般的な画面/ピクセル座標)向けに設計されていることに注意してください。これを、+ yが上向きの典型的なデカルトシステムに適応させるには、垂直軸に沿った比較が逆になります。例:

return !(r2.left > r1.right
    || r2.right < r1.left
    || r2.top < r1.bottom
    || r2.bottom > r1.top);

アイデアは、長方形が重ならないすべての可能な条件をキャプチャし、それら重なるかどうかを確認するために回答を否定することです。軸の方向に関係なく、次の場合に2つの長方形が重ならないことは簡単にわかります。

  • r2の左端はr1の右端よりもさらに右

     ________     ________
    |        |   |        |
    |   r1   |   |   r2   |
    |        |   |        |
    |________|   |________|
    
  • または、r2の右端がr1の左端よりさらに左にある

     ________     ________
    |        |   |        |
    |   r2   |   |   r1   |
    |        |   |        |
    |________|   |________|
    
  • または、r2の上端がr1の下端より下にある

     ________ 
    |        |
    |   r1   |
    |        |
    |________|
     ________ 
    |        |
    |   r2   |
    |        |
    |________|
    
  • または、r2の下端がr1の上端より上にある

     ________ 
    |        |
    |   r2   |
    |        |
    |________|
     ________ 
    |        |
    |   r1   |
    |        |
    |________|
    

元の関数-およびそれが機能する理由の代替説明-は、次の場所にあります。http//tekpool.wordpress.com/2006/10/11/rectangle-intersection-determine-if-two-given-rectangles-intersect-お互いかどうか


1
驚くほど直感的で、答えを見つけるのが難しいとき、反対の質問への答えを見つけることはあなたを助けるかもしれないことをもう一度示します。ありがとう!
ロードウェイク14年

1
(画像のように)y軸が下を向いていることに注意してください。それ以外の場合、不等式r2.top> r1.bottomおよびr2.bottom <r1.topを逆にする必要があります。
user1443778

@ user1443778良いキャッチ!先に進み、このアルゴリズムの背後にあるロジックを座標系に依存しない方法で大まかに説明しました。
ポンカドゥードル

11

オブジェクトに沿った境界ボックスが必要な場合は、メタネットによる分離軸定理でこのチュートリアルを試してください:http : //www.metanetsoftware.com/technique/tutorialA.html

SATは最速のソリューションではありませんが、比較的簡単です。オブジェクトを分離する単一の線(または3Dの場合は平面)を見つけようとしています。この行が存在する場合、ボックスのいずれかの端に並ぶことが保証されているため、すべての端をテストして、ボックスを分離するかどうかを確認します。

これは、x / y軸のみに制限することで、軸に合わせたボックスでも機能します。


私はローテーションを考えていませんでしたが、それは興味深いリンクです。
イアン

5

上記のDoBoxesIntersectは、優れたペアワイズソリューションです。ただし、多数のボックスがある場合は、O(N ^ 2)の問題がまだあり、その上でKajが言及しているような何かを行う必要があるかもしれません。(3D衝突検出の文献では、これはブロードフェーズアルゴリズムとナローフェーズアルゴリズムの両方を持っていることで知られています。可能な限りすべてのオーバーラップのペアを見つけるために本当に速いことを行います。ペアは実際のペアです。)

以前に使用したブロードフェーズアルゴリズムは、「スイープアンドプルーン」です。2Dの場合、各ボックスの開始と終了の2つのソートされたリストを維持します。ボックスの移動がフレームごとの>>ボックススケールでない限り、これらのリストの順序はあまり変わらないため、バブルまたは挿入ソートを使用してそれを維持できます。「Real-Time Rendering」という本には、実行可能な最適化に関する優れた記事がありますが、広いフェーズではO(N + K)時間に要約されます。どのボックスのペアがフレーム間で交差しているかを追跡するためにN ^ 2ブール値の余裕がある場合のパフォーマンス。その後、全体でO(N + K ^ 2)時間になります。これは、多数のボックスがあるが重複が少ない場合は<< O(N ^ 2)です。


5

ここでは非常に単純な問題のための多くの数学、rect、top、left、bottom、rightに対して4つのポイントが決定されていると仮定します...

2つの長方形が衝突するかどうかを判断する場合、衝突を防ぐ可能性のあるすべての極端なものを見るだけでよく、これらのどれも満たされない場合、2つの長方形が衝突しなければなりません、境界衝突を含めたい場合は、単に>と<を置き換えます適切な> =および= <

struct aRect{
  float top;
  float left;
  float bottom;
  float right;
};

bool rectCollision(rect a, rect b)
{
  return ! ( b.left > a.right || b.right < a.left || b.top < a.bottom || b.bottom > a.top);
}

正直に言って、なぜこれがトップ投票の回答ではないのかわかりません。シンプルで正確、効率的です。
3Dave

3

ZorbaTHutの回答の代替バージョン:

bool DoBoxesIntersect(Box a, Box b) {
     return (abs(a.x - b.x) < (a.width + b.width) / 2) &&
     (abs(a.y - b.y) < (a.height + b.height) / 2);
}

実際、その算術はどちらの方法でもうまく機能します。<のどちらの側に対しても算術演算を行うことができ、それは変更されません(負の乗算は、小なりを変更する必要があることを意味します)。その例では、ボックスは衝突しないはずです。ボックスAの中心が1の場合、-4から6の範囲になります。ボックスbの中心は10にあり、7.5から12.5の範囲にありますが、衝突はありません。しかし、より速く、ほとんどのチェックがとにかくfalseを返しますから、短絡を実装言語で実行され、短絡はとして後にカットすることができます
DELETER

はい、私は今朝目が覚めたときにこれを実現しました。クリスは彼の<>ミックスアップで私を弾きました。
イアン

1
2つの問題:最初に、除算は乗算よりもかなり遅くなる傾向があります。次に、関連する値が整数の場合、整数の切り捨ての問題が発生する可能性があります(ax = 0、bx = 9、a.width = 9、b.width = 10:abs(0-9)<(9 + 10) / 2、9 <19 / 2、9 <9、ボックスが
完全に

2

解決しようとする問題によっては、オブジェクトを移動しながら追跡することをお勧めします。つまり、ソートされたxの開始位置と終了位置のリストと、開始位置と終了y位置のリストを保持します。オーバーラップチェックを大量に行う必要があるため、最適化する必要がある場合は、これを使用して、左に近い人を終了している人をすぐに調べることができます。すぐに。同じことが上、下、右にも当てはまります。
簿記にはもちろん時間がかかるため、移動するオブジェクトはほとんどないが、重複チェックが多い状況に適しています。
別のオプションは、おおよその位置に基づいてオブジェクトをバケットする空間ハッシュです(サイズは複数のバケットにemを配置する場合があります)が、多くのオブジェクトがあり、簿記コストのために反復ごとに移動するオブジェクトが比較的少ない場合のみです。
基本的に(n * n)/ 2を回避するもの(オブジェクトaをbに対してチェックする場合、bに対して明らかにチェックする必要はありません)は、バウンディングボックスチェックを最適化する以上に役立ちます。バウンディングボックスチェックがボトルネックである場合、問題の代替ソリューションを検討することを真剣に勧めます。


2

中心間の距離は角の間の距離と同じではありません(たとえば、1つのボックスが他のボックスの内側にある場合)。

中心間の距離(たとえば、x):abs(x1+1/2*w1 - x2+1/2*w2)または1/2 * abs(2*(x1-x2)+(w1-w2)

最小距離は1/2 w1 + 1/2 w2 or 1/2 (w1+w2)です。半分はキャンセルします。

return 
ABS(2*(x1 - x2) + (w1-w2) ) < (w1+w2)) &&
ABS(2*(y1 - y2) + (h1-h2) ) < (h1+h2));

1
そこにある「return」ステートメントとは何ですか?
doppelgreener

1

これは、2の補数アーキテクチャを想定した Javaでの実装です。2の補数を使用していない場合は、代わりに標準のMath.abs関数呼び出しを使用します。

boolean intersects(IntAxisAlignedBox left, IntAxisAlignedBox right) {
    return
        (
            lineDeltaFactor(left.min.x, left.max.x, right.min.x, right.max.x) |
            lineDeltaFactor(left.min.y, left.max.y, right.min.y, right.max.y) |
            lineDeltaFactor(left.min.z, left.max.z, right.min.z, right.max.z)
        ) == 0;
}

int lineDeltaFactor(int leftMin, int leftMax, int rightMin, int rightMax) {
    final int
            leftWidth = leftMax - leftMin,
            rightWidth = rightMax - rightMin,

            leftMid = leftMin + ((leftMax - leftMin) >> 1),
            rightMid = rightMin + ((rightMax - rightMin) >> 1);

    return (abs(leftMid - rightMid) << 1) / (leftWidth + rightWidth + 1);
}

int abs(int value) {
    final int mask = value >> (Integer.SIZE - 1);

    value ^= mask;
    value += mask & 1;
    return value;
}

半分まともなコンパイラ/ LLVMインラインがこれらの機能を拡張すると仮定して、高価なスタックジャグリングとvテーブルのルックアップを回避します。これ、32ビットの極値(Integer.MAX_VALUEおよびInteger.MIN_VALUE)に近い入力値では失敗します。


0

最速の方法は、4つの値すべてを単一のベクトルレジスタに結合することです。

次の値を持つベクトルにボックスを保存します[ min.x, min.y, -max.x, -max.y ]。このようなボックスを保存する場合、交差テストには3つのCPU命令しかかかりません。

_mm_shuffle_ps 2番目のボックスを並べ替えて、最小と最大の半分を反転します。

_mm_xor_ps_mm_set1_ps(-0.0f)2番目のボックスで4つの値すべての符号を反転するマジックナンバーを使用します。

_mm_cmple_ps 4つの値すべてを互いに比較し、次の2つのレジスタを比較します。

[ a.min.x, a.min.y, -a.max.x, -a.max.y ] < [ b.max.x, b.max.y, -b.min.x, -b.min.y ]

最後に、必要に応じ_mm_movemask_psて、ベクトルユニットからスカラーレジスタに結果を取得します。値0は、ボックスが交差することを意味します。または、2つ以上のボックスがある場合、これは不要です。値をベクトルレジスタに残し、ビットごとの演算を使用して複数のボックスからの結果を結合します。

言語もプラットフォームも指定していませんが、このような、または非常に類似したSIMDのサポートは、すべてのプラットフォームと言語で利用できます。モバイルでは、ARMにはNEON SIMDと非常に類似したものがあります。.NETにはSystem.Runtime.Intrinsics名前空間にVector128などがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.