web-dev-qa-db-ja.com

2つの長方形の交差を検出するアルゴリズム?

2つの長方形が交差するかどうかを検出するアルゴリズムを探しています(1つは任意の角度で、もう1つは垂直/水平線のみで)。

1つのコーナーが他のALMOSTにあるかどうかをテストします。長方形が十字形を形成している場合、失敗します。

線の傾斜を使用しないようにすることをお勧めします。これには、垂直線に特別な場合が必要になります。

139
user20493

標準的な方法は、軸テストの分離を行うことです(その上でGoogle検索を行います)。

要するに:

  • 2つのオブジェクトを区切る線が見つかれば、2つのオブジェクトは交差しません。例えばオブジェクト/オブジェクトのすべてのポイントは、線の異なる側にあります。

面白いのは、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);

ここで、長方形Aのすべてのポイントを長方形Bのエッジに対してテストし、逆も同様です。分離するエッジを見つけた場合、オブジェクトは交差しません(Bの他のすべてのポイントがテスト対象のエッジの反対側にある場合-下の図を参照)。分離するエッジが見つからない場合は、長方形が交差しているか、1つの長方形が他の長方形に含まれています。

このテストは、任意の凸多角形で機能します。

修正:分離するエッジを識別するには、1つの長方形のすべてのポイントを他の長方形の各エッジに対してテストするだけでは不十分です。候補エッジE(下)は、AのすべてのポイントがEの同じ半平面にあるため、分離エッジとして識別されます。ただし、Bの頂点Vb1およびVb2は、分離エッジではありません。その半平面にもあります。そうでなかった場合にのみ、分離エッジになります http://www.iassess.com/collision.png

157

基本的に次の図を見てください。


2つのボックスが衝突すると、線AとBが重なります。

これは、X軸とY軸の両方で実行する必要があり、長方形を衝突させるには両方をオーバーラップする必要があることに注意してください。

gamasutra.com には、質問に答える良い記事があります(写真は記事からのものです)。 5年前に同様のアルゴリズムを実行しましたが、後でここに投稿するにはコードスニペットを見つける必要があります

修正:分離軸定理は、2つの凸形状は重ならないと述べています分離軸が存在する場合(つまり、示されている投影が重ならない軸)したがって、「分離軸が存在する」=>「オーバーラップなし」。これは双方向の意味合いではないため、逆を結論付けることはできません。

16
m_pGladiator

Cocoaでは、selectedArea四角形が回転したNSViewのフレーム四角形と交差するかどうかを簡単に検出できます。ポリゴン、法線などを計算する必要さえありません。これらのメソッドをNSViewサブクラスに追加するだけです。たとえば、ユーザーはNSViewのスーパービューの領域を選択し、その後、DoesThisRectSelectMeメソッドを呼び出して、selectedArea rectを渡します。 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;
}
4
Leonardo

m_pGladiatorの答えは正しく、私はそれを好む。 分離軸テストは、長方形の重なりを検出する最も簡単で標準的な方法です。投影間隔が重ならない線は、separating axisと呼びます。 Nils Pipenbrinckのソリューションは一般的すぎます。 dot productを使用して、一方の形状が他方のエッジの一方の側に完全にあるかどうかを確認します。このソリューションは、実際には、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 ':Math.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle) rectBの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(Game Development Conference 2007)を参照できますPPT www.realtimecollisiondetection.net/pubs/GDC07_Ericson_Physics_Tutorial_SAT.ppt

4
tristan

ある長方形の線が他の長方形の線と交差するかどうかを確認します。単純な線分交差は簡単にコード化できます。

さらに速度が必要な場合は、ラインセグメントの交差(スイープライン)用の高度なアルゴリズムがあります。 http://en.wikipedia.org/wiki/Line_segment_intersection を参照してください

2
Louis Brandy

1つの解決策は、No Fit Polygonと呼ばれるものを使用することです。このポリゴンは2つのポリゴンから計算され(概念的には一方を他方の周りにスライドさせることにより)、相対オフセットが与えられるとポリゴンが重なる領域を定義します。このNFPを取得したら、2つのポリゴンの相対オフセットで指定されたポイントを使用して、包含テストを実行するだけです。この包含テストは迅速かつ簡単ですが、最初にNFPを作成する必要があります。

WebでNo Fit Polygonを検索し、凸多角形のアルゴリズムを見つけることができるかどうかを確認します(凹多角形がある場合、より複雑になります)。何も見つからない場合は、ハワードドットでメールしてください。

2
Howard May

