technohippy-Pongo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +17 -0
- data/Manifest.txt +52 -0
- data/README.txt +93 -0
- data/lib/pongo.rb +33 -0
- data/lib/pongo/abstract_collection.rb +135 -0
- data/lib/pongo/abstract_constraint.rb +25 -0
- data/lib/pongo/abstract_item.rb +93 -0
- data/lib/pongo/abstract_particle.rb +226 -0
- data/lib/pongo/angular_constraint.rb +108 -0
- data/lib/pongo/apengine.rb +157 -0
- data/lib/pongo/circle_particle.rb +36 -0
- data/lib/pongo/collision.rb +11 -0
- data/lib/pongo/collision_detector.rb +222 -0
- data/lib/pongo/collision_event.rb +23 -0
- data/lib/pongo/collision_resolver.rb +39 -0
- data/lib/pongo/composite.rb +52 -0
- data/lib/pongo/container/container.rb +7 -0
- data/lib/pongo/container/shoes_container.rb +14 -0
- data/lib/pongo/group.rb +166 -0
- data/lib/pongo/iforce.rb +7 -0
- data/lib/pongo/logger/logger.rb +21 -0
- data/lib/pongo/logger/shoes_logger.rb +15 -0
- data/lib/pongo/logger/standard_logger.rb +11 -0
- data/lib/pongo/rectangle_particle.rb +79 -0
- data/lib/pongo/renderer/renderer.rb +61 -0
- data/lib/pongo/renderer/shoes_renderer.rb +51 -0
- data/lib/pongo/renderer/tk_renderer.rb +73 -0
- data/lib/pongo/rim_particle.rb +53 -0
- data/lib/pongo/spring_constraint.rb +128 -0
- data/lib/pongo/spring_constraint_particle.rb +235 -0
- data/lib/pongo/util/interval.rb +13 -0
- data/lib/pongo/util/math_util.rb +27 -0
- data/lib/pongo/util/numeric_ext.rb +4 -0
- data/lib/pongo/util/vector.rb +133 -0
- data/lib/pongo/vector_force.rb +34 -0
- data/lib/pongo/wheel_particle.rb +103 -0
- metadata +90 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'pongo/circle_particle'
|
2
|
+
require 'pongo/rectangle_particle'
|
3
|
+
require 'pongo/util/math_util'
|
4
|
+
require 'pongo/util/vector'
|
5
|
+
|
6
|
+
module Pongo
|
7
|
+
class SpringConstraintParticle < RectangleParticle
|
8
|
+
attr_accessor :parent, :p1, :p2, :avg_velocity, :lamda, :scale_to_length, :rca, :rcb, :s, :rect_scale, :rect_height, :fixed_end_limit
|
9
|
+
|
10
|
+
def initialize(p1, p2, p, rect_height, rect_scale, scale_to_length)
|
11
|
+
super(0.0, 0.0, 0.0, 0.0, :fixed => false)
|
12
|
+
|
13
|
+
@p1 = p1
|
14
|
+
@p2 = p2
|
15
|
+
|
16
|
+
self.lamda = Vector.new
|
17
|
+
self.avg_velocity = Vector.new
|
18
|
+
|
19
|
+
@parent = p
|
20
|
+
@rect_scale = rect_scale
|
21
|
+
@rect_height = rect_height
|
22
|
+
@scale_to_length = scale_to_length
|
23
|
+
|
24
|
+
@fixed_end_limit = 0.0
|
25
|
+
@rca = Vector.new
|
26
|
+
@rcb = Vector.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def mass
|
30
|
+
(@p1.mass * @p2.mass) / 2.0
|
31
|
+
end
|
32
|
+
|
33
|
+
def elasticity
|
34
|
+
(@p1.elasticity * @p2.elasticity) / 2.0
|
35
|
+
end
|
36
|
+
|
37
|
+
def friction
|
38
|
+
(@p1.friction * @p2.friction) / 2.0
|
39
|
+
end
|
40
|
+
|
41
|
+
def velocity
|
42
|
+
p1v = @p1.velocity
|
43
|
+
p2v = @p2.velocity
|
44
|
+
@avg_velocity.set_to((p1v.x + p2v.x) / 2.0, (p1v.y + p2v.y) / 2.0)
|
45
|
+
end
|
46
|
+
|
47
|
+
def init
|
48
|
+
#throw NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
def draw
|
52
|
+
#throw NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
def init_display
|
56
|
+
#throw NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
def inv_mass
|
60
|
+
if @p1.fixed? and @p2.fixed?
|
61
|
+
0
|
62
|
+
else
|
63
|
+
1.0 / self.mass
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def fixed?
|
68
|
+
@parent.fixed?
|
69
|
+
end
|
70
|
+
alias fixed fixed?
|
71
|
+
|
72
|
+
# called only on collision
|
73
|
+
def update_position
|
74
|
+
@curr.set_to(@parent.center)
|
75
|
+
self.width = @scale_to_length \
|
76
|
+
? @parent.curr_length * @rect_scale \
|
77
|
+
: @parent.rest_length * @rect_scale
|
78
|
+
self.height = @rect_height
|
79
|
+
self.radian = @parent.radian
|
80
|
+
end
|
81
|
+
|
82
|
+
def resolve_collision(mtd, vel, n, d, o, p)
|
83
|
+
test_particle_events(p)
|
84
|
+
return if fixed? or not p.solid?
|
85
|
+
|
86
|
+
t = contact_point_param(p)
|
87
|
+
c1 = 1 -t
|
88
|
+
c2 = t
|
89
|
+
|
90
|
+
# if one is fixed then move the other particle the entire way out of
|
91
|
+
# collision. also, dispose of collisions at the sides of the scp. The higher
|
92
|
+
# the fixedEndLimit value, the more of the scp not be effected by collision.
|
93
|
+
if @p1.fixed?
|
94
|
+
return if c2 <= @fixed_end_limit
|
95
|
+
lamda.set_to(mtd.x / c2, mtd.y / c2)
|
96
|
+
@p2.curr.plus!(lamda)
|
97
|
+
@p2.velocity = vel
|
98
|
+
elsif @p2.fixed?
|
99
|
+
return if c1 <= @fixed_end_limit
|
100
|
+
lamda.set_to(mtd.x / c1, mtd.y / c1)
|
101
|
+
@p1.curr.plus!(lamda)
|
102
|
+
@p1.velocity = vel
|
103
|
+
else
|
104
|
+
# else both non fixed - move proportionally out of collision
|
105
|
+
denom = c1 * c1 + c2 * c2
|
106
|
+
return if denom == 0
|
107
|
+
lamda.set_to(mtd.x / denom, mtd.y / denom)
|
108
|
+
|
109
|
+
@p1.curr.plus!(lamda * c1)
|
110
|
+
@p2.curr.plus!(lamda * c2)
|
111
|
+
|
112
|
+
if t == 0.5
|
113
|
+
# if collision is in the middle of SCP set the velocity of both end
|
114
|
+
# particles
|
115
|
+
@p1.velocity = vel
|
116
|
+
@p2.velocity = vel
|
117
|
+
else
|
118
|
+
# otherwise change the velocity of the particle closest to contact
|
119
|
+
corr_particle = t < 0.5 ? @p1 : @p2
|
120
|
+
corr_particle.velocity = vel
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# given point c, returns a parameterized location on this SCP. Note
|
126
|
+
# this is just treating the SCP as if it were a line segment (ab).
|
127
|
+
def closest_param_point(c)
|
128
|
+
ab = @p2.curr - @p1.curr
|
129
|
+
t = (ab.dot(c - @p1.curr)) / (ab.dot(ab))
|
130
|
+
MathUtil.clamp(t, 0.0, 1.0)
|
131
|
+
end
|
132
|
+
|
133
|
+
# returns a contact location on this SCP expressed as a parametric value in [0,1]
|
134
|
+
def get_contact_point_param(p)
|
135
|
+
if p.is_a?(CircleParticle)
|
136
|
+
closest_param_point(p.curr)
|
137
|
+
elsif p.is_a?(RectangleParticle)
|
138
|
+
# go through the sides of the colliding rectangle as line segments
|
139
|
+
shortest_index = 0
|
140
|
+
params_list = []
|
141
|
+
shortest_distance = Numeric::POSITIVE_INFINITY
|
142
|
+
4.times do |i|
|
143
|
+
set_corners(p, i)
|
144
|
+
|
145
|
+
# check for closest points on SCP to side of rectangle
|
146
|
+
d = closest_pt_segment_segment
|
147
|
+
if d < shortest_distance
|
148
|
+
shortest_distance = d
|
149
|
+
shortest_index = i
|
150
|
+
params_list[i] = @s
|
151
|
+
end
|
152
|
+
params_list[shortest_index]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
alias contact_point_param get_contact_point_param
|
157
|
+
|
158
|
+
def set_corners(r, i)
|
159
|
+
rx = r.curr.x
|
160
|
+
ry = r.curr.y
|
161
|
+
|
162
|
+
axes = r.axes
|
163
|
+
extents = r.extents
|
164
|
+
|
165
|
+
ae0_x = axes[0].x * extents[0]
|
166
|
+
ae0_y = axes[0].y * extents[0]
|
167
|
+
ae1_x = axes[1].x * extents[1]
|
168
|
+
ae1_y = axes[1].y * extents[1]
|
169
|
+
|
170
|
+
emx = ae0_x - ae1_x
|
171
|
+
emy = ae0_y - ae1_y
|
172
|
+
epx = ae0_x + ae1_x
|
173
|
+
epy = ae0_y + ae1_y
|
174
|
+
|
175
|
+
case i
|
176
|
+
when 0
|
177
|
+
# 0 and 1
|
178
|
+
@rca.x = rx - epx;
|
179
|
+
@rca.y = ry - epy;
|
180
|
+
@rcb.x = rx + emx;
|
181
|
+
@rcb.y = ry + emy;
|
182
|
+
when 1
|
183
|
+
# 1 and 2
|
184
|
+
@rca.x = rx + emx;
|
185
|
+
@rca.y = ry + emy;
|
186
|
+
@rcb.x = rx + epx;
|
187
|
+
@rcb.y = ry + epy;
|
188
|
+
when 2
|
189
|
+
# 2 and 3
|
190
|
+
@rca.x = rx + epx;
|
191
|
+
@rca.y = ry + epy;
|
192
|
+
@rcb.x = rx - emx;
|
193
|
+
@rcb.y = ry - emy;
|
194
|
+
when 3
|
195
|
+
# 3 and 0
|
196
|
+
@rca.x = rx - emx;
|
197
|
+
@rca.y = ry - emy;
|
198
|
+
@rcb.x = rx - epx;
|
199
|
+
@rcb.y = ry - epy;
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# pp1-pq1 will be the SCP line segment on which we need parameterized s.
|
204
|
+
def closest_pt_segment_segment
|
205
|
+
pp1 = @p1.curr
|
206
|
+
pq1 = @p2.curr
|
207
|
+
pp2 = @rca
|
208
|
+
pq2 = @rcb
|
209
|
+
|
210
|
+
d1 = pq1 - pp1
|
211
|
+
d2 = pq2 - pp2
|
212
|
+
r = pp1 - pp2
|
213
|
+
|
214
|
+
a = d1.dot(d1)
|
215
|
+
e = d2.dot(d2)
|
216
|
+
f = d2.dot(r)
|
217
|
+
|
218
|
+
c = d1.dot(r)
|
219
|
+
b = d1.dot(d2)
|
220
|
+
denom = a * e - b * b
|
221
|
+
|
222
|
+
s = denom != 0.0 ? MathUtil.clamp((b * f - c * e) / denom, 0.0, 1.0) : 0.5
|
223
|
+
t = (b * s + f) / e
|
224
|
+
|
225
|
+
t, s = t < 0 \
|
226
|
+
? [0.0, MathUtil.clamp(-c / a, 0.0, 1.0)] \
|
227
|
+
: [1.0, MathUtil.clamp((b - c) / a, 0.0, 1.0)]
|
228
|
+
|
229
|
+
c1 = pp1 + (d1 * s)
|
230
|
+
c2 = pp2 + (d2 * t)
|
231
|
+
c1mc2 = c1 - c2
|
232
|
+
c1mc2.dot(c1mc2)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Pongo
|
2
|
+
module MathUtil
|
3
|
+
ONE_EIGHTY_OVER_PI = 180.0 / Math::PI;
|
4
|
+
PI_OVER_ONE_EIGHTY = Math::PI / 180.0;
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def clamp(n, min, max)
|
9
|
+
if n < min; min
|
10
|
+
elsif max < n; max
|
11
|
+
else ; n
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign(val)
|
16
|
+
val < 0 ? -1 : 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def max(v1, v2)
|
20
|
+
v1 < v2 ? v2 : v1
|
21
|
+
end
|
22
|
+
|
23
|
+
def min(v1, v2)
|
24
|
+
v1 < v2 ? v1 : v2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Pongo
|
2
|
+
class Vector
|
3
|
+
attr_accessor :x, :y
|
4
|
+
|
5
|
+
def initialize(px=0.0, py=0.0)
|
6
|
+
set_to(px, py)
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_to(px, py=nil)
|
10
|
+
@x, @y = py ? [px.to_f, py.to_f] : [px.x, px.y]
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy(v)
|
15
|
+
set_to(v.x, v.y)
|
16
|
+
end
|
17
|
+
|
18
|
+
def dup
|
19
|
+
Vector.new(@x, @y)
|
20
|
+
end
|
21
|
+
|
22
|
+
def dot(v)
|
23
|
+
@x * v.x + @y * v.y
|
24
|
+
end
|
25
|
+
|
26
|
+
def cross(v)
|
27
|
+
@x * v.y - @y * v.x
|
28
|
+
end
|
29
|
+
|
30
|
+
def +(v)
|
31
|
+
Vector.new(@x + v.x, @y + v.y)
|
32
|
+
end
|
33
|
+
alias plus +
|
34
|
+
|
35
|
+
def plus!(v)
|
36
|
+
@x += v.x
|
37
|
+
@y += v.y
|
38
|
+
self
|
39
|
+
end
|
40
|
+
alias plus_equals plus!
|
41
|
+
|
42
|
+
def -(v)
|
43
|
+
Vector.new(@x - v.x, @y - v.y)
|
44
|
+
end
|
45
|
+
alias minus -
|
46
|
+
|
47
|
+
def minus!(v)
|
48
|
+
@x -= v.x
|
49
|
+
@y -= v.y
|
50
|
+
self
|
51
|
+
end
|
52
|
+
alias minus_equals minus!
|
53
|
+
|
54
|
+
def mult(s)
|
55
|
+
Vector.new(@x * s, @y * s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def mult!(s)
|
59
|
+
@x *= s
|
60
|
+
@y *= s
|
61
|
+
self
|
62
|
+
end
|
63
|
+
alias mult_equals mult!
|
64
|
+
|
65
|
+
def times(v)
|
66
|
+
Vector.new(x * v.x, y * v.y)
|
67
|
+
end
|
68
|
+
|
69
|
+
def *(s_or_v)
|
70
|
+
if s_or_v.is_a? Vector
|
71
|
+
times(s_or_v)
|
72
|
+
else
|
73
|
+
mult(s_or_v)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def /(s)
|
78
|
+
s = 0.0001 if s == 0
|
79
|
+
Vector.new(@x / s, @y / s)
|
80
|
+
end
|
81
|
+
alias div /
|
82
|
+
|
83
|
+
def div!(s)
|
84
|
+
s = 0.0001 if s == 0
|
85
|
+
@x /= s
|
86
|
+
@y /= s
|
87
|
+
self
|
88
|
+
end
|
89
|
+
alias div_equals div!
|
90
|
+
|
91
|
+
def magnitude
|
92
|
+
Math.sqrt(@x * @x + @y * @y)
|
93
|
+
end
|
94
|
+
|
95
|
+
def distance(v)
|
96
|
+
minus(v).magnitude
|
97
|
+
end
|
98
|
+
|
99
|
+
def normalize
|
100
|
+
if @x == 0 and @y == 0
|
101
|
+
mult(10000.0)
|
102
|
+
else
|
103
|
+
mult(1.0 / magnitude)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def normalize!
|
108
|
+
unless @x == 0 and @y == 0
|
109
|
+
mult!(1.0 / magnitude)
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
"#{@x} : #{@y}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def rotate(rad)
|
119
|
+
Vector.new(
|
120
|
+
@x * Math.cos(rad) - @y * Math.sin(rad),
|
121
|
+
@x * Math.sin(rad) + @y * Math.cos(rad)
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def rotate!(rad)
|
126
|
+
@x, @y = [
|
127
|
+
@x * Math.cos(rad) - @y * Math.sin(rad),
|
128
|
+
@x * Math.sin(rad) + @y * Math.cos(rad)
|
129
|
+
]
|
130
|
+
self
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pongo
|
2
|
+
# A force represented by a 2D vector.
|
3
|
+
class VectorForce < IForce
|
4
|
+
attr_accessor :fvx, :fvy, :value, :scale_mass
|
5
|
+
|
6
|
+
def initialize(use_mass, vx, vy)
|
7
|
+
@fvx = vx
|
8
|
+
@fvy = vy
|
9
|
+
@scale_mass = use_mass
|
10
|
+
@value = Vector.new(vx, vy)
|
11
|
+
end
|
12
|
+
|
13
|
+
def vx=(x)
|
14
|
+
@fvx = x
|
15
|
+
@value.x = x
|
16
|
+
end
|
17
|
+
|
18
|
+
def vy=(y)
|
19
|
+
@fvy = y
|
20
|
+
@value.y = y
|
21
|
+
end
|
22
|
+
|
23
|
+
def use_mass=(b)
|
24
|
+
@scale_mass = b
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_value(invmass)
|
28
|
+
if @scale_mass
|
29
|
+
@value.set_to(@fvx * invmass, @fvy * invmass)
|
30
|
+
end
|
31
|
+
@value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|