Throw an exception if a negative time delta is passed to World.step.
[scd.git] / test / net / habraun / sd / WorldTest.scala
blobe8a504cec4259323ee0f1dd3c1ce30c1e28b5215
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 math._
25 import org.junit._
26 import org.junit.Assert._
30 class WorldTest {
32 @Test
33 def addBodyStepExpectBodyMoved {
34 val world = new World
35 val body = new Body
36 body.velocity = Vec2D(1, 0)
37 world.add(body)
38 world.step(2.0)
39 assertEquals(Vec2D(2, 0), body.position)
44 @Test
45 def addAndRemoveBodyExpectBodyNotMoved {
46 val world = new World
47 val body = new Body
48 body.velocity = Vec2D(1, 0)
49 world.add(body)
50 world.remove(body)
51 world.step(2.0)
52 assertEquals(Vec2D(0, 0), body.position)
57 @Test
58 def addBodyApplyForceCheckVelocity {
59 val world = new World
60 val body = new Body
61 body.mass = 5
62 body.applyForce(Vec2D(5, 0))
63 world.add(body)
64 world.step(2.0)
65 assertEquals(Vec2D(2, 0), body.velocity)
70 @Test
71 def addBodyApplyForceStepTwiceCheckVelocity {
72 val world = new World
73 val body = new Body
74 body.mass = 5
75 body.applyForce(Vec2D(5, 0))
76 world.add(body)
77 world.step(2.0)
78 world.step(2.0)
79 assertEquals(Vec2D(2, 0), body.velocity)
84 @Test
85 def addBodyApplyForceStepCheckPosition {
86 val world = new World
87 val body = new Body
88 body.mass = 5
89 body.applyForce(Vec2D(5, 0))
90 world.add(body)
91 world.step(2.0)
92 assertEquals(Vec2D(4, 0), body.position)
97 @Test
98 def addBodyApplyImpulseCheckVelocity {
99 val world = new World
100 val body = new Body
101 body.mass = 5
102 body.velocity = Vec2D(3, 0)
103 body.applyImpulse(Vec2D(5, 0))
104 world.add(body)
105 world.step(2.0)
106 assertEquals(Vec2D(4, 0), body.velocity)
111 @Test
112 def addBodyApplyImpulseCheckVelocity2 {
113 val world = new World
114 val body = new Body
115 body.mass = 5
116 body.velocity = Vec2D(3, 0)
117 body.applyImpulse(Vec2D(5, 0))
118 world.add(body)
119 world.step(5.0)
120 assertEquals(Vec2D(4, 0), body.velocity)
125 @Test
126 def addBodyApplyImpulseCheckImpulse {
127 val world = new World
128 val body = new Body
129 body.applyImpulse(Vec2D(10, 10))
130 world.add(body)
131 world.step(2.0)
132 assertEquals(Vec2D(0, 0), body.appliedImpulse)
137 @Test
138 def addBodyDisallowXMovementStepCheckPosition {
139 val world = new World
140 val body = new Body
141 body.allowXMovement(false)
142 body.applyForce(Vec2D(1, 1))
143 world.add(body)
144 world.step(2.0)
145 assertEquals(Vec2D(0, 4), body.position)
150 @Test
151 def addBodyDisallowYMovementStepCheckPosition {
152 val world = new World
153 val body = new Body
154 body.allowYMovement(false)
155 body.applyForce(Vec2D(1, 1))
156 world.add(body)
157 world.step(2.0)
158 assertEquals(Vec2D(4, 0), body.position)
163 @Test
164 def verifyInitialBroadPhase {
165 val world = new World
166 assertTrue(world.broadPhase.isInstanceOf[SimpleBroadPhase])
171 @Test
172 def verifyInitialNarrowPhase {
173 val world = new World
174 assertTrue(world.narrowPhase.isInstanceOf[SimpleNarrowPhase])
179 @Test { val expected = classOf[NullPointerException] }
180 def setBroadPhaseNullExpectException {
181 val world = new World
182 world.broadPhase = null
187 @Test { val expected = classOf[NullPointerException] }
188 def setNarrowPhaseNullExpectException {
189 val world = new World
190 world.narrowPhase = null
195 @Test
196 def addBodyVerifyItIsPassedToBroadPhase {
197 val world = new World
199 val broadPhase = new BroadPhase {
200 var passedBodies: List[Body] = null
201 def detectPossibleCollisions(bodies: List[Body]) = { passedBodies = bodies; Nil }
203 world.broadPhase = broadPhase
205 val body = new Body
206 world.add(body)
207 world.step(2.0)
209 assertEquals(body::Nil, broadPhase.passedBodies)
214 @Test
215 def addBroadPhaseReturningBodyPairsVerifyTheyArePassedToNarrowPhase {
216 val world = new World
218 val b1 = new Body
219 val b2 = new Body
220 val b3 = new Body
221 val b4 = new Body
223 world.broadPhase = new BroadPhase {
224 def detectPossibleCollisions(bodies: List[Body]) = (b1, b2)::(b3, b4)::Nil
227 val narrowPhase = new NarrowPhase {
228 var passedPairs: List[(Body, Body)] = Nil
229 def inspectCollision(delta: Double, b1: Body, b2: Body) = { passedPairs = passedPairs:::List((b1, b2)); None }
231 world.narrowPhase = narrowPhase
233 world.step(2.0)
235 assertEquals((b1, b2)::(b3, b4)::Nil, narrowPhase.passedPairs)
240 @Test
241 def verifyCollisionEffects {
242 val world = new World
244 val b1 = new Body
245 b1.position = Vec2D(0, 1)
246 b1.mass = 4
247 b1.velocity = Vec2D(-10, -10)
248 val b2 = new Body
249 b2.position = Vec2D(0, -1)
250 b2.mass = 8
251 b2.velocity = Vec2D(5, 5)
253 world.narrowPhase = new NarrowPhase {
254 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
255 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, -1), Vec2D(0, 1), Vec2D(0, 0))))
259 world.add(b1)
260 world.add(b2)
261 world.step(2.0)
263 assertEquals(Vec2D(0, 80), b1.appliedImpulse)
264 assertEquals(Vec2D(0, -80), b2.appliedImpulse)
269 @Test
270 def verifyCollisionEffectsWithBody1Static {
271 val world = new World
273 val b1 = new Body
274 b1.mass = Double.PositiveInfinity
275 val b2 = new Body
276 b2.mass = 5
277 b2.velocity = Vec2D(1, 1)
279 world.narrowPhase = new NarrowPhase {
280 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
281 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, -1), Vec2D(0, 1), Vec2D(0, 0))))
285 world.add(b1)
286 world.add(b2)
287 world.step(2.0)
289 assertEquals(Vec2D(0, 0), b1.appliedImpulse)
290 assertEquals(Vec2D(0, -10), b2.appliedImpulse)
295 @Test
296 def verifyCollisionEffectsWithBody2Static {
297 val world = new World
299 val b1 = new Body
300 b1.mass= 5
301 b1.velocity = Vec2D(1, 1)
302 val b2 = new Body
303 b2.mass = Double.PositiveInfinity
305 world.narrowPhase = new NarrowPhase {
306 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
307 Some(Collision(1.0, Contact(b1, b2, Vec2D(0, 1), Vec2D(0, -1), Vec2D(0, 0))))
311 world.add(b1)
312 world.add(b2)
313 world.step(2.0)
315 assertEquals(Vec2D(0, -10), b1.appliedImpulse)
316 assertEquals(Vec2D(0, 0), b2.appliedImpulse)
321 @Test
322 def verifyForceIsAppliedBeforeCollisionDetection {
323 val world = new World
325 val broadPhase = new BroadPhase {
326 var v: Vec2D = null
327 def detectPossibleCollisions(bodies: List[Body]) = {
328 v = bodies(0).velocity
332 world.broadPhase = broadPhase
334 val body = new Body
335 body.mass = 5
336 body.applyForce(Vec2D(10, 0))
337 world.add(body)
339 world.step(2.0)
341 assertEquals(Vec2D(4, 0), broadPhase.v)
346 @Test
347 def verifyImpulseIsAppliedBeforeCollisionDetection {
348 val world = new World
350 val broadPhase = new BroadPhase {
351 var v: Vec2D = null
352 def detectPossibleCollisions(bodies: List[Body]) = {
353 v = bodies(0).velocity
357 world.broadPhase = broadPhase
359 val body = new Body
360 body.mass = 5
361 body.applyImpulse(Vec2D(10, 0))
362 world.add(body)
364 world.step(2.0)
366 assertEquals(Vec2D(2, 0), broadPhase.v)
371 @Test
372 def addBodiesThatWouldIntersectAfterMovementVerifyTheyDont {
373 val world = new World
375 val b1 = new Body
376 b1.shape = Circle(2)
377 b1.position = Vec2D(0, 0)
378 b1.velocity = Vec2D(1, 0)
379 val b2 = new Body
380 b2.shape = Circle(2)
381 b2.position = Vec2D(5, 0)
382 b2.velocity = Vec2D(0, 0)
383 world.add(b1)
384 world.add(b2)
386 val broadPhase = new BroadPhase {
387 def detectPossibleCollisions(bodies: List[Body]) = {
388 (b1, b2)::Nil
391 world.broadPhase = broadPhase
393 val narrowPhase = new NarrowPhase {
394 def inspectCollision(delta: Double, b1: Body, b2: Body) = {
395 Some(Collision(0.5, Contact(b1, b2, Vec2D(1, 0), Vec2D(-1, 0), Vec2D(3, 0))))
398 world.narrowPhase = narrowPhase
400 world.step(2.0)
402 val expectedPosition = Vec2D(1, 0)
404 assertEquals(expectedPosition.x, b1.position.x, 0.01)
405 assertEquals(expectedPosition.y, b1.position.y, 0.01)
410 @Test
411 def testForIntersectionAtHighPositionValues {
412 // This is a real-world example that led to a bug. That's where all the odd numbers come from.
414 val world = new World
416 val b1 = new Body
417 b1.shape = Circle(5)
418 b1.position = Vec2D(83.9699958296336, 488.77572653374307)
419 b1.velocity = Vec2D(-445.3620529718986, -50.442888201040574)
420 val b2 = new Body
421 b2.shape = Circle(30)
422 b2.position = Vec2D(50.0, 469.8163204364546)
423 b2.velocity = Vec2D(0.0, 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.4766389925763854, Contact(b1, b2,
437 Vec2D(-0.8732041733361332, -0.4873545646327327),
438 Vec2D(0.8732041733361332, 0.4873545646327327), Vec2D(3, 0))))
441 world.narrowPhase = narrowPhase
443 world.step(0.02)
445 assertTrue((b2.position - b1.position).squaredLength >= 35.0 * 35.0)
450 @Test { val expected = classOf[IllegalArgumentException] }
451 def stepPassNegativeDelta {
452 val world = new World
453 world.step(-1.0)