web-dev-qa-db-ja.com

線分が2Dで軸に沿った長方形と交差するかどうかをテストする方法は?

線分が2Dで軸に沿った長方形と交差するかどうかをテストする方法は?セグメントは、p1、p2の2つの端で定義されます。長方形は、左上と右下の点で定義されます。

38
metamal

元のポスターは、ラインセグメントとポリゴンの交点を検出したいと考えていました。交差点がある場合は、交差点を配置する必要はありませんでした。それが意図したとおりの場合、Liang-BarskyまたはCohen-Sutherlandよりも少ない作業を実行できます。

セグメントの端点をp1 =(x1 y1)およびp2 =(x2 y2)とします。
長方形の角が(xBL yBL)と(xTR yTR)になるようにします。

その後、あなたがしなければならないすべては

A.長方形の四隅すべてが線の同じ側にあるかどうかを確認します。 p1とp2を通る線の陰的な方程式は次のとおりです。

F(x y) = (y2-y1)*x + (x1-x2)*y + (x2*y1-x1*y2)

F(x y)= 0の場合、(x y)はライン上にあります。
F(x y)> 0の場合、(x y)はラインの「上」です。
F(x y)<0の場合、(x y)はラインの「下」です。

四隅すべてをF(x y)に代入します。それらがすべて負またはすべて正の場合、交差はありません。一部が陽性で一部が陰性の場合は、ステップBに進みます。

B.端点をx軸に投影し、セグメントの影がポリゴンの影と交差するかどうかを確認します。 y軸で繰り返します。

(x1> xTRおよびx2> xTR)の場合、交差はありません(線は長方形の右側にあります)。
(x1 <xBLおよびx2 <xBL)の場合、交差はありません(長方形の左側に線があります)。
(y1> yTRおよびy2> yTR)の場合、交差はありません(線は長方形の上にあります)。
(y1 <yBLおよびy2 <yBL)の場合、交差はありません(線は長方形の下にあります)。
その他、交差点があります。コーエン・サザーランドか、質問に対する他の回答で言及されているコードを実行してください。

もちろん、最初にBを実行してから、Aを実行することもできます。

アレホ

51
user37968

非常にシンプルで実用的なソリューションを書いた:

      bool SegmentIntersectRectangle(double a_rectangleMinX,
                                 double a_rectangleMinY,
                                 double a_rectangleMaxX,
                                 double a_rectangleMaxY,
                                 double a_p1x,
                                 double a_p1y,
                                 double a_p2x,
                                 double a_p2y)
  {
    // Find min and max X for the segment

    double minX = a_p1x;
    double maxX = a_p2x;

    if(a_p1x > a_p2x)
    {
      minX = a_p2x;
      maxX = a_p1x;
    }

    // Find the intersection of the segment's and rectangle's x-projections

    if(maxX > a_rectangleMaxX)
    {
      maxX = a_rectangleMaxX;
    }

    if(minX < a_rectangleMinX)
    {
      minX = a_rectangleMinX;
    }

    if(minX > maxX) // If their projections do not intersect return false
    {
      return false;
    }

    // Find corresponding min and max Y for min and max X we found before

    double minY = a_p1y;
    double maxY = a_p2y;

    double dx = a_p2x - a_p1x;

    if(Math::Abs(dx) > 0.0000001)
    {
      double a = (a_p2y - a_p1y) / dx;
      double b = a_p1y - a * a_p1x;
      minY = a * minX + b;
      maxY = a * maxX + b;
    }

    if(minY > maxY)
    {
      double tmp = maxY;
      maxY = minY;
      minY = tmp;
    }

    // Find the intersection of the segment's and rectangle's y-projections

    if(maxY > a_rectangleMaxY)
    {
      maxY = a_rectangleMaxY;
    }

    if(minY < a_rectangleMinY)
    {
      minY = a_rectangleMinY;
    }

    if(minY > maxY) // If Y-projections do not intersect return false
    {
      return false;
    }

    return true;
  }
26
metamal

セグメントから長方形を作成し、他の長方形がそれに衝突するかどうかをテストすることもできます。これは、単なる一連の比較であるためです。 pygameソースから:

def _rect_collide(a, b):
    return a.x + a.w > b.x and b.x + b.w > a.x and \
           a.y + a.h > b.y and b.y + b.h > a.y
7
asolano

長方形が揃っているので、Liang-Barskyは良い解決策かもしれません。ここで速度が重要な場合は、Cohen-Sutherlandよりも高速です。

グラフの説明
別の良い説明
そしてもちろん、ウィキペディア

7
ryan_s

Cohen-Sutherlandアルゴリズム を使用します。

