各Boxオブジェクトにはプロパティx、y、width、heightがあり、中心は原点にあり、オブジェクトも境界ボックスも回転しないと仮定します。
各Boxオブジェクトにはプロパティx、y、width、heightがあり、中心は原点にあり、オブジェクトも境界ボックスも回転しないと仮定します。
回答:
(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バウンディングボックスの交差がパフォーマンスのボトルネックになることはまれです。
abs(5 - 10) * 2 < (10 + 4)
=>になり10 < 14
ます。topleft-corner-and-sizeで動作させるには、簡単な調整が必要です。
これは、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-お互いかどうか
オブジェクトに沿った境界ボックスが必要な場合は、メタネットによる分離軸定理でこのチュートリアルを試してください:http : //www.metanetsoftware.com/technique/tutorialA.html
SATは最速のソリューションではありませんが、比較的簡単です。オブジェクトを分離する単一の線(または3Dの場合は平面)を見つけようとしています。この行が存在する場合、ボックスのいずれかの端に並ぶことが保証されているため、すべての端をテストして、ボックスを分離するかどうかを確認します。
これは、x / y軸のみに制限することで、軸に合わせたボックスでも機能します。
上記のDoBoxesIntersectは、優れたペアワイズソリューションです。ただし、多数のボックスがある場合は、O(N ^ 2)の問題がまだあり、その上でKajが言及しているような何かを行う必要があるかもしれません。(3D衝突検出の文献では、これはブロードフェーズアルゴリズムとナローフェーズアルゴリズムの両方を持っていることで知られています。可能な限りすべてのオーバーラップのペアを見つけるために本当に速いことを行います。ペアは実際のペアです。)
以前に使用したブロードフェーズアルゴリズムは、「スイープアンドプルーン」です。2Dの場合、各ボックスの開始と終了の2つのソートされたリストを維持します。ボックスの移動がフレームごとの>>ボックススケールでない限り、これらのリストの順序はあまり変わらないため、バブルまたは挿入ソートを使用してそれを維持できます。「Real-Time Rendering」という本には、実行可能な最適化に関する優れた記事がありますが、広いフェーズではO(N + K)時間に要約されます。どのボックスのペアがフレーム間で交差しているかを追跡するためにN ^ 2ブール値の余裕がある場合のパフォーマンス。その後、全体でO(N + K ^ 2)時間になります。これは、多数のボックスがあるが重複が少ない場合は<< O(N ^ 2)です。
ここでは非常に単純な問題のための多くの数学、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);
}
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);
}
解決しようとする問題によっては、オブジェクトを移動しながら追跡することをお勧めします。つまり、ソートされたxの開始位置と終了位置のリストと、開始位置と終了y位置のリストを保持します。オーバーラップチェックを大量に行う必要があるため、最適化する必要がある場合は、これを使用して、左に近い人を終了している人をすぐに調べることができます。すぐに。同じことが上、下、右にも当てはまります。
簿記にはもちろん時間がかかるため、移動するオブジェクトはほとんどないが、重複チェックが多い状況に適しています。
別のオプションは、おおよその位置に基づいてオブジェクトをバケットする空間ハッシュです(サイズは複数のバケットにemを配置する場合があります)が、多くのオブジェクトがあり、簿記コストのために反復ごとに移動するオブジェクトが比較的少ない場合のみです。
基本的に(n * n)/ 2を回避するもの(オブジェクトaをbに対してチェックする場合、bに対して明らかにチェックする必要はありません)は、バウンディングボックスチェックを最適化する以上に役立ちます。バウンディングボックスチェックがボトルネックである場合、問題の代替ソリューションを検討することを真剣に勧めます。
中心間の距離は角の間の距離と同じではありません(たとえば、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));
これは、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
)に近い入力値では失敗します。
最速の方法は、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などがあります。