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.Planet;
23 
24 import core.stdc.stdlib;
25 
26 import demo.dchip;
27 
28 import demo.ChipmunkDebugDraw;
29 import demo.ChipmunkDemo;
30 import demo.types;
31 
32 cpBody* planetBody;
33 
34 cpFloat gravityStrength = 5.0e6f;
35 
36 void update(cpSpace* space, double dt)
37 {
38     cpSpaceStep(space, dt);
39 
40     // Update the static body_ spin so that it looks like it's rotating.
41     cpBodyUpdatePosition(planetBody, dt);
42 }
43 
44 void planetGravityVelocityFunc(cpBody* body_, cpVect gravity, cpFloat damping, cpFloat dt)
45 {
46     // Gravitational acceleration is proportional to the inverse square of
47     // distance, and directed toward the origin. The central planet is assumed
48     // to be massive enough that it affects the satellites but not vice versa.
49     cpVect  p      = cpBodyGetPos(body_);
50     cpFloat sqdist = cpvlengthsq(p);
51     cpVect  g      = cpvmult(p, -gravityStrength / (sqdist * cpfsqrt(sqdist)));
52 
53     cpBodyUpdateVelocity(body_, g, damping, dt);
54 }
55 
56 cpVect rand_pos(cpFloat radius)
57 {
58     cpVect v;
59 
60     do
61     {
62         v = cpv(frand() * (640 - 2 * radius) - (320 - radius), frand() * (480 - 2 * radius) - (240 - radius));
63     }
64     while (cpvlength(v) < 85.0f);
65 
66     return v;
67 }
68 
69 void add_box(cpSpace* space)
70 {
71     const cpFloat size = 10.0f;
72     const cpFloat mass = 1.0f;
73 
74     cpVect verts[] = [
75         cpv(-size, -size),
76         cpv(-size, size),
77         cpv(size, size),
78         cpv(size, -size),
79     ];
80 
81     cpFloat radius = cpvlength(cpv(size, size));
82     cpVect  pos    = rand_pos(radius);
83 
84     cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts.ptr, cpvzero)));
85     body_.velocity_func = &planetGravityVelocityFunc;
86     cpBodySetPos(body_, pos);
87 
88     // Set the box's velocity to put it into a circular orbit from its
89     // starting position.
90     cpFloat r = cpvlength(pos);
91     cpFloat v = cpfsqrt(gravityStrength / r) / r;
92     cpBodySetVel(body_, cpvmult(cpvperp(pos), v));
93 
94     // Set the box's angular velocity to match its orbital period and
95     // align its initial angle with its position.
96     cpBodySetAngVel(body_, v);
97     cpBodySetAngle(body_, cpfatan2(pos.y, pos.x));
98 
99     cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body_, 4, verts.ptr, cpvzero));
100     cpShapeSetElasticity(shape, 0.0f);
101     cpShapeSetFriction(shape, 0.7f);
102 }
103 
104 cpSpace* init()
105 {
106     // Create a rouge body_ to control the planet manually.
107     planetBody = cpBodyNew(INFINITY, INFINITY);
108     cpBodySetAngVel(planetBody, 0.2f);
109 
110     cpSpace* space = cpSpaceNew();
111     cpSpaceSetIterations(space, 20);
112 
113     for (int i = 0; i < 30; i++)
114         add_box(space);
115 
116     cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(planetBody, 70.0f, cpvzero));
117     cpShapeSetElasticity(shape, 1.0f);
118     cpShapeSetFriction(shape, 1.0f);
119     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
120 
121     return space;
122 }
123 
124 void destroy(cpSpace* space)
125 {
126     ChipmunkDemoFreeSpaceChildren(space);
127     cpBodyFree(planetBody);
128     cpSpaceFree(space);
129 }
130 
131 ChipmunkDemo Planet = {
132     "Planet",
133     1.0 / 60.0,
134     &init,
135     &update,
136     &ChipmunkDemoDefaultDrawImpl,
137     &destroy,
138 };