クリッピングに使用されますが、このタスクのために少し調整することができます。それはあなたの長方形を「中央の正方形」として二次元空間を三目並べボードに分割します。
次に、ラインの2つのポイントのそれぞれが9つの領域のどれにあるかを確認します。

  • 両方のポイントが左、右、上、または下の場合、簡単に拒否されます。
  • どちらかの点が内側にある場合は、簡単に受け入れます。
  • 残りのまれなケースでは、四角形のどの領域と交差するかを、それらがどの領域にあるかに基づいて計算することができます。
5
Kevin Conner

または、すでにJavaメソッドにあるコードを使用/コピーします

Java.awt.geom.Rectangle2D.intersectsLine(double x1, double y1, double x2, double y2)

便宜上、静的に変換した後のメソッドは次のとおりです。

/**
 * Code copied from {@link Java.awt.geom.Rectangle2D#intersectsLine(double, double, double, double)}
 */
public class RectangleLineIntersectTest {
    private static final int OUT_LEFT = 1;
    private static final int OUT_TOP = 2;
    private static final int OUT_RIGHT = 4;
    private static final int OUT_BOTTOM = 8;

    private static int outcode(double pX, double pY, double rectX, double rectY, double rectWidth, double rectHeight) {
        int out = 0;
        if (rectWidth <= 0) {
            out |= OUT_LEFT | OUT_RIGHT;
        } else if (pX < rectX) {
            out |= OUT_LEFT;
        } else if (pX > rectX + rectWidth) {
            out |= OUT_RIGHT;
        }
        if (rectHeight <= 0) {
            out |= OUT_TOP | OUT_BOTTOM;
        } else if (pY < rectY) {
            out |= OUT_TOP;
        } else if (pY > rectY + rectHeight) {
            out |= OUT_BOTTOM;
        }
        return out;
    }

    public static boolean intersectsLine(double lineX1, double lineY1, double lineX2, double lineY2, double rectX, double rectY, double rectWidth, double rectHeight) {
        int out1, out2;
        if ((out2 = outcode(lineX2, lineY2, rectX, rectY, rectWidth, rectHeight)) == 0) {
            return true;
        }
        while ((out1 = outcode(lineX1, lineY1, rectX, rectY, rectWidth, rectHeight)) != 0) {
            if ((out1 & out2) != 0) {
                return false;
            }
            if ((out1 & (OUT_LEFT | OUT_RIGHT)) != 0) {
                double x = rectX;
                if ((out1 & OUT_RIGHT) != 0) {
                    x += rectWidth;
                }
                lineY1 = lineY1 + (x - lineX1) * (lineY2 - lineY1) / (lineX2 - lineX1);
                lineX1 = x;
            } else {
                double y = rectY;
                if ((out1 & OUT_BOTTOM) != 0) {
                    y += rectHeight;
                }
                lineX1 = lineX1 + (y - lineY1) * (lineX2 - lineX1) / (lineY2 - lineY1);
                lineY1 = y;
            }
        }
        return true;
    }
}
3
Craigo

簡単なGoogle検索で、交差をテストするためのC++コードを含むページがポップアップ表示されました。

基本的には、ラインとすべての境界線または長方形の間の交差をテストします。

長方形と線の交差コード

1
Vincent McNabb

PHPのコーディング例(ポリゴンの外側の座標を取得するために、getLeft()、getRight()、getTop()、getBottom()などのメソッドを持つオブジェクトモデルを使用しています)また、getWidth()とgetHeight()もあります-入力されたパラメーターに応じて、未知数を計算してキャッシュします。つまり、x1、y1と... w、hまたはx2、y2でポリゴンを作成できます。それは他のものを計算することができます)

「n」を使用して、オーバーラップをチェックする「新しい」アイテムを指定します($ nItemはポリゴンオブジェクトのインスタンスです)-もう一度テストするアイテム(これはbin/sortナップザックプログラムです)は、 (同じ)ポリゴンオブジェクトの複数のインスタンス。

public function checkForOverlaps(BinPack_Polygon $nItem) {
  // grab some local variables for the stuff re-used over and over in loop
  $nX = $nItem->getLeft();
  $nY = $nItem->getTop();
  $nW = $nItem->getWidth();
  $nH = $nItem->getHeight();
  // loop through the stored polygons checking for overlaps
  foreach($this->packed as $_i => $pI) {
    if(((($pI->getLeft()  - $nW) < $nX) && ($nX < $pI->getRight())) &&
       ((($pI->getTop()  - $nH) < $nY) && ($nY < $pI->getBottom()))) {
      return false;
    }
  }
  return true;
}
0
Scott

私は同様の問題を見ていましたが、これが私が思いついたものです。私は最初にエッジを比較していて、何かに気づきました。最初のボックスの反対の軸内にあるエッジの中間点が、同じ軸の最初のボックスの外側のポイントのそのエッジの長さの半分以内にある場合、その辺のどこかに交差があります。しかし、それは1次元的に考えていて、2番目のボックスの両側を見て理解する必要がありました。

