📐
点に衝突した円の補正と反射
わかっていること
点
a = V[400.0, 300.0]
円
c = V[440.0, 220.0]
radius = 120
速度ベクトル
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
radius = 120
speed = d - c
ac = c - a
c2 = nil
e = 1.0
distance = c.distance_to(a)
gap = distance - radius
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)
arrow_head(a + V.from_angle(270.deg_to_rad), a, "a")
circle_draw(c, radius)
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)
arrow_head(c2 + V.from_angle(270.deg_to_rad), c2, "c2", color: :blue_light)
end
end
def window_size_default
V[800, 400]
end
show
end
Discussion