1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module dchip.cpSpace;
23 
24 import std..string;
25 
26 import dchip.cpArray;
27 import dchip.cpBB;
28 import dchip.cpBBTree;
29 import dchip.cpBody;
30 import dchip.chipmunk;
31 import dchip.chipmunk_private;
32 import dchip.chipmunk_types;
33 import dchip.cpArbiter;
34 import dchip.cpConstraint;
35 import dchip.cpHashSet;
36 import dchip.cpShape;
37 import dchip.cpSpaceHash;
38 import dchip.cpSpaceStep;
39 import dchip.cpSpatialIndex;
40 import dchip.cpVect;
41 import dchip.util;
42 
43 alias cpSpaceArbiterApplyImpulseFunc = void function(cpArbiter* arb);
44 
45 /// Basic Unit of Simulation in Chipmunk
46 struct cpSpace
47 {
48     /// Number of iterations to use in the impulse solver to solve contacts.
49     int iterations;
50 
51     /// Gravity to pass to rigid bodies when integrating velocity.
52     cpVect gravity;
53 
54     /// Damping rate expressed as the fraction of velocity bodies retain each second.
55     /// A value of 0.9 would mean that each body_'s velocity will drop 10% per second.
56     /// The default value is 1.0, meaning no damping is applied.
57     /// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring.
58     cpFloat damping = 0;
59 
60     /// Speed threshold for a body_ to be considered idle.
61     /// The default value of 0 means to let the space guess a good threshold based on gravity.
62     cpFloat idleSpeedThreshold = 0;
63 
64     /// Time a group of bodies must remain idle in order to fall asleep.
65     /// Enabling sleeping also implicitly enables the the contact graph.
66     /// The default value of INFINITY disables the sleeping algorithm.
67     cpFloat sleepTimeThreshold = 0;
68 
69     /// Amount of encouraged penetration between colliding shapes.
70     /// Used to reduce oscillating contacts and keep the collision cache warm.
71     /// Defaults to 0.1. If you have poor simulation quality,
72     /// increase this number as much as possible without allowing visible amounts of overlap.
73     cpFloat collisionSlop = 0;
74 
75     /// Determines how fast overlapping shapes are pushed apart.
76     /// Expressed as a fraction of the error remaining after each second.
77     /// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz.
78     cpFloat collisionBias = 0;
79 
80     /// Number of frames that contact information should persist.
81     /// Defaults to 3. There is probably never a reason to change this value.
82     cpTimestamp collisionPersistence;
83 
84     /// Rebuild the contact graph during each step. Must be enabled to use the cpBodyEachArbiter() function.
85     /// Disabled by default for a small performance boost. Enabled implicitly when the sleeping feature is enabled.
86     cpBool enableContactGraph;
87 
88     /// User definable data pointer.
89     /// Generally this points to your game's controller or game state
90     /// class so you can access it when given a cpSpace reference in a callback.
91     cpDataPointer data;
92 
93     /// The designated static body_ for this space.
94     /// You can modify this body_, or replace it with your own static body_.
95     /// By default it points to a statically allocated cpBody in the cpSpace struct.
96     cpBody* staticBody;
97 
98     version (CHIP_ALLOW_PRIVATE_ACCESS)
99         cpTimestamp stamp;
100     else
101         package cpTimestamp stamp;
102 
103     version (CHIP_ALLOW_PRIVATE_ACCESS)
104         cpFloat curr_dt = 0;
105     else
106         package cpFloat curr_dt = 0;
107 
108     version (CHIP_ALLOW_PRIVATE_ACCESS)
109         cpArray * bodies;
110     else
111         package cpArray * bodies;
112 
113     version (CHIP_ALLOW_PRIVATE_ACCESS)
114         cpArray * rousedBodies;
115     else
116         package cpArray * rousedBodies;
117 
118     version (CHIP_ALLOW_PRIVATE_ACCESS)
119         cpArray * sleepingComponents;
120     else
121         package cpArray * sleepingComponents;
122 
123     version (CHIP_ALLOW_PRIVATE_ACCESS)
124         cpSpatialIndex * staticShapes;
125     else
126         package cpSpatialIndex * staticShapes;
127 
128     version (CHIP_ALLOW_PRIVATE_ACCESS)
129         cpSpatialIndex * activeShapes;
130     else
131         package cpSpatialIndex * activeShapes;
132 
133     version (CHIP_ALLOW_PRIVATE_ACCESS)
134         cpArray * arbiters;
135     else
136         package cpArray * arbiters;
137 
138     version (CHIP_ALLOW_PRIVATE_ACCESS)
139         cpContactBufferHeader * contactBuffersHead;
140     else
141         package cpContactBufferHeader * contactBuffersHead;
142 
143     version (CHIP_ALLOW_PRIVATE_ACCESS)
144         cpHashSet * cachedArbiters;
145     else
146         package cpHashSet * cachedArbiters;
147 
148     version (CHIP_ALLOW_PRIVATE_ACCESS)
149         cpArray * pooledArbiters;
150     else
151         package cpArray * pooledArbiters;
152 
153     version (CHIP_ALLOW_PRIVATE_ACCESS)
154         cpArray * constraints;
155     else
156         package cpArray * constraints;
157 
158     version (CHIP_ALLOW_PRIVATE_ACCESS)
159         cpArray * allocatedBuffers;
160     else
161         package cpArray * allocatedBuffers;
162 
163     version (CHIP_ALLOW_PRIVATE_ACCESS)
164         int locked;
165     else
166         package int locked;
167 
168     version (CHIP_ALLOW_PRIVATE_ACCESS)
169         cpHashSet * collisionHandlers;
170     else
171         package cpHashSet * collisionHandlers;
172 
173     version (CHIP_ALLOW_PRIVATE_ACCESS)
174         cpCollisionHandler defaultHandler;
175     else
176         package cpCollisionHandler defaultHandler;
177 
178     version (CHIP_ALLOW_PRIVATE_ACCESS)
179         cpBool skipPostStep;
180     else
181         package cpBool skipPostStep;
182 
183     version (CHIP_ALLOW_PRIVATE_ACCESS)
184         cpArray * postStepCallbacks;
185     else
186         package cpArray * postStepCallbacks;
187 
188     version (CHIP_ALLOW_PRIVATE_ACCESS)
189         cpBody _staticBody;
190     else
191         package cpBody _staticBody;
192 }
193 
194 mixin template CP_DefineSpaceStructGetter(type, string member, string name)
195 {
196     mixin(q{
197         type cpSpaceGet%s(const cpSpace * space) { return cast(typeof(return))space.%s; }
198     }.format(name, member));
199 }
200 
201 mixin template CP_DefineSpaceStructSetter(type, string member, string name)
202 {
203     mixin(q{
204         void cpSpaceSet%s(cpSpace * space, type value) { space.%s = value; }
205     }.format(name, member));
206 }
207 
208 mixin template CP_DefineSpaceStructProperty(type, string member, string name)
209 {
210     mixin CP_DefineSpaceStructGetter!(type, member, name);
211     mixin CP_DefineSpaceStructSetter!(type, member, name);
212 }
213 
214 mixin CP_DefineSpaceStructProperty!(int, "iterations", "Iterations");
215 mixin CP_DefineSpaceStructProperty!(cpVect, "gravity", "Gravity");
216 mixin CP_DefineSpaceStructProperty!(cpFloat, "damping", "Damping");
217 mixin CP_DefineSpaceStructProperty!(cpFloat, "idleSpeedThreshold", "IdleSpeedThreshold");
218 mixin CP_DefineSpaceStructProperty!(cpFloat, "sleepTimeThreshold", "SleepTimeThreshold");
219 mixin CP_DefineSpaceStructProperty!(cpFloat, "collisionSlop", "CollisionSlop");
220 mixin CP_DefineSpaceStructProperty!(cpFloat, "collisionBias", "CollisionBias");
221 mixin CP_DefineSpaceStructProperty!(cpTimestamp, "collisionPersistence", "CollisionPersistence");
222 mixin CP_DefineSpaceStructProperty!(cpBool, "enableContactGraph", "EnableContactGraph");
223 mixin CP_DefineSpaceStructProperty!(cpDataPointer, "data", "UserData");
224 mixin CP_DefineSpaceStructGetter!(cpBody*, "staticBody", "StaticBody");
225 mixin CP_DefineSpaceStructGetter!(cpFloat, "curr_dt", "CurrentTimeStep");
226 
227 /// returns true from inside a callback and objects cannot be added/removed.
228 cpBool cpSpaceIsLocked(cpSpace* space)
229 {
230     return cast(bool)space.locked;
231 }
232 
233 /// Post Step callback function type.
234 alias cpPostStepFunc = void function(cpSpace* space, void* key, void* data);
235 
236 /// Point query callback function type.
237 alias cpSpacePointQueryFunc = void function(cpShape* shape, void* data);
238 
239 /// Nearest point query callback function type.
240 alias cpSpaceNearestPointQueryFunc = void function(cpShape* shape, cpFloat distance, cpVect point, void* data);
241 
242 /// Segment query callback function type.
243 alias cpSpaceSegmentQueryFunc = void function(cpShape* shape, cpFloat t, cpVect n, void* data);
244 
245 /// Rectangle Query callback function type.
246 alias cpSpaceBBQueryFunc = void function(cpShape* shape, void* data);
247 
248 /// Shape query callback function type.
249 alias cpSpaceShapeQueryFunc = void function(cpShape* shape, cpContactPointSet* points, void* data);
250 
251 /// Space/body_ iterator callback function type.
252 alias cpSpaceBodyIteratorFunc = void function(cpBody* bdy, void* data);
253 
254 /// Space/body_ iterator callback function type.
255 alias cpSpaceShapeIteratorFunc = void function(cpShape* shape, void* data);
256 
257 /// Space/constraint iterator callback function type.
258 alias cpSpaceConstraintIteratorFunc = void function(cpConstraint* constraint, void* data);
259 
260 // Equal function for arbiterSet.
261 cpBool arbiterSetEql(cpShape** shapes, cpArbiter* arb)
262 {
263     cpShape* a = shapes[0];
264     cpShape* b = shapes[1];
265 
266     return ((a == arb.a && b == arb.b) || (b == arb.a && a == arb.b));
267 }
268 
269 //MARK: Collision Handler Set HelperFunctions
270 
271 // Equals function for collisionHandlers.
272 cpBool handlerSetEql(cpCollisionHandler* check, cpCollisionHandler* pair)
273 {
274     return ((check.a == pair.a && check.b == pair.b) || (check.b == pair.a && check.a == pair.b));
275 }
276 
277 // Transformation function for collisionHandlers.
278 void* handlerSetTrans(cpCollisionHandler* handler, void* unused)
279 {
280     cpCollisionHandler* copy = cast(cpCollisionHandler*)cpcalloc(1, cpCollisionHandler.sizeof);
281     (*copy) = (*handler);
282 
283     return copy;
284 }
285 
286 //MARK: Misc Helper Funcs
287 
288 // Default collision functions.
289 cpBool alwaysCollide(cpArbiter* arb, cpSpace* space, void* data)
290 {
291     return 1;
292 }
293 
294 void nothing(cpArbiter* arb, cpSpace* space, void* data)
295 {
296 }
297 
298 // function to get the estimated velocity of a shape for the cpBBTree.
299 cpVect shapeVelocityFunc(cpShape* shape)
300 {
301     return shape.body_.v;
302 }
303 
304 //MARK: Memory Management Functions
305 
306 cpSpace* cpSpaceAlloc()
307 {
308     return cast(cpSpace*)cpcalloc(1, cpSpace.sizeof);
309 }
310 
311 __gshared cpCollisionHandler cpDefaultCollisionHandler = { 0, 0, &alwaysCollide, &alwaysCollide, &nothing, &nothing, null };
312 
313 cpSpace* cpSpaceInit(cpSpace* space)
314 {
315     version (CHIP_ENABLE_WARNINGS)
316     {
317         static cpBool done = cpFalse;
318 
319         if (!done)
320         {
321             import std.stdio;
322             stderr.writefln("Initializing cpSpace - Chipmunk v%s (Debug Enabled)", cpVersionString);
323             stderr.writefln("Compile without the CHIP_ENABLE_WARNINGS to disable debug mode and runtime assertion checks");
324             done = cpTrue;
325         }
326     }
327 
328     space.iterations = 10;
329 
330     space.gravity = cpvzero;
331     space.damping = 1.0f;
332 
333     space.collisionSlop        = 0.1f;
334     space.collisionBias        = cpfpow(1.0f - 0.1f, 60.0f);
335     space.collisionPersistence = 3;
336 
337     space.locked = 0;
338     space.stamp  = 0;
339 
340     space.staticShapes = cpBBTreeNew(cast(cpSpatialIndexBBFunc)&cpShapeGetBB, null);
341     space.activeShapes = cpBBTreeNew(cast(cpSpatialIndexBBFunc)&cpShapeGetBB, space.staticShapes);
342     cpBBTreeSetVelocityFunc(space.activeShapes, cast(cpBBTreeVelocityFunc)&shapeVelocityFunc);
343 
344     space.allocatedBuffers = cpArrayNew(0);
345 
346     space.bodies = cpArrayNew(0);
347     space.sleepingComponents = cpArrayNew(0);
348     space.rousedBodies       = cpArrayNew(0);
349 
350     space.sleepTimeThreshold = INFINITY;
351     space.idleSpeedThreshold = 0.0f;
352     space.enableContactGraph = cpFalse;
353 
354     space.arbiters       = cpArrayNew(0);
355     space.pooledArbiters = cpArrayNew(0);
356 
357     space.contactBuffersHead = null;
358     space.cachedArbiters     = cpHashSetNew(0, cast(cpHashSetEqlFunc)&arbiterSetEql);
359 
360     space.constraints = cpArrayNew(0);
361 
362     space.defaultHandler    = cpDefaultCollisionHandler;
363     space.collisionHandlers = cpHashSetNew(0, cast(cpHashSetEqlFunc)&handlerSetEql);
364     cpHashSetSetDefaultValue(space.collisionHandlers, &cpDefaultCollisionHandler);
365 
366     space.postStepCallbacks = cpArrayNew(0);
367     space.skipPostStep      = cpFalse;
368 
369     cpBodyInitStatic(&space._staticBody);
370     space.staticBody = &space._staticBody;
371 
372     return space;
373 }
374 
375 cpSpace* cpSpaceNew()
376 {
377     return cpSpaceInit(cpSpaceAlloc());
378 }
379 
380 extern(C) void cpConstraintFreeWrap(void* constraint)
381 {
382     cpConstraintFree(cast(cpConstraint*)constraint);
383 }
384 
385 /** Workarounds for https://github.com/slembcke/Chipmunk2D/issues/56. */
386 void freeWrap(void *ptr, void *unused) { cpfree(ptr); }
387 void shapeFreeWrap(void *ptr, void *unused) { cpShapeFree(cast(cpShape*)ptr); }
388 void bodyFreeWrap(void* ptr, void *unused) { cpBodyFree(cast(cpBody*)ptr); }
389 void constraintFreeWrap(void* ptr, void *unused) { cpConstraintFree(cast(cpConstraint*)ptr); }
390 
391 void cpSpaceDestroy(cpSpace* space)
392 {
393     cpSpaceEachBody(space, &cpBodyActivateWrap, null);
394 
395     cpSpatialIndexFree(space.staticShapes);
396     cpSpatialIndexFree(space.activeShapes);
397 
398     cpArrayFree(space.bodies);
399     cpArrayFree(space.sleepingComponents);
400     cpArrayFree(space.rousedBodies);
401 
402     cpArrayFree(space.constraints);
403 
404     cpHashSetFree(space.cachedArbiters);
405 
406     cpArrayFree(space.arbiters);
407     cpArrayFree(space.pooledArbiters);
408 
409     if (space.allocatedBuffers)
410     {
411         cpArrayFreeEach(space.allocatedBuffers, &cpfree);
412         cpArrayFree(space.allocatedBuffers);
413     }
414 
415     if (space.postStepCallbacks)
416     {
417         cpArrayFreeEach(space.postStepCallbacks, &cpfree);
418         cpArrayFree(space.postStepCallbacks);
419     }
420 
421     if (space.collisionHandlers)
422         cpHashSetEach(space.collisionHandlers, &freeWrap, null);
423     cpHashSetFree(space.collisionHandlers);
424 }
425 
426 void cpSpaceFree(cpSpace* space)
427 {
428     if (space)
429     {
430         cpSpaceDestroy(space);
431         cpfree(space);
432     }
433 }
434 
435 void cpAssertSpaceUnlocked(S)(S space)
436 {
437     cpAssertHard(!space.locked,
438                  "This operation cannot be done safely during a call to cpSpaceStep() or during a query. "~
439                  "Put these calls into a post-step callback."
440                  );
441 }
442 
443 //MARK: Collision Handler Function Management
444 
445 void cpSpaceAddCollisionHandler(
446     cpSpace* space,
447     cpCollisionType a, cpCollisionType b,
448     cpCollisionBeginFunc begin,
449     cpCollisionPreSolveFunc preSolve,
450     cpCollisionPostSolveFunc postSolve,
451     cpCollisionSeparateFunc separate,
452     void* data
453     )
454 {
455     cpAssertSpaceUnlocked(space);
456 
457     // Remove any old function so the new one will get added.
458     cpSpaceRemoveCollisionHandler(space, a, b);
459 
460     cpCollisionHandler handler = {
461         a, b,
462         begin ? begin : &alwaysCollide,
463         preSolve ? preSolve : &alwaysCollide,
464         postSolve ? postSolve : &nothing,
465         separate ? separate : &nothing,
466         data
467     };
468 
469     cpHashSetInsert(space.collisionHandlers, CP_HASH_PAIR(a, b), &handler, null, cast(cpHashSetTransFunc)&handlerSetTrans);
470 }
471 
472 void cpSpaceRemoveCollisionHandler(cpSpace* space, cpCollisionType a, cpCollisionType b)
473 {
474     cpAssertSpaceUnlocked(space);
475 
476     struct IDS
477     {
478         cpCollisionType a, b;
479     }
480     IDS ids = { a, b };
481     cpCollisionHandler* old_handler = cast(cpCollisionHandler*)cpHashSetRemove(space.collisionHandlers, CP_HASH_PAIR(a, b), &ids);
482     cpfree(old_handler);
483 }
484 
485 void cpSpaceSetDefaultCollisionHandler(
486     cpSpace* space,
487     cpCollisionBeginFunc begin,
488     cpCollisionPreSolveFunc preSolve,
489     cpCollisionPostSolveFunc postSolve,
490     cpCollisionSeparateFunc separate,
491     void* data
492     )
493 {
494     cpAssertSpaceUnlocked(space);
495 
496     cpCollisionHandler handler = {
497         0, 0,
498         begin ? begin : &alwaysCollide,
499         preSolve ? preSolve : &alwaysCollide,
500         postSolve ? postSolve : &nothing,
501         separate ? separate : &nothing,
502         data
503     };
504 
505     space.defaultHandler = handler;
506     cpHashSetSetDefaultValue(space.collisionHandlers, &space.defaultHandler);
507 }
508 
509 //MARK: Body, Shape, and Joint Management
510 cpShape* cpSpaceAddShape(cpSpace* space, cpShape* shape)
511 {
512     cpBody* body_ = shape.body_;
513 
514     if (cpBodyIsStatic(body_))
515         return cpSpaceAddStaticShape(space, shape);
516 
517     cpAssertHard(shape.space != space, "You have already added this shape to this space. You must not add it a second time.");
518     cpAssertHard(!shape.space, "You have already added this shape to another space. You cannot add it to a second.");
519     cpAssertSpaceUnlocked(space);
520 
521     cpBodyActivate(body_);
522     cpBodyAddShape(body_, shape);
523 
524     cpShapeUpdate(shape, body_.p, body_.rot);
525     cpSpatialIndexInsert(space.activeShapes, shape, shape.hashid);
526     shape.space = space;
527 
528     return shape;
529 }
530 
531 cpShape* cpSpaceAddStaticShape(cpSpace* space, cpShape* shape)
532 {
533     cpAssertHard(shape.space != space, "You have already added this shape to this space. You must not add it a second time.");
534     cpAssertHard(!shape.space, "You have already added this shape to another space. You cannot add it to a second.");
535     cpAssertHard(cpBodyIsRogue(shape.body_), "You are adding a static shape to a dynamic body_. Did you mean to attach it to a static or rogue body_? See the documentation for more information.");
536     cpAssertSpaceUnlocked(space);
537 
538     cpBody* body_ = shape.body_;
539     cpBodyAddShape(body_, shape);
540     cpShapeUpdate(shape, body_.p, body_.rot);
541     cpSpatialIndexInsert(space.staticShapes, shape, shape.hashid);
542     shape.space = space;
543 
544     return shape;
545 }
546 
547 cpBody* cpSpaceAddBody(cpSpace* space, cpBody* body_)
548 {
549     cpAssertHard(!cpBodyIsStatic(body_), "Do not add static bodies to a space. Static bodies do not move and should not be simulated.");
550     cpAssertHard(body_.space != space, "You have already added this body_ to this space. You must not add it a second time.");
551     cpAssertHard(!body_.space, "You have already added this body_ to another space. You cannot add it to a second.");
552     cpAssertSpaceUnlocked(space);
553 
554     cpArrayPush(space.bodies, body_);
555     body_.space = space;
556 
557     return body_;
558 }
559 
560 cpConstraint* cpSpaceAddConstraint(cpSpace* space, cpConstraint* constraint)
561 {
562     cpAssertHard(constraint.space != space, "You have already added this constraint to this space. You must not add it a second time.");
563     cpAssertHard(!constraint.space, "You have already added this constraint to another space. You cannot add it to a second.");
564     cpAssertHard(constraint.a && constraint.b, "Constraint is attached to a null body_.");
565     cpAssertSpaceUnlocked(space);
566 
567     cpBodyActivate(constraint.a);
568     cpBodyActivate(constraint.b);
569     cpArrayPush(space.constraints, constraint);
570 
571     // Push onto the heads of the bodies' constraint lists
572     cpBody* a = constraint.a;
573     cpBody* b = constraint.b;
574     constraint.next_a = a.constraintList;
575     a.constraintList  = constraint;
576     constraint.next_b = b.constraintList;
577     b.constraintList  = constraint;
578     constraint.space  = space;
579 
580     return constraint;
581 }
582 
583 struct arbiterFilterContext
584 {
585     cpSpace* space;
586     cpBody* body_;
587     cpShape* shape;
588 };
589 
590 cpBool cachedArbitersFilter(cpArbiter* arb, arbiterFilterContext* context)
591 {
592     cpShape* shape = context.shape;
593     cpBody * body_  = context.body_;
594 
595     // Match on the filter shape, or if it's null the filter body_
596     if (
597         (body_ == arb.body_a && (shape == arb.a || shape == null)) ||
598         (body_ == arb.body_b && (shape == arb.b || shape == null))
599         )
600     {
601         // Call separate when removing shapes.
602         if (shape && arb.state != cpArbiterStateCached)
603             cpArbiterCallSeparate(arb, context.space);
604 
605         cpArbiterUnthread(arb);
606         cpArrayDeleteObj(context.space.arbiters, arb);
607         cpArrayPush(context.space.pooledArbiters, arb);
608 
609         return cpFalse;
610     }
611 
612     return cpTrue;
613 }
614 
615 void cpSpaceFilterArbiters(cpSpace* space, cpBody* body_, cpShape* filter)
616 {
617     cpSpaceLock(space);
618     {
619         arbiterFilterContext context = { space, body_, filter };
620         cpHashSetFilter(space.cachedArbiters, safeCast!cpHashSetFilterFunc(&cachedArbitersFilter), &context);
621     }
622     cpSpaceUnlock(space, cpTrue);
623 }
624 
625 void cpSpaceRemoveShape(cpSpace* space, cpShape* shape)
626 {
627     cpBody* body_ = shape.body_;
628 
629     if (cpBodyIsStatic(body_))
630     {
631         cpSpaceRemoveStaticShape(space, shape);
632     }
633     else
634     {
635         cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)");
636         cpAssertSpaceUnlocked(space);
637 
638         cpBodyActivate(body_);
639         cpBodyRemoveShape(body_, shape);
640         cpSpaceFilterArbiters(space, body_, shape);
641         cpSpatialIndexRemove(space.activeShapes, shape, shape.hashid);
642         shape.space = null;
643     }
644 }
645 
646 void cpSpaceRemoveStaticShape(cpSpace* space, cpShape* shape)
647 {
648     cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a static or sleeping shape that was not added to the space. (Removed twice maybe?)");
649     cpAssertSpaceUnlocked(space);
650 
651     cpBody* body_ = shape.body_;
652 
653     if (cpBodyIsStatic(body_))
654         cpBodyActivateStatic(body_, shape);
655     cpBodyRemoveShape(body_, shape);
656     cpSpaceFilterArbiters(space, body_, shape);
657     cpSpatialIndexRemove(space.staticShapes, shape, shape.hashid);
658     shape.space = null;
659 }
660 
661 void cpSpaceRemoveBody(cpSpace* space, cpBody* body_)
662 {
663     cpAssertHard(cpSpaceContainsBody(space, body_), "Cannot remove a body_ that was not added to the space. (Removed twice maybe?)");
664     cpAssertSpaceUnlocked(space);
665 
666     cpBodyActivate(body_);
667 
668     //	cpSpaceFilterArbiters(space, body_, null);
669     cpArrayDeleteObj(space.bodies, body_);
670     body_.space = null;
671 }
672 
673 void cpSpaceRemoveConstraint(cpSpace* space, cpConstraint* constraint)
674 {
675     cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)");
676     cpAssertSpaceUnlocked(space);
677 
678     cpBodyActivate(constraint.a);
679     cpBodyActivate(constraint.b);
680     cpArrayDeleteObj(space.constraints, constraint);
681 
682     cpBodyRemoveConstraint(constraint.a, constraint);
683     cpBodyRemoveConstraint(constraint.b, constraint);
684     constraint.space = null;
685 }
686 
687 cpBool cpSpaceContainsShape(cpSpace* space, cpShape* shape)
688 {
689     return (shape.space == space);
690 }
691 
692 cpBool cpSpaceContainsBody(cpSpace* space, cpBody* body_)
693 {
694     return (body_.space == space);
695 }
696 
697 cpBool cpSpaceContainsConstraint(cpSpace* space, cpConstraint* constraint)
698 {
699     return (constraint.space == space);
700 }
701 
702 //MARK: Static/rogue body_ conversion.
703 
704 void cpSpaceConvertBodyToStatic(cpSpace* space, cpBody* body_)
705 {
706     cpAssertHard(!cpBodyIsStatic(body_), "Body is already static.");
707     cpAssertHard(cpBodyIsRogue(body_), "Remove the body_ from the space before calling this function.");
708     cpAssertSpaceUnlocked(space);
709 
710     cpBodySetMass(body_, INFINITY);
711     cpBodySetMoment(body_, INFINITY);
712 
713     cpBodySetVel(body_, cpvzero);
714     cpBodySetAngVel(body_, 0.0f);
715 
716     body_.node.idleTime = INFINITY;
717 
718     mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{
719         cpSpatialIndexRemove(space.activeShapes, shape, shape.hashid);
720         cpSpatialIndexInsert(space.staticShapes, shape, shape.hashid);
721     }));
722 }
723 
724 void cpSpaceConvertBodyToDynamic(cpSpace* space, cpBody* body_, cpFloat m, cpFloat i)
725 {
726     cpAssertHard(cpBodyIsStatic(body_), "Body is already dynamic.");
727     cpAssertSpaceUnlocked(space);
728 
729     cpBodyActivateStatic(body_, null);
730 
731     cpBodySetMass(body_, m);
732     cpBodySetMoment(body_, i);
733 
734     body_.node.idleTime = 0.0f;
735     mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{
736         cpSpatialIndexRemove(space.staticShapes, shape, shape.hashid);
737         cpSpatialIndexInsert(space.activeShapes, shape, shape.hashid);
738     }));
739 }
740 
741 //MARK: Iteration
742 
743 void cpSpaceEachBody(cpSpace* space, cpSpaceBodyIteratorFunc func, void* data)
744 {
745     cpSpaceLock(space);
746     {
747         cpArray* bodies = space.bodies;
748 
749         for (int i = 0; i < bodies.num; i++)
750         {
751             func(cast(cpBody*)bodies.arr[i], data);
752         }
753 
754         cpArray* components = space.sleepingComponents;
755 
756         for (int i = 0; i < components.num; i++)
757         {
758             cpBody* root = cast(cpBody*)components.arr[i];
759 
760             cpBody* body_ = root;
761 
762             while (body_)
763             {
764                 cpBody* next = body_.node.next;
765                 func(body_, data);
766                 body_ = next;
767             }
768         }
769     }
770     cpSpaceUnlock(space, cpTrue);
771 }
772 
773 struct spaceShapeContext
774 {
775     cpSpaceShapeIteratorFunc func;
776     void* data;
777 }
778 
779 void spaceEachShapeIterator(cpShape* shape, spaceShapeContext* context)
780 {
781     context.func(shape, context.data);
782 }
783 
784 void cpSpaceEachShape(cpSpace* space, cpSpaceShapeIteratorFunc func, void* data)
785 {
786     cpSpaceLock(space);
787     {
788         spaceShapeContext context = { func, data };
789         cpSpatialIndexEach(space.activeShapes, safeCast!cpSpatialIndexIteratorFunc(&spaceEachShapeIterator), &context);
790         cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(&spaceEachShapeIterator), &context);
791     }
792     cpSpaceUnlock(space, cpTrue);
793 }
794 
795 void cpSpaceEachConstraint(cpSpace* space, cpSpaceConstraintIteratorFunc func, void* data)
796 {
797     cpSpaceLock(space);
798     {
799         cpArray* constraints = space.constraints;
800 
801         for (int i = 0; i < constraints.num; i++)
802         {
803             func(cast(cpConstraint*)constraints.arr[i], data);
804         }
805     }
806     cpSpaceUnlock(space, cpTrue);
807 }
808 
809 //MARK: Spatial Index Management
810 
811 void updateBBCache(cpShape* shape, void* unused)
812 {
813     cpBody* body_ = shape.body_;
814     cpShapeUpdate(shape, body_.p, body_.rot);
815 }
816 
817 void cpSpaceReindexStatic(cpSpace* space)
818 {
819     cpAssertHard(!space.locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");
820 
821     cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(&updateBBCache), null);
822     cpSpatialIndexReindex(space.staticShapes);
823 }
824 
825 void cpSpaceReindexShape(cpSpace* space, cpShape* shape)
826 {
827     cpAssertHard(!space.locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");
828 
829     cpBody* body_ = shape.body_;
830     cpShapeUpdate(shape, body_.p, body_.rot);
831 
832     // attempt to rehash the shape in both hashes
833     cpSpatialIndexReindexObject(space.activeShapes, shape, shape.hashid);
834     cpSpatialIndexReindexObject(space.staticShapes, shape, shape.hashid);
835 }
836 
837 void cpSpaceReindexShapesForBody(cpSpace* space, cpBody* body_)
838 {
839     mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", "cpSpaceReindexShape(space, shape);"));
840 }
841 
842 void copyShapes(cpShape* shape, cpSpatialIndex* index)
843 {
844     cpSpatialIndexInsert(index, shape, shape.hashid);
845 }
846 
847 void cpSpaceUseSpatialHash(cpSpace* space, cpFloat dim, int count)
848 {
849     cpSpatialIndex* staticShapes = cpSpaceHashNew(dim, count, safeCast!cpSpatialIndexBBFunc(&cpShapeGetBB), null);
850     cpSpatialIndex* activeShapes = cpSpaceHashNew(dim, count, safeCast!cpSpatialIndexBBFunc(&cpShapeGetBB), staticShapes);
851 
852     cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(&copyShapes), staticShapes);
853     cpSpatialIndexEach(space.activeShapes, safeCast!cpSpatialIndexIteratorFunc(&copyShapes), activeShapes);
854 
855     cpSpatialIndexFree(space.staticShapes);
856     cpSpatialIndexFree(space.activeShapes);
857 
858     space.staticShapes = staticShapes;
859     space.activeShapes = activeShapes;
860 }