2つの線分が交差する場所をどのように検出しますか?[閉まっている]


518

2つの線が交差するかどうか、また交差する場合はどのx、y点で交差するかをどのように判断しますか?


四角形の端を完全な多角形ではなく、別個の線と考えると役立つ場合があります。
ライアングラハム、

モデレーターのメモ:この投稿がトピックに関するものであるかどうかに関するディスカッションは、メタスタックオーバーフローに属しています。これに関するこれ以上のコメントは削除されます。
Martijn Pieters

回答:


659

ベクトルのクロス積を使用するこの問題への素晴らしいアプローチがあります。2次元ベクトルクロス積v  ×  wv x  w y  −  v y  w xと定義します。

2つの線分セグメントがpからp  +  rまで、およびqからq  +  sまでと仮定します。次に、1行目の任意の点はp  +  t  r(スカラーパラメーター  tの場合)として、2行目の任意の点はq  +  u  s(スカラーパラメーターuの場合)として表現できます  。

Two line segments intersecting

2つの線は、次のようなtuが見つかると交差します。

p + t  r = q + u  s

Formulae for the point of intersection

両側をsで交差させ、

p + t  r)× s =(q + u  s)× s

そしてs  ×  s = 0なので、これは

t  (r × s)=(qp)× s

したがって、tについて解く:

t =(qp)× s /(r × s

同様に、uを解くことができます。

p + t  r)× r =(q + u  s)× r

u  (s × r)=(pq)× r

u =(pq)× r /(s × r

計算ステップの数を減らすには、これを次のように書き直すと便利です(s  ×  r = −  r  ×  sである)。

u =(q p)× r /(r × s

現在、4つのケースがあります。

  1. もしr  ×  s  = 0及び(Q  -  P)×  R  = 0、2本の線が同一直線上にあります。

    この場合、2番目のセグメントの終点(qおよびq  +  s)を、最初の線分の方程式( p + t r)。

    t 0 =(qp)・  r /(r  ・  r

    t 1 =(q + sp)・  r /(r  ・  r)= t 0 + s  ・  r /(r  ・  r

    tの間隔が 0T 1つの交差区間[0、1]次に、線分が同一直線上と重複しています。そうでなければ、それらは同一線上にあり、互いに素です。

    srが反対方向を指している場合、s  ・  r <0なので、チェックされる間隔は[ t 0t 1 ] ではなく[ t 1t 0 ]であることに注意してください。

  2. 場合、R  ×  S  = 0及び(Q  -  P)×  R  ≠0、2本の線が平行と非交差です。

  3. もしR  ×  S  ≠0かつ0≤  T  ≤1,0≤  U  ≤1、2つの線分は、点で接するP + T  R = Q + U  S

  4. そうでない場合、2つの線分は平行ではありませんが交差しません。

クレジット:この方法は、Ronald Goldmanの記事「3スペースでの2本の線の交差」のGraphics Gems、304ページに掲載されている3D線交差アルゴリズムの2次元の特殊化です。3次元では、通常のケースは線は歪んでいます(平行でも交差でもありません)。この場合、メソッドは2つの線の最接近点を示します。


5
@myrkos:いいえ。最初のラインセグメントは「pからp + rまで」であるため、パラメトリックな用語で「p + tr」として表される場合、セグメントは0≤t≤1に対応します。同様に、他のセグメントも同様です。
Gareth Rees、2012年

7
ガレス、私は何かが欠けているに違いないと感じていますが、どうやって(ベクトル)をベクトルで割りますか?あなたのためのソリューショントンuがで終わり/ (r × s)ますが、(r × s)右、ベクトルのですか?ベクトル(0, 0, rx * sy - ry * sx)。左側も同様にz軸に平行なベクトルです。では、zコンポーネントを他のzコンポーネントで除算するだけですか?tの式は実際に|(q − p) × s| / |(r × s)|ですか?
LarsH 2012

7
@LarsH:最初の段落を参照してください。
Gareth Rees、2012

35
興味のある方のために、これは機能しているように見える、ラインのPointF開始および終了座標を取る単純なC#実装です:ideone.com/PnPJgb
Matt

24
@Mattに続いてJavaScript実装をまとめました。Tekitoが指摘したエラーを修正しました。
pgkelley 2013

230

FWIW、次の関数(C)は、線の交点を検出し、交点を決定します。これは、Andre LeMotheの「Windows Game Programming Gurusのトリック」のアルゴリズムに基づいています。これは、他の回答におけるいくつかのアルゴリズム(Garethのアルゴリズムなど)と似ています。LeMotheはその後、クラマーの法則(私に尋ねないでください)を使用して、方程式自体を解きます。

私の微弱な小惑星クローンで機能することを証明でき、Elemental、Dan、Wodzuによる他の回答で説明されているエッジのケースを正しく処理しているようです。また、乗算と除算で平方根がないため、KingNestorが投稿したコードよりもおそらく高速です。

私の場合は問題になりませんでしたが、ゼロで除算される可能性はあると思います。とにかくクラッシュを回避するために変更するのは簡単です。

// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines 
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s1_x, s1_y, s2_x, s2_y;
    s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
    s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

    float s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        // Collision detected
        if (i_x != NULL)
            *i_x = p0_x + (t * s1_x);
        if (i_y != NULL)
            *i_y = p0_y + (t * s1_y);
        return 1;
    }

    return 0; // No collision
}

ところで、私はLeMotheの本の中で彼は明らかにアルゴリズムを正しく理解しているが、彼が間違った数のプラグインと間違った計算をしていることを示す具体的な例を言わなければなりません。例えば:

(4 *(4-1)+ 12 *(7-1))/(17 * 4 + 12 * 10)

= 844 / 0.88

= 0.44

それは私を何時間も混乱させた。:(


9
function getLineIntersection(p0_x、p0_y、p1_x、p1_y、p2_x、p2_y、p3_x、p3_y){var s1_x、s1_y、s2_x、s2_y; s1_x = p1_x-p0_x; s1_y = p1_y-p0_y; s2_x = p3_x-p2_x; s2_y = p3_y-p2_y; var s、t; s =(-s1_y *(p0_x-p2_x)+ s1_x *(p0_y-p2_y))/(-s2_x * s1_y + s1_x * s2_y); t =(s2_x *(p0_y-p2_y)-s2_y *(p0_x-p2_x))/(-s2_x * s1_y + s1_x * s2_y);
cortijon

5
if(s> = 0 && s <= 1 && t> = 0 && t <= 1){//衝突が検出されましたvar intX = p0_x +(t * s1_x); var intY = p0_y +(t * s1_y); return [intX、intY]; } nullを返します。//衝突なし}
cortijon

13
良いアルゴリズムですが、行列式が0の場合は処理できません(上記の-s2_x * s1_y + s1_x * s2_y)。0(または0に近い)の場合、線は平行または同一線上にあります。同一線上にある場合、交差は別の線分である可能性があります。
2013

16
2つの除算演算は速度を上げるために回避できます(除算には乗算よりもコストがかかります)。線が交差する場合は1つの除算が必要であり、線が交差しない場合は0が必要です。一つは、最初の分母を計算し、それがゼロである場合、早期停止(おそらくは共直線性を検出するためのコードを追加する。)次に、代わりに算出すべきであるst直接二分母と分母の関係をテストします。線が交差していることが確認された場合にのみ、実際にt(ではなくs)の値を計算する必要があります。
Qwertie 2013

18
ここに掲載されているすべてのアルゴリズムでパフォーマンステストを行いましたが、これは他のアルゴリズムの少なくとも2倍の速度です。投稿ありがとうございます!
lajos 2013年

63

問題はこの質問に帰着します:AからBへとCからDへの2本の線が交差していますか?次に、それを4回(線と長方形の4辺のそれぞれの間で)質問できます。

これを行うためのベクトル数学は次のとおりです。AからBへの線が問題の線であり、CからDへの線が長方形の線の1つであると想定しています。私の表記はAx、「Aのx座標」とCy「Cのy座標」です。そして「*」はドット積を意味するので、例えばA*B = Ax*Bx + Ay*By

E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy ) 
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )

このh数字が鍵です。場合hの間にある01、ラインが交差し、それ以外の場合はそうではありません。もしF*Pゼロであり、もちろんあなたは計算を行うことはできませんが、この場合にはラインが平行に、したがって、唯一の交差明白な例です。

正確な交点はC + F*hです。

もっと楽しく:

場合h正確 01、線が終点で接触する。これは、「交差点」と考えるか、必要に応じて考えることができます。

具体的には hは、他のラインに正確に接触するためにラインの長さを掛ける必要がある量です。

したがって、の場合h<0、長方形の線は特定の線の「後ろ」にあり(「方向」は「AからBへ」です)、h>1長方形の線が特定の線の「前」にあることを意味します。

導出:

AとCは、線の始点を指すベクトルです。EとFは、ラインを形成するAとCの端からのベクトルです。

平面内の任意の2つの非平行線のために、正確に一つのスカラーのペアが存在しなければならないgh、この式が成立するように:

A + E*g = C + F*h

どうして?平行ではない2本の線が交差する必要があるため、両方の線をそれぞれある程度拡大して互いに接触させることができます。

最初は、これは2つの未知数を含む単一の方程式のように見えます! しかし、これが2Dベクトル方程式であると考えると、これは実際にはxyます。)

これらの変数の1つを排除する必要があります。簡単な方法は、E項をゼロにすることです。これを行うには、Eでゼロにドット化するベクトルを使用して、方程式の両側のドット積を計算します。このベクトルは、P上記で呼び出し、Eの明らかな変換を行いました。

あなたは今持っています:

A*P = C*P + F*P*h
(A-C)*P = (F*P)*h
( (A-C)*P ) / (F*P) = h

29
このアルゴリズムは素晴らしいです。しかし、Dan @ stackoverflow.com / questions / 563198 / … およびElemental @ stackoverflow.com / questions / 563198 / … によって指摘されているように、そこには穴が開いています。ありがとう。
Chantz、2009年

2
このアルゴリズムは数値的に安定していますか?私は同様のアプローチを試しましたが、フロートで作業すると奇妙な結果が出ることがわかりました。
milosz 2010

3
このアルゴリズムには別の問題があるようです。ポイントが供給されると、A = {1、0} B = {2、0} C = {0、0} D = {1,0}になりますが、線分は端にはっきりと接触していますが、F P(およびE Q、以下のユーザーの修正に合わせて)は両方とも0なので、0で除算するとhとgが見つかります。まだこの問題の解決策に取り組んでいますが、問題は指摘する価値があると思いました。
キャンドリュー

12
この答えは単に間違っています。A = {0,0}、B = {0,1}、C = {0,2} D = {2,0}を試してください
Tim Cooper

6
A + E*g = C + F*h2つの線が交差するのは、その方程式の解(平行でない場合)に両方がgありh、0と1の間にある場合(終点での接触をカウントするかどうかに応じて、内部または排他的)です。
Daniel Fischer

46

私は上記のJasonによってとてもエレガントに説明されたアルゴリズムを実装しようとしました。残念ながら、デバッグで数学を使って作業しているときに、それが機能しない多くのケースを見つけました。

たとえば、点A(10,10)B(20,20)C(10,1)D(1,10)がh = .5を与えることを考慮しますが、これらのセグメントがそれぞれの近くにないことを調べることにより、明らかですその他。

これをグラフにすると、0 <h <1の基準は、インターセプトポイントが存在する場合にCDにあることを示すだけで、そのポイントがABにあるかどうかは何も伝えないことを示しています。クロスポイントがあることを確認するには、変数gに対して対称的な計算を行う必要があり、インターセプトの要件は次のとおりです。0<g <1 AND 0 <h <1


2
受け入れられた答えがうまくいかない理由を理解するために、髪を抜いてきました。本当にありがとう!
Matt Bridges、

1
また、注目すべき境界条件は、この場合に動作すること(すなわち、H = 0またはh = 1またはg = 0またはg = 1の行「わずか」タッチ用
元素

結果の視覚化に問題がある人のために、私はこれをJavascriptで実装しました:jsfiddle.net/ferrybig/eokwL9mp
Ferrybig

45

これがギャビンの答えの改善です。marcpのソリューションも同様ですが、どちらも分割を延期します。

これは実際には、Gareth Reesの回答の実用的なアプリケーションでもあることがわかります。これは、2Dのクロス積に相当するものがperp-dot-productであり、このコードが3つ使用しているためです。3Dに切り替えて、クロス積を使用し、最後にsとtの両方を補間すると、3Dの線の間に2つの最も近い点が生じます。とにかく、2Dソリューション:

int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t;
    s10_x = p1_x - p0_x;
    s10_y = p1_y - p0_y;
    s32_x = p3_x - p2_x;
    s32_y = p3_y - p2_y;

    denom = s10_x * s32_y - s32_x * s10_y;
    if (denom == 0)
        return 0; // Collinear
    bool denomPositive = denom > 0;

    s02_x = p0_x - p2_x;
    s02_y = p0_y - p2_y;
    s_numer = s10_x * s02_y - s10_y * s02_x;
    if ((s_numer < 0) == denomPositive)
        return 0; // No collision

    t_numer = s32_x * s02_y - s32_y * s02_x;
    if ((t_numer < 0) == denomPositive)
        return 0; // No collision

    if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive))
        return 0; // No collision
    // Collision detected
    t = t_numer / denom;
    if (i_x != NULL)
        *i_x = p0_x + (t * s10_x);
    if (i_y != NULL)
        *i_y = p0_y + (t * s10_y);

    return 1;
}

