📐

# 動く地面から受ける速度ベクトルの求め方

2023/10/26に公開

## 計算方法

``````b = V[711.0, 369.0]
c = V[106.0, 500.0]
d = V[415.0, 388.0]
``````

``````bc = c - b
n = bc.normalize.perp  # => (-0.21162476017560725, -0.9773509916507052)
``````

bd は地面の速度ベクトルで、

``````bd = d - b  # => (-296.0, 19.0)
``````

``````bdx = n * bd.dot(n)  # => (-9.326569864243474, -43.07308983104811)
``````

それが球に与える力になる。簡単に書くと、

``````bdx = bd.project_onto_normalized(n)  # => (-9.326569864243474, -43.07308983104811)
``````

でよい。

``````bdx = n * bc.normalize.cross(bd)  # => (-9.326569864243474, -43.07308983104811)
``````

が、内積を使った方がわかりやすい。

## 確認

``````bd.magnitude  # => 296.60917045836595
``````

なのに対して実際の球にかかる力

``````bdx.magnitude  # => 44.07126017061634
``````

は、小さいのがわかる。

## 最適化

``````n = bc.perp
``````

``````bdx = n * bd.dot(n) / n.length_squared  # => (-9.326569864243474, -43.07308983104811)
``````

``````bdx = bd.project_onto(n)  # => (-9.326569864243474, -43.07308983104811)
``````

とも書ける。

## まとめ

``````bc = c - b  # => (-605.0, 131.0)
``````

``````velocity = d - b  # => (-296.0, 19.0)
``````

``````n = bc.perp  # => (-131.0, -605.0)
``````

``````velocity.project_onto(n)  # => (-9.326569864243474, -43.07308983104811)
``````
シミュレーション
``````class Wall
attr_accessor :velocity

attr_accessor :p0
attr_accessor :p1

def initialize
@p0 = V.zero
@p1 = V.zero
@velocity = V.zero
end

def update
if \$app.pause
return
end

@p0.replace(@p0 + @velocity)
@p1.replace(@p1 + @velocity)
end

def draw
\$app.line_draw(p0, p1, infinity: true)
\$app.vector_draw(p0, p1)
end
end

class Ball
attr_accessor :location
attr_accessor :velocity
attr_accessor :restitution

def initialize
@location = \$app.window_wh * V[0.5, 0.5]
@velocity = V.zero
@restitution = 1.0
end

def update
if \$app.pause
return
end

a = @location
b = \$app.wall.p0
c = \$app.wall.p1
bc = c - b
n = bc.normalize.perp
ba = @location - b

distance = ba.dot(n)
t = distance / @radius
if t < 1.0
if false
a.replace(a + n * (1 - t) * @radius)                              # 位置補正(方法1)
# a.replace(b + ba.project_onto(bc) + n * @radius)                # 位置補正(方法2)
@velocity = @velocity - @velocity.project_onto_normalized(n) * (1 + @restitution) # 反射
end
@velocity += \$app.wall.velocity.project_onto_normalized(n)
end

a.replace(a + @velocity)
end

def draw
\$app.vputs "ball velocity: #{@velocity.round(2)} (#{@velocity.length.round(2)})"
\$app.point_draw(@location)
\$app.vector_draw(@location, @location + @velocity.clamp_length_min(@radius))
end
end

class App < Base
attr_accessor :ball
attr_accessor :wall
attr_accessor :pause

def initialize
super

@pause = true

preset1
end

def preset1
@ball = Ball.new
@ball.location = window_wh * V[0.5, 0.5]
@ball.velocity = V.zero

@wall = Wall.new
@wall.p0 = window_wh * V[0.95, 0.72]
@wall.p1 = window_wh * V[0.40, 0.78]
@wall.velocity = V[-2.0, 0.0]

@points = [@ball.location, @wall.p0, @wall.p1]
end

def button_down(id)
super

case id
when Gosu::KB_R
preset1
when Gosu::KB_P
@pause ^= true
end
end

def draw
super

@wall.update
@ball.update

@wall.draw
@ball.draw

vputs "[r] reset"
vputs "[p] pause: #{@pause}"
end

show
end
``````
ベクトル確認
``````class App < Base
def initialize
super

preset1
end

def preset1
a = V[238.0, 318.0]
b = V[711.0, 369.0]
c = V[106.0, 500.0]
d = V[415.0, 388.0]
@points = [a, b, c, d]
end

def button_down(id)
super

case id
when Gosu::KB_1
preset1
end
end

def draw
super

a, b, c, d = @points

bd = d - b
bc = c - b
n = bc.normalize.perp

bdx = bd.project_onto_normalized(n)

# 線
line_draw(b, c)
vector_draw(b, c, "b", "c")
vector_draw(b, d, "", "d", name: "bd", line_width: nil)

# 法線と本当のベクトル
line_draw(b, b + n, infinity: true)
vector_draw(b, b + bdx, "", "", name: "bdx", color: :blue)

# 球
point_draw(a)
circle_draw(a, radius, color: :grey)
vector_draw(a, a + bdx, "", "", color: :blue)

vputs "a: #{a.round}"
vputs "b: #{b.round}"
vputs "c: #{c.round}"
vputs "d: #{d.round}"
vputs "bdx: #{bdx.round(2)}"
end

show
end
``````

1. 変数名としてはいまいち。もっと良い名前を考えたい。 ↩︎

2. n.dot(n) ↩︎