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 demo.ContactGraph; 23 24 import core.stdc.stdlib; 25 26 import std.math; 27 28 alias M_PI_2 = PI_2; 29 30 import demo.dchip; 31 32 import demo.ChipmunkDebugDraw; 33 import demo.ChipmunkDemo; 34 import demo.types; 35 36 // static body_ that we will be making into a scale 37 static cpBody* scaleStaticBody; 38 static cpBody* ballBody; 39 40 static void ScaleIterator(cpBody* body_, cpArbiter* arb, cpVect* sum) 41 { 42 (*sum) = cpvadd(*sum, cpArbiterTotalImpulseWithFriction(arb)); 43 } 44 45 static void BallIterator(cpBody* body_, cpArbiter* arb, int* count) 46 { 47 // body_ is the body_ we are iterating the arbiters for. 48 // CP_ARBITER_GET_*() in an arbiter iterator always returns the body_/shape for the iterated body_ first. 49 mixin(CP_ARBITER_GET_SHAPES!("arb", "ball", "other")); 50 ChipmunkDebugDrawBB(cpShapeGetBB(other), RGBAColor(1, 0, 0, 1)); 51 52 (*count)++; 53 } 54 55 struct CrushingContext 56 { 57 cpFloat magnitudeSum = 0; 58 cpVect vectorSum; 59 } 60 61 static void EstimateCrushing(cpBody* body_, cpArbiter* arb, CrushingContext* context) 62 { 63 cpVect j = cpArbiterTotalImpulseWithFriction(arb); 64 context.magnitudeSum += cpvlength(j); 65 context.vectorSum = cpvadd(context.vectorSum, j); 66 } 67 68 static void update(cpSpace* space, double dt) 69 { 70 cpSpaceStep(space, dt); 71 72 ChipmunkDemoPrintString("Place objects on the scale to weigh them. The ball marks the shapes it's sitting on.\n"); 73 74 // Sum the total impulse applied to the scale from all collision pairs in the contact graph. 75 // If your compiler supports blocks, your life is a little easier. 76 cpVect impulseSum = cpvzero; 77 cpBodyEachArbiter(scaleStaticBody, safeCast!cpBodyArbiterIteratorFunc(&ScaleIterator), &impulseSum); 78 79 // Force is the impulse divided by the timestep. 80 cpFloat force = cpvlength(impulseSum) / dt; 81 82 // Weight can be found similarly from the gravity vector. 83 cpVect g = cpSpaceGetGravity(space); 84 cpFloat weight = cpvdot(g, impulseSum) / (cpvlengthsq(g) * dt); 85 86 ChipmunkDemoPrintString("Total force: %5.2f, Total weight: %5.2f. ", force, weight); 87 88 // Highlight and count the number of shapes the ball is touching. 89 int count = 0; 90 cpBodyEachArbiter(ballBody, safeCast!cpBodyArbiterIteratorFunc(&BallIterator), &count); 91 92 ChipmunkDemoPrintString("The ball is touching %d shapes.\n", count); 93 94 CrushingContext crush = { 0.0f, cpvzero }; 95 cpBodyEachArbiter(ballBody, safeCast!cpBodyArbiterIteratorFunc(&EstimateCrushing), &crush); 96 97 cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum)) * dt; 98 99 if (crushForce > 10.0f) 100 { 101 ChipmunkDemoPrintString("The ball is being crushed. (f: %.2f)", crushForce); 102 } 103 else 104 { 105 ChipmunkDemoPrintString("The ball is not being crushed. (f: %.2f)", crushForce); 106 } 107 } 108 109 enum WIDTH = 4.0f; 110 enum HEIGHT = 30.0f; 111 112 static cpSpace* init() 113 { 114 cpSpace* space = cpSpaceNew(); 115 cpSpaceSetIterations(space, 30); 116 cpSpaceSetGravity(space, cpv(0, -300)); 117 cpSpaceSetCollisionSlop(space, 0.5); 118 119 // For cpBodyEachArbiter() to work you must explicitly enable the contact graph or enable sleeping. 120 // Generating the contact graph is a small but measurable ~5-10% performance hit so it's not enabled by default. 121 // cpSpaceSetEnableContactGraph(space, cpTrue); 122 cpSpaceSetSleepTimeThreshold(space, 1.0f); 123 124 cpBody * body_; 125 cpBody * staticBody = cpSpaceGetStaticBody(space); 126 cpShape* shape; 127 128 // Create segments around the edge of the screen. 129 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); 130 cpShapeSetElasticity(shape, 1.0f); 131 cpShapeSetFriction(shape, 1.0f); 132 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 133 134 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); 135 cpShapeSetElasticity(shape, 1.0f); 136 cpShapeSetFriction(shape, 1.0f); 137 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 138 139 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); 140 cpShapeSetElasticity(shape, 1.0f); 141 cpShapeSetFriction(shape, 1.0f); 142 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 143 144 scaleStaticBody = cpBodyNewStatic(); 145 shape = cpSpaceAddShape(space, cpSegmentShapeNew(scaleStaticBody, cpv(-240, -180), cpv(-140, -180), 4.0f)); 146 cpShapeSetElasticity(shape, 1.0f); 147 cpShapeSetFriction(shape, 1.0f); 148 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 149 150 // add some boxes to stack on the scale 151 for (int i = 0; i < 5; i++) 152 { 153 body_ = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForBox(1.0f, 30.0f, 30.0f))); 154 cpBodySetPos(body_, cpv(0, i * 32 - 220)); 155 156 shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, 30.0f, 30.0f)); 157 cpShapeSetElasticity(shape, 0.0f); 158 cpShapeSetFriction(shape, 0.8f); 159 } 160 161 // Add a ball that we'll track which objects are beneath it. 162 cpFloat radius = 15.0f; 163 ballBody = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); 164 cpBodySetPos(ballBody, cpv(120, -240 + radius + 5)); 165 166 shape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero)); 167 cpShapeSetElasticity(shape, 0.0f); 168 cpShapeSetFriction(shape, 0.9f); 169 170 return space; 171 } 172 173 static void destroy(cpSpace* space) 174 { 175 ChipmunkDemoFreeSpaceChildren(space); 176 cpSpaceFree(space); 177 178 cpBodyFree(scaleStaticBody); 179 } 180 181 ChipmunkDemo ContactGraph = { 182 "Contact Graph", 183 1.0 / 60.0, 184 &init, 185 &update, 186 &ChipmunkDemoDefaultDrawImpl, 187 &destroy, 188 };