Class: Pongo::CollisionDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/pongo/collision_detector.rb

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.coll_depthObject

Returns the value of attribute coll_depth.



5
6
7
# File 'lib/pongo/collision_detector.rb', line 5

def coll_depth
  @coll_depth
end

.coll_normalObject

Returns the value of attribute coll_normal.



5
6
7
# File 'lib/pongo/collision_detector.rb', line 5

def coll_normal
  @coll_normal
end

.cpaObject

Returns the value of attribute cpa.



5
6
7
# File 'lib/pongo/collision_detector.rb', line 5

def cpa
  @cpa
end

.cpbObject

Returns the value of attribute cpb.



5
6
7
# File 'lib/pongo/collision_detector.rb', line 5

def cpb
  @cpb
end

Class Method Details

.closest_vertex_on_obb(point, rect) ⇒ Object

Returns the location of the closest vertex on rect to point



206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/pongo/collision_detector.rb', line 206

def closest_vertex_on_obb(point, rect)
  d = point - rect.samp
  q = rect.samp.dup

  2.times do |i|
    dist = d.dot(rect.axes[i])
    if    dist >= 0; dist = rect.extents[i] 
    elsif dist <  0; dist = -rect.extents[i]
    end

    q.plus!(rect.axes[i] * dist)
  end
  q
end

.norm_vs_norm(obj_a, obj_b) ⇒ Object

default test for two non-multisampled particles



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pongo/collision_detector.rb', line 27

def norm_vs_norm(obj_a, obj_b)
  obj_a.samp.copy(obj_a.curr)

  obj_b.samp.copy(obj_b.curr)
  if test_types(obj_a, obj_b)
    CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
    true
  else
    false
  end
end

.samp_vs_norm(obj_a, obj_b) ⇒ Object

Tests two particles where one is multisampled and the other is not. Let objectA be the multisampled particle.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/pongo/collision_detector.rb', line 41

def samp_vs_norm(obj_a, obj_b)
  return if norm_vs_norm(obj_a, obj_b)

  s = 1 / (obj_a.multisample + 1)
  t = s
  obj_a.multisample.times do
    obj_a.samp.set_to(
      obj_a.prev.x + t * (obj_a.curr.x - obj_a.prev.x),
      obj_a.prev.y + t * (obj_a.curr.y - obj_a.prev.y)
    )
    if test_types(obj_a, obj_b)
      CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
      return
    end
    t += s
  end
end

.samp_vs_samp(obj_a, obj_b) ⇒ Object

Tests two particles where both are of equal multisample rate



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/pongo/collision_detector.rb', line 60

def samp_vs_samp(obj_a, obj_b)
  return if norm_vs_norm(obj_a, obj_b)

  s = 1 / (obj_a.multisample + 1)
  t = s

  obj_a.multisample.times do
    obj_a.samp.set_to(
      obj_a.prev.x + t * (obj_a.curr.x - obj_a.prev.x),
      obj_a.prev.y + t * (obj_a.curr.y - obj_a.prev.y)
    )
    obj_b.samp.set_to(
      obj_b.prev.x + t * (obj_b.curr.x - obj_b.prev.x),
      obj_b.prev.y + t * (obj_b.curr.y - obj_b.prev.y)
    )
    if test_types(obj_a, obj_b)
      CollisionResolver.resolve(@cpa, @cpb, @coll_normal, @coll_depth)
      return
    end
    t += s
  end
end

.test(obj_a, obj_b) ⇒ Object

Tests the collision between two objects. This initial test determines the multisampling state of the two particles.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/pongo/collision_detector.rb', line 9

def test(obj_a, obj_b)
  return if obj_a == obj_b # TODO: added by ando
  return if obj_a.fixed? and obj_b.fixed?

  if obj_a.multisample == 0 and obj_b.multisample == 0
    norm_vs_norm(obj_a, obj_b)
  elsif obj_a.multisample > 0 and obj_b.multisample == 0
    samp_vs_norm(obj_a, obj_b)
  elsif obj_b.multisample > 0 and obj_a.multisample == 0
    samp_vs_norm(obj_b, obj_a)
  elsif obj_a.multisample == obj_b.multisample
    samp_vs_samp(obj_a, obj_b)
  else
    norm_vs_norm(obj_a, obj_b)
  end
end

.test_circle_vs_circle(cir_a, cir_b) ⇒ Object

Tests the collision between two CircleParticles. If there is a collision it determines its axis and depth, and then passes it off to the CollisionResolver for handling.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/pongo/collision_detector.rb', line 173

