1 
2 // written in the D programming language
3 
4 module samples.Joints;
5 
6 import dchip.all;
7 
8 import samples.ChipmunkDemo;
9 
10 import std.math;
11 
12 static cpSpace *space;
13 
14 enum M_PI = PI;
15 enum M_PI_2 = PI*0.5f;
16 
17 static cpBody *
18 addBall(cpVect pos, cpVect boxOffset)
19 {
20     cpFloat radius = 15.0f;
21     cpFloat mass = 1.0f;
22     cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
23     _body.p = cpvadd(pos, boxOffset);
24 
25     cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
26     shape.e = 0.0f; shape.u = 0.7f;
27 
28     return _body;
29 }
30 
31 static cpBody *
32 addLever(cpVect pos, cpVect boxOffset)
33 {
34     cpFloat mass = 1.0f;
35     cpVect a = cpv(0,  15);
36     cpVect b = cpv(0, -15);
37 
38     cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b)));
39     _body.p = cpvadd(pos, cpvadd(boxOffset, cpv(0, -15)));
40 
41     cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, a, b, 5.0f));
42     shape.e = 0.0f; shape.u = 0.7f;
43 
44     return _body;
45 }
46 
47 static cpBody *
48 addBar(cpVect pos, cpVect boxOffset)
49 {
50     cpFloat mass = 2.0f;
51     cpVect a = cpv(0,  30);
52     cpVect b = cpv(0, -30);
53 
54     cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b)));
55     _body.p = cpvadd(pos, boxOffset);
56 
57     cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, a, b, 5.0f));
58     shape.e = 0.0f; shape.u = 0.7f;
59 
60     return _body;
61 }
62 
63 static cpBody *
64 addWheel(cpVect pos, cpVect boxOffset)
65 {
66     cpFloat radius = 15.0f;
67     cpFloat mass = 1.0f;
68     cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
69     _body.p = cpvadd(pos, boxOffset);
70 
71     cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
72     shape.e = 0.0f; shape.u = 0.7f;
73     shape.group = 1; // use a group to keep the car parts from colliding
74 
75     return _body;
76 }
77 
78 static cpBody *
79 addChassis(cpVect pos, cpVect boxOffset)
80 {
81     int num = 4;
82     cpVect verts[] = [
83         cpv(-40,-15),
84         cpv(-40, 15),
85         cpv( 40, 15),
86         cpv( 40,-15),
87     ];
88 
89 
90     cpFloat mass = 5.0f;
91     cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, num, verts.ptr, cpvzero)));
92     _body.p = cpvadd(pos, boxOffset);
93 
94     cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
95     shape.e = 0.0f; shape.u = 0.7f;
96     shape.group = 1; // use a group to keep the car parts from colliding
97 
98     return _body;
99 }
100 
101 static cpSpace *
102 init()
103 {
104     space = cpSpaceNew();
105     space.iterations = 10;
106     space.gravity = cpv(0, -100);
107     space.sleepTimeThreshold = 0.5f;
108 
109     cpBody *staticBody = space.staticBody;
110     cpShape *shape;
111 
112     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f));
113     shape.e = 1.0f; shape.u = 1.0f;
114     shape.layers = NOT_GRABABLE_MASK;
115 
116     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,120), cpv(320,120), 0.0f));
117     shape.e = 1.0f; shape.u = 1.0f;
118     shape.layers = NOT_GRABABLE_MASK;
119 
120     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f));
121     shape.e = 1.0f; shape.u = 1.0f;
122     shape.layers = NOT_GRABABLE_MASK;
123 
124     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-120), cpv(320,-120), 0.0f));
125     shape.e = 1.0f; shape.u = 1.0f;
126     shape.layers = NOT_GRABABLE_MASK;
127 
128     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
129     shape.e = 1.0f; shape.u = 1.0f;
130     shape.layers = NOT_GRABABLE_MASK;
131 
132 
133     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
134     shape.e = 1.0f; shape.u = 1.0f;
135     shape.layers = NOT_GRABABLE_MASK;
136 
137     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160,-240), cpv(-160,240), 0.0f));
138     shape.e = 1.0f; shape.u = 1.0f;
139     shape.layers = NOT_GRABABLE_MASK;
140 
141     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,-240), cpv(0,240), 0.0f));
142     shape.e = 1.0f; shape.u = 1.0f;
143     shape.layers = NOT_GRABABLE_MASK;
144 
145     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(160,-240), cpv(160,240), 0.0f));
146     shape.e = 1.0f; shape.u = 1.0f;
147     shape.layers = NOT_GRABABLE_MASK;
148 
149     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
150     shape.e = 1.0f; shape.u = 1.0f;
151     shape.layers = NOT_GRABABLE_MASK;
152 
153     cpVect boxOffset;
154     cpBody *body1;
155     cpBody *body2;
156 
157     cpVect posA = cpv( 50, 60);
158     cpVect posB = cpv(110, 60);
159 
160     // Pin Joints - Link shapes with a solid bar or pin.
161     // Keeps the anchor points the same distance apart from when the joint was created.
162     boxOffset = cpv(-320, -240);
163     body1 = addBall(posA, boxOffset);
164     body2 = addBall(posB, boxOffset);
165     cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15,0), cpv(-15,0)));
166 
167     // Slide Joints - Like pin joints but with a min/max distance.
168     // Can be used for a cheap approximation of a rope.
169     boxOffset = cpv(-160, -240);
170     body1 = addBall(posA, boxOffset);
171     body2 = addBall(posB, boxOffset);
172     cpSpaceAddConstraint(space, cpSlideJointNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 40.0f));
173 
174     // Pivot Joints - Holds the two anchor points together. Like a swivel.
175     boxOffset = cpv(0, -240);
176     body1 = addBall(posA, boxOffset);
177     body2 = addBall(posB, boxOffset);
178     cpSpaceAddConstraint(space, cpPivotJointNew(body1, body2, cpvadd(boxOffset, cpv(80,60))));
179     // cpPivotJointNew() takes it's anchor parameter in world coordinates. The anchors are calculated from that
180     // cpPivotJointNew2() lets you specify the two anchor points explicitly
181 
182     // Groove Joints - Like a pivot joint, but one of the anchors is a line segment that the pivot can slide in
183     boxOffset = cpv(160, -240);
184     body1 = addBall(posA, boxOffset);
185     body2 = addBall(posB, boxOffset);
186     cpSpaceAddConstraint(space, cpGrooveJointNew(body1, body2, cpv(30,30), cpv(30,-30), cpv(-30,0)));
187 
188     // Damped Springs
189     boxOffset = cpv(-320, -120);
190     body1 = addBall(posA, boxOffset);
191     body2 = addBall(posB, boxOffset);
192     cpSpaceAddConstraint(space, cpDampedSpringNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 5.0f, 0.3f));
193 
194     // Damped Rotary Springs
195     boxOffset = cpv(-160, -120);
196     body1 = addBar(posA, boxOffset);
197     body2 = addBar(posB, boxOffset);
198     // Add some pin joints to hold the circles in place.
199     cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
200     cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
201     cpSpaceAddConstraint(space, cpDampedRotarySpringNew(body1, body2, 0.0f, 3000.0f, 60.0f));
202 
203     // Rotary Limit Joint
204     boxOffset = cpv(0, -120);
205     body1 = addLever(posA, boxOffset);
206     body2 = addLever(posB, boxOffset);
207     // Add some pin joints to hold the circles in place.
208     cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
209     cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
210     // Hold their rotation within 90 degrees of each other.
211     cpSpaceAddConstraint(space, cpRotaryLimitJointNew(body1, body2, -M_PI_2, M_PI_2));
212 
213     // Ratchet Joint - A rotary ratchet, like a socket wrench
214     boxOffset = cpv(160, -120);
215     body1 = addLever(posA, boxOffset);
216     body2 = addLever(posB, boxOffset);
217     // Add some pin joints to hold the circles in place.
218     cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
219     cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
220     // Ratchet every 90 degrees
221     cpSpaceAddConstraint(space, cpRatchetJointNew(body1, body2, 0.0f, M_PI_2));
222 
223     // Gear Joint - Maintain a specific angular velocity ratio
224     boxOffset = cpv(-320, 0);
225     body1 = addBar(posA, boxOffset);
226     body2 = addBar(posB, boxOffset);
227     // Add some pin joints to hold the circles in place.
228     cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
229     cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
230     // Force one to sping 2x as fast as the other
231     cpSpaceAddConstraint(space, cpGearJointNew(body1, body2, 0.0f, 2.0f));
232 
233     // Simple Motor - Maintain a specific angular relative velocity
234     boxOffset = cpv(-160, 0);
235     body1 = addBar(posA, boxOffset);
236     body2 = addBar(posB, boxOffset);
237     // Add some pin joints to hold the circles in place.
238     cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
239     cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
240     // Make them spin at 1/2 revolution per second in relation to each other.
241     cpSpaceAddConstraint(space, cpSimpleMotorNew(body1, body2, M_PI));
242 
243     // Make a car with some nice soft suspension
244     boxOffset = cpv(0, 0);
245     cpBody *wheel1 = addWheel(posA, boxOffset);
246     cpBody *wheel2 = addWheel(posB, boxOffset);
247     cpBody *chassis = addChassis(cpv(80, 100), boxOffset);
248 
249     cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero));
250     cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv( 30, -10), cpv( 30, -40), cpvzero));
251 
252     cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 1.5f));
253     cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv( 30, 0), cpvzero, 50.0f, 20.0f, 1.5f));
254 
255     return space;
256 }
257 
258 static void
259 update(int ticks)
260 {
261     cpSpaceStep(space, 1.0f/60.0f);
262 }
263 
264 static void
265 destroy()
266 {
267     ChipmunkDemoFreeSpaceChildren(space);
268     cpSpaceFree(space);
269 }
270 
271 chipmunkDemo Joints = {
272     "Joints and Constraints",
273     null,
274     &init,
275     &update,
276     &destroy,
277 };