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.Slice; 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 enum DENSITY = (1.0 / 10000.0); 37 38 static void ClipPoly(cpSpace* space, cpShape* shape, cpVect n, cpFloat dist) 39 { 40 cpBody* body_ = cpShapeGetBody(shape); 41 42 int count = cpPolyShapeGetNumVerts(shape); 43 int clippedCount = 0; 44 45 cpVect* clipped = cast(cpVect*)alloca((count + 1) * cpVect.sizeof); 46 47 for (int i = 0, j = count - 1; i < count; j = i, i++) 48 { 49 cpVect a = cpBodyLocal2World(body_, cpPolyShapeGetVert(shape, j)); 50 cpFloat a_dist = cpvdot(a, n) - dist; 51 52 if (a_dist < 0.0) 53 { 54 clipped[clippedCount] = a; 55 clippedCount++; 56 } 57 58 cpVect b = cpBodyLocal2World(body_, cpPolyShapeGetVert(shape, i)); 59 cpFloat b_dist = cpvdot(b, n) - dist; 60 61 if (a_dist * b_dist < 0.0f) 62 { 63 cpFloat t = cpfabs(a_dist) / (cpfabs(a_dist) + cpfabs(b_dist)); 64 65 clipped[clippedCount] = cpvlerp(a, b, t); 66 clippedCount++; 67 } 68 } 69 70 cpVect centroid = cpCentroidForPoly(clippedCount, clipped); 71 cpFloat mass = cpAreaForPoly(clippedCount, clipped) * DENSITY; 72 cpFloat moment = cpMomentForPoly(mass, clippedCount, clipped, cpvneg(centroid)); 73 74 cpBody* new_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); 75 cpBodySetPos(new_body, centroid); 76 cpBodySetVel(new_body, cpBodyGetVelAtWorldPoint(body_, centroid)); 77 cpBodySetAngVel(new_body, cpBodyGetAngVel(body_)); 78 79 cpShape* new_shape = cpSpaceAddShape(space, cpPolyShapeNew(new_body, clippedCount, clipped, cpvneg(centroid))); 80 81 // Copy whatever properties you have set on the original shape that are important 82 cpShapeSetFriction(new_shape, cpShapeGetFriction(shape)); 83 } 84 85 // Context structs are annoying, use blocks or closures instead if your compiler supports them. 86 struct SliceContext 87 { 88 cpVect a, b; 89 cpSpace* space; 90 }; 91 92 static void SliceShapePostStep(cpSpace* space, cpShape* shape, SliceContext* context) 93 { 94 cpVect a = context.a; 95 cpVect b = context.b; 96 97 // Clipping plane normal and distance. 98 cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); 99 cpFloat dist = cpvdot(a, n); 100 101 ClipPoly(space, shape, n, dist); 102 ClipPoly(space, shape, cpvneg(n), -dist); 103 104 cpBody* body_ = cpShapeGetBody(shape); 105 cpSpaceRemoveShape(space, shape); 106 cpSpaceRemoveBody(space, body_); 107 cpShapeFree(shape); 108 cpBodyFree(body_); 109 } 110 111 static void SliceQuery(cpShape* shape, cpFloat t, cpVect n, SliceContext* context) 112 { 113 cpVect a = context.a; 114 cpVect b = context.b; 115 116 // Check that the slice was complete by checking that the endpoints aren't in the sliced shape. 117 if (!cpShapePointQuery(shape, a) && !cpShapePointQuery(shape, b)) 118 { 119 // Can't modify the space during a query. 120 // Must make a post-step callback to do the actual slicing. 121 cpSpaceAddPostStepCallback(context.space, safeCast!cpPostStepFunc(&SliceShapePostStep), shape, context); 122 } 123 } 124 125 static void update(cpSpace* space, double dt) 126 { 127 cpSpaceStep(space, dt); 128 129 static cpBool lastClickState = cpFalse; 130 static cpVect sliceStart = { 0.0, 0.0 }; 131 132 // Annoying state tracking code that you wouldn't need 133 // in a real event driven system. 134 if (ChipmunkDemoRightClick != lastClickState) 135 { 136 if (ChipmunkDemoRightClick) 137 { 138 // MouseDown 139 sliceStart = ChipmunkDemoMouse; 140 } 141 else 142 { 143 // MouseUp 144 SliceContext context = { sliceStart, ChipmunkDemoMouse, space }; 145 cpSpaceSegmentQuery(space, sliceStart, ChipmunkDemoMouse, GRABABLE_MASK_BIT, CP_NO_GROUP, safeCast!cpSpaceSegmentQueryFunc(&SliceQuery), &context); 146 } 147 148 lastClickState = ChipmunkDemoRightClick; 149 } 150 151 if (ChipmunkDemoRightClick) 152 { 153 ChipmunkDebugDrawSegment(sliceStart, ChipmunkDemoMouse, RGBAColor(1, 0, 0, 1)); 154 } 155 } 156 157 static cpSpace* init() 158 { 159 ChipmunkDemoMessageString = "Right click and drag to slice up the block.".dup; 160 161 cpSpace* space = cpSpaceNew(); 162 cpSpaceSetIterations(space, 30); 163 cpSpaceSetGravity(space, cpv(0, -500)); 164 cpSpaceSetSleepTimeThreshold(space, 0.5f); 165 cpSpaceSetCollisionSlop(space, 0.5f); 166 167 cpBody * body_; 168 cpBody * staticBody = cpSpaceGetStaticBody(space); 169 cpShape* shape; 170 171 // Create segments around the edge of the screen. 172 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000, -240), cpv(1000, -240), 0.0f)); 173 cpShapeSetElasticity(shape, 1.0f); 174 cpShapeSetFriction(shape, 1.0f); 175 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 176 177 cpFloat width = 200.0f; 178 cpFloat height = 300.0f; 179 cpFloat mass = width * height * DENSITY; 180 cpFloat moment = cpMomentForBox(mass, width, height); 181 182 body_ = cpSpaceAddBody(space, cpBodyNew(mass, moment)); 183 184 shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, width, height)); 185 cpShapeSetFriction(shape, 0.6f); 186 187 return space; 188 } 189 190 static void destroy(cpSpace* space) 191 { 192 ChipmunkDemoFreeSpaceChildren(space); 193 cpSpaceFree(space); 194 } 195 196 ChipmunkDemo Slice = { 197 "Slice.", 198 1.0 / 60.0, 199 &init, 200 &update, 201 &ChipmunkDemoDefaultDrawImpl, 202 &destroy, 203 };