📐

# 点に衝突した円の補正と反射

2023/10/01に公開

## わかっていること

``````a = V[400.0, 300.0]
``````

``````c = V[440.0, 220.0]
``````

``````d = V[280.0, 220.0]
speed = d - c  # => (-160.0, 0.0)
``````

``````e = 1.0
``````

## 円に点がめり込んでいるか？

``````ac = c - a          # => (40.0, -80.0)
acn = ac.normalize  # => (0.4472135954999579, -0.8944271909999159)
``````

その長さが距離になる。

``````distance = ac.length         # => 89.44271909999159
distance = c.distance_to(a)  # => 89.44271909999159
``````

``````gap = distance - radius  # => -30.557280900008408
gap.round.negative?      # => true
``````

## 円の位置を補正する

ずれた分だけ押し返す場合

``````c2 = c + acn * -gap  # => (453.6656314599949, 192.6687370800101)
``````

``````c2 = a + acn * radius  # => (453.6656314599949, 192.6687370800101)
``````

どちらの方法でも結果は同じだが、もし点が円だと仮定すると前者の方がシンプルになりそう。

## 速度ベクトルを反射させる

``````speed2 = (speed - acn * speed.dot(acn) * 2) * e                # => (-96.0, -128.0)
speed2 = (speed - speed.project_onto_normalized(acn) * 2) * e  # => (-96.0, -128.0)
speed2 = speed.bounce(acn) * e                                 # => (-96.0, -128.0)
``````
コード
``````class App < Base
def initialize
super

a = window_wh * V[0.50, 0.75]  # => (400.0, 300.0)
c = window_wh * V[0.55, 0.55]  # => (440.00000000000006, 220.00000000000003)
d = window_wh * V[0.35, 0.55]  # => (280.0, 220.00000000000003)
@points = [a, c, d]
end

def draw
super

a, c, d = @points
speed = d - c

ac = c - a

c2 = nil
e = 1.0

distance = c.distance_to(a)
if gap.round.negative?
c2 = c + ac.normalize * -gap
speed2 = speed.bounce(ac.normalize) * e
line_draw(a, a + ac.perp, color: :blue_light, infinity: true)
end

vputs "a: #{a.round}"
vputs "c: #{c.round}"
vputs "d: #{d.round}"
vputs "ac: #{ac.round}"
vputs "speed: #{speed.round}"

vector_draw(c, d, "c", "d")

line_draw(a, c)
point_draw(a)

if c2
vputs "c2: #{c2.round}" if c2
vputs "speed2: #{speed2.round}" if speed2
circle_draw(c2, radius, color: :blue_light, line_width: 4)
vector_draw(c2, c2 + speed2, "", "", color: :blue_light)