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