基本的にそれは最後の瞬間まで除算を延期し、特定の計算が行われる前にほとんどのテストを移動し、それによりアーリーアウトを追加します。最後に、ラインが平行な場合に発生するゼロによる除算も回避されます。

また、ゼロとの比較ではなく、イプシロン検定の使用を検討することもできます。平行に非常に近い線は、わずかにずれた結果を生成する可能性があります。これはバグではなく、浮動小数点演算の制限です。


1
一部のポイントの値が0である場合は失敗します。それは正しく発生しませんか?
hfossli 2013

1
分割を延期するときに導入されたバグを修正しました。numerとdenomが両方とも負の場合、tは正になる可能性があります。
iMalc 2013

2
p0-p1が垂直で、p2-p3が水平で、2つのセグメントが交差している場合は機能しません。(最初のリターンが実行されます)
Fabio Dalla Libera

Coolinearケースには、2つの可能性があります。最初のshoulはfalseを返し、2番目はtrueを返します。あなたのコードではこれはテストされていません。ここではほとんどの回答が常にfalseを返します。ソリューションが実際に機能していないように見えるのは残念です。
AlexWien 2014年

3
これらすべてがなぜs32_yそれがどのようなものであるかを説明するものの代わりにそのような曖昧な変数名を使用する理由を教えてくれpoint2YDifferenceますか?
Supuhstar 2017年

40

質問C:2つの線分が交差するかどうかをどのように検出しますか?

同じトピックを検索しましたが、答えに満足できませんでした。そこで、2つの線分が多くの画像と交差するかどうかを確認する方法を非常に詳細に説明する記事を書きました。完全な(そしてテスト済みの)Javaコードがあります。

これが最も重要な部分にトリミングされた記事です:

線分aが線分bと交差するかどうかをチェックするアルゴリズムは、次のようになります。

ここに画像の説明を入力してください

バウンディングボックスとは何ですか?2つの線分の2つの境界ボックスを次に示します。

ここに画像の説明を入力してください

両方の境界ボックスに交点がある場合は、1つの点が(0 | 0)になるように線分aを移動します。これで、aで定義された原点を通る線ができました。次に、線分bを同じ方法で移動し、線分bの新しい点が線aの異なる側にあるかどうかを確認します。この場合は、逆に確認してください。この場合も、線分が交差しています。そうでない場合、それらは交差しません。

質問A:2つの線分はどこで交差しますか?

2つの線分aとbが交差していることがわかります。それがわからない場合は、「質問C」で提供したツールで確認してください。

