2点間の線分上に点が存在することを確認したい

いまつくっているアプリケーションに必要な「線分上に点が存在するかどうか」を確認する処理を考えます。

./line-point.png

線分の上に点が存在する場合、点と線分の端点を結ぶ直線と、線分のなす角度 θ1\theta_1, θ2\theta_2 は0°になります。

角度を計算するには内積を利用できそうです。

ab=a1b1+a2b2\bm{a} \cdot \bm{b} = a_1b_1 + a_2b_2
cosθ=ababcos\theta = \frac{\bm{a} \cdot \bm{b}}{|\bm{a}| |\bm{b}|}
./angle1.png

まずは端点から点にのびるベクトル p1\bm{p_1} と、線分のベクトル ab\bm{ab} を考えます。 このとき、先ほどの内積の公式を用いて、cosθ1cos\theta_1 を計算できます。

このcosθ1cos\theta_1が1に近ければ、θ1\theta_1は0°に近いと言えます。

./angle2.png

同様に、cosθ2cos\theta_2も計算します。cosθ1cos\theta_1もしくはcosθ2cos\theta_2のどちらかが1に近ければ(=どちらかの角度が0に近ければ)線分上に点があると言うことができそうです。

./angle-out.png

ただし、cosθ1,cosθ2cos\theta_1, cos\theta_2いずれの値も正でなければ、線分の外側の直線上に点が存在することになります。

TypeScriptで実装した場合、最終的には以下のようなコードになりました。

type Position2D = [number, number]

const subtract = ([ax, ay]: Readonly<Position2D>, [bx, by]: Readonly<Position2D>): Position2D => [ax - bx, ay - by]
const length = ([x, y]: Readonly<Position2D>): number => Math.sqrt(x * x + y * y)
const innerProduction = ([ax, ay]: Readonly<Position2D>, [bx, by]: Readonly<Position2D>): number => ax * bx + ay * by

function pointInLine(a: Position2D, b: Position2D, point: Position2D) {
  // 線分の端点1から点に向かって伸びるベクトルp1
  const p1 = subtract(point, a)
  // p1の長さ
  const lp1 = length(p1)
  // 線分の端点1からもう一方の端点に向かって伸びるベクトルab
  const ab = subtract(b, a)
  // p1とabのなす角 (cos)
  const angle1 = innerProduction(ab, p1) / (length(ab) * lp1)
  
  // p2もp1と同様に計算する
  const p2 = subtract(point, b)
  const lp2 = length(p2)
  const ba = subtract(a, b)
  const angle2 = innerProduction(ba, p2) / (length(ba) * lp2)
  
  return (angle1 > 0) && (angle2 > 0) &&    // cos()の値が正であれば点は線側にある
    (Math.max(angle1, angle2) > 0.999)      // どちらかのcos()の値がほぼ1 -> 角度は0
}

おおむねこんなかんじでしょうか。ではでは。

クリックすると匿名でいいねできます。

(@piyoppi/counter-tools を使っています )