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,36 @@
|
|
1
|
+
require 'pongo/abstract_particle'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
# A circle shaped particle.
|
5
|
+
class CircleParticle < AbstractParticle
|
6
|
+
attr_accessor :radius
|
7
|
+
|
8
|
+
def initialize(x, y, radius, options={})
|
9
|
+
options = {:fixed => false, :mass => 1, :elasticity => 0.3, :friction => 0}.update(options)
|
10
|
+
super(x, y, options[:fixed], options[:mass], options[:elasticity], options[:friction])
|
11
|
+
@radius = radius
|
12
|
+
end
|
13
|
+
|
14
|
+
def projection(axis)
|
15
|
+
c = @samp.dot(axis)
|
16
|
+
@interval.min = c - @radius
|
17
|
+
@interval.max = c + @radius
|
18
|
+
@interval
|
19
|
+
end
|
20
|
+
alias get_projection projection
|
21
|
+
|
22
|
+
def interval_x
|
23
|
+
@interval.min = @samp.x - @radius
|
24
|
+
@interval.max = @samp.x + @radius
|
25
|
+
@interval
|
26
|
+
end
|
27
|
+
alias get_interval_x interval_x
|
28
|
+
|
29
|
+
def interval_y
|
30
|
+
@interval.min = @samp.y - @radius
|
31
|
+
@interval.max = @samp.y + @radius
|
32
|
+
@interval
|
33
|
+
end
|
34
|
+
alias get_interval_y interval_y
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'pongo/util/numeric_ext'
|
2
|
+
module Pongo
|
3
|
+
class CollisionDetector
|
4
|
+
class <<self
|
5
|
+
attr_accessor :cpa, :cpb, :coll_normal, :coll_depth
|
6
|
+
|
7
|
+
# Tests the collision between two objects. This initial test determines
|
8
|
+
# the multisampling state of the two particles.
|
9
|
+
def test(obj_a, obj_b)
|
10
|
+
return if obj_a == obj_b # TODO: added by ando
|
11
|
+
return if obj_a.fixed? and obj_b.fixed?
|
12
|
+
|
13
|
+
if obj_a.multisample == 0 and obj_b.multisample == 0
|
14
|
+
norm_vs_norm(obj_a, obj_b)
|
15
|
+
elsif obj_a.multisample > 0 and obj_b.multisample == 0
|
16
|
+
samp_vs_norm(obj_a, obj_b)
|
17
|
+
elsif obj_b.multisample > 0 and obj_a.multisample == 0
|
18
|
+
samp_vs_norm(obj_b, obj_a)
|
19
|
+
elsif obj_a.multisample == obj_b.multisample
|
20
|
+
samp_vs_samp(obj_a, obj_b)
|
21
|
+
else
|
22
|
+
norm_vs_norm(obj_a, obj_b)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# default test for two non-multisampled particles
|
27
|
+
def norm_vs_norm(obj_a, obj_b)
|
28
|
+
obj_a.samp.copy(obj_a.curr)
|
29
|
+
|
30
|
+
obj_b.samp.copy(obj_b.curr)
|
31
|
+
if test_types(obj_a, obj_b)
|
32
|
+
CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Tests two particles where one is multisampled and the other is not. Let objectA
|
40
|
+
# be the multisampled particle.
|
41
|
+
def samp_vs_norm(obj_a, obj_b)
|
42
|
+
return if norm_vs_norm(obj_a, obj_b)
|
43
|
+
|
44
|
+
s = 1 / (obj_a.multisample + 1)
|
45
|
+
t = s
|
46
|
+
obj_a.multisample.times do
|
47
|
+
obj_a.samp.set_to(
|
48
|
+
obj_a.prev.x + t * (obj_a.curr.x - obj_a.prev.x),
|
49
|
+
obj_a.prev.y + t * (obj_a.curr.y - obj_a.prev.y)
|
50
|
+
)
|
51
|
+
if test_types(obj_a, obj_b)
|
52
|
+
CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
|
53
|
+
return
|
54
|
+
end
|
55
|
+
t += s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Tests two particles where both are of equal multisample rate
|
60
|
+
def samp_vs_samp(obj_a, obj_b)
|
61
|
+
return if norm_vs_norm(obj_a, obj_b)
|
62
|
+
|
63
|
+
s = 1 / (obj_a.multisample + 1)
|
64
|
+
t = s
|
65
|
+
|
66
|
+
obj_a.multisample.times do
|
67
|
+
obj_a.samp.set_to(
|
68
|
+
obj_a.prev.x + t * (obj_a.curr.x - obj_a.prev.x),
|
69
|
+
obj_a.prev.y + t * (obj_a.curr.y - obj_a.prev.y)
|
70
|
+
)
|
71
|
+
obj_b.samp.set_to(
|
72
|
+
obj_b.prev.x + t * (obj_b.curr.x - obj_b.prev.x),
|
73
|
+
obj_b.prev.y + t * (obj_b.curr.y - obj_b.prev.y)
|
74
|
+
)
|
75
|
+
if test_types(obj_a, obj_b)
|
76
|
+
CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
|
77
|
+
return
|
78
|
+
end
|
79
|
+
t += s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Tests collision based on primitive type.
|
84
|
+
def test_types(obj_a, obj_b)
|
85
|
+
if obj_a.is_a?(RectangleParticle) and obj_b.is_a?(RectangleParticle)
|
86
|
+
test_obb_vs_obb(obj_a, obj_b)
|
87
|
+
elsif obj_a.is_a?(CircleParticle) and obj_b.is_a?(CircleParticle)
|
88
|
+
test_circle_vs_circle(obj_a, obj_b)
|
89
|
+
elsif obj_a.is_a?(RectangleParticle) and obj_b.is_a?(CircleParticle)
|
90
|
+
test_obb_vs_circle(obj_a, obj_b)
|
91
|
+
elsif obj_a.is_a?(CircleParticle) and obj_b.is_a?(RectangleParticle)
|
92
|
+
test_obb_vs_circle(obj_b, obj_a)
|
93
|
+
else
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Tests the collision between two RectangleParticles (aka OBBs). If there is a
|
99
|
+
# collision it determines its axis and depth, and then passes it off to the
|
100
|
+
# CollisionResolver for handling.
|
101
|
+
def test_obb_vs_obb(rect_a, rect_b)
|
102
|
+
@coll_depth = Numeric::POSITIVE_INFINITY
|
103
|
+
2.times do |i|
|
104
|
+
axis_a = rect_a.axes[i]
|
105
|
+
depth_a = test_intervals(rect_a.projection(axis_a), rect_b.projection(axis_a))
|
106
|
+
return false if depth_a == 0
|
107
|
+
|
108
|
+
axis_b = rect_b.axes[i]
|
109
|
+
depth_b = test_intervals(rect_a.projection(axis_b), rect_b.projection(axis_b))
|
110
|
+
return false if depth_b == 0
|
111
|
+
|
112
|
+
abs_a = depth_a.abs
|
113
|
+
abs_b = depth_b.abs
|
114
|
+
|
115
|
+
if abs_a < @coll_depth.abs or abs_b < @coll_depth.abs
|
116
|
+
altb = abs_a < abs_b
|
117
|
+
@coll_normal = altb ? axis_a : axis_b
|
118
|
+
@coll_depth = altb ? depth_a : depth_b
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@cpa = rect_a
|
123
|
+
@cpb = rect_b
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
# Tests the collision between a RectangleParticle (aka an OBB) and a
|
128
|
+
# CircleParticle. If there is a collision it determines its axis and depth, and
|
129
|
+
# then passes it off to the CollisionResolver.
|
130
|
+
def test_obb_vs_circle(rect_a, cir_a)
|
131
|
+
@coll_depth = Numeric::POSITIVE_INFINITY
|
132
|
+
depths = []
|
133
|
+
|
134
|
+
# first go through the axes of the rectangle
|
135
|
+
2.times do |i|
|
136
|
+
box_axis = rect_a.axes[i]
|
137
|
+
depth = test_intervals(rect_a.projection(box_axis), cir_a.projection(box_axis))
|
138
|
+
return false if depth == 0
|
139
|
+
|
140
|
+
if depth.abs < @coll_depth.abs
|
141
|
+
@coll_normal = box_axis
|
142
|
+
@coll_depth = depth
|
143
|
+
end
|
144
|
+
depths[i] = depth
|
145
|
+
end
|
146
|
+
|
147
|
+
# determine if the circle's center is in a vertex region
|
148
|
+
r = cir_a.radius
|
149
|
+
if depths[0].abs < r and depths[1].abs < r
|
150
|
+
vertex = closest_vertex_on_obb(cir_a.samp, rect_a)
|
151
|
+
|
152
|
+
# get the distance from the closest vertex on rect to circle center
|
153
|
+
@coll_normal = vertex - cir_a.samp
|
154
|
+
mag = @coll_normal.magnitude
|
155
|
+
@coll_depth = r - mag
|
156
|
+
|
157
|
+
if @coll_depth > 0
|
158
|
+
# there is a collision in one of the vertex regions
|
159
|
+
@coll_normal.div!(mag)
|
160
|
+
else
|
161
|
+
# rect_a is in vertex region, but is not colliding
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
@cpa = rect_a
|
166
|
+
@cpb = cir_a
|
167
|
+
true
|
168
|
+
end
|
169
|
+
|
170
|
+
# Tests the collision between two CircleParticles. If there is a collision it
|
171
|
+
# determines its axis and depth, and then passes it off to the CollisionResolver
|
172
|
+
# for handling.
|
173
|
+
def test_circle_vs_circle(cir_a, cir_b)
|
174
|
+
depth_x = test_intervals(cir_a.interval_x, cir_b.interval_x)
|
175
|
+
return false if depth_x == 0
|
176
|
+
|
177
|
+
depth_y = test_intervals(cir_a.interval_y, cir_b.interval_y)
|
178
|
+
return false if depth_y == 0
|
179
|
+
|
180
|
+
@coll_normal = cir_a.samp - cir_b.samp
|
181
|
+
mag = @coll_normal.magnitude
|
182
|
+
@coll_depth = cir_a.radius + cir_b.radius - mag
|
183
|
+
|
184
|
+
if @coll_depth > 0
|
185
|
+
@coll_normal.div!(mag)
|
186
|
+
@cpa = cir_a
|
187
|
+
@cpb = cir_b
|
188
|
+
true
|
189
|
+
else
|
190
|
+
false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns 0 if intervals do not overlap. Returns smallest depth if they do.
|
195
|
+
def test_intervals(interval_a, interval_b)
|
196
|
+
return 0 if interval_a.max < interval_b.min
|
197
|
+
return 0 if interval_b.max < interval_a.min
|
198
|
+
|
199
|
+
len_a = interval_b.max - interval_a.min
|
200
|
+
len_b = interval_b.min - interval_a.max
|
201
|
+
|
202
|
+
len_a.abs < len_b.abs ? len_a : len_b
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns the location of the closest vertex on rect to point
|
206
|
+
def closest_vertex_on_obb(point, rect)
|
207
|
+
d = point - rect.samp
|
208
|
+
q = rect.samp.dup
|
209
|
+
|
210
|
+
2.times do |i|
|
211
|
+
dist = d.dot(rect.axes[i])
|
212
|
+
if dist >= 0; dist = rect.extents[i]
|
213
|
+
elsif dist < 0; dist = -rect.extents[i]
|
214
|
+
end
|
215
|
+
|
216
|
+
q.plus!(rect.axes[i] * dist)
|
217
|
+
end
|
218
|
+
q
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pongo/spring_constraint_particle'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
class CollisionEvent
|
5
|
+
COLLIDE = :collide
|
6
|
+
FIRST_COLLIDE = :first_collide
|
7
|
+
|
8
|
+
attr_accessor :type, :colliding_item
|
9
|
+
|
10
|
+
def initialize(type, colliding_item=nil)
|
11
|
+
@type = type
|
12
|
+
@colliding_item = colliding_item
|
13
|
+
end
|
14
|
+
|
15
|
+
def colliding_item
|
16
|
+
if @colliding_item.is_a?(SpringConstraintParticle)
|
17
|
+
@colliding_item.parent
|
18
|
+
else
|
19
|
+
@colliding_item
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'pongo/util/math_util'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
class CollisionResolver
|
5
|
+
def self.resolve(pa, pb, normal, depth)
|
6
|
+
mtd = normal * depth
|
7
|
+
te = pa.elasticity + pb.elasticity
|
8
|
+
sum_inv_mass = pa.inv_mass + pb.inv_mass
|
9
|
+
|
10
|
+
# the total friction in a collision is combined but clamped to [0,1]
|
11
|
+
tf = MathUtil.clamp(1 - (pa.friction + pb.friction), 0, 1)
|
12
|
+
|
13
|
+
# get the collision components, vn and vt
|
14
|
+
ca = pa.components(normal)
|
15
|
+
cb = pb.components(normal)
|
16
|
+
|
17
|
+
# calculate the coefficient of restitution as the normal component
|
18
|
+
vn_a = (cb.vn * ((te + 1) * pa.inv_mass) +
|
19
|
+
(ca.vn * (pb.inv_mass - te * pa.inv_mass))) / sum_inv_mass
|
20
|
+
vn_b = (ca.vn * ((te + 1) * pb.inv_mass) +
|
21
|
+
(cb.vn * (pa.inv_mass - te * pb.inv_mass))) / sum_inv_mass
|
22
|
+
|
23
|
+
# apply friction to the tangental component
|
24
|
+
ca.vt.mult!(tf)
|
25
|
+
cb.vt.mult!(tf)
|
26
|
+
|
27
|
+
# scale the mtd by the ratio of the masses. heavier particles move less
|
28
|
+
mtd_a = mtd * ( pa.inv_mass / sum_inv_mass)
|
29
|
+
mtd_b = mtd * (-pb.inv_mass / sum_inv_mass)
|
30
|
+
|
31
|
+
# add the tangental component to the normal component for the new velocity
|
32
|
+
vn_a.plus!(ca.vt)
|
33
|
+
vn_b.plus!(cb.vt)
|
34
|
+
|
35
|
+
pa.resolve_collision(mtd_a, vn_a, normal, depth, -1, pb)
|
36
|
+
pb.resolve_collision(mtd_b, vn_b, normal, depth, 1, pa)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pongo/abstract_collection'
|
2
|
+
require 'pongo/util/numeric_ext'
|
3
|
+
|
4
|
+
module Pongo
|
5
|
+
# The Composite class can contain Particles, and Constraints. Composites can be added
|
6
|
+
# to a parent Group, along with Particles and Constraints. Members of a Composite
|
7
|
+
# are not checked for collision with one another, internally.
|
8
|
+
class Composite < AbstractCollection
|
9
|
+
attr_accessor :delta
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
@delta = Vector.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# Rotates the Composite to an angle specified in radians, around a given center
|
17
|
+
def rotate_by_radian(angle_radians, center)
|
18
|
+
particles.each do |p|
|
19
|
+
radius = p.center.distance(center)
|
20
|
+
angle = relative_angle(center, p.center) + angle_radians
|
21
|
+
p.px = (Math.cos(angle) * radius) + center.x
|
22
|
+
p.py = (Math.sin(angle) * radius) + center.y
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Rotates the Composite to an angle specified in degrees, around a given center
|
27
|
+
def rotate_by_angle(angle_degrees, center)
|
28
|
+
rotate_by_radian(angle_degrees * Numeric::PI_OVER_ONE_EIGHTY, center)
|
29
|
+
end
|
30
|
+
|
31
|
+
# The fixed state of the Composite. Setting this value to true or false will
|
32
|
+
# set all of this Composite's component particles to that value. Getting this
|
33
|
+
# value will return false if any of the component particles are not fixed.
|
34
|
+
def fixed?
|
35
|
+
particles.each do |p|
|
36
|
+
return false unless p.fixed?
|
37
|
+
end
|
38
|
+
true
|
39
|
+
end
|
40
|
+
alias fixed fixed?
|
41
|
+
|
42
|
+
def fixed=(b)
|
43
|
+
particles.each {|p| p.fixed = b}
|
44
|
+
end
|
45
|
+
|
46
|
+
def relative_angle(center, p)
|
47
|
+
@delta.set_to(p.x - center.x, p.y - center.y)
|
48
|
+
Math.atan2(@delta.y, @delta.x)
|
49
|
+
end
|
50
|
+
alias get_relative_angle relative_angle
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'pongo/container/container'
|
2
|
+
require 'pongo/renderer/shoes_renderer'
|
3
|
+
require 'pongo/logger/shoes_logger'
|
4
|
+
|
5
|
+
module Pongo
|
6
|
+
module Container
|
7
|
+
class ShoesContainer < Container
|
8
|
+
def initialize(shoes)
|
9
|
+
@renderer = Renderer::ShoesRenderer.new(shoes)
|
10
|
+
@logger = Logger::ShoesLogger.new(shoes)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/pongo/group.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
module Pongo
|
2
|
+
# The Group class can contain Particles, Constraints, and Composites. Groups can be
|
3
|
+
# assigned to be checked for collision with other Groups or internally.
|
4
|
+
class Group < AbstractCollection
|
5
|
+
attr_accessor :composites, :collision_list, :collide_internal
|
6
|
+
|
7
|
+
def initialize(collide_internal=false)
|
8
|
+
super()
|
9
|
+
@composites = []
|
10
|
+
@collision_list = []
|
11
|
+
self.collide_internal = collide_internal
|
12
|
+
end
|
13
|
+
|
14
|
+
def init
|
15
|
+
super
|
16
|
+
@composites.each {|c| c.init}
|
17
|
+
end
|
18
|
+
|
19
|
+
def collide_internal!
|
20
|
+
@collide_internal = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(item)
|
24
|
+
case item
|
25
|
+
when Composite
|
26
|
+
add_composite(item)
|
27
|
+
when Group
|
28
|
+
add_collidable(item)
|
29
|
+
when Array
|
30
|
+
add_collidable_list(item)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_composite(c)
|
37
|
+
@composites << c
|
38
|
+
c.is_parented = true
|
39
|
+
c.init if @is_parented
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove_composite(c)
|
43
|
+
if @composites.delete(c)
|
44
|
+
c.is_parented = false
|
45
|
+
c.cleanup
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def draw
|
50
|
+
super
|
51
|
+
@composites.each {|c| c.draw}
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_collidable(g)
|
55
|
+
@collision_list << g
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_collidable(g)
|
59
|
+
@collision_list.delete(g)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_collidable_list(*list)
|
63
|
+
list = list.first if list.first.is_a?(Array)
|
64
|
+
list.each {|g| @collision_list << g}
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_all
|
68
|
+
@particles + @constraints + @composites
|
69
|
+
end
|
70
|
+
alias all get_all
|
71
|
+
|
72
|
+
def clieanup
|
73
|
+
super
|
74
|
+
@composites.each {|c| c.cleanup}
|
75
|
+
end
|
76
|
+
|
77
|
+
def integrate(dt2)
|
78
|
+
super
|
79
|
+
@composites.each {|cmp| cmp.integrate(dt2)}
|
80
|
+
end
|
81
|
+
|
82
|
+
def satisfy_constraints
|
83
|
+
super
|
84
|
+
@composites.each {|cmp| cmp.satisfy_constraints}
|
85
|
+
end
|
86
|
+
|
87
|
+
def check_collisions
|
88
|
+
check_collision_group_internal if @collide_internal
|
89
|
+
@collision_list.each {|g| check_collision_vs_group(g) if g}
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_collision_group_internal
|
93
|
+
# check collisions not in composites
|
94
|
+
check_internal_collisions
|
95
|
+
|
96
|
+
# for every composite in this Group..
|
97
|
+
valid_composites.each do |ca|
|
98
|
+
# .. vs non composite particles and constraints in this group
|
99
|
+
ca.check_collisions_vs_collection(self)
|
100
|
+
|
101
|
+
# ...vs every other composite in this Group
|
102
|
+
valid_composites.each do |cb|
|
103
|
+
ca.check_collisions_vs_collection(cb)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_collision_vs_group(g)
|
109
|
+
# check particles and constraints not in composites of either group
|
110
|
+
check_collisions_vs_collection(g)
|
111
|
+
|
112
|
+
# for every composite in this group..
|
113
|
+
valid_composites.each do |c|
|
114
|
+
# check vs the particles and constraints of g
|
115
|
+
c.check_collisions_vs_collection(g)
|
116
|
+
|
117
|
+
# check vs composites of g
|
118
|
+
g.valid_composites.each do |g|
|
119
|
+
c.check_collections_vs_collection(g)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# check particles and constraints of this group vs the composites of g
|
124
|
+
g.valid_composites.each do |gc|
|
125
|
+
check_collisions_vs_collection(gc)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def valid_composites
|
130
|
+
@composites.select{|c| not c.nil?}
|
131
|
+
end
|
132
|
+
|
133
|
+
def circle(x, y, radius, options={})
|
134
|
+
cir = CircleParticle.new(x, y, radius, options)
|
135
|
+
cir.always_redraw! if options[:always_redraw]
|
136
|
+
self << cir
|
137
|
+
cir
|
138
|
+
end
|
139
|
+
|
140
|
+
def rectangle(x, y, width, height, options={})
|
141
|
+
rect = RectangleParticle.new(x, y, width, height, options)
|
142
|
+
rect.always_redraw! if options[:always_redraw]
|
143
|
+
self << rect
|
144
|
+
rect
|
145
|
+
end
|
146
|
+
|
147
|
+
def wheel(x, y, radius, options={})
|
148
|
+
wh = WheelParticle.new(x, y, radius, options)
|
149
|
+
wh.always_redraw! if options[:always_redraw]
|
150
|
+
self << wh
|
151
|
+
wh
|
152
|
+
end
|
153
|
+
|
154
|
+
def connect(p1, p2, options={})
|
155
|
+
con = SpringConstraint.new(p1, p2, options)
|
156
|
+
self << con
|
157
|
+
con
|
158
|
+
end
|
159
|
+
|
160
|
+
def brace(p1, p2, p3, min_ang, max_ang, options={})
|
161
|
+
ang = AngularConstraint.new(p1, p2, p3, min_ang, max_ang, options)
|
162
|
+
self << ang
|
163
|
+
ang
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|