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.cpGearJoint;
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 
32 //~ const cpConstraintClass* cpGearJointGetClass();
33 
34 /// @private
35 struct cpGearJoint
36 {
37     cpConstraint constraint;
38     cpFloat phase = 0, ratio = 0;
39     cpFloat ratio_inv = 0;
40 
41     cpFloat iSum = 0;
42 
43     cpFloat bias = 0;
44     cpFloat jAcc = 0;
45 }
46 
47 mixin CP_DefineConstraintProperty!("cpGearJoint", cpFloat, "phase", "Phase");
48 mixin CP_DefineConstraintGetter!("cpGearJoint", cpFloat, "ratio", "Ratio");
49 
50 void preStep(cpGearJoint* joint, cpFloat dt)
51 {
52     cpBody* a = joint.constraint.a;
53     cpBody* b = joint.constraint.b;
54 
55     // calculate moment of inertia coefficient.
56     joint.iSum = 1.0f / (a.i_inv * joint.ratio_inv + joint.ratio * b.i_inv);
57 
58     // calculate bias velocity
59     cpFloat maxBias = joint.constraint.maxBias;
60     joint.bias = cpfclamp(-bias_coef(joint.constraint.errorBias, dt) * (b.a * joint.ratio - a.a - joint.phase) / dt, -maxBias, maxBias);
61 }
62 
63 void applyCachedImpulse(cpGearJoint* joint, cpFloat dt_coef)
64 {
65     cpBody* a = joint.constraint.a;
66     cpBody* b = joint.constraint.b;
67 
68     cpFloat j = joint.jAcc * dt_coef;
69     a.w -= j * a.i_inv * joint.ratio_inv;
70     b.w += j * b.i_inv;
71 }
72 
73 void applyImpulse(cpGearJoint* joint, cpFloat dt)
74 {
75     cpBody* a = joint.constraint.a;
76     cpBody* b = joint.constraint.b;
77 
78     // compute relative rotational velocity
79     cpFloat wr = b.w * joint.ratio - a.w;
80 
81     cpFloat jMax = joint.constraint.maxForce * dt;
82 
83     // compute normal impulse
84     cpFloat j    = (joint.bias - wr) * joint.iSum;
85     cpFloat jOld = joint.jAcc;
86     joint.jAcc = cpfclamp(jOld + j, -jMax, jMax);
87     j = joint.jAcc - jOld;
88 
89     // apply impulse
90     a.w -= j * a.i_inv * joint.ratio_inv;
91     b.w += j * b.i_inv;
92 }
93 
94 cpFloat getImpulse(cpGearJoint* joint)
95 {
96     return cpfabs(joint.jAcc);
97 }
98 
99 __gshared cpConstraintClass klass;
100 
101 void _initModuleCtor_cpGearJoint()
102 {
103     klass = cpConstraintClass(
104         cast(cpConstraintPreStepImpl)&preStep,
105         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
106         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
107         cast(cpConstraintGetImpulseImpl)&getImpulse,
108     );
109 }
110 
111 const(cpConstraintClass *) cpGearJointGetClass()
112 {
113     return cast(cpConstraintClass*)&klass;
114 }
115 
116 cpGearJoint *
117 cpGearJointAlloc()
118 {
119     return cast(cpGearJoint*)cpcalloc(1, cpGearJoint.sizeof);
120 }
121 
122 cpGearJoint* cpGearJointInit(cpGearJoint* joint, cpBody* a, cpBody* b, cpFloat phase, cpFloat ratio)
123 {
124     cpConstraintInit(cast(cpConstraint*)joint, &klass, a, b);
125 
126     joint.phase     = phase;
127     joint.ratio     = ratio;
128     joint.ratio_inv = 1.0f / ratio;
129 
130     joint.jAcc = 0.0f;
131 
132     return joint;
133 }
134 
135 cpConstraint* cpGearJointNew(cpBody* a, cpBody* b, cpFloat phase, cpFloat ratio)
136 {
137     return cast(cpConstraint*)cpGearJointInit(cpGearJointAlloc(), a, b, phase, ratio);
138 }
139 
140 void cpGearJointSetRatio(cpConstraint* constraint, cpFloat value)
141 {
142     mixin(cpConstraintCheckCast("constraint", "cpGearJoint"));
143     (cast(cpGearJoint*)constraint).ratio     = value;
144     (cast(cpGearJoint*)constraint).ratio_inv = 1.0f / value;
145     cpConstraintActivateBodies(constraint);
146 }