🎡

# 新井式回転抽選器シミュレーション

2023/10/27に公開

## 球 vs 壁

``````a = wall.p0
b = wall.p1
ab = b - a
ac = ball.location - a
n = ab.normalize.perp
distance = ac.dot(n)
if t < 1.0
ball.location = ball.location + n * (1.0 - t) * ball.radius
ball.velocity = ball.velocity - ball.velocity.project_onto_normalized(n) * (1 + ball.restitution)
end
``````

## 球 vs 球

``````ab = b.location - a.location
distance = ab.length
if gap.round.negative?
len = -gap
n = ab.normalize
am = a.mass
bm = b.mass
a.location = a.location + n * len * -bm / (am + bm)
b.location = b.location + n * len *  am / (am + bm)
as = a.velocity.dot(n)
bs = b.velocity.dot(n)
e = 1.0
as2 = (am*as + bm*bs - (e * bm * (as - bs))) / (am + bm)
bs2 = (am*as + bm*bs + (e * am * (as - bs))) / (am + bm)
a.velocity = a.velocity.reject_from_normalized(n) + n * as2
b.velocity = b.velocity.reject_from_normalized(n) + n * bs2
end
``````

コード
``````class Ball
attr_accessor :location
attr_accessor :velocity
attr_accessor :mass

def initialize
@location = \$app.window_wh.center + V.rand_norm
@velocity = V.rand_norm
@acceleration = V.zero
@gravity = V[0.0, 0.12]
@restitution = 0.999
end

def update
\$app.rebound(self, \$app.pivot)

\$app.walls.each do |wall|
a = wall.p0
b = wall.p1
ab = b - a
ac = @location - a
n = ab.normalize.perp
distance = ac.dot(n)
if t < 1.0
@location = @location + n * (1.0 - t) * @radius
@velocity = @velocity - @velocity.project_onto_normalized(n) * (1 + @restitution)
end
end

@velocity += @acceleration
@location += @velocity
@acceleration = V.zero
end

def draw
end

@acceleration += force / @mass
end
end

class Wall
attr_accessor :p0
attr_accessor :p1

def initialize(index)
@index = index
end

def update
@p0 = position(0)
@p1 = position(1)
end

def draw
\$app.line_draw(p0, p1, line_width: 2)
end

def position(i)
center = \$app.window_wh.center
speed = 60 * 12
angle = (1.0 / \$app.walls.size * (@index + i)) + (1.0 / speed) * \$app.frame_count
center + V.from_angle(angle.turn_to_rad) * center * 0.95
end
end

class Pivot
attr_accessor :location
attr_accessor :velocity
attr_accessor :mass

def initialize
@location = \$app.window_wh.center
@velocity = V.zero
@mass = 1000000000.0
end

def update
@location += velocity
end

def draw
\$app.circle_draw(location, radius, sides: 16, line_width: 3)
end
end

class App < Base
attr_accessor :pivot
attr_accessor :balls
attr_accessor :walls

def initialize
super

reset
end

def reset
@pivot = Pivot.new
@walls = 8.times.collect.with_index { |i| Wall.new(i) }
@balls = 50.times.collect { Ball.new }
end

def button_down(id)
super

case id
when Gosu::KB_R
reset
end
end

def update
super

@pivot.update
@walls.each(&:update)
@balls.combination(2) { |a, b| rebound(a, b) }
@balls.each(&:update)
end

def draw
super

@pivot.draw
@walls.each(&:draw)
@balls.each(&:draw)
end

def rebound(a, b)
ab = b.location - a.location
distance = ab.length
if gap.round.negative?
len = -gap
n = ab.normalize

am = a.mass
bm = b.mass

a.location = a.location + n * len * -bm / (am + bm)
b.location = b.location + n * len *  am / (am + bm)

as = a.velocity.dot(n)
bs = b.velocity.dot(n)

e = 1.0
as2 = (am*as + bm*bs - (e * bm * (as - bs))) / (am + bm)
bs2 = (am*as + bm*bs + (e * am * (as - bs))) / (am + bm)

a.velocity = a.velocity.reject_from_normalized(n) + n * as2
b.velocity = b.velocity.reject_from_normalized(n) + n * bs2
end
end

def window_size_default
V[800, 800]
end

show
end
``````