2本の線(および線セグメント)の交点を見つける

前書き



多くの場合、ゲームを開発するときに、直線、セグメント、光線などの交点を見つける必要があります。この記事では、これを可能な限り簡単な方法で実装する方法を説明します。



画像



人気のある方法とその批判



おそらく多くの人が学校の代数からの方法を覚えているでしょう-2つの直線の方程式を作り、それらの右辺を等しくし、xを見つけ、それを直線の方程式に代入してyを見つけます(詳細はこちら)。



ただし、この方法はコードを書くときに非常に面倒になります(おそらくこれがこの記事を今読んでいる理由です)、さらに、それは普遍的ではありません:直線の1つがY軸に平行である場合、勾配を計算するときにゼロによる除算エラーが発生するため、この場合のコードを登録します。 2つの線が平行である場合は、このケースの処理もいじくり回す必要があります。そのようなコードは長くて醜くなります。



この問題に対するより洗練された解決策を探して、私はベクトル乗算に基づくいくつかの非常に興味深い方法に出くわしました(habr.com/ru/post/267037)およびレイキャスティング(ru.wikipedia.org/wiki/Ray_casting#Concept)。しかし、私の意見では、それらは計算上不必要に複雑です。したがって、私はあなたの注意(そして批判)に私の方法を提示します。



私のやり方



仕事



2つのセグメントの座標が示されています。セグメントが交差するかどうか、交差する場合はどの時点で交差するかを確認する必要があります。この目的のために、関数を記述します。



決定



画像



誤解を避けるための凡例:a-ベクトルa、a(y)-ベクトルaのY軸への投影、a {x1、y1}-ベクトルa、座標x1、y1で指定。



セグメントを2つのベクトルの形式で表現しましょう。a{x2-x1; y2-y1}およびb {x3-x4; y3-x4}。ベクトルbは、予想とは反対の方向にあることに注意してください。ベクトルc {x3-x1;を紹介しましょう。 y3-y1}。 a * k + b * n = cであることに注意してください。ここで、k、nはいくつかの係数です。したがって、次の方程式のシステムが得られます。a



(x)* k + b(x)* n = c(x)

a(y)* k + b(y)* n = c(y)

私たちのタスクは、これらの係数を見つけることになります。 (実を言うと、そのうちの1つだけを見つけるだけで十分です)。



下の式の両辺にq = -a(x)/ a(y)を掛けることを提案します。したがって、2つの方程式を追加した後、すぐにkを取り除きます。nを見つけることは、通常の線形方程式を解くことに還元されます。nには解がない場合があることに注意することが重要です。



注意深い読者は、(y)= 0の場合、エラーが発生することに気付くでしょう。(y)を見つける段階で分岐を書いてみましょう。このケースはさらに単純です。なぜなら、1つの未知の方程式がすぐに得られるからです。



自分でnを出力することをお勧めします。そうすれば、以下のコードから何が得られるかが明確になります。



nがわかれば、交点を見つけることができます。このために、点(x3、y3)の座標からベクトルb * nを減算します。



それを一緒に入れて



float dot[2];  //  

bool cross(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
    float n;
    if (y2 - y1 != 0) {  // a(y)
        float q = (x2 - x1) / (y1 - y2);   
        float sn = (x3 - x4) + (y3 - y4) * q; if (!sn) { return 0; }  // c(x) + c(y)*q
        float fn = (x3 - x1) + (y3 - y1) * q;   // b(x) + b(y)*q
        n = fn / sn;
    }
    else {
        if (!(y3 - y4)) { return 0; }  // b(y)
        n = (y3 - y1) / (y3 - y4);   // c(y)/b(y)
    }
    dot[0] = x3 + (x4 - x3) * n;  // x3 + (-b(x))*n
    dot[1] = y3 + (y4 - y3) * n;  // y3 +(-b(y))*n
    return 1;
}


この関数は、頂点の座標を取得し、セグメントの直線(つまり、直線)が交差する場合は値1を返し、交差しない場合は値0を返します。頂点の座標が必要な場合は、ドット[]配列から取得できます。



重要:2つの一致する線を導入する場合、アルゴリズムは交差を表示しません。アルゴリズムは、ラインセグメントが存在するラインの交点を見つけるため、そのポイントはセグメントの外側にある可能性があります(コードをチェックインする必要があります)。



関数を適用してみましょう:



int main() {
    if (cross(1,1,7,2, 7,3,5,6)) {
        std::cout << dot[0] << " " << dot[1] << std::endl;
    }
    else {
        std::cout<<"Not cross!"<<std::endl;
    }
    return 0;
}


あとがき



私は自分の問題をグーグルで調べてアルゴリズムを自分で開発する過程でこの方法に出くわしませんでしたが、完全にオリジナル(そして正しい)のふりをしていません。コメントへようこそ!



All Articles