📐

点と直線の距離の求め方

2023/09/13に公開

目的

直線 ab と点 c の距離を求める。

考え方

見えない線と二つの三角形をイメージする。

続いて

  1. 右の三角形の底辺
  2. 左の三角形の対辺

に着目すると、一つ目は正射影のことなので

  • 「ベクトル 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