これで、いくつかのケースを通過し、7年生の数学で解を得ることができます(コードとインタラクティブな例を参照)。

質問B:2つの線が交差するかどうかをどのように検出しますか?

さんがあなたのポイントとしましょうA = (x1, y1)、ポイントをB = (x2, y2)C = (x_3, y_3)D = (x_4, y_4)。最初の行はAB(A!= B)で定義され、2行目はCD(C!= D)で定義されます。

function doLinesIntersect(AB, CD) {
    if (x1 == x2) {
        return !(x3 == x4 && x1 != x3);
    } else if (x3 == x4) {
        return true;
    } else {
        // Both lines are not parallel to the y-axis
        m1 = (y1-y2)/(x1-x2);
        m2 = (y3-y4)/(x3-x4);
        return m1 != m2;
    }
}

質問D:2つの線はどこで交差しますか?

交差するかどうかは、質問Bで確認してください。

線aとbは、各線の2つの点によって定義されます。基本的に、質問Aで使用したのと同じロジックを適用できます。


15
明確にするために、この回答の質問Bは、ラインセグメントではなく、交差する2つのラインについてです。私は文句を言っていません。間違いではありません。だれでも騙されてほしくない。
2013

1
「質問C」はありません。質問Dは唯一の質問A.上に戻ってバウンス
コンラートViltersten

21

ここで一度受け入れられた答えは正しくありません(それ以降受け入れられていないので、残念です!)。すべての非交差を正しく削除するわけではありません。当然、動作するように見えるかもしれませんが、特に0と1がhに対して有効であると見なされる場合は、失敗する可能性があります。

次のケースを考えてみましょう:

(4,1)-(5,1)および(0,0)-(0,2)の行

これらは明らかに重ならない垂直線です。

A =(4,1)
B =(5,1)
C =(0,0)
D =(0,2)
E =(5,1)-(4,1)=(-1,0)
F = (0,2)-(0,0)=(0、-2)
P =(0,1)
h =((4,1)-(0,0))ドット(0,1)/((0 、-2)ドット(0,1))= 0

上記の回答によれば、これらの2つの線分は端点(値0および1)で交わります。そのエンドポイントは次のようになります。

(0,0)+(0、-2)* 0 =(0,0)

したがって、2つの線分は(0,0)で交わるように見えます。(0,0)は線CDにありますが、線ABにはありません。では、何が問題になっていますか?その答えは、0と1の値は無効であり、エンドポイントの交差を正しく予測するために時々発生するだけであるということです。1本の線の延長(他の線ではない)が線セグメントと交わる場合、アルゴリズムは線セグメントの交差を予測しますが、これは正しくありません。AB対CDでテストし、次にCD対ABでテストすることにより、この問題は解消されると思います。両方が0と1の間に収まる場合にのみ、それらは交差していると言えます。

エンドポイントを予測する必要がある場合は、ベクトル外積法を使用することをお勧めします。

-ダン


4
「受け入れられた」答えは変わる可能性があるため、別の名前で呼ぶ必要があります。(実際、あなたのコメントから変更されたと思います)
Johannes Hoff

14

iMalcの答えのPythonバージョン:

def find_intersection( p0, p1, p2, p3 ) :

    s10_x = p1[0] - p0[0]
    s10_y = p1[1] - p0[1]
    s32_x = p3[0] - p2[0]
    s32_y = p3[1] - p2[1]

    denom = s10_x * s32_y - s32_x * s10_y

    if denom == 0 : return None # collinear

    denom_is_positive = denom > 0

    s02_x = p0[0] - p2[0]
    s02_y = p0[1] - p2[1]

    s_numer = s10_x * s02_y - s10_y * s02_x

    if (s_numer < 0) == denom_is_positive : return None # no collision

    t_numer = s32_x * s02_y - s32_y * s02_x

    if (t_numer < 0) == denom_is_positive : return None # no collision

    if (s_numer > denom) == denom_is_positive or (t_numer > denom) == denom_is_positive : return None # no collision


    # collision detected

    t = t_numer / denom

    intersection_point = [ p0[0] + (t * s10_x), p0[1] + (t * s10_y) ]


    return intersection_point

数字を浮動小数点数にするか、使用する行8を変更する必要があることを覚えておいてくださいdenom = float(...)
Jonno_FTW

11

2つのラインセグメントの正しい交点を見つけることは、多くのエッジケースを伴う重要なタスクです。これは、Javaで十分に文書化され、機能し、テストされたソリューションです。

本質的に、2つのラインセグメントの交点を見つけると、次の3つのことが起こります。

  1. セグメントが交差していません

  2. ユニークな交差点があります

  3. 交差点は別のセグメントです

:コードでは、x1 = x2およびy1 = y2のラインセグメント(x1、y1)、(x2、y2)が有効なラインセグメントであると想定しています。数学的に言えば、線分は別個の点で構成されますが、完全を期すために、この実装では線分を点にできます。

コードは私のgithubリポジトリから取得されます

/**
 * This snippet finds the intersection of two line segments.
 * The intersection may either be empty, a single point or the
 * intersection is a subsegment there's an overlap.
 */

import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;

import java.util.ArrayList;
import java.util.List;

public class LineSegmentLineSegmentIntersection {

  // Small epsilon used for double value comparison.
  private static final double EPS = 1e-5;

  // 2D Point class.
  public static class Pt {
    double x, y;
    public Pt(double x, double y) {
      this.x = x; 
      this.y = y;
    }
    public boolean equals(Pt pt) {
      return abs(x - pt.x) < EPS && abs(y - pt.y) < EPS;
    }
  }

  // Finds the orientation of point 'c' relative to the line segment (a, b)
  // Returns  0 if all three points are collinear.
  // Returns -1 if 'c' is clockwise to segment (a, b), i.e right of line formed by the segment.
  // Returns +1 if 'c' is counter clockwise to segment (a, b), i.e left of line
  // formed by the segment.
  public static int orientation(Pt a, Pt b, Pt c) {
    double value = (b.y - a.y) * (c.x - b.x) - 
                   (b.x - a.x) * (c.y - b.y);
    if (abs(value) < EPS) return 0;
    return (value > 0) ? -1 : +1;
  }

  // Tests whether point 'c' is on the line segment (a, b).
  // Ensure first that point c is collinear to segment (a, b) and
  // then check whether c is within the rectangle formed by (a, b)
  public static boolean pointOnLine(Pt a, Pt b, Pt c) {
    return orientation(a, b, c) == 0 && 
           min(a.x, b.x) <= c.x && c.x <= max(a.x, b.x) && 
           min(a.y, b.y) <= c.y && c.y <= max(a.y, b.y);
  }

  // Determines whether two segments intersect.
  public static boolean segmentsIntersect(Pt p1, Pt p2, Pt p3, Pt p4) {

    // Get the orientation of points p3 and p4 in relation
    // to the line segment (p1, p2)
    int o1 = orientation(p1, p2, p3);
    int o2 = orientation(p1, p2, p4);
    int o3 = orientation(p3, p4, p1);
    int o4 = orientation(p3, p4, p2);

    // If the points p1, p2 are on opposite sides of the infinite
    // line formed by (p3, p4) and conversly p3, p4 are on opposite
    // sides of the infinite line formed by (p1, p2) then there is
    // an intersection.
    if (o1 != o2 && o3 != o4) return true;

    // Collinear special cases (perhaps these if checks can be simplified?)
    if (o1 == 0 && pointOnLine(p1, p2, p3)) return true;
    if (o2 == 0 && pointOnLine(p1, p2, p4)) return true;
    if (o3 == 0 && pointOnLine(p3, p4, p1)) return true;
    if (o4 == 0 && pointOnLine(p3, p4, p2)) return true;

    return false;
  }

