みなさんいかがお過ごしでしょうか(^q^)
すごく間が開いてしまいましたが、線分どうしの当たり判定いってみましょう(^q^)
線分どうしの当たり判定
線分同士の関係として、平行な時とそうでないときに分けて考えることにしましょう。
というのも、線分を延長したものである直線には、重要な性質があるからです。
平行でないとき
まず、平行でないときについて考えましょう。図のAB,CD,EFについて考えます。 線分の両端をずっと延長すると直線となります。直線には、「平行でない場合必ず1点で交わる」という性質があります。
もちろん直線の場合なので、線分ならばちゃんとその範囲に収まっていなければなりません。
その交点が AB(もしくはCD)上にあれば、はじめて2つの線分は当たっていることになります。
図では、ABとCDの交点はそれぞれの線分上にあるので当たっています。しかし、CDとEFの交点は線分から外れていますので、当たっていません。
以下、図の点X1 をXと表します。
いま、実数 s,t によって、AX = sAB, CX = tCD と表すと、 0≦s≦1, 0≦t≦1 ならば線分は当たっていることになります。s,tの値を求めれば良いわけです。
いま、AX = AC+CX = AC+tCD です。あるベクトルが、もう一つのベクトルの実数倍のときはそれらは平行なので、ベクトルAB、(AC+tCD)は平行です。
平行なベクトルのウェッジ積は0となるので、AB∧(AC+tCD) = 0 です。これを変形して、tを求めましょう。
AB∧(AC+tCD) = 0
AB∧AC + tAB∧CD = 0 (分配法則)
t = -AB∧AC / AB∧CD
つまり、tに関する条件は 0≦-AB∧AC / AB∧CD≦1 となります。
これと同じ操作を CX = CA+AX = CA+sAB にも使うと、sに関する条件は0≦-CD∧CA / CD∧AB≦1 です。
2つの線分が平行でないときは、次のことが言えることになります。
線分AB,CDについて、
0≦-AB∧AC / AB∧CD≦1 かつ 0≦-CD∧CA / CD∧AB≦1 ならば、当たっている。
-AB∧AC / AB∧CD = AB∧CA / AB∧CD
-CD∧CA / CD∧AB = CD∧AC / CD∧AB
と変形して使ってもよいでしょう。(ウェッジ積の諸法則より)
平行なとき
float型などを使っているときは計算誤差などの関係で線分どうしがぴったり平行になることはあまりありませんが、int型で表現している場合はよくなります。
ABとCDが平行なときは、tの値の分母AB∧CD=0 なので、求めることができません。場合分けをしていきます。 平行な場合は、直線として一致している場合とそうでない場合があります。
ABとEFのように一致していない場合は、線分の交点もないことがわかります。
ABとCDのように直線として一致している場合は、線分が重なっている可能性があります。
一致している場合とそうでない場合の区別をつけるには、ウェッジ積を使うことができます。
一致していないABとEFに関して、たとえばABとAEでウェッジ積を取ると、平行でないのでAB∧AE≠0です。
一致しているABとCDに関しては、AB∧AC=0 です。
こうして、始点→終点 のベクトルと 始点→もう一方の始点 のベクトルのウェッジ積が0かどうかで区別することができます。
さて、直線として一致している場合はどう判定すればよいでしょう。
直線上では、x座標とy座標が比例するので、x座標だけを考えて比較することができます。
簡単のため、ABでx座標のより小さい方をA、CDでx座標のより小さい方をC としましょう。
プログラムでは始点にx座標の大きいデータがセットされることもあるので、始点・終点にかかわらず、より左にある点を得るためのメンバ関数などを用意するとよいでしょう。
また、点A,B,C,D のx座標をそれぞれ a,b,c,d とします。
「AB hit CD ⇔ AB上にあり、CD上にもある点Pが存在する」なので、点Pのx座標をpとすると、
a≦p≦b, c≦p≦d が条件になります。pの値は分からないので、消去していきます。
条件より、a≦p≦d, c≦p≦b がいえます。よって、a≦d, c≦b とすることができますので、これを条件としましょう。
2つの線分AB,CDが平行のとき、A,B,C,Dのx座標をそれぞれa,b,c,d (a≦b,c≦d)とすると、
AB∧AC = 0, a≦d, c≦b ならば当たっている。
それと、ABとCDが平行かどうかを調べる方法も必要ですね。ウェッジ積で平行を調べることができるので、AB∧CD=0 かどうかで判定しましょう。
線分の長さが0のときも平行と判定されてしまいますが、「平行なとき」のアルゴリズムが適用できると思うので問題ないでしょう。
まとめ
以上のことをまとめてみましょう。
線分AB, CDについて、
AB∧CD ≠ 0 のとき
0 ≦ AB∧CA / AB∧CD ≦ 1 かつ 0 ≦ CD∧AC / CD∧AB ≦ 1 ならば、当たっている。
AB∧CD = 0 のとき、点A,B,C,Dのx座標をそれぞれa,b,c,d (a≦b,c≦d)とすると、
AB∧AC = 0, a≦d, c≦b ならば当たっている。
あいかわらず読む気の失せる文章ですが、お役に立てれば幸いです(^q^)
参考
線分どうしの当たり判定を考えるのがけっこうキツかったので、以下の文章を参考にしました・・・というか、平行でない場合の考え方はまったく一緒なんですが(^q^)
線分と線分の衝突 from ○×つくろーどっとコム
プログラムの例も置いときますね(^q^)
► スポイラーを表示