📐
直線上で線分に点が含まれるか判定する方法
目的
線分 ab に点 e が含まれるかを知りたい。
考え方
- 線分の両端から点に向かってベクトルを作る
- 二つのベクトルが向き合っていたら線分内にある
計算手順
a = V[320.0, 90.0]
b = V[480.0, 70.0]
e = V[400.0, 80.0]
ae = e - a # => (80.0, -10.0)
be = e - b # => (-80.0, 10.0)
ae.dot(be) # => -6500.0
二つのベクトルが対向しているかどうかは、ベクトル同士の内積で判断できる。内積は、二つのベクトルの角度が近いほど大きな正値を返し、そうでない場合は大きな負値を返す。ここで重要なのは向きであるため、符号だけを考慮する。値の符号が負であれば、ベクトルが対向していると判断できるため、線分内に存在すると判断できる。
範囲外になるケース
どちらも同じ方向を向いているため正値になっているのがわかる。
参照
コード
class App < Base
def initialize
super
self.width, self.height = V[800, 200]
a = window_wh * V[0.40, 0.45] # => (320.0, 90.0)
b = window_wh * V[0.60, 0.35] # => (480.0, 70.0)
c = window_wh * V[0.50, 0.97] # => (400.0, 194.0)
d = window_wh * V[0.50, 0.80] # => (400.0, 160.0)
@points.concat([a, b, c, d])
@mode = 0
end
def button_down(id)
super
if id == Gosu::KB_Z
@mode = @mode.next.modulo(2)
end
end
def draw
super
a, b, c, d = @points
cd = d - c
ab = b - a
ac = c - a
t = ab.cross(ac) / cd.cross(ab)
e = c.lerp(d, t)
ae = e - a
be = e - b
vputs "a: #{a.round}"
vputs "b: #{b.round}"
vputs "e: #{e.round}"
vputs "ae・be = #{ae.dot(be).round}"
segment_draw(a, b, "a", "b")
vector_draw(c, d, "", "")
point_draw(c.lerp(d, t))
arrow_head(c, c.lerp(d, t), "e")
line_draw(a - ab * 100, b + ab * 100)
if @mode == 1
vector_draw(a, e, "", "", color: Gosu::Color::BLUE)
vector_draw(b, e, "", "", color: Gosu::Color::RED)
end
end
show
end
Discussion