点と直線の最近傍点
★:点と直線が最も近い場所
●:ドラッグして動かせます
ここでは点と直線の最も近い位置(グラフ上の★の位置)を求める方法について記載します。
考え方
点と直線があった時に、点と直線が一番近いのはどこかという事を考えていきます。
点から直線に向かっていくつか線を引いてみましたが、点から直線に垂直におろした緑の線が最短距離になりそうですね。
なので、求めるのは点から直線にまっすぐに線を落とした時に直線とぶつかる場所という事になります。
点と直線の一番近い点を求める
点と直線の一番近い点を $点P$ とすると、当たり前ですが $点P$ は直線の上にある事がわかります。
直線上の任意の点を $A$ 、直線の方向を表すベクトルを $\vec{v}$ とします。
すると、$点P$ は $ 点A $ の座標に $ \vec{v} $ を伸ばしたベクトルを足した位置にある事がわかります。
ここで、$\vec{v}$ を正規化して長さを1にしました。
正規化したベクトルを $\vec{n}$ とします。
こうすると、$\vec{n}\ $を何倍すれば $点P$ の位置にくるのかを考えればよいということになります。
この何倍にすればいいかという数値を $t$ としておきます。
この $t$ がわかってしまえば、$ 点A $ の座標に $ \vec{n} $ を $t倍$ したベクトル($t\vec{n}$)を足してあげれば $点P$ の座標になります。
$ P = A + t\vec{n} $
$t$ を求める
$t$ は $ 点A $ から $ 点P $ までの距離になりますが、この $t$ を求めるにはベクトルの内積を使います。
$点A$ から $点B$ に向かうベクトルを $\vec{b}$とします。
すると、$t$ は $\vec{n}$ と $\vec{b}$ の内積で求まります。
$ t = \vec{n} \cdot \vec{b} $
なぜこれで $t$ が求まるの?と気になる方は、ベクトルの内積の特性の方に記載していますのでご参照下さい。
まとめ
$P = A + t\vec{n}$
$t = \vec{n} \cdot \vec{b}$
サンプルコード
/**
* 点と直線の最近傍点を取得する
* @param p 点
* @param line 直線
*/
function getNearest(p:Vector2, line:Line){
// 直線の向きを表すベクトルを正規化したものをnとする
const n = line.v.normalize;
// 直線上の任意の1点から点へ向かうベクトルをaとする
const a = Vector2.sub(point, line.p);
// nとaの内積
const dot = Vector2.dot(n, a);
// 直線上の1点に n を dot 倍したベクトルを足した場所が最近傍点
const near = Vector2.add(line.p, n.times(dot));
return near;
}