  public static List<Pt> getCommonEndpoints(Pt p1, Pt p2, Pt p3, Pt p4) {

    List<Pt> points = new ArrayList<>();

    if (p1.equals(p3)) {
      points.add(p1);
      if (p2.equals(p4)) points.add(p2);

    } else if (p1.equals(p4)) {
      points.add(p1);
      if (p2.equals(p3)) points.add(p2);

    } else if (p2.equals(p3)) {
      points.add(p2);
      if (p1.equals(p4)) points.add(p1);

    } else if (p2.equals(p4)) {
      points.add(p2);
      if (p1.equals(p3)) points.add(p1);
    }

    return points;
  }

  // Finds the intersection point(s) of two line segments. Unlike regular line 
  // segments, segments which are points (x1 = x2 and y1 = y2) are allowed.
  public static Pt[] lineSegmentLineSegmentIntersection(Pt p1, Pt p2, Pt p3, Pt p4) {

    // No intersection.
    if (!segmentsIntersect(p1, p2, p3, p4)) return new Pt[]{};

    // Both segments are a single point.
    if (p1.equals(p2) && p2.equals(p3) && p3.equals(p4))
      return new Pt[]{p1};

    List<Pt> endpoints = getCommonEndpoints(p1, p2, p3, p4);
    int n = endpoints.size();

    // One of the line segments is an intersecting single point.
    // NOTE: checking only n == 1 is insufficient to return early
    // because the solution might be a sub segment.
    boolean singleton = p1.equals(p2) || p3.equals(p4);
    if (n == 1 && singleton) return new Pt[]{endpoints.get(0)};

    // Segments are equal.
    if (n == 2) return new Pt[]{endpoints.get(0), endpoints.get(1)};

    boolean collinearSegments = (orientation(p1, p2, p3) == 0) && 
                                (orientation(p1, p2, p4) == 0);

    // The intersection will be a sub-segment of the two
    // segments since they overlap each other.
    if (collinearSegments) {

      // Segment #2 is enclosed in segment #1
      if (pointOnLine(p1, p2, p3) && pointOnLine(p1, p2, p4))
        return new Pt[]{p3, p4};

      // Segment #1 is enclosed in segment #2
      if (pointOnLine(p3, p4, p1) && pointOnLine(p3, p4, p2))
        return new Pt[]{p1, p2};

      // The subsegment is part of segment #1 and part of segment #2.
      // Find the middle points which correspond to this segment.
      Pt midPoint1 = pointOnLine(p1, p2, p3) ? p3 : p4;
      Pt midPoint2 = pointOnLine(p3, p4, p1) ? p1 : p2;

      // There is actually only one middle point!
      if (midPoint1.equals(midPoint2)) return new Pt[]{midPoint1};

      return new Pt[]{midPoint1, midPoint2};
    }

    /* Beyond this point there is a unique intersection point. */

    // Segment #1 is a vertical line.
    if (abs(p1.x - p2.x) < EPS) {
      double m = (p4.y - p3.y) / (p4.x - p3.x);
      double b = p3.y - m * p3.x;
      return new Pt[]{new Pt(p1.x, m * p1.x + b)};
    }

    // Segment #2 is a vertical line.
    if (abs(p3.x - p4.x) < EPS) {
      double m = (p2.y - p1.y) / (p2.x - p1.x);
      double b = p1.y - m * p1.x;
      return new Pt[]{new Pt(p3.x, m * p3.x + b)};
    }

    double m1 = (p2.y - p1.y) / (p2.x - p1.x);
    double m2 = (p4.y - p3.y) / (p4.x - p3.x);
    double b1 = p1.y - m1 * p1.x;
    double b2 = p3.y - m2 * p3.x;
    double x = (b2 - b1) / (m1 - m2);
    double y = (m1 * b2 - m2 * b1) / (m1 - m2);

    return new Pt[]{new Pt(x, y)};
  }

}

次に簡単な使用例を示します。

  public static void main(String[] args) {

    // Segment #1 is (p1, p2), segment #2 is (p3, p4)
    Pt p1, p2, p3, p4;

    p1 = new Pt(-2, 4); p2 = new Pt(3, 3);
    p3 = new Pt(0, 0);  p4 = new Pt(2, 4);
    Pt[] points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
    Pt point = points[0];

    // Prints: (1.636, 3.273)
    System.out.printf("(%.3f, %.3f)\n", point.x, point.y);

    p1 = new Pt(-10, 0); p2 = new Pt(+10, 0);
    p3 = new Pt(-5, 0);  p4 = new Pt(+5, 0);
    points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
    Pt point1 = points[0], point2 = points[1];

    // Prints: (-5.000, 0.000) (5.000, 0.000)
    System.out.printf("(%.3f, %.3f) (%.3f, %.3f)\n", point1.x, point1.y, point2.x, point2.y);
  }

それは私の地理座標系でうまくいきました!ありがとう!しかし、それは無限の線の交差のためのものであり、私は有限の線の交差をもっと探しています。
M.ウスマンカーン

8

良い説明と明確な解決策がNumeric Recipesシリーズにあることを述べたかっただけです。私は第3版を持っています。答えは1117ページのセクション21.4にあります。異なる命名法の別の解決策は、Marina Gavrilova Reliable Line Section Intersection Testingの論文にあります。彼女の解決策は、私の考えでは、少し単純です。

私の実装は以下です:

bool NuGeometry::IsBetween(const double& x0, const double& x, const double& x1){
   return (x >= x0) && (x <= x1);
}

bool NuGeometry::FindIntersection(const double& x0, const double& y0, 
     const double& x1, const double& y1,
     const double& a0, const double& b0, 
     const double& a1, const double& b1, 
     double& xy, double& ab) {
   // four endpoints are x0, y0 & x1,y1 & a0,b0 & a1,b1
   // returned values xy and ab are the fractional distance along xy and ab
   // and are only defined when the result is true

   bool partial = false;
   double denom = (b0 - b1) * (x0 - x1) - (y0 - y1) * (a0 - a1);
   if (denom == 0) {
      xy = -1;
      ab = -1;
   } else {
      xy = (a0 * (y1 - b1) + a1 * (b0 - y1) + x1 * (b1 - b0)) / denom;
      partial = NuGeometry::IsBetween(0, xy, 1);
      if (partial) {
         // no point calculating this unless xy is between 0 & 1
         ab = (y1 * (x0 - a1) + b1 * (x1 - x0) + y0 * (a1 - x1)) / denom; 
      }
   }
   if ( partial && NuGeometry::IsBetween(0, ab, 1)) {
      ab = 1-ab;
      xy = 1-xy;
      return true;
   }  else return false;
}

p1 =(0,0)、p2 =(10,0)、p3 =(9,0)、p4 =(20,0)では機能しません
padmalcom 2017

「うまくいかない」というあなたの定義次第ではないでしょうか。Denomは0なのでfalseを返しますが、交差しないので正しいと思われます。同一線上は交差と同じではありません。
marcp

8

上記のソリューションはたくさんありますが、以下のソリューションはかなりシンプルで理解しやすいと思います。

2つのセグメントVector ABとVector CDが交差するのは、

  1. 端点aとbは、セグメントCDの反対側にあります。
  2. 端点cとdは、線分ABの反対側にあります。

より具体的には、2つのトリプルa、c、dとb、c、dの1つが反時計回りの順序である場合に限り、aとbはセグメントCDの反対側にあります。

Intersect(a, b, c, d)
 if CCW(a, c, d) == CCW(b, c, d)
    return false;
 else if CCW(a, b, c) == CCW(a, b, d)
    return false;
 else
    return true;

ここでCCWは反時計回りを表し、ポイントの方向に基づいてtrue / falseを返します。

出典:http : //compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf ページ2


2
私はあなたがもう少し具体的であるべきだと思います:CCWテストはどのように定義されていますか?外積のサイン付き?
ocramz

ありがとう。この疑似コードにより、Scratchでの非常に簡単な実装が可能になりました。このプロジェクトを参照してください。scratch.mit.edu
Ruud Helderman

8

CおよびObjective-C

ガレス・リースの回答に基づく

const AGKLine AGKLineZero = (AGKLine){(CGPoint){0.0, 0.0}, (CGPoint){0.0, 0.0}};

AGKLine AGKLineMake(CGPoint start, CGPoint end)
{
    return (AGKLine){start, end};
}