突然、2番目のボックスの「中間点」を見つけ、中間点の座標を比較して、最初の外側の寸法の(2番目のボックスの)辺の長さの1/2以内に収まるかどうかを確認した、そしてどこかに交差点があります。

i.e. box 1 is bounded by x1,y1 to x2,y2
box 2 is bounded by a1,b1 to a2,b2

the width and height of box 2 is:
w2 = a2 - a1   (half of that is w2/2)
h2 = b2 - b1   (half of that is h2/2)
the midpoints of box 2 are:
am = a1 + w2/2
bm = b1 + h2/2

So now you just check if
(x1 - w2/2) < am < (x2 + w2/2) and (y1 - h2/2) < bm < (y2 + h2/2) 
then the two overlap somewhere.
If you want to check also for edges intersecting to count as 'overlap' then
 change the < to <=

もちろん、他の方法で同じように簡単に比較することもできます(box1の中点がbox 2の外側の寸法の1/2の長さ以内であることを確認する)

さらに簡略化-中点を半分の長さにシフトすると、そのボックスの原点と同じになります。つまり、境界点の範囲内にあるかどうかをその点だけで確認し、プレーンを左上にシフトすることで、下隅が最初のボックスの下隅になります。はるかに少ない数学:

(x1 - w2) < a1 < x2
&&
(y1 - h2) < b1 < y2
[overlap exists]

または非置換:

( (x1-(a2-a1)) < a1 < x2 ) && ( (y1-(b2-b1)) < b1 < y2 ) [overlap exists]
( (x1-(a2-a1)) <= a1 <= x2 ) && ( (y1-(b2-b1)) <= b1 <= y2 ) [overlap or intersect exists]
0
Scott

私のソリューションのいくつかのサンプルコード(php):

// returns 'true' on overlap checking against an array of similar objects in $this->packed
public function checkForOverlaps(BinPack_Polygon $nItem) {
  $nX = $nItem->getLeft();
  $nY = $nItem->getTop();
  $nW = $nItem->getWidth();
  $nH = $nItem->getHeight();
  // loop through the stored polygons checking for overlaps
  foreach($this->packed as $_i => $pI) {
    if(((($pI->getLeft() - $nW) < $nX) && ($nX < $pI->getRight())) && ((($pI->getTop() - $nH) < $nY) && ($nY < $pI->getBottom()))) {
      return true;
    }
  }
  return false;
}
0
Scott

これが@metamalの答えのjavascriptバージョンです

var isRectangleIntersectedByLine = function (
  a_rectangleMinX,
  a_rectangleMinY,
  a_rectangleMaxX,
  a_rectangleMaxY,
  a_p1x,
  a_p1y,
  a_p2x,
  a_p2y) {

  // Find min and max X for the segment
  var minX = a_p1x
  var maxX = a_p2x

  if (a_p1x > a_p2x) {
    minX = a_p2x
    maxX = a_p1x
  }

  // Find the intersection of the segment's and rectangle's x-projections
  if (maxX > a_rectangleMaxX)
    maxX = a_rectangleMaxX

  if (minX < a_rectangleMinX)
    minX = a_rectangleMinX

  // If their projections do not intersect return false
  if (minX > maxX)
    return false

  // Find corresponding min and max Y for min and max X we found before
  var minY = a_p1y
  var maxY = a_p2y

  var dx = a_p2x - a_p1x

  if (Math.abs(dx) > 0.0000001) {
    var a = (a_p2y - a_p1y) / dx
    var b = a_p1y - a * a_p1x
    minY = a * minX + b
    maxY = a * maxX + b
  }

  if (minY > maxY) {
    var tmp = maxY
    maxY = minY
    minY = tmp
  }

  // Find the intersection of the segment's and rectangle's y-projections
  if(maxY > a_rectangleMaxY)
    maxY = a_rectangleMaxY

  if (minY < a_rectangleMinY)
    minY = a_rectangleMinY

  // If Y-projections do not intersect return false
  if(minY > maxY)
    return false

  return true
}
0
Breck

少しナプキンで解決しました。

次に、mとcを見つけて、式y = mx + cを求めます。

y = (Point2.Y - Point1.Y) / (Point2.X - Point1.X)

P1座標を代入してcを見つけます

次に、長方形の頂点について、X値を線方程式に入れ、Y値を取得し、Y値が下に示す長方形の境界内にあるかどうかを確認します

(長方形の定数値X1、X2、Y1、Y2を見つけることができます)

X1 <= x <= X2 & 
Y1 <= y <= Y2

Y値が上記の条件を満たし、(Point1.Y、Point2.Y)の間にある場合、交差があります。これがカットに失敗した場合は、すべての頂点を試してください。

0
Gishu