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.Crane;
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 cpBody* dollyBody = null;
37 
38 // Constraint used as a servo motor to move the dolly back and forth.
39 static cpConstraint* dollyServo = null;
40 
41 // Constraint used as a winch motor to lift the load.
42 static cpConstraint* winchServo = null;
43 
44 // Temporary joint used to hold the hook to the load.
45 static cpConstraint* hookJoint = null;
46 
47 static void update(cpSpace* space, double dt)
48 {
49     // Set the first anchor point (the one attached to the static body) of the dolly servo to the mouse's x position.
50     cpPivotJointSetAnchr1(dollyServo, cpv(ChipmunkDemoMouse.x, 100));
51 
52     // Set the max length of the winch servo to match the mouse's height.
53     cpSlideJointSetMax(winchServo, cpfmax(100 - ChipmunkDemoMouse.y, 50));
54 
55     if (hookJoint && ChipmunkDemoRightClick)
56     {
57         cpSpaceRemoveConstraint(space, hookJoint);
58         cpConstraintFree(hookJoint);
59         hookJoint = null;
60     }
61 
62     cpSpaceStep(space, dt);
63 }
64 
65 enum
66 {
67     HOOK_SENSOR = 1,
68     CRATE,
69 };
70 
71 static void AttachHook(cpSpace* space, cpBody* hook, cpBody* crate)
72 {
73     hookJoint = cpSpaceAddConstraint(space, cpPivotJointNew(hook, crate, cpBodyGetPos(hook)));
74 }
75 
76 static cpBool HookCrate(cpArbiter* arb, cpSpace* space, void* data)
77 {
78     if (hookJoint == null)
79     {
80         // Get pointers to the two bodies in the collision pair and define local variables for them.
81         // Their order matches the order of the collision types passed
82         // to the collision handler this function was defined for
83         mixin(CP_ARBITER_GET_BODIES!("arb", "hook", "crate"));
84 
85         // additions and removals can't be done in a normal callback.
86         // Schedule a post step callback to do it.
87         // Use the hook as the key and pass along the arbiter.
88         cpSpaceAddPostStepCallback(space, safeCast!cpPostStepFunc(&AttachHook), hook, crate);
89     }
90 
91     return cpTrue;     // return value is ignored for sensor callbacks anyway
92 }
93 
94 static cpSpace* init()
95 {
96     ChipmunkDemoMessageString = "Control the crane by moving the mouse. Right click to release.".dup;
97 
98     cpSpace* space = cpSpaceNew();
99     cpSpaceSetIterations(space, 30);
100     cpSpaceSetGravity(space, cpv(0, -100));
101     cpSpaceSetDamping(space, 0.8);
102 
103     cpBody * staticBody = cpSpaceGetStaticBody(space);
104     cpShape* shape;
105 
106     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f));
107     cpShapeSetElasticity(shape, 1.0f);
108     cpShapeSetFriction(shape, 1.0f);
109     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
110 
111     // Add a body for the dolly.
112     dollyBody = cpSpaceAddBody(space, cpBodyNew(10, INFINITY));
113     cpBodySetPos(dollyBody, cpv(0, 100));
114 
115     // Add a block so you can see it.
116     cpSpaceAddShape(space, cpBoxShapeNew(dollyBody, 30, 30));
117 
118     // Add a groove joint for it to move back and forth on.
119     cpSpaceAddConstraint(space, cpGrooveJointNew(staticBody, dollyBody, cpv(-250, 100), cpv(250, 100), cpvzero));
120 
121     // Add a pivot joint to act as a servo motor controlling it's position
122     // By updating the anchor points of the pivot joint, you can move the dolly.
123     dollyServo = cpSpaceAddConstraint(space, cpPivotJointNew(staticBody, dollyBody, cpBodyGetPos(dollyBody)));
124 
125     // Max force the dolly servo can generate.
126     cpConstraintSetMaxForce(dollyServo, 10000);
127 
128     // Max speed of the dolly servo
129     cpConstraintSetMaxBias(dollyServo, 100);
130 
131     // You can also change the error bias to control how it slows down.
132     //cpConstraintSetErrorBias(dollyServo, 0.2);
133 
134     // Add the crane hook.
135     cpBody* hookBody = cpSpaceAddBody(space, cpBodyNew(1, INFINITY));
136     cpBodySetPos(hookBody, cpv(0, 50));
137 
138     // Add a sensor shape for it. This will be used to figure out when the hook touches a box.
139     shape = cpSpaceAddShape(space, cpCircleShapeNew(hookBody, 10, cpvzero));
140     cpShapeSetSensor(shape, cpTrue);
141     cpShapeSetCollisionType(shape, HOOK_SENSOR);
142 
143     // Add a slide joint to act as a winch motor
144     // By updating the max length of the joint you can make it pull up the load.
145     winchServo = cpSpaceAddConstraint(space, cpSlideJointNew(dollyBody, hookBody, cpvzero, cpvzero, 0, INFINITY));
146 
147     // Max force the dolly servo can generate.
148     cpConstraintSetMaxForce(winchServo, 30000);
149 
150     // Max speed of the dolly servo
151     cpConstraintSetMaxBias(winchServo, 60);
152 
153     // TODO cleanup
154     // Finally a box to play with
155     cpBody* boxBody = cpSpaceAddBody(space, cpBodyNew(30, cpMomentForBox(30, 50, 50)));
156     cpBodySetPos(boxBody, cpv(200, -200));
157 
158     // Add a block so you can see it.
159     shape = cpSpaceAddShape(space, cpBoxShapeNew(boxBody, 50, 50));
160     cpShapeSetFriction(shape, 0.7);
161     cpShapeSetCollisionType(shape, CRATE);
162 
163     cpSpaceAddCollisionHandler(space, HOOK_SENSOR, CRATE, safeCast!cpCollisionBeginFunc(&HookCrate), null, null, null, null);
164 
165     return space;
166 }
167 
168 static void destroy(cpSpace* space)
169 {
170     ChipmunkDemoFreeSpaceChildren(space);
171     cpSpaceFree(space);
172 }
173 
174 ChipmunkDemo Crane = {
175     "Crane",
176     1.0 / 60.0,
177     &init,
178     &update,
179     &ChipmunkDemoDefaultDrawImpl,
180     &destroy,
181 };