double AGKLineLength(AGKLine l)
{
    return CGPointLengthBetween_AGK(l.start, l.end);
}

BOOL AGKLineIntersection(AGKLine l1, AGKLine l2, CGPoint *out_pointOfIntersection)
{
    // http://stackoverflow.com/a/565282/202451

    CGPoint p = l1.start;
    CGPoint q = l2.start;
    CGPoint r = CGPointSubtract_AGK(l1.end, l1.start);
    CGPoint s = CGPointSubtract_AGK(l2.end, l2.start);

    double s_r_crossProduct = CGPointCrossProductZComponent_AGK(r, s);
    double t = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), s) / s_r_crossProduct;
    double u = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), r) / s_r_crossProduct;

    if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
    {
        if(out_pointOfIntersection != NULL)
        {
            *out_pointOfIntersection = CGPointZero;
        }
        return NO;
    }
    else
    {
        if(out_pointOfIntersection != NULL)
        {
            CGPoint i = CGPointAdd_AGK(p, CGPointMultiply_AGK(r, t));
            *out_pointOfIntersection = i;
        }
        return YES;
    }
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointSubtract_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x - p2.x, p1.y - p2.y};
}

CGPoint CGPointAdd_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x + p2.x, p1.y + p2.y};
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointMultiply_AGK(CGPoint p1, CGFloat factor)
{
    return (CGPoint){p1.x * factor, p1.y * factor};
}

関数と構造体の多くはプライベートですが、何が起こっているかを簡単に知ることができるはずです。これはこのリポジトリで公開されていますhttps://github.com/hfossli/AGGeometryKit/


AGPointZeroはこのコードのどこから来ていますか?
シーニカス2014

1
@seanicusは、代わりにCGPointを使用するようにサンプルを更新しました
hfossli 2014

6

