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 dchip.cpConstraint;
23 
24 import std.string;
25 
26 import dchip.cpBody;
27 import dchip.chipmunk;
28 import dchip.chipmunk_types;
29 import dchip.cpSpace;
30 
31 alias cpConstraintPreStepImpl = void function(cpConstraint* constraint, cpFloat dt);
32 alias cpConstraintApplyCachedImpulseImpl = void function(cpConstraint* constraint, cpFloat dt_coef);
33 alias cpConstraintApplyImpulseImpl = void function(cpConstraint* constraint, cpFloat dt);
34 alias cpConstraintGetImpulseImpl = cpFloat function(cpConstraint* constraint);
35 
36 /// @private
37 struct cpConstraintClass
38 {
39     cpConstraintPreStepImpl preStep;
40     cpConstraintApplyCachedImpulseImpl applyCachedImpulse;
41     cpConstraintApplyImpulseImpl applyImpulse;
42     cpConstraintGetImpulseImpl getImpulse;
43 }
44 
45 /// Callback function type that gets called before solving a joint.
46 alias cpConstraintPreSolveFunc = void function(cpConstraint* constraint, cpSpace* space);
47 
48 /// Callback function type that gets called after solving a joint.
49 alias cpConstraintPostSolveFunc = void function(cpConstraint* constraint, cpSpace* space);
50 
51 /// Opaque cpConstraint struct.
52 struct cpConstraint
53 {
54     version (CHIP_ALLOW_PRIVATE_ACCESS)
55         /* const */ cpConstraintClass * klass;
56     else
57         package /* const */ cpConstraintClass * klass;
58 
59     /// The first body connected to this constraint.
60     cpBody* a;
61 
62     /// The second body connected to this constraint.
63     cpBody* b;
64 
65     version (CHIP_ALLOW_PRIVATE_ACCESS)
66         cpSpace * space;
67     else
68         package cpSpace * space;
69 
70     version (CHIP_ALLOW_PRIVATE_ACCESS)
71         cpConstraint * next_a;
72     else
73         package cpConstraint * next_a;
74 
75     version (CHIP_ALLOW_PRIVATE_ACCESS)
76         cpConstraint * next_b;
77     else
78         package cpConstraint * next_b;
79 
80     /// The maximum force that this constraint is allowed to use.
81     /// Defaults to infinity.
82     cpFloat maxForce = 0;
83 
84     /// The rate at which joint error is corrected.
85     /// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will
86     /// correct 10% of the error every 1/60th of a second.
87     cpFloat errorBias = 0;
88 
89     /// The maximum rate at which joint error is corrected.
90     /// Defaults to infinity.
91     cpFloat maxBias = 0;
92 
93     /// Function called before the solver runs.
94     /// Animate your joint anchors, update your motor torque, etc.
95     cpConstraintPreSolveFunc preSolve;
96 
97     /// Function called after the solver runs.
98     /// Use the applied impulse to perform effects like breakable joints.
99     cpConstraintPostSolveFunc postSolve;
100 
101     /// User definable data pointer.
102     /// Generally this points to your the game object class so you can access it
103     /// when given a cpConstraint reference in a callback.
104     cpDataPointer data;
105 }
106 
107 /// @private
108 void cpConstraintActivateBodies(cpConstraint* constraint)
109 {
110     cpBody* a = constraint.a;
111 
112     if (a)
113         cpBodyActivate(a);
114 
115     cpBody* b = constraint.b;
116 
117     if (b)
118         cpBodyActivate(b);
119 }
120 
121 mixin template CP_DefineConstraintStructGetter(type, string member, string name)
122 {
123     mixin(q{
124         type cpConstraintGet%s(const cpConstraint * constraint) { return cast(typeof(return))constraint.%s; }
125     }.format(name, member));
126 }
127 
128 mixin template CP_DefineConstraintStructSetter(type, string member, string name)
129 {
130     mixin(q{
131         void cpConstraintSet%s(cpConstraint * constraint, type value)
132         {
133             cpConstraintActivateBodies(constraint);
134             constraint.%s = value;
135         }
136     }.format(name, member));
137 }
138 
139 mixin template CP_DefineConstraintStructProperty(type, string member, string name)
140 {
141     mixin CP_DefineConstraintStructGetter!(type, member, name);
142     mixin CP_DefineConstraintStructSetter!(type, member, name);
143 }
144 
145 mixin CP_DefineConstraintStructGetter!(cpSpace*, "space", "Space");
146 
147 mixin CP_DefineConstraintStructGetter!(cpBody*, "a", "A");
148 mixin CP_DefineConstraintStructGetter!(cpBody*, "b", "B");
149 mixin CP_DefineConstraintStructProperty!(cpFloat, "maxForce", "MaxForce");
150 mixin CP_DefineConstraintStructProperty!(cpFloat, "errorBias", "ErrorBias");
151 mixin CP_DefineConstraintStructProperty!(cpFloat, "maxBias", "MaxBias");
152 mixin CP_DefineConstraintStructProperty!(cpConstraintPreSolveFunc, "preSolve", "PreSolveFunc");
153 mixin CP_DefineConstraintStructProperty!(cpConstraintPostSolveFunc, "postSolve", "PostSolveFunc");
154 mixin CP_DefineConstraintStructProperty!(cpDataPointer, "data", "UserData");
155 
156 // Get the last impulse applied by this constraint.
157 cpFloat cpConstraintGetImpulse(cpConstraint* constraint)
158 {
159     return constraint.klass.getImpulse(constraint);
160 }
161 
162 string cpConstraintCheckCast(string constraint, string struct_)
163 {
164     return `cpAssertHard(%1$s.klass == %2$sGetClass(), "Constraint is not a %2$s");`.format(constraint, struct_);
165 }
166 
167 mixin template CP_DefineConstraintGetter(string struct_, type, string member, string name)
168 {
169     enum mixStr = `mixin(cpConstraintCheckCast("constraint", "%1$s"));`.format(struct_);
170 
171     mixin(q{
172         type %1$sGet%2$s(const cpConstraint* constraint)
173         {
174             %4$s
175             return cast(typeof(return))((cast(%1$s*)constraint).%3$s);
176         }
177     }.format(struct_, name, member, mixStr));
178 }
179 
180 mixin template CP_DefineConstraintSetter(string struct_, type, string member, string name)
181 {
182     enum mixStr = `mixin(cpConstraintCheckCast("constraint", "%1$s"));`.format(struct_);
183 
184     mixin(q{
185         void %1$sSet%2$s(cpConstraint * constraint, type value)
186         {
187             %4$s
188             cpConstraintActivateBodies(constraint);
189             (cast(%1$s*)constraint).%3$s= value;
190         }
191     }.format(struct_, name, member, mixStr));
192 }
193 
194 mixin template CP_DefineConstraintProperty(string struct_, type, string member, string name)
195 {
196     mixin CP_DefineConstraintGetter!(struct_, type, member, name);
197     mixin CP_DefineConstraintSetter!(struct_, type, member, name);
198 }
199 
200 void cpConstraintDestroy(cpConstraint* constraint)
201 {
202 }
203 
204 void cpConstraintFree(cpConstraint* constraint)
205 {
206     if (constraint)
207     {
208         cpConstraintDestroy(constraint);
209         cpfree(constraint);
210     }
211 }
212 
213 // *** declared in util.h TODO move declaration to chipmunk_private.h
214 
215 void cpConstraintInit(cpConstraint* constraint, const cpConstraintClass* klass, cpBody* a, cpBody* b)
216 {
217     constraint.klass = cast(typeof(constraint.klass))klass;
218 
219     constraint.a     = a;
220     constraint.b     = b;
221     constraint.space = null;
222 
223     constraint.next_a = null;
224     constraint.next_b = null;
225 
226     constraint.maxForce  = cast(cpFloat)INFINITY;
227     constraint.errorBias = cpfpow(1.0f - 0.1f, 60.0f);
228     constraint.maxBias   = cast(cpFloat)INFINITY;
229 
230     constraint.preSolve  = null;
231     constraint.postSolve = null;
232 }