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 };