これは私にとってはうまく機能しています。ここから撮影。

 // calculates intersection and checks for parallel lines.  
 // also checks that the intersection point is actually on  
 // the line segment p1-p2  
 Point findIntersection(Point p1,Point p2,  
   Point p3,Point p4) {  
   float xD1,yD1,xD2,yD2,xD3,yD3;  
   float dot,deg,len1,len2;  
   float segmentLen1,segmentLen2;  
   float ua,ub,div;  

   // calculate differences  
   xD1=p2.x-p1.x;  
   xD2=p4.x-p3.x;  
   yD1=p2.y-p1.y;  
   yD2=p4.y-p3.y;  
   xD3=p1.x-p3.x;  
   yD3=p1.y-p3.y;    

   // calculate the lengths of the two lines  
   len1=sqrt(xD1*xD1+yD1*yD1);  
   len2=sqrt(xD2*xD2+yD2*yD2);  

   // calculate angle between the two lines.  
   dot=(xD1*xD2+yD1*yD2); // dot product  
   deg=dot/(len1*len2);  

   // if abs(angle)==1 then the lines are parallell,  
   // so no intersection is possible  
   if(abs(deg)==1) return null;  

   // find intersection Pt between two lines  
   Point pt=new Point(0,0);  
   div=yD2*xD1-xD2*yD1;  
   ua=(xD2*yD3-yD2*xD3)/div;  
   ub=(xD1*yD3-yD1*xD3)/div;  
   pt.x=p1.x+ua*xD1;  
   pt.y=p1.y+ua*yD1;  

   // calculate the combined length of the two segments  
   // between Pt-p1 and Pt-p2  
   xD1=pt.x-p1.x;  
   xD2=pt.x-p2.x;  
   yD1=pt.y-p1.y;  
   yD2=pt.y-p2.y;  
   segmentLen1=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // calculate the combined length of the two segments  
   // between Pt-p3 and Pt-p4  
   xD1=pt.x-p3.x;  
   xD2=pt.x-p4.x;  
   yD1=pt.y-p3.y;  
   yD2=pt.y-p4.y;  
   segmentLen2=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // if the lengths of both sets of segments are the same as  
   // the lenghts of the two lines the point is actually  
   // on the line segment.  

   // if the point isn’t on the line, return null  
   if(abs(len1-segmentLen1)>0.01 || abs(len2-segmentLen2)>0.01)  
     return null;  

   // return the valid intersection  
   return pt;  
 }  

 class Point{  
   float x,y;  
   Point(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  

   void set(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  
 }  

8
このコードにはいくつかの問題があります。ゼロ除算により例外が発生する可能性があります。それは平方根を取るので遅いです。また、ファッジファクターを使用するため、誤検知が発生する場合があります。あなたはこれよりも上手にできる!
Gareth Rees、

解決策としては問題ありませんが、Jasonによって与えられた解決策は間違いなく計算が速く、この解決策に関する多くの問題を回避します
Elemental

6

私はこれらの答えのいくつかを試しましたが、それらは私(申し訳ありませんが)にはうまくいきませんでした。もう少しネット検索した後、私はこれを見つけまし

彼のコードを少し変更して、交点を返すか、交点が見つからない場合は-1、-1を返すこの関数を用意しました。

    Public Function intercetion(ByVal ax As Integer, ByVal ay As Integer, ByVal bx As Integer, ByVal by As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal dx As Integer, ByVal dy As Integer) As Point
    '//  Determines the intersection point of the line segment defined by points A and B
    '//  with the line segment defined by points C and D.
    '//
    '//  Returns YES if the intersection point was found, and stores that point in X,Y.
    '//  Returns NO if there is no determinable intersection point, in which case X,Y will
    '//  be unmodified.

    Dim distAB, theCos, theSin, newX, ABpos As Double

    '//  Fail if either line segment is zero-length.
    If ax = bx And ay = by Or cx = dx And cy = dy Then Return New Point(-1, -1)

    '//  Fail if the segments share an end-point.
    If ax = cx And ay = cy Or bx = cx And by = cy Or ax = dx And ay = dy Or bx = dx And by = dy Then Return New Point(-1, -1)

    '//  (1) Translate the system so that point A is on the origin.
    bx -= ax
    by -= ay
    cx -= ax
    cy -= ay
    dx -= ax
    dy -= ay

    '//  Discover the length of segment A-B.
    distAB = Math.Sqrt(bx * bx + by * by)

    '//  (2) Rotate the system so that point B is on the positive X axis.
    theCos = bx / distAB
    theSin = by / distAB
    newX = cx * theCos + cy * theSin
    cy = cy * theCos - cx * theSin
    cx = newX
    newX = dx * theCos + dy * theSin
    dy = dy * theCos - dx * theSin
    dx = newX

    '//  Fail if segment C-D doesn't cross line A-B.
    If cy < 0 And dy < 0 Or cy >= 0 And dy >= 0 Then Return New Point(-1, -1)

    '//  (3) Discover the position of the intersection point along line A-B.
    ABpos = dx + (cx - dx) * dy / (dy - cy)

    '//  Fail if segment C-D crosses line A-B outside of segment A-B.
    If ABpos < 0 Or ABpos > distAB Then Return New Point(-1, -1)

    '//  (4) Apply the discovered position to line A-B in the original coordinate system.
    '*X=Ax+ABpos*theCos
    '*Y=Ay+ABpos*theSin

    '//  Success.
    Return New Point(ax + ABpos * theCos, ay + ABpos * theSin)
End Function

6

Gatin の回答にはいくらか興味があるようで、cortijon がコメントで javascriptバージョンを提案し、iMalcは計算が少し少ないバージョンを提供しましたです。さまざまなコード提案の欠点を指摘する人もいれば、一部のコード提案の効率性についてコメントする人もいます。

Gavinの答えを介してiMalcが提供するアルゴリズムは、現在JavaScriptプロジェクトで使用しているものであり、誰かを助けることができる場合は、ここでクリーンアップされたバージョンを提供したかっただけです。

// Some variables for reuse, others may do this differently
var p0x, p1x, p2x, p3x, ix,
    p0y, p1y, p2y, p3y, iy,
    collisionDetected;

// do stuff, call other functions, set endpoints...

// note: for my purpose I use |t| < |d| as opposed to
// |t| <= |d| which is equivalent to 0 <= t < 1 rather than
// 0 <= t <= 1 as in Gavin's answer - results may vary

var lineSegmentIntersection = function(){
    var d, dx1, dx2, dx3, dy1, dy2, dy3, s, t;

    dx1 = p1x - p0x;      dy1 = p1y - p0y;
    dx2 = p3x - p2x;      dy2 = p3y - p2y;
    dx3 = p0x - p2x;      dy3 = p0y - p2y;

    collisionDetected = 0;

    d = dx1 * dy2 - dx2 * dy1;

    if(d !== 0){
        s = dx1 * dy3 - dx3 * dy1;
        if((s <= 0 && d < 0 && s >= d) || (s >= 0 && d > 0 && s <= d)){
            t = dx2 * dy3 - dx3 * dy2;
            if((t <= 0 && d < 0 && t > d) || (t >= 0 && d > 0 && t < d)){
                t = t / d;
                collisionDetected = 1;
                ix = p0x + t * dx1;
                iy = p0y + t * dy1;
            }
        }
    }
};

私はあなたのような線で何が起こっているか理解することができますどのように理解していないt = dx2 * dy3 - dx3 * dy2;...
Supuhstar

@Supuhstarそれはベクトル数学とドット積とクロス積の定義に関係しています。たとえば、投稿したコードは製品間の操作を表しています。これは、1つのラインセグメントを別のラインセグメントに投影して、他のラインセグメントのどこにあるかを決定する方法です。したがって、tは正規化された値です。0と1の間の場合、2つのセグメントが交差しています。0未満または1より大きい場合は、そうではありません。
Nolo

@Supuhstarまた、投影で実際の点を見つけるには、結果をスケーリングする必要があることにも注意してください。だところt/dに入っています。
NOLO

1
つまり、そのような変数名を見て、何が起こっているのかをどのように理解するのでしょうか。なぜないようなものcrossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)scaledResult = crossProduct / dotProduct
Supuhstar 2017年

1
@Supuhstarああ、私はあなたが何を意味するのか分かります。えーと、まあ、効率性にこだわる以上の話をする理由は本当にないと思いますが、コンパイラーは、与えられたほとんどのコードを取り、それを何を計算すべきかを変えずに可能です。一方、名前p1x, p1yなどは、x値とy値でポイントを説明するためのものなのでp1x、の略語point1xでもあります。同様d1xに、私の心では、ギリシャ文字の略語deltaXか、と言うこともできますdifferenceInX。(詳細)
Nolo

5

この問題にはもっと簡単な解決策があると思います。今日は別のアイデアを思いつきましたが、それは(少なくとも今のところ2Dでは)うまく機能しているようです。あなたがしなければならないのは、2本の線の間の交点を計算し、計算された交点が両方の線分の境界ボックス内にあるかどうかを確認することです。そうである場合、線分は交差します。それでおしまい。

編集:

これは私が交差点を計算する方法です(私はこのコードスニペットをどこで見つけたかもうわかりません)

Point3D

から来た

System.Windows.Media.Media3D

public static Point3D? Intersection(Point3D start1, Point3D end1, Point3D start2, Point3D end2) {

        double a1 = end1.Y - start1.Y;
        double b1 = start1.X - end1.X;
        double c1 = a1 * start1.X + b1 * start1.Y;

        double a2 = end2.Y - start2.Y;
        double b2 = start2.X - end2.X;
        double c2 = a2 * start2.X + b2 * start2.Y;

        double det = a1 * b2 - a2 * b1;
        if (det == 0) { // lines are parallel
            return null;
        }

        double x = (b2 * c1 - b1 * c2) / det;
        double y = (a1 * c2 - a2 * c1) / det;

        return new Point3D(x, y, 0.0);
    }

これは私の(答えのために簡略化した)BoundingBoxクラスです。

public class BoundingBox {
    private Point3D min = new Point3D();
    private Point3D max = new Point3D();

    public BoundingBox(Point3D point) {
        min = point;
        max = point;
    }

    public Point3D Min {
        get { return min; }
        set { min = value; }
    }

    public Point3D Max {
        get { return max; }
        set { max = value; }
    }

    public bool Contains(BoundingBox box) {
        bool contains =
            min.X <= box.min.X && max.X >= box.max.X &&
            min.Y <= box.min.Y && max.Y >= box.max.Y &&
            min.Z <= box.min.Z && max.Z >= box.max.Z;
        return contains;
    }

    public bool Contains(Point3D point) {
        return Contains(new BoundingBox(point));
    }

}

3

このソリューションは役立つかもしれません

public static float GetLineYIntesept(PointF p, float slope)
    {
        return p.Y - slope * p.X;
    }

    public static PointF FindIntersection(PointF line1Start, PointF line1End, PointF line2Start, PointF line2End)
    {

        float slope1 = (line1End.Y - line1Start.Y) / (line1End.X - line1Start.X);
        float slope2 = (line2End.Y - line2Start.Y) / (line2End.X - line2Start.X);

        float yinter1 = GetLineYIntesept(line1Start, slope1);
        float yinter2 = GetLineYIntesept(line2Start, slope2);

        if (slope1 == slope2 && yinter1 != yinter2)
            return PointF.Empty;

        float x = (yinter2 - yinter1) / (slope1 - slope2);

        float y = slope1 * x + yinter1;

        return new PointF(x, y);
    }

3

上記のクリスの答えをJavaScriptに移植しました。多くの異なる答えを試みた後、彼は正しいポイントを提供しました。自分が必要なポイントを獲得できていなかったことに夢中になっていたと思いました。

function getLineLineCollision(p0, p1, p2, p3) {
    var s1, s2;
    s1 = {x: p1.x - p0.x, y: p1.y - p0.y};
    s2 = {x: p3.x - p2.x, y: p3.y - p2.y};

    var s10_x = p1.x - p0.x;
    var s10_y = p1.y - p0.y;
    var s32_x = p3.x - p2.x;
    var s32_y = p3.y - p2.y;

    var denom = s10_x * s32_y - s32_x * s10_y;

    if(denom == 0) {
        return false;
    }

    var denom_positive = denom > 0;

    var s02_x = p0.x - p2.x;
    var s02_y = p0.y - p2.y;

    var s_numer = s10_x * s02_y - s10_y * s02_x;

    if((s_numer < 0) == denom_positive) {
        return false;
    }

    var t_numer = s32_x * s02_y - s32_y * s02_x;

    if((t_numer < 0) == denom_positive) {
        return false;
    }

    if((s_numer > denom) == denom_positive || (t_numer > denom) == denom_positive) {
        return false;
    }

    var t = t_numer / denom;

    var p = {x: p0.x + (t * s10_x), y: p0.y + (t * s10_y)};
    return p;
}

2

私はいろいろな方法を試しましたが、自分で書くことにしました。だからここにあります:

bool IsBetween (float x, float b1, float b2)
{
   return ( ((x >= (b1 - 0.1f)) && 
        (x <= (b2 + 0.1f))) || 
        ((x >= (b2 - 0.1f)) &&
        (x <= (b1 + 0.1f))));
}

bool IsSegmentsColliding(   POINTFLOAT lineA,
                POINTFLOAT lineB,
                POINTFLOAT line2A,
                POINTFLOAT line2B)
{
    float deltaX1 = lineB.x - lineA.x;
    float deltaX2 = line2B.x - line2A.x;
    float deltaY1 = lineB.y - lineA.y;
    float deltaY2 = line2B.y - line2A.y;

    if (abs(deltaX1) < 0.01f && 
        abs(deltaX2) < 0.01f) // Both are vertical lines
        return false;
    if (abs((deltaY1 / deltaX1) -
        (deltaY2 / deltaX2)) < 0.001f) // Two parallel line
        return false;

    float xCol = (  (   (deltaX1 * deltaX2) * 
                        (line2A.y - lineA.y)) - 
                    (line2A.x * deltaY2 * deltaX1) + 
                    (lineA.x * deltaY1 * deltaX2)) / 
                 ((deltaY1 * deltaX2) - (deltaY2 * deltaX1));
    float yCol = 0;
    if (deltaX1 < 0.01f) // L1 is a vertical line
        yCol = ((xCol * deltaY2) + 
                (line2A.y * deltaX2) - 
                (line2A.x * deltaY2)) / deltaX2;
    else // L1 is acceptable
        yCol = ((xCol * deltaY1) +
                (lineA.y * deltaX1) -
                (lineA.x * deltaY1)) / deltaX1;

    bool isCol =    IsBetween(xCol, lineA.x, lineB.x) &&
            IsBetween(yCol, lineA.y, lineB.y) &&
            IsBetween(xCol, line2A.x, line2B.x) &&
            IsBetween(yCol, line2A.y, line2B.y);
    return isCol;
}

これらの2つの数式に基づいて:(私は直線と他の数式からそれらを簡略化しました)

xの式

yの式


機能しますが、この座標を入力しようとします(同一直線上/重複している場合、誤った結果が返されます):PointA1 =(0,0)PointA2 =(0,2)およびPointB1 =(0,1)PointB2 =(0,5)
dns

@dnsまあ、それはコードが平行線に対してfalseを返すためです。問題はあると思いますが、答えは無限にあるので、関数が何を返すべきかわかりません。
Soroush Falahati 2017

2

これはGareth Reeの回答に基づいています。また、ラインセグメントが重複している場合は、それらを返します。C ++でコーディングされたVは、単純なベクトルクラスです。2Dの2つのベクトルの外積が単一のスカラーを返す場合。それは私の学校の自動テストシステムによってテストされ、合格しました。

//Required input point must be colinear with the line
bool on_segment(const V& p, const LineSegment& l)
{
    //If a point is on the line, the sum of the vectors formed by the point to the line endpoints must be equal
    V va = p - l.pa;
    V vb = p - l.pb;
    R ma = va.magnitude();
    R mb = vb.magnitude();
    R ml = (l.pb - l.pa).magnitude();
    R s = ma + mb;
    bool r = s <= ml + epsilon;
    return r;
}

//Compute using vector math
// Returns 0 points if the lines do not intersect or overlap
// Returns 1 point if the lines intersect
//  Returns 2 points if the lines overlap, contain the points where overlapping start starts and stop
std::vector<V> intersect(const LineSegment& la, const LineSegment& lb)
{
    std::vector<V> r;

    //http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    V oa, ob, da, db; //Origin and direction vectors
    R sa, sb; //Scalar values
    oa = la.pa;
    da = la.pb - la.pa;
    ob = lb.pa;
    db = lb.pb - lb.pa;

    if (da.cross(db) == 0 && (ob - oa).cross(da) == 0) //If colinear
    {
        if (on_segment(lb.pa, la) && on_segment(lb.pb, la))
        {
            r.push_back(lb.pa);
            r.push_back(lb.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb) && on_segment(la.pb, lb))
        {
            r.push_back(la.pa);
            r.push_back(la.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb))
            r.push_back(la.pa);

        if (on_segment(la.pb, lb))
            r.push_back(la.pb);

        if (on_segment(lb.pa, la))
            r.push_back(lb.pa);

        if (on_segment(lb.pb, la))
            r.push_back(lb.pb);

        if (r.size() == 0)
            dprintf("colinear, non-overlapping\n");
        else
            dprintf("colinear, overlapping\n");

        return r;
    }

    if (da.cross(db) == 0 && (ob - oa).cross(da) != 0)
    {
        dprintf("parallel non-intersecting\n");
        return r;
    }

    //Math trick db cross db == 0, which is a single scalar in 2D.
    //Crossing both sides with vector db gives:
    sa = (ob - oa).cross(db) / da.cross(db);

    //Crossing both sides with vector da gives
    sb = (oa - ob).cross(da) / db.cross(da);

    if (0 <= sa && sa <= 1 && 0 <= sb && sb <= 1)
    {
        dprintf("intersecting\n");
        r.push_back(oa + da * sa);
        return r;
    }

    dprintf("non-intersecting, non-parallel, non-colinear, non-overlapping\n");
    return r;
}

2

これは、C#でのラインセグメントの基本的な実装と、対応する交差点検出コードです。これにはと呼ばれる2Dベクトル/ポイント構造体Vector2fが必要ですが、これをX / Yプロパティを持つ他のタイプに置き換えることができます。また、置き換えることができfloatdouble、より良いスーツがニーズいる場合。

このコードは、.NET物理ライブラリーBoingで使用されています。

public struct LineSegment2f
{
    public Vector2f From { get; }
    public Vector2f To { get; }

    public LineSegment2f(Vector2f @from, Vector2f to)
    {
        From = @from;
        To = to;
    }

    public Vector2f Delta => new Vector2f(To.X - From.X, To.Y - From.Y);

    /// <summary>
    /// Attempt to intersect two line segments.
    /// </summary>
    /// <remarks>
    /// Even if the line segments do not intersect, <paramref name="t"/> and <paramref name="u"/> will be set.
    /// If the lines are parallel, <paramref name="t"/> and <paramref name="u"/> are set to <see cref="float.NaN"/>.
    /// </remarks>
    /// <param name="other">The line to attempt intersection of this line with.</param>
    /// <param name="intersectionPoint">The point of intersection if within the line segments, or empty..</param>
    /// <param name="t">The distance along this line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <param name="u">The distance along the other line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <returns><c>true</c> if the line segments intersect, otherwise <c>false</c>.</returns>
    public bool TryIntersect(LineSegment2f other, out Vector2f intersectionPoint, out float t, out float u)
    {
        var p = From;
        var q = other.From;
        var r = Delta;
        var s = other.Delta;

        // t = (q − p) × s / (r × s)
        // u = (q − p) × r / (r × s)

        var denom = Fake2DCross(r, s);

        if (denom == 0)
        {
            // lines are collinear or parallel
            t = float.NaN;
            u = float.NaN;
            intersectionPoint = default(Vector2f);
            return false;
        }

        var tNumer = Fake2DCross(q - p, s);
        var uNumer = Fake2DCross(q - p, r);

        t = tNumer / denom;
        u = uNumer / denom;

        if (t < 0 || t > 1 || u < 0 || u > 1)
        {
            // line segments do not intersect within their ranges
            intersectionPoint = default(Vector2f);
            return false;
        }

        intersectionPoint = p + r * t;
        return true;
    }

    private static float Fake2DCross(Vector2f a, Vector2f b)
    {
        return a.X * b.Y - a.Y * b.X;
    }
}

1

与えられた2つの線分が交差するかどうかをチェックするC ++プログラム

#include <iostream>
using namespace std;

struct Point
{
    int x;
    int y;
};

// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Point p, Point q, Point r)
{
    if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
        q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
       return true;

    return false;
}

// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
    // See 10th slides from following link for derivation of the formula
    // http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf
    int val = (q.y - p.y) * (r.x - q.x) -
              (q.x - p.x) * (r.y - q.y);

    if (val == 0) return 0;  // colinear

    return (val > 0)? 1: 2; // clock or counterclock wise
}

// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Point p1, Point q1, Point p2, Point q2)
{
    // Find the four orientations needed for general and
    // special cases
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);

    // General case
    if (o1 != o2 && o3 != o4)
        return true;

    // Special Cases
    // p1, q1 and p2 are colinear and p2 lies on segment p1q1
    if (o1 == 0 && onSegment(p1, p2, q1)) return true;

    // p1, q1 and p2 are colinear and q2 lies on segment p1q1
    if (o2 == 0 && onSegment(p1, q2, q1)) return true;

    // p2, q2 and p1 are colinear and p1 lies on segment p2q2
    if (o3 == 0 && onSegment(p2, p1, q2)) return true;

     // p2, q2 and q1 are colinear and q1 lies on segment p2q2
    if (o4 == 0 && onSegment(p2, q1, q2)) return true;

    return false; // Doesn't fall in any of the above cases
}

// Driver program to test above functions
int main()
{
    struct Point p1 = {1, 1}, q1 = {10, 1};
    struct Point p2 = {1, 2}, q2 = {10, 2};

    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {10, 0}, q1 = {0, 10};
    p2 = {0, 0}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {-5, -5}, q1 = {0, 0};
    p2 = {1, 1}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    return 0;
}

1

@Gareth Reesの回答に基づいて、Pythonのバージョン:

import numpy as np

def np_perp( a ) :
    b = np.empty_like(a)
    b[0] = a[1]
    b[1] = -a[0]
    return b

def np_cross_product(a, b):
    return np.dot(a, np_perp(b))

def np_seg_intersect(a, b, considerCollinearOverlapAsIntersect = False):
    # /programming/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
    # http://www.codeproject.com/Tips/862988/Find-the-intersection-point-of-two-line-segments
    r = a[1] - a[0]
    s = b[1] - b[0]
    v = b[0] - a[0]
    num = np_cross_product(v, r)
    denom = np_cross_product(r, s)
    # If r x s = 0 and (q - p) x r = 0, then the two lines are collinear.
    if np.isclose(denom, 0) and np.isclose(num, 0):
        # 1. If either  0 <= (q - p) * r <= r * r or 0 <= (p - q) * s <= * s
        # then the two lines are overlapping,
        if(considerCollinearOverlapAsIntersect):
            vDotR = np.dot(v, r)
            aDotS = np.dot(-v, s)
            if (0 <= vDotR  and vDotR <= np.dot(r,r)) or (0 <= aDotS  and aDotS <= np.dot(s,s)):
                return True
        # 2. If neither 0 <= (q - p) * r = r * r nor 0 <= (p - q) * s <= s * s
        # then the two lines are collinear but disjoint.
        # No need to implement this expression, as it follows from the expression above.
        return None
    if np.isclose(denom, 0) and not np.isclose(num, 0):
        # Parallel and non intersecting
        return None
    u = num / denom
    t = np_cross_product(v, s) / denom
    if u >= 0 and u <= 1 and t >= 0 and t <= 1:
        res = b[0] + (s*u)
        return res
    # Otherwise, the two line segments are not parallel but do not intersect.
    return None

0

長方形の各辺が線分であり、ユーザーが描いた部分が線分である場合、ユーザーが描いた線分と4つの辺の線分との交差をチェックするだけです。これは、各セグメントの開始点と終了点を考えると、かなり単純な練習になるはずです。


3
これは元々フレーム化された質問に対する妥当な回答でしたが、質問は大幅に編集されているため、あまり意味がありません。
GS-

0

t3chb0tの答えに基づいて:

int intersezione_linee(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
   //L1: estremi (x1,y1)(x2,y2) L2: estremi (x3,y3)(x3,y3)
   int d;
   d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
   if(!d)
       return 0;
   p_x = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/d;
   p_y = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/d;
   return 1;
}

int in_bounding_box(int x1, int y1, int x2, int y2, int p_x, int p_y)
{
    return p_x>=x1 && p_x<=x2 && p_y>=y1 && p_y<=y2;

}

int intersezione_segmenti(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
    if (!intersezione_linee(x1,y1,x2,y2,x3,y3,x4,y4,p_x,p_y))
        return 0;

    return in_bounding_box(x1,y1,x2,y2,p_x,p_y) && in_bounding_box(x3,y3,x4,y4,p_x,p_y);
}

0

これらのアルゴリズムを「マルチビュージオメトリ」という本から読みました

次のテキストを使用して

'転置記号として

*ドット積として

xをクロス演算子として、演算子として使用する場合

1.行の定義

点x_vec =(x、y) 'はax + by + c = 0の直線上にあります

L =(a、b、c) '、点を(x、y、1)'として同次座標として表す

線方程式は次のように書くことができます

(x、y、1)(a、b、c) '= 0またはx' * L = 0

2.ラインの交差

L1 =(a1、b1、c1) '、L2 =(a2、b2、c2)'の2つの行があります

xが点、ベクトルであり、x = L1 x L2(L1クロス積L2)であると仮定します。

注意してください。xは常に2Dの点です。(L1xL2)が3要素ベクトルであり、xが2D座標であると混乱する場合は、同次座標をお読みください。

トリプル積によれば、

L1 *(L1 x L2)= 0、およびL2 *(L1 x L2)= 0、L1、L2同一平面のため

(L1xL2)をベクトルxで置き換えると、L1 * x = 0、L2 * x = 0となります。これは、xがL1とL2の両方にあることを意味し、xは交点です。

ここで、xは同次座標です。xの最後の要素がゼロの場合、L1とL2が平行であることを意味します。


0

多くの回答がすべての計算を1つの関数にまとめています。コードの他の場所で使用するために線の傾き、y切片、またはx切片を計算する必要がある場合は、それらの計算を重複して行うことになります。それぞれの関数を分離し、明白な変数名を使用し、コードをコメントしてわかりやすくしました。ラインがエンドポイントを超えて無限に交差するかどうかを知る必要があったので、JavaScriptで:

http://jsfiddle.net/skibulk/evmqq00u/

var point_a = {x:0, y:10},
    point_b = {x:12, y:12},
    point_c = {x:10, y:0},
    point_d = {x:0, y:0},
    slope_ab = slope(point_a, point_b),
    slope_bc = slope(point_b, point_c),
    slope_cd = slope(point_c, point_d),
    slope_da = slope(point_d, point_a),
    yint_ab = y_intercept(point_a, slope_ab),
    yint_bc = y_intercept(point_b, slope_bc),
    yint_cd = y_intercept(point_c, slope_cd),
    yint_da = y_intercept(point_d, slope_da),
    xint_ab = x_intercept(point_a, slope_ab, yint_ab),
    xint_bc = x_intercept(point_b, slope_bc, yint_bc),
    xint_cd = x_intercept(point_c, slope_cd, yint_cd),
    xint_da = x_intercept(point_d, slope_da, yint_da),
    point_aa = intersect(slope_da, yint_da, xint_da, slope_ab, yint_ab, xint_ab),
    point_bb = intersect(slope_ab, yint_ab, xint_ab, slope_bc, yint_bc, xint_bc),
    point_cc = intersect(slope_bc, yint_bc, xint_bc, slope_cd, yint_cd, xint_cd),
    point_dd = intersect(slope_cd, yint_cd, xint_cd, slope_da, yint_da, xint_da);

console.log(point_a, point_b, point_c, point_d);
console.log(slope_ab, slope_bc, slope_cd, slope_da);
console.log(yint_ab, yint_bc, yint_cd, yint_da);
console.log(xint_ab, xint_bc, xint_cd, xint_da);
console.log(point_aa, point_bb, point_cc, point_dd);

function slope(point_a, point_b) {
  var i = (point_b.y - point_a.y) / (point_b.x - point_a.x);
  if (i === -Infinity) return Infinity;
  if (i === -0) return 0;
  return i;
}

function y_intercept(point, slope) {
    // Horizontal Line
    if (slope == 0) return point.y;
  // Vertical Line
    if (slope == Infinity)
  {
    // THE Y-Axis
    if (point.x == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return point.y - (slope * point.x);
}

function x_intercept(point, slope, yint) {
    // Vertical Line
    if (slope == Infinity) return point.x;
  // Horizontal Line
    if (slope == 0)
  {
    // THE X-Axis
    if (point.y == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return -yint / slope;
}

// Intersection of two infinite lines
function intersect(slope_a, yint_a, xint_a, slope_b, yint_b, xint_b) {
  if (slope_a == slope_b)
  {
    // Equal Lines
    if (yint_a == yint_b && xint_a == xint_b) return Infinity;
    // Parallel Lines
    return null;
  }
  // First Line Vertical
    if (slope_a == Infinity)
  {
    return {
        x: xint_a,
      y: (slope_b * xint_a) + yint_b
    };
  }
  // Second Line Vertical
    if (slope_b == Infinity)
  {
    return {
        x: xint_b,
      y: (slope_a * xint_b) + yint_a
    };
  }
  // Not Equal, Not Parallel, Not Vertical
  var i = (yint_b - yint_a) / (slope_a - slope_b);
  return {
    x: i,
    y: (slope_a * i) + yint_a
  };
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.