-
Notifications
You must be signed in to change notification settings - Fork 0
/
circle_collision.rb
200 lines (164 loc) · 5.22 KB
/
circle_collision.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# Based on http://processing.org/learning/topics/circlecollision.html
# by Joe Holt
class Sketch < Processing::App
# This inner class demonstrates the use of Ruby-Processing's emulation of
# Java inner classes. The Balls are able to call Processing::App methods.
class Ball
attr_accessor :x, :y, :r, :m, :vec
def initialize(r = 0.0, vec = nil, x = 0.0, y = 0.0)
@x, @y, @r = x, y, r
@m = r * 0.1
@vec = vec
end
def move
@x += @vec.x
@y += @vec.y
end
def draw
r = @r * 2
ellipse @x, @y, r, r
@px, @py = @x, @y
end
def erase
r = @r * 2
rect @px, @py, r, r
end
end
def setup
smooth
no_stroke
frame_rate 30
rect_mode RADIUS
@balls = []
5.times { @balls << Ball.new(10, PVector.new(2.15, -1.35), *empty_space(15)) }
2.times { @balls << Ball.new(40, PVector.new(-1.65, 0.42), *empty_space(45)) }
@frame_time = nil
@frame_count = 0
end
def draw
t = Time.now
fps = 1.0 / (t - @frame_time) if @frame_time
@frame_time = t
@frame_count += 1
# erase previous screen
if @frame_count == 1
background 51
else
fill 51
@balls.each { |ball| ball.erase }
end
# move the balls
fill 240
@balls.each do |ball|
ball.move
ball.draw
check_boundary_collision ball
end
check_object_collisions
end
def empty_space(r)
x = y = nil
while !x || !empty_space?(x, y, r) do
x = rand(width)
y = rand(height)
end
return x, y
end
def empty_space?(x, y, r)
@balls.each do |ball|
vx = x - ball.x
vy = y - ball.y
mag = sqrt(vx * vx + vy * vy)
return false if mag < r + ball.r
end
return true
end
def check_object_collisions
(0...(@balls.length)).each do |ia|
((ia+1)...(@balls.length)).each do |ib|
ba = @balls[ia]
bb = @balls[ib]
# get distances between the balls components
bVect = PVector.new
bVect.x = bb.x - ba.x
bVect.y = bb.y - ba.y
# calculate magnitude of the vector separating the balls
bVectMag = sqrt(bVect.x * bVect.x + bVect.y * bVect.y)
next if bVectMag >= ba.r + bb.r
# get angle of bVect
theta = atan2(bVect.y, bVect.x)
# precalculate trig values
sine = sin(theta)
cosine = cos(theta)
# bTemp will hold rotated ball positions. You just
# need to worry about bTemp[1] position
bTemp = [Ball.new, Ball.new]
# bb's position is relative to ba's
# so you can use the vector between them (bVect) as the
# reference point in the rotation expressions.
# bTemp[0].x and bTemp[0].y will initialize
# automatically to 0.0, which is what you want
# since bb will rotate around ba
bTemp[1].x = cosine * bVect.x + sine * bVect.y
bTemp[1].y = cosine * bVect.y - sine * bVect.x
# rotate Temporary velocities
vTemp = [PVector.new, PVector.new]
vTemp[0].x = cosine * ba.vec.x + sine * ba.vec.y
vTemp[0].y = cosine * ba.vec.y - sine * ba.vec.x
vTemp[1].x = cosine * bb.vec.x + sine * bb.vec.y
vTemp[1].y = cosine * bb.vec.y - sine * bb.vec.x
# Now that velocities are rotated, you can use 1D
# conservation of momentum equations to calculate
# the final velocity along the x-axis.
vFinal = [PVector.new, PVector.new]
# final rotated velocity for ba
vFinal[0].x = ((ba.m - bb.m) * vTemp[0].x + 2 * bb.m *
vTemp[1].x) / (ba.m + bb.m)
vFinal[0].y = vTemp[0].y
# final rotated velocity for ba
vFinal[1].x = ((bb.m - ba.m) * vTemp[1].x + 2 * ba.m *
vTemp[0].x) / (ba.m + bb.m)
vFinal[1].y = vTemp[1].y
# hack to avoid clumping
bTemp[0].x += vFinal[0].x
bTemp[1].x += vFinal[1].x
# Rotate ball positions and velocities back
# Reverse signs in trig expressions to rotate
# in the opposite direction
# rotate balls
bFinal = [Ball.new, Ball.new]
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x
# update balls to screen position
bb.x = ba.x + bFinal[1].x
bb.y = ba.y + bFinal[1].y
ba.x = ba.x + bFinal[0].x
ba.y = ba.y + bFinal[0].y
# update velocities
ba.vec.x = cosine * vFinal[0].x - sine * vFinal[0].y
ba.vec.y = cosine * vFinal[0].y + sine * vFinal[0].x
bb.vec.x = cosine * vFinal[1].x - sine * vFinal[1].y
bb.vec.y = cosine * vFinal[1].y + sine * vFinal[1].x
end
end
end
def check_boundary_collision(ball)
if ball.x > width-ball.r
ball.x = width-ball.r
ball.vec.x *= -1
elsif ball.x < ball.r
ball.x = ball.r
ball.vec.x *= -1
end
if ball.y > height-ball.r
ball.y = height-ball.r
ball.vec.y *= -1
elsif ball.y < ball.r
ball.y = ball.r
ball.vec.y *= -1
end
end
end
Sketch.new(:width => 400, :height => 400, :title => "CircleCollision2")