World: Made the bodies accessible read-only as an Iterable.
[scd.git] / test / net / habraun / sd / WorldTest.scala
blob788a65c082b8f0e878c645462153c07725acdb9a
1 /*
2 Copyright (c) 2009 Hanno Braun <hanno@habraun.net>
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
19 package net.habraun.sd
23 import collision._
24 import math._
26 import scala.collection.mutable._
28 import org.junit._
29 import org.junit.Assert._
33 class WorldTest {
35 @Test
36 def addBodyStepExpectBodyMoved {
37 val world = new World
38 val body = new Body
39 body.velocity = Vec2D(1, 0)
40 world.add(body)
41 world.step(2.0)
42 assertEquals(Vec2D(2, 0), body.position)
47 @Test
48 def addAndRemoveBodyExpectBodyNotMoved {
49 val world = new World
50 val body = new Body
51 body.velocity = Vec2D(1, 0)
52 world.add(body)
53 world.remove(body)
54 world.step(2.0)
55 assertEquals(Vec2D(0, 0), body.position)
60 @Test
61 def addBodyApplyForceCheckVelocity {
62 val world = new World
63 val body = new Body
64 body.mass = 5
65 body.applyForce(Vec2D(5, 0))
66 world.add(body)
67 world.step(2.0)
68 assertEquals(Vec2D(2, 0), body.velocity)
73 @Test
74 def addBodyApplyForceStepTwiceCheckVelocity {
75 val world = new World
76 val body = new Body
77 body.mass = 5
78 body.applyForce(Vec2D(5, 0))
79 world.add(body)
80 world.step(2.0)
81 world.step(2.0)
82 assertEquals(Vec2D(2, 0), body.velocity)
87 @Test
88 def addBodyApplyForceStepCheckPosition {
89 val world = new World
90 val body = new Body
91 body.mass = 5
92 body.applyForce(Vec2D(5, 0))
93 world.add(body)
94 world.step(2.0)
95 assertEquals(Vec2D(4, 0), body.position)
100 @Test
101 def addBodyApplyImpulseCheckVelocity {
102 val world = new World
103 val body = new Body
104 body.mass = 5
105 body.velocity = Vec2D(3, 0)
106 body.applyImpulse(Vec2D(5, 0))
107 world.add(body)
108 world.step(2.0)
109 assertEquals(Vec2D(4, 0), body.velocity)
114 @Test
115 def addBodyApplyImpulseCheckVelocity2 {
116 val world = new World
117 val body = new Body
118 body.mass = 5
119 body.velocity = Vec2D(3, 0)
120 body.applyImpulse(Vec2D(5, 0))
121 world.add(body)
122 world.step(5.0)
123 assertEquals(Vec2D(4, 0), body.velocity)
128 @Test
129 def addBodyApplyImpulseCheckImpulse {
130 val world = new World
131 val body = new Body
132 body.applyImpulse(Vec2D(10, 10))
133 world.add(body)
134 world.step(2.0)
135 assertEquals(Vec2D(0, 0), body.appliedImpulse)
140 @Test
141 def applyImpulseToStaticBody {
142 val body = new Body
143 body.mass = Double.PositiveInfinity
144 body.applyImpulse(Vec2D(2, 0))
146 val world = new World
147 world.add(body)
148 world.step(2.0)
150 assertEquals(Vec2D(0, 0), body.velocity)
155 @Test
156 def addBodyDisallowXMovementStepCheckPosition {
157 val world = new World
158 val body = new Body
159 body.allowXMovement(false)
160 body.applyForce(Vec2D(1, 1))
161 world.add(body)
162 world.step(2.0)
163 assertEquals(Vec2D(0, 4), body.position)
168 @Test
169 def addBodyDisallowYMovementStepCheckPosition {
170 val world = new World
171 val body = new Body
172 body.allowYMovement(false)
173 body.applyForce(Vec2D(1, 1))
174 world.add(body)
175 world.step(2.0)
176 assertEquals(Vec2D(4, 0), body.position)
181 @Test
182 def verifyInitialBroadPhase {
183 val world = new World
184 assertTrue(world.broadPhase.isInstanceOf[SimpleBroadPhase])
189 @Test
190 def verifyInitialNarrowPhase {
191 val world = new World
192 assertTrue(world.narrowPhase.isInstanceOf[SimpleNarrowPhase])
197 @Test { val expected = classOf[NullPointerException] }
198 def setBroadPhaseNullExpectException {
199 val world = new World
200 world.broadPhase = null
205 @Test { val expected = classOf[NullPointerException] }
206 def setNarrowPhaseNullExpectException {
207 val world = new World
208 world.narrowPhase = null
213 @Test
214 def addBodyVerifyItIsPassedToBroadPhase {
215 val world = new World
217 val broadPhase = new BroadPhase {
218 var passedBodies: List[Body] = null
219 def detectPossibleCollisions(bodies: List[Body]) = { passedBodies = bodies; Nil }
221 world.broadPhase = broadPhase
223 val body = new Body
224 world.add(body)
225 world.step(2.0)
227 assertEquals(body::Nil, broadPhase.passedBodies)
232 @Test
233 def addBroadPhaseReturningBodyPairsVerifyTheyArePassedToNarrowPhase {
234 val world = new World
236 val b1 = new Body
237 val b2 = new Body
238 val b3 = new Body
239 val b4 = new Body
241 world.broadPhase = new BroadPhase {
242 def detectPossibleCollisions(bodies: List[Body]) = (b1, b2)::(b3, b4)::Nil
245 val narrowPhase = new NarrowPhase {
246 var passedPairs: List[(Body, Body)] = Nil
247 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
248 passedPairs = passedPairs:::List((b1, b2))
249 None
252 world.narrowPhase = narrowPhase
254 world.step(2.0)
256 assertEquals((b1, b2)::(b3, b4)::Nil, narrowPhase.passedPairs)
261 def verifyDeltaIsPassedToNarrowPhase {
262 val world = new World
264 world.add(new Body)
265 world.add(new Body)
267 val narrowPhase = new NarrowPhase {
268 var d = 0.0
269 def inspectCollision(delta: Double, b1: Body, b2: Body) = { d = delta; None }
271 world.narrowPhase = narrowPhase
273 val d = 2.0
274 world.step(d)
276 assertEquals(d, narrowPhase.d)
281 @Test
282 def verifyCollisionEffects {
283 val world = new World
285 val b1 = new Body
286 b1.position = Vec2D(0, 1)
287 b1.mass = 4
288 b1.velocity = Vec2D(-10, -10)
289 val b2 = new Body
290 b2.position = Vec2D(0, -1)
291 b2.mass = 8
292 b2.velocity = Vec2D(5, 5)
294 world.narrowPhase = new NarrowPhase {
295 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
296 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, -1), Vec2D(0, 1), Vec2D(0, 0))))
300 world.add(b1)
301 world.add(b2)
302 world.step(2.0)
304 assertEquals(Vec2D(0, 80), b1.appliedImpulse)
305 assertEquals(Vec2D(0, -80), b2.appliedImpulse)
310 @Test
311 def verifyCollisionEffectsWithBody1Static {
312 val world = new World
314 val b1 = new Body
315 b1.mass = Double.PositiveInfinity
316 val b2 = new Body
317 b2.mass = 5
318 b2.velocity = Vec2D(1, 1)
320 world.narrowPhase = new NarrowPhase {
321 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
322 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, -1), Vec2D(0, 1), Vec2D(0, 0))))
326 world.add(b1)
327 world.add(b2)
328 world.step(2.0)
330 assertEquals(Vec2D(0, 0), b1.appliedImpulse)
331 assertEquals(Vec2D(0, -10), b2.appliedImpulse)
336 @Test
337 def verifyCollisionEffectsWithBody2Static {
338 val world = new World
340 val b1 = new Body
341 b1.mass= 5
342 b1.velocity = Vec2D(1, 1)
343 val b2 = new Body
344 b2.mass = Double.PositiveInfinity
346 world.narrowPhase = new NarrowPhase {
347 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
348 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, 1), Vec2D(0, -1), Vec2D(0, 0))))
352 world.add(b1)
353 world.add(b2)
354 world.step(2.0)
356 assertEquals(Vec2D(0, -10), b1.appliedImpulse)
357 assertEquals(Vec2D(0, 0), b2.appliedImpulse)
362 @Test
363 def verifyForceIsAppliedBeforeCollisionDetection {
364 val world = new World
366 val broadPhase = new BroadPhase {
367 var v: Vec2D = null
368 def detectPossibleCollisions(bodies: List[Body]) = {
369 v = bodies(0).velocity
373 world.broadPhase = broadPhase
375 val body = new Body
376 body.mass = 5
377 body.applyForce(Vec2D(10, 0))
378 world.add(body)
380 world.step(2.0)
382 assertEquals(Vec2D(4, 0), broadPhase.v)
387 @Test
388 def verifyImpulseIsAppliedBeforeCollisionDetection {
389 val world = new World
391 val broadPhase = new BroadPhase {
392 var v: Vec2D = null
393 def detectPossibleCollisions(bodies: List[Body]) = {
394 v = bodies(0).velocity
398 world.broadPhase = broadPhase
400 val body = new Body
401 body.mass = 5
402 body.applyImpulse(Vec2D(10, 0))
403 world.add(body)
405 world.step(2.0)
407 assertEquals(Vec2D(2, 0), broadPhase.v)
412 @Test
413 def addBodiesThatWouldIntersectAfterMovementVerifyTheyDont {
414 val world = new World
416 val b1 = new Body
417 b1.shape = Circle(2)
418 b1.position = Vec2D(0, 0)
419 b1.velocity = Vec2D(1, 0)
420 val b2 = new Body
421 b2.shape = Circle(2)
422 b2.position = Vec2D(5, 0)
423 b2.velocity = Vec2D(0, 0)
424 world.add(b1)
425 world.add(b2)
427 val broadPhase = new BroadPhase {
428 def detectPossibleCollisions(bodies: List[Body]) = {
429 (b1, b2)::Nil
432 world.broadPhase = broadPhase
434 val narrowPhase = new NarrowPhase {
435 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
436 Some(Collision(0.5, Contact(b1, b2, Vec2D(1, 0), Vec2D(-1, 0), Vec2D(3, 0))))
439 world.narrowPhase = narrowPhase
441 world.step(2.0)
443 val expectedPosition = Vec2D(1, 0)
445 assertEquals(expectedPosition.x, b1.position.x, 0.01)
446 assertEquals(expectedPosition.y, b1.position.y, 0.01)
451 @Test
452 def testForIntersectionAtHighPositionValues {
453 // This is a real-world example that led to a bug. That's where all the odd numbers come from.
455 val world = new World
457 val b1 = new Body
458 b1.shape = Circle(5)
459 b1.position = Vec2D(83.9699958296336, 488.77572653374307)
460 b1.velocity = Vec2D(-445.3620529718986, -50.442888201040574)
461 val b2 = new Body
462 b2.shape = Circle(30)
463 b2.position = Vec2D(50.0, 469.8163204364546)
464 b2.velocity = Vec2D(0.0, 0.0)
465 world.add(b1)
466 world.add(b2)
468 val broadPhase = new BroadPhase {
469 def detectPossibleCollisions(bodies: List[Body]) = {
470 (b1, b2)::Nil
473 world.broadPhase = broadPhase
475 val narrowPhase = new NarrowPhase {
476 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
477 Some(Collision(0.4766389925763854, Contact(b1, b2,
478 Vec2D(-0.8732041733361332, -0.4873545646327327),
479 Vec2D(0.8732041733361332, 0.4873545646327327), Vec2D(3, 0))))
482 world.narrowPhase = narrowPhase
484 world.step(0.02)
486 assertTrue((b2.position - b1.position).squaredLength >= 35.0 * 35.0)
491 @Test { val expected = classOf[IllegalArgumentException] }
492 def stepPassNegativeDelta {
493 val world = new World
494 world.step(-1.0)
499 @Test
500 def verifyBodyIterableIsAccessible {
501 val world = new World
502 assertTrue(world.bodies.isInstanceOf[Iterable[Body]])
507 @Test
508 def verifyBodyIterableCantChangeWorld {
509 val world = new World
510 val body = new Body
511 world.bodies.asInstanceOf[HashSet[Body]].addEntry(body)
512 assertFalse(world.bodies.exists(_ == body))