2つの長方形が交差しているかどうかを検出するアルゴリズムを探しています(1つは任意の角度で、もう1つは垂直/水平線のみで)。
一方のコーナーがもう一方のALMOSTにあるかどうかのテストは機能します。長方形が十字のような形状を形成する場合、失敗します。
垂直線の特別なケースを必要とする線の勾配を使用しないことは良い考えのようです。
2つの長方形が交差しているかどうかを検出するアルゴリズムを探しています(1つは任意の角度で、もう1つは垂直/水平線のみで)。
一方のコーナーがもう一方のALMOSTにあるかどうかのテストは機能します。長方形が十字のような形状を形成する場合、失敗します。
垂直線の特別なケースを必要とする線の勾配を使用しないことは良い考えのようです。
回答:
標準的な方法は、分離軸テストを実行することです(その上でGoogle検索を実行します)。
要するに:
面白いのは、2つの長方形のすべてのエッジをチェックするだけで十分なことです。長方形が重ならない場合、エッジの1つが分離軸になります。
2Dでは、スロープを使用せずにこれを行うことができます。エッジは、2つの頂点間の差として簡単に定義されます。たとえば、
edge = v(n) - v(n-1)
90度回転させると、これに垂直になります。2Dでは、これは次のように簡単です。
rotated.x = -unrotated.y
rotated.y = unrotated.x
したがって、三角法や勾配は含まれていません。ベクトルをユニット長に正規化する必要もありません。
ポイントがラインの片側または反対側にあるかどうかをテストする場合は、ドット積を使用できます。サインはあなたがどちら側にいるのかを教えてくれます:
// rotated: your rotated edge
// v(n-1) any point from the edge.
// testpoint: the point you want to find out which side it's on.
side = sign (rotated.x * (testpoint.x - v(n-1).x) +
rotated.y * (testpoint.y - v(n-1).y);
次に、長方形Bのエッジに対して長方形Aのすべての点をテストします。逆も同様です。分離エッジが見つかった場合、オブジェクトは交差しません(Bの他のすべてのポイントがテスト対象のエッジの反対側にある場合-下の図を参照)。分離エッジが見つからない場合は、長方形が交差しているか、1つの長方形が他の長方形に含まれています。
このテストは、凸状のポリゴンで機能します。
修正:分離エッジを特定するには、1つの長方形のすべてのポイントを他の各エッジに対してテストするだけでは不十分です。Aのすべての点がEの同じ半平面にあるため、候補エッジE(下)は分離エッジとして識別されます。ただし、Bの頂点Vb1およびVb2は分離エッジではありません。その半平面にもあります。それがそうでなかったならば、それは分離エッジであったにちがいない http://www.iassess.com/collision.png
基本的に次の図を見てください。
2つのボックスが衝突すると、ラインAとBが重なります。
これはX軸とY軸の両方で実行する必要があり、四角形が衝突するためには両方が重なり合う必要があることに注意してください。
質問に答えるgamasutra.comの良い記事があります(写真は記事からのものです)。5年前に同様のアルゴリズムを実行しましたが、後でここに投稿するには、コードスニペットを見つける必要があります
修正:分離軸定理は、分離軸が存在する場合(つまり、図に示されている投影が重複しない場合)、2つの凸形状は重複しないと述べています。したがって、「分離軸が存在します」=>「オーバーラップなし」。これは二重の意味合いではないので、逆を結論付けることはできません。
m_pGladiatorの答えは正しいです。 分離軸テストは、長方形の重なりを検出する最も簡単で標準的な方法です。投影間隔が重ならない線を分離軸と呼びます。Nils Pipenbrinckの解法は一般的すぎます。ドット積を使用して、1つの形状が完全にもう一方の端の片側にあるかどうかをチェックします。このソリューションは、実際にはnエッジの凸多角形を誘発する可能性があります。ただし、2つの長方形には最適化されていません。
m_pGladiatorの答えの重要な点は、両方の軸(xとy)で2つの長方形の投影をチェックする必要があるということです。2つの投影が重なり合っている場合、これらの2つの長方形が重なり合っていると言えます。したがって、m_pGladiatorの回答に対する上記のコメントは間違っています。
単純な状況では、2つの長方形が回転されない場合は、構造を持つ長方形を提示します。
struct Rect {
x, // the center in x axis
y, // the center in y axis
width,
height
}
長方形A、BにrectA、rectBという名前を付けます。
if Math.abs(rectA.x - rectB.x) < (Math.abs(rectA.width + rectB.width) / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(rectA.height + rectB.height) / 2))
then
// A and B collide
end if
2つの長方形のいずれかが回転している場合、x軸とy軸へのそれらの投影を決定するためにいくつかの努力が必要になる場合があります。構造体RotatedRectを次のように定義します。
struct RotatedRect : Rect {
double angle; // the rotating angle oriented to its center
}
違いは、幅が少し異なる点です。rectAの場合はwidthA '、rectBの場合はMath.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle)
widthB'です。Math.sqrt(rectB.width*rectB.width + rectB.height*rectB.height) * Math.cos(rectB.angle)
if Math.abs(rectA.x - rectB.x) < (Math.abs(widthA' + widthB') / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(heightA' + heightB') / 2))
then
// A and B collide
end if
GDC(ゲーム開発会議2007)PPTを参照できます。www.realtimecollisiondetection.net/ pubs / GDC07_Ericson_Physics_Tutorial_SAT.ppt
Cocoaでは、selectedArea rectが回転したNSViewのフレームrectと交差するかどうかを簡単に検出できます。ポリゴンを計算する必要さえありません、法線など。これらのメソッドをNSViewサブクラスに追加するだけです。たとえば、ユーザーがNSViewのスーパービュー上の領域を選択してから、selectedArea rectを渡してメソッドDoesThisRectSelectMeを呼び出します。API convertRect:がその仕事をします。NSViewをクリックして選択すると、同じトリックが機能します。その場合は、以下のように単にhitTestメソッドをオーバーライドします。API convertPoint:その仕事をします;-)
- (BOOL)DoesThisRectSelectMe:(NSRect)selectedArea
{
NSRect localArea = [self convertRect:selectedArea fromView:self.superview];
return NSIntersectsRect(localArea, self.bounds);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
NSPoint localPoint = [self convertPoint:aPoint fromView:self.superview];
return NSPointInRect(localPoint, self.bounds) ? self : nil;
}
1つの長方形からの線のいずれかが他からの線のいずれかと交差するかどうかを確認します。単純な線分の交差は簡単にコーディングできます。
さらに速度が必要な場合は、ラインセグメントの交差(スイープライン)の高度なアルゴリズムがあります。http://en.wikipedia.org/wiki/Line_segment_intersectionを参照してください
1つの解決策は、No Fit Polygonと呼ばれるものを使用することです。このポリゴンは、2つのポリゴンから(概念的には一方を他方の周りにスライドさせることによって)計算され、相対オフセットが与えられたときにポリゴンがオーバーラップする領域を定義します。このNFPを取得したら、2つのポリゴンの相対オフセットで指定されたポイントで包含テストを実行するだけです。この包含テストは迅速かつ簡単ですが、最初にNFPを作成する必要があります。
Webで「適合しないポリゴン」を検索して、凸面ポリゴンのアルゴリズムを見つけることができるかどうかを確認します(凹面ポリゴンがある場合は、さらに複雑になります)。何も見つからない場合は、ハワードドットJドットでGmailドットコムにメールしてください。
考えられるすべてのケースに対応できると思います。次のテストを行います。
上記の2つのテストでfalseが返された場合、これらの2つの長方形は重なりません。
Javaを使用している場合、Shapeインターフェースのすべての実装には、長方形をとる交差メソッドがあります。
さて、ブルートフォースメソッドは、水平方向の四角形のエッジをウォークし、エッジに沿った各ポイントをチェックして、それが他の四角形の上にあるかどうかを確認します。
数学的な答えは、両方の長方形の各エッジを表す方程式を作成することです。これで、長方形Aの4本の線のいずれかが長方形Bの線のいずれかと交差するかどうかを簡単に見つけることができます。これは、単純な(高速)線形方程式ソルバーです。
-アダム
角度を付けた長方形の各辺と、軸合わせされた長方形の各辺との交点を見つけることができます。これを行うには、各辺が置かれている無限直線の方程式(つまり、基本的にv1 + t(v2-v1)およびv'1 + t '(v'2-v'1))を見つけ、これらの2つの方程式が等しい場合(それらが平行である場合は、それをテストできます)、tを解いて線を満たし、その点が2つの頂点間の線分にあるかどうか、つまり0 <= tが真であるかどうかをテストします<= 1および0 <= t '<= 1。
ただし、これは、1つの四角形が他の四角形を完全に覆っている場合には適用されません。どちらかの四角形の4つの点すべてが他の四角形の内側にあるかどうかをテストすることでカバーできます。
これは、この問題の3Dバージョンに対して私が行うことです。
2つの長方形を方程式P1およびP2で記述される平面としてモデル化し、次にP1 = P2を記述して、平面が平行(交差なし)または同じ平面内にある場合は存在しない交差線方程式から導きます。その場合、0 = 0になります。その場合は、2D長方形交差アルゴリズムを使用する必要があります。
次に、両方の長方形の平面にあるその線が両方の長方形を通過するかどうかを確認します。もしそうなら、あなたは2つの長方形の交差を持っています、そうでなければあなたはそうではありません(またはそうすべきではありません、私は私の頭のコーナーケースを逃したかもしれません)。
線が同じ平面の長方形を通過するかどうかを確認するには、線と長方形の辺の2点の交点を見つけ(線の方程式を使用してそれらをモデル化)、交点がにあることを確認します範囲。
それは数学的な説明ですが、残念ながら私には上記を行うコードがありません。
分離軸テストを使用するよりもわずかに速いテストを実行する別の方法は、いずれかの長方形(任意に選択された)の各頂点で巻き数アルゴリズム(象限でのみ- 恐ろしく遅い角度合計ではない)を使用することです。頂点のいずれかにゼロ以外の曲がり番号がある場合、2つの長方形が重なります。
このアルゴリズムは、分離軸テストよりも多少時間がかかりますが、エッジが2つの象限を横切る場合に半平面テストしか必要ないため、高速です(分離軸法を使用した最大32のテストとは対照的)。
このアルゴリズムには、任意のポリゴン(凸面または凹面)のオーバーラップをテストするために使用できるという利点があります。私の知る限り、アルゴリズムは2D空間でのみ機能します。
他に何か不足しているのはなぜこれをそんなに複雑にするのですか?
(x1、y1)と(X1、Y1)が四角形の角である場合、交差を見つけるには、次のようにします。
xIntersect = false;
yIntersect = false;
if (!(Math.min(x1, x2, x3, x4) > Math.max(X1, X2, X3, X4) || Math.max(x1, x2, x3, x4) < Math.min(X1, X2, X3, X4))) xIntersect = true;
if (!(Math.min(y1, y2, y3, y4) > Math.max(Y1, Y2, Y3, Y4) || Math.max(y1, y2, y3, y4) < Math.min(Y1, Y2, Y3, Y4))) yIntersect = true;
if (xIntersect && yIntersect) {alert("Intersect");}
私はそれを次のように実装しました:
bool rectCollision(const CGRect &boundsA, const Matrix3x3 &mB, const CGRect &boundsB)
{
float Axmin = boundsA.origin.x;
float Axmax = Axmin + boundsA.size.width;
float Aymin = boundsA.origin.y;
float Aymax = Aymin + boundsA.size.height;
float Bxmin = boundsB.origin.x;
float Bxmax = Bxmin + boundsB.size.width;
float Bymin = boundsB.origin.y;
float Bymax = Bymin + boundsB.size.height;
// find location of B corners in A space
float B0x = mB(0,0) * Bxmin + mB(0,1) * Bymin + mB(0,2);
float B0y = mB(1,0) * Bxmin + mB(1,1) * Bymin + mB(1,2);
float B1x = mB(0,0) * Bxmax + mB(0,1) * Bymin + mB(0,2);
float B1y = mB(1,0) * Bxmax + mB(1,1) * Bymin + mB(1,2);
float B2x = mB(0,0) * Bxmin + mB(0,1) * Bymax + mB(0,2);
float B2y = mB(1,0) * Bxmin + mB(1,1) * Bymax + mB(1,2);
float B3x = mB(0,0) * Bxmax + mB(0,1) * Bymax + mB(0,2);
float B3y = mB(1,0) * Bxmax + mB(1,1) * Bymax + mB(1,2);
if(B0x<Axmin && B1x<Axmin && B2x<Axmin && B3x<Axmin)
return false;
if(B0x>Axmax && B1x>Axmax && B2x>Axmax && B3x>Axmax)
return false;
if(B0y<Aymin && B1y<Aymin && B2y<Aymin && B3y<Aymin)
return false;
if(B0y>Aymax && B1y>Aymax && B2y>Aymax && B3y>Aymax)
return false;
float det = mB(0,0)*mB(1,1) - mB(0,1)*mB(1,0);
float dx = mB(1,2)*mB(0,1) - mB(0,2)*mB(1,1);
float dy = mB(0,2)*mB(1,0) - mB(1,2)*mB(0,0);
// find location of A corners in B space
float A0x = (mB(1,1) * Axmin - mB(0,1) * Aymin + dx)/det;
float A0y = (-mB(1,0) * Axmin + mB(0,0) * Aymin + dy)/det;
float A1x = (mB(1,1) * Axmax - mB(0,1) * Aymin + dx)/det;
float A1y = (-mB(1,0) * Axmax + mB(0,0) * Aymin + dy)/det;
float A2x = (mB(1,1) * Axmin - mB(0,1) * Aymax + dx)/det;
float A2y = (-mB(1,0) * Axmin + mB(0,0) * Aymax + dy)/det;
float A3x = (mB(1,1) * Axmax - mB(0,1) * Aymax + dx)/det;
float A3y = (-mB(1,0) * Axmax + mB(0,0) * Aymax + dy)/det;
if(A0x<Bxmin && A1x<Bxmin && A2x<Bxmin && A3x<Bxmin)
return false;
if(A0x>Bxmax && A1x>Bxmax && A2x>Bxmax && A3x>Bxmax)
return false;
if(A0y<Bymin && A1y<Bymin && A2y<Bymin && A3y<Bymin)
return false;
if(A0y>Bymax && A1y>Bymax && A2y>Bymax && A3y>Bymax)
return false;
return true;
}
行列mBは、B空間の点をA空間の点に変換する任意のアフィン変換行列です。これには、単純な回転と平行移動、回転とスケーリング、フルアフィンワープが含まれますが、パースペクティブワープは含まれません。
それは可能な限り最適ではないかもしれません。速度は大きな問題ではありませんでした。しかし、それは私にとってはうまくいくようです。
以下は、受け入れられた回答のMATLAB実装です。
function olap_flag = ol(A,B,sub)
%A and B should be 4 x 2 matrices containing the xy coordinates of the corners in clockwise order
if nargin == 2
olap_flag = ol(A,B,1) && ol(B,A,1);
return;
end
urdl = diff(A([1:4 1],:));
s = sum(urdl .* A, 2);
sdiff = B * urdl' - repmat(s,[1 4]);
olap_flag = ~any(max(sdiff)<0);
これは従来の方法で、線ごとに行き、線が交差しているかどうかを確認します。これはMATLABのコードです。
C1 = [0, 0]; % Centre of rectangle 1 (x,y)
C2 = [1, 1]; % Centre of rectangle 2 (x,y)
W1 = 5; W2 = 3; % Widths of rectangles 1 and 2
H1 = 2; H2 = 3; % Heights of rectangles 1 and 2
% Define the corner points of the rectangles using the above
R1 = [C1(1) + [W1; W1; -W1; -W1]/2, C1(2) + [H1; -H1; -H1; H1]/2];
R2 = [C2(1) + [W2; W2; -W2; -W2]/2, C2(2) + [H2; -H2; -H2; H2]/2];
R1 = [R1 ; R1(1,:)] ;
R2 = [R2 ; R2(1,:)] ;
plot(R1(:,1),R1(:,2),'r')
hold on
plot(R2(:,1),R2(:,2),'b')
%% lines of Rectangles
L1 = [R1(1:end-1,:) R1(2:end,:)] ;
L2 = [R2(1:end-1,:) R2(2:end,:)] ;
%% GEt intersection points
P = zeros(2,[]) ;
count = 0 ;
for i = 1:4
line1 = reshape(L1(i,:),2,2) ;
for j = 1:4
line2 = reshape(L2(j,:),2,2) ;
point = InterX(line1,line2) ;
if ~isempty(point)
count = count+1 ;
P(:,count) = point ;
end
end
end
%%
if ~isempty(P)
fprintf('Given rectangles intersect at %d points:\n',size(P,2))
plot(P(1,:),P(2,:),'*k')
end
関数InterXは、https://in.mathworks.com/matlabcentral/fileexchange/22441-curve-intersections?focused = 5165138&tab = functionからダウンロードできます。