📐
点と直線の距離の求め方
目的
直線 ab と点 c の距離を求める。
考え方
見えない線と二つの三角形をイメージする。
続いて
- 右の三角形の底辺
- 左の三角形の対辺
に着目すると、一つ目は正射影のことなので
- 「ベクトル ac」と「ベクトル ab の法線の正規化」の内積
で求まり、二つ目は直立射影のことなので
- 「ベクトル ac」と「ベクトル ab の正規化」の外積
で求まる。
計算手順
a = V[400, 320]
b = V[240, 80]
c = V[480, 80]
ab = b - a # => (-160, -240)
ac = c - a # => (80, -240)
右の三角形に着目した場合:
ac.dot(ab.perp.normalize).round # => 200
左の三角形に着目した場合:
ab.normalize.cross(ac).round # => 200
二通りの方法で距離が求まることがわかる。
接触判定
点 c との距離は 0 なので接触している。もし行きすぎると負の値になる。
円との接触判定
円の半径は 50 なので点 c との距離が 50 以下になれば接触していると判断できる。
直線に衝突した円を反射させる例
# 直線
a = V[400, 320]
b = V[240, 80]
ab = b - a # => (-160, -240)
# 円
c = V[480, 80]
radius = 201
speed = V[-1.0, 0.0]
# 判定
ac = c - a # => (80, -240)
abn = ab.normalize # => (-0.5547001962252291, -0.8320502943378436)
len = abn.cross(ac) # => 199.6920706410825
gap = len - radius # => -1.3079293589175052
if gap.round.negative?
speed += -speed.project_onto_normalized(abn.perp) * 2
end
speed # => (0.38461538461538436, -0.923076923076923)
コード
class App < Base
def initialize
super
self.width, self.height = V[800, 400]
a = window_wh * V[0.5, 0.8] # => (400.0, 320.0)
b = window_wh * V[0.3, 0.2] # => (240.0, 80.0)
c = window_wh * V[0.6, 0.2] # => (480.0, 80.0)
@points.concat([a, b, c])
@mode = 0
end
def button_down(id)
super
if id == Gosu::KB_Z
@mode = @mode.next.modulo(2)
end
end
def draw
super
radius = 50
a, b, c = @points
ab = b - a
ac = c - a
d = a + ac.project_onto(ab.perp)
e = a + ac.reject_from(ab.perp)
vector_draw(a, b, "a", "b")
if true
point_draw(c)
arrow_head(c + V.from_angle(270.deg_to_rad), c, "c")
circle_draw(c, radius)
end
if @mode == 1
vputs "(abの正規化)×ac = #{ab.normalize_or_zero.cross(ac).round}"
vputs "ac・(abの法線の正規化) = #{ac.dot(ab.perp.normalize_or_zero).round}"
line_draw(a, a + ab.perp * 2)
line_draw(a, c)
line_draw(b, b + ab)
line_draw(a, a - ab)
line_draw(c, d)
line_draw(c, e)
vector_draw(a, d, "", "d")
vector_draw(a, e, "", "e")
end
end
def font_size_default
16
end
show
end
Discussion