def test_circle_vs_circle(cir_a, cir_b)
  depth_x = test_intervals(cir_a.interval_x, cir_b.interval_x)
  return false if depth_x == 0

  depth_y = test_intervals(cir_a.interval_y, cir_b.interval_y)
  return false if depth_y == 0

  @coll_normal = cir_a.samp - cir_b.samp
  mag = @coll_normal.magnitude
  @coll_depth = cir_a.radius + cir_b.radius - mag

  if @coll_depth > 0
    @coll_normal.div!(mag)
    @cpa = cir_a
    @cpb = cir_b
    true
  else
    false
  end
end

.test_intervals(interval_a, interval_b) ⇒ Object

Returns 0 if intervals do not overlap. Returns smallest depth if they do.



195
196
197
198
199
200
201
202
203
# File 'lib/pongo/collision_detector.rb', line 195

def test_intervals(interval_a, interval_b)
  return 0 if interval_a.max < interval_b.min
  return 0 if interval_b.max < interval_a.min

  len_a = interval_b.max - interval_a.min
  len_b = interval_b.min - interval_a.max

  len_a.abs < len_b.abs ? len_a : len_b
end

.test_obb_vs_circle(rect_a, cir_a) ⇒ Object

Tests the collision between a RectangleParticle (aka an OBB) and a CircleParticle. If there is a collision it determines its axis and depth, and then passes it off to the CollisionResolver.



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
# File 'lib/pongo/collision_detector.rb', line 130

def test_obb_vs_circle(rect_a, cir_a)
  @coll_depth = Numeric::POSITIVE_INFINITY
  depths = []

  # first go through the axes of the rectangle
  2.times do |i|
    box_axis = rect_a.axes[i]
    depth = test_intervals(rect_a.projection(box_axis), cir_a.projection(box_axis))
    return false if depth == 0

    if depth.abs < @coll_depth.abs
      @coll_normal = box_axis
      @coll_depth = depth
    end
    depths[i] = depth
  end

  # determine if the circle's center is in a vertex region
  r = cir_a.radius
  if depths[0].abs < r and depths[1].abs < r
    vertex = closest_vertex_on_obb(cir_a.samp, rect_a)

    # get the distance from the closest vertex on rect to circle center
    @coll_normal = vertex - cir_a.samp
    mag = @coll_normal.magnitude
    @coll_depth = r - mag

    if @coll_depth > 0
      # there is a collision in one of the vertex regions
      @coll_normal.div!(mag)
    else
      # rect_a is in vertex region, but is not colliding
      return false
    end
  end
  @cpa = rect_a
  @cpb = cir_a
  true
end

.test_obb_vs_obb(rect_a, rect_b) ⇒ Object

Tests the collision between two RectangleParticles (aka OBBs). If there is a collision it determines its axis and depth, and then passes it off to the CollisionResolver for handling.



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
# File 'lib/pongo/collision_detector.rb', line 101

def test_obb_vs_obb(rect_a, rect_b)
  @coll_depth = Numeric::POSITIVE_INFINITY
  2.times do |i|
    axis_a = rect_a.axes[i]
    depth_a = test_intervals(rect_a.projection(axis_a), rect_b.projection(axis_a))
    return false if depth_a == 0

    axis_b = rect_b.axes[i]
    depth_b = test_intervals(rect_a.projection(axis_b), rect_b.projection(axis_b))
    return false if depth_b == 0

    abs_a = depth_a.abs
    abs_b = depth_b.abs

    if abs_a < @coll_depth.abs or abs_b < @coll_depth.abs
      altb = abs_a < abs_b
      @coll_normal = altb ? axis_a : axis_b
      @coll_depth = altb ? depth_a : depth_b
    end
  end

  @cpa = rect_a
  @cpb = rect_b
  true
end

.test_types(obj_a, obj_b) ⇒ Object

Tests collision based on primitive type.



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/pongo/collision_detector.rb', line 84

def test_types(obj_a, obj_b)
  if obj_a.is_a?(RectangleParticle) and obj_b.is_a?(RectangleParticle)
    test_obb_vs_obb(obj_a, obj_b)
  elsif obj_a.is_a?(CircleParticle) and obj_b.is_a?(CircleParticle)
    test_circle_vs_circle(obj_a, obj_b)
  elsif obj_a.is_a?(RectangleParticle) and obj_b.is_a?(CircleParticle)
    test_obb_vs_circle(obj_a, obj_b)
  elsif obj_a.is_a?(CircleParticle) and obj_b.is_a?(RectangleParticle)
    test_obb_vs_circle(obj_b, obj_a)
  else
    false
  end
end