可能性のあるすべてのケースを処理できると思います。次のテストを実行します。

  1. 長方形1の頂点のいずれかが長方形2の内側にあることを確認し、その逆も同様です。他の長方形の中にある頂点を見つけた場合はいつでも、それらが交差して検索を停止すると結論付けることができます。これは、1つの長方形が完全に他の長方形の内側にあることに注意します。
  2. 上記のテストが決定的でない場合、1つの長方形の各線と他の長方形の各線の交点を見つけます。交差点が見つかったら、対応する4つの点によって作成された想像上の四角形の内側にあるかどうかを確認します。そのようなポイントが見つかると、それらが交差して検索を停止すると結論付けます。

上記の2つのテストがfalseを返す場合、これらの2つの長方形は重なりません。

1
John Smith

Javaを使用している場合、Shapeインターフェイスのすべての実装には、長方形を取る intersects メソッドがあります。

0
Brendan Cashman

他の何かが欠けているのか、なぜこれをそんなに複雑にするのですか?

(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");}
0
user1517108

さて、ブルートフォースの方法は、水平の長方形のエッジを歩いて、エッジに沿って各ポイントをチェックし、それが他の長方形の上にあるのか、それとも他の長方形の中にあるのかを確認することです。

数学的な答えは、両方の長方形の各エッジを記述する方程式を作成することです。これで、長方形Aの4本の線のいずれかが長方形Bの線のいずれかと交差するかどうかを簡単に見つけることができます。これは単純な(高速)線形方程式ソルバーです。

-アダム

0
Adam Davis

角のある長方形の各辺と、軸に沿った長方形の各辺の交差点を見つけることができます。これを行うには、各辺が存在する無限直線の方程式を見つけます(v1 + t(v2-v1) and v'1 + t '(v'2-v'1)基本的に)、2つの方程式が等しい場合にtを解くことにより線が交わるポイントを見つけ(平行であれば、それをテストできます)、そのポイントが2つの頂点間の線分上にあるかどうかをテストします。すなわち、0 <= t <= 1および0 <= t '<= 1であることは事実ですか。

ただし、一方の長方形がもう一方の長方形を完全に覆っている場合には、これはカバーしません。いずれかの長方形の4つの点すべてがもう一方の長方形の内側にあるかどうかをテストすることでカバーできます。

0
HenryR

これは、この問題のDバージョンに対して、私がやることです:

2つの長方形を方程式P1およびP2で記述された平面としてモデル化し、P1 = P2を記述して、平面が平行(交差なし)または同じ平面にある場合は存在しない交差方程式の線から導出します。その場合、0 = 0になります。その場合、2D長方形交差アルゴリズムを使用する必要があります。

次に、両方の長方形の平面にあるその線が両方の長方形を通過するかどうかを確認します。もしそうなら、あなたは2つの長方形の交差点を持っています。

線が同じ平面内の長方形を通過するかどうかを調べるには、線と長方形の辺の交点の2点を見つけ(線方程式を使用してモデル化)、交点がinにあることを確認します範囲。

それが数学的な説明です。残念ながら、上記のコードはありません。

0
freespace

受け入れられた答えの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);
0
Jed

これは従来の方法で、1行ずつ移動して、線が交差しているかどうかを確認します。これは、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

分離軸テストを使用するよりもわずかに高速なテストを実行する別の方法は、いずれかの各頂点でワインディングナンバーアルゴリズム(象限のみ-not恐ろしく遅い角度合計)を使用することです長方形(任意に選択)。頂点のいずれかにゼロ以外のワインディング番号がある場合、2つの長方形は重なります。

このアルゴリズムは、分離軸テストよりもやや長めですが、エッジが2つの象限を横切る場合に半平面テストしか必要ないため、高速です(分離軸法を使用した最大32テストとは対照的)

このアルゴリズムには、any polygon(凸または凹)の重なりをテストするために使用できるというさらなる利点があります。私の知る限り、アルゴリズムは2D空間でのみ機能します。

0
Mads

2つの四角形がある場合、独自の単純なメソッドがあります。

R1 =(min_x1、max_x1、min_y1、max_y1)

R2 =(min_x2、max_x2、min_y2、max_y2)

次の場合にのみ重複します。

オーバーラップ=(max_x1> min_x2)および(max_x2> min_x1)および(max_y1> min_y2)および(max_y2> min_y1)

あなたは3Dボックスでもそれを行うことができます。実際には、任意の数の次元で機能します。

0
BitFarmer

他の回答で十分なことが言われているので、擬似コードのワンライナーを追加します。

!(a.left > b.right || b.left > a.right || a.top > b.bottom || b.top > a.bottom);
0
Przemek

このように実装しました:

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空間の点に変換するアフィン変換行列です。これには、単純な回転と平行移動、回転とスケーリング、完全なアフィンワープが含まれますが、パースペクティブワープは含まれません。

それは可能な限り最適ではないかもしれません。速度は大きな問題ではありませんでした。しかし、それは私にとってはうまくいくようです。

0
Robotbugs