直線と直線の交点
この記事では直線と直線の交点の求め方についてまとめています。
考え方
2つの直線上の任意の点を $A, B$、直線の交点を $P$ とします。
それぞれの直線の向きを表すベクトルを $\vec{v_{1}}, \vec{v_{2}} $ 、$A$ から $B$ に向かうベクトルを $\vec{v}$ とします。
$A$ から $P$ に向かうベクトルを $\vec{v_{1}'}$ とします。
$B$ から $P$ に向かうベクトルを考えます。
$\vec{v_{2}}$ を何倍かすれば、$P$ の位置にくることになりますが、何倍にすればいいかはまだわからないので、この数値を $t$ としておきます。
すると $B$ から $P$ に向かうベクトルは $t\vec{v_{2}}$ と書くことができます。
$\vec{v_{1}'}$ は $\vec{v}$ に $t\vec{v_{2}}$ を足したものと表現できるので
$ \vec{v_{1}'} = \vec{v} + t\vec{v_{2}} $
と書くことができます。
$t$ の値が何になるのか、まだわかりませんが $t$ の値が分かってしまえば
$P$ の 位置は $B$ に $t\vec{v_{2}}$ を足した場所なので
$ P = B + t\vec{v_{2}} $
となります。
これはなんとかして $t$ の値を求めたいところです。
今のままでは、まだ $t$ を導くための情報が足りないのでさらに情報を増やします。
$ \vec{v_{1}} $ と $ \vec{v_{1}'} $ は平行の関係にあるので、外積が0になります。
$ \vec{v_{1}} \times \vec{v_{1}'} = 0$
いったん情報をまとめます。
$ \vec{v_{1}'} = \vec{v} + t\vec{v_{2}} $
$ \vec{v_{1}} \times \vec{v_{1}'} = 0$
下の式に上の式を代入します。
$ \vec{v_{1}} \times (\vec{v} + t\vec{v_{2}}) = 0$
左辺に $t$ があるので、この式を $t$ について解けば、$t$ が求められそうです。
計算
$ \vec{v_{1}} \times (\vec{v} + t\vec{v_{2}}) = 0$
この式を $t$ について解いていきます。
$ \vec{v_{1}} \times (\vec{v} + t\vec{v_{2}}) = 0$
$ \vec{v_{1}} \times \vec{v} + \vec{v_{1}} \times t\vec{v_{2}} = 0 \ \ \ \ \ \ \ $※1 外積は展開可能
$ \vec{v_{1}} \times \vec{v} + t(\vec{v_{1}} \times \vec{v_{2}}) = 0 \ \ \ \ $※2 $t$は外に出せる
$t(\vec{v_{1}} \times \vec{v_{2}}) = -\vec{v_{1}} \times \vec{v}$
$t(\vec{v_{1}} \times \vec{v_{2}}) = \vec{v} \times \vec{v_{1}} \ \ \ \ \ \ \ \ \ \ $ ※3 外積は入れ替えると符号反転
$t = \frac{ \vec{v} \times \vec{v_{1}} } {\vec{v_{1}} \times \vec{v_{2}}}$
外積の性質を利用しながら式変形をすることで、$t$ はただの外積の割り算になるという事がわかりました。
$t$ の求め方が分かってしまえば、最終的に知りたかった直線の交点 $P$ を求める事ができます。
最終的に $P$ の位置は以下の式になります。
$ P = B + t\vec{v_{2}} $
$t = \frac{ \vec{v} \times \vec{v_{1}} } {\vec{v_{1}} \times \vec{v_{2}}}$
ただし、直線が平行なとき、$t$ の分母が 0 になってしまうので、平行な場合は $t$ は求まりません。
直線が平行な場合は交点が存在しないので当然と言えば当然と思えますよね。
直線の方程式 $ y = ax + b $ を使う方法は?
直線の交点というと、2つの直線を $ y = ax + b $ という式で表して連立方程式にして解くという方法もあります。
しかし、$ y = ax + b $ の式では $x$ 軸に垂直な直線を表せないと思うので、プログラム的にはちょっと都合が悪いです。
もしやるとしたら、まず直線が $x$ 軸に垂直かどうか調べて、垂直の場合とそうでない場合とで処理を分岐するみたいなことをする必要がありそうなので、それであれば外積を使って計算する方がスマートだなと思いました。
サンプルコード
/**
* 直線と直線の衝突点を取得
* 直線同士が平行な場合、0 devide が発生するので、事前に判定してから呼び出すこと
* @param l1 直線1
* @param l2 直線2
*/
function getCollisionPoint(l1:Line, l2:Line) {
const v = Vector2.sub(l2.p, l1.p);
const v1 = l1.v;
const v2 = l2.v;
const t2 = Vector2.cross(v,v1) / Vector2.cross(v1, v2);
return Vector2.add(l2.p, v2.clone().times(t2));
}