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.cpRotaryLimitJoint;
23 
24 import std..string;
25 
26 import dchip.constraints_util;
27 import dchip.chipmunk;
28 import dchip.cpBody;
29 import dchip.cpConstraint;
30 import dchip.chipmunk_types;
31 import dchip.cpVect;
32 
33 //~ const cpConstraintClass* cpRotaryLimitJointGetClass();
34 
35 /// @private
36 struct cpRotaryLimitJoint
37 {
38     cpConstraint constraint;
39     cpFloat min = 0, max = 0;
40 
41     cpFloat iSum = 0;
42 
43     cpFloat bias = 0;
44     cpFloat jAcc = 0;
45 }
46 
47 mixin CP_DefineConstraintProperty!("cpRotaryLimitJoint", cpFloat, "min", "Min");
48 mixin CP_DefineConstraintProperty!("cpRotaryLimitJoint", cpFloat, "max", "Max");
49 
50 void preStep(cpRotaryLimitJoint* joint, cpFloat dt)
51 {
52     cpBody* a = joint.constraint.a;
53     cpBody* b = joint.constraint.b;
54 
55     cpFloat dist  = b.a - a.a;
56     cpFloat pdist = 0.0f;
57 
58     if (dist > joint.max)
59     {
60         pdist = joint.max - dist;
61     }
62     else if (dist < joint.min)
63     {
64         pdist = joint.min - dist;
65     }
66 
67     // calculate moment of inertia coefficient.
68     joint.iSum = 1.0f / (a.i_inv + b.i_inv);
69 
70     // calculate bias velocity
71     cpFloat maxBias = joint.constraint.maxBias;
72     joint.bias = cpfclamp(-bias_coef(joint.constraint.errorBias, dt) * pdist / dt, -maxBias, maxBias);
73 
74     // If the bias is 0, the joint is not at a limit. Reset the impulse.
75     if (!joint.bias)
76         joint.jAcc = 0.0f;
77 }
78 
79 void applyCachedImpulse(cpRotaryLimitJoint* joint, cpFloat dt_coef)
80 {
81     cpBody* a = joint.constraint.a;
82     cpBody* b = joint.constraint.b;
83 
84     cpFloat j = joint.jAcc * dt_coef;
85     a.w -= j * a.i_inv;
86     b.w += j * b.i_inv;
87 }
88 
89 void applyImpulse(cpRotaryLimitJoint* joint, cpFloat dt)
90 {
91     if (!joint.bias)
92         return;                  // early exit
93 
94     cpBody* a = joint.constraint.a;
95     cpBody* b = joint.constraint.b;
96 
97     // compute relative rotational velocity
98     cpFloat wr = b.w - a.w;
99 
100     cpFloat jMax = joint.constraint.maxForce * dt;
101 
102     // compute normal impulse
103     cpFloat j    = -(joint.bias + wr) * joint.iSum;
104     cpFloat jOld = joint.jAcc;
105 
106     if (joint.bias < 0.0f)
107     {
108         joint.jAcc = cpfclamp(jOld + j, 0.0f, jMax);
109     }
110     else
111     {
112         joint.jAcc = cpfclamp(jOld + j, -jMax, 0.0f);
113     }
114     j = joint.jAcc - jOld;
115 
116     // apply impulse
117     a.w -= j * a.i_inv;
118     b.w += j * b.i_inv;
119 }
120 
121 cpFloat getImpulse(cpRotaryLimitJoint* joint)
122 {
123     return cpfabs(joint.jAcc);
124 }
125 
126 __gshared cpConstraintClass klass;
127 
128 void _initModuleCtor_cpRotaryLimitJoint()
129 {
130     klass = cpConstraintClass(
131         cast(cpConstraintPreStepImpl)&preStep,
132         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
133         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
134         cast(cpConstraintGetImpulseImpl)&getImpulse,
135     );
136 }
137 
138 const(cpConstraintClass *) cpRotaryLimitJointGetClass()
139 {
140     return cast(cpConstraintClass*)&klass;
141 }
142 
143 cpRotaryLimitJoint *
144 cpRotaryLimitJointAlloc()
145 {
146     return cast(cpRotaryLimitJoint*)cpcalloc(1, cpRotaryLimitJoint.sizeof);
147 }
148 
149 cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint* joint, cpBody* a, cpBody* b, cpFloat min, cpFloat max)
150 {
151     cpConstraintInit(cast(cpConstraint*)joint, &klass, a, b);
152 
153     joint.min = min;
154     joint.max = max;
155 
156     joint.jAcc = 0.0f;
157 
158     return joint;
159 }
160 
161 cpConstraint* cpRotaryLimitJointNew(cpBody* a, cpBody* b, cpFloat min, cpFloat max)
162 {
163     return cast(cpConstraint*)cpRotaryLimitJointInit(cpRotaryLimitJointAlloc(), a, b, min, max);
164 }