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.cpGrooveJoint;
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* cpGrooveJointGetClass();
34 
35 /// @private
36 struct cpGrooveJoint
37 {
38     cpConstraint constraint;
39     cpVect grv_n, grv_a, grv_b;
40     cpVect anchr2;
41 
42     cpVect grv_tn;
43     cpFloat clamp = 0;
44     cpVect r1, r2;
45     cpMat2x2 k;
46 
47     cpVect jAcc;
48     cpVect bias;
49 }
50 
51 mixin CP_DefineConstraintGetter!("cpGrooveJoint", cpVect, "grv_a", "GrooveA");
52 
53 /// Set endpoint a of a groove joint's groove
54 void cpGrooveJointSetGrooveA(cpConstraint* constraint, cpVect value);
55 mixin CP_DefineConstraintGetter!("cpGrooveJoint", cpVect, "grv_b", "GrooveB");
56 
57 /// Set endpoint b of a groove joint's groove
58 void cpGrooveJointSetGrooveB(cpConstraint* constraint, cpVect value);
59 mixin CP_DefineConstraintProperty!("cpGrooveJoint", cpVect, "anchr2", "Anchr2");
60 
61 void preStep(cpGrooveJoint* joint, cpFloat dt)
62 {
63     cpBody* a = joint.constraint.a;
64     cpBody* b = joint.constraint.b;
65 
66     // calculate endpoints in worldspace
67     cpVect ta = cpBodyLocal2World(a, joint.grv_a);
68     cpVect tb = cpBodyLocal2World(a, joint.grv_b);
69 
70     // calculate axis
71     cpVect  n = cpvrotate(joint.grv_n, a.rot);
72     cpFloat d = cpvdot(ta, n);
73 
74     joint.grv_tn = n;
75     joint.r2     = cpvrotate(joint.anchr2, b.rot);
76 
77     // calculate tangential distance along the axis of r2
78     cpFloat td = cpvcross(cpvadd(b.p, joint.r2), n);
79 
80     // calculate clamping factor and r2
81     if (td <= cpvcross(ta, n))
82     {
83         joint.clamp = 1.0f;
84         joint.r1    = cpvsub(ta, a.p);
85     }
86     else if (td >= cpvcross(tb, n))
87     {
88         joint.clamp = -1.0f;
89         joint.r1    = cpvsub(tb, a.p);
90     }
91     else
92     {
93         joint.clamp = 0.0f;
94         joint.r1    = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a.p);
95     }
96 
97     // Calculate mass tensor
98     joint.k = k_tensor(a, b, joint.r1, joint.r2);
99 
100     // calculate bias velocity
101     cpVect delta = cpvsub(cpvadd(b.p, joint.r2), cpvadd(a.p, joint.r1));
102     joint.bias = cpvclamp(cpvmult(delta, -bias_coef(joint.constraint.errorBias, dt) / dt), joint.constraint.maxBias);
103 }
104 
105 void applyCachedImpulse(cpGrooveJoint* joint, cpFloat dt_coef)
106 {
107     cpBody* a = joint.constraint.a;
108     cpBody* b = joint.constraint.b;
109 
110     apply_impulses(a, b, joint.r1, joint.r2, cpvmult(joint.jAcc, dt_coef));
111 }
112 
113 cpVect grooveConstrain(cpGrooveJoint* joint, cpVect j, cpFloat dt)
114 {
115     cpVect n      = joint.grv_tn;
116     cpVect jClamp = (joint.clamp * cpvcross(j, n) > 0.0f) ? j : cpvproject(j, n);
117     return cpvclamp(jClamp, joint.constraint.maxForce * dt);
118 }
119 
120 void applyImpulse(cpGrooveJoint* joint, cpFloat dt)
121 {
122     cpBody* a = joint.constraint.a;
123     cpBody* b = joint.constraint.b;
124 
125     cpVect r1 = joint.r1;
126     cpVect r2 = joint.r2;
127 
128     // compute impulse
129     cpVect vr = relative_velocity(a, b, r1, r2);
130 
131     cpVect j    = cpMat2x2Transform(joint.k, cpvsub(joint.bias, vr));
132     cpVect jOld = joint.jAcc;
133     joint.jAcc = grooveConstrain(joint, cpvadd(jOld, j), dt);
134     j = cpvsub(joint.jAcc, jOld);
135 
136     // apply impulse
137     apply_impulses(a, b, joint.r1, joint.r2, j);
138 }
139 
140 cpFloat getImpulse(cpGrooveJoint* joint)
141 {
142     return cpvlength(joint.jAcc);
143 }
144 
145 __gshared cpConstraintClass klass;
146 
147 void _initModuleCtor_cpGrooveJoint()
148 {
149     klass = cpConstraintClass(
150         cast(cpConstraintPreStepImpl)&preStep,
151         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
152         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
153         cast(cpConstraintGetImpulseImpl)&getImpulse,
154     );
155 }
156 
157 const(cpConstraintClass *) cpGrooveJointGetClass()
158 {
159     return cast(cpConstraintClass*)&klass;
160 }
161 
162 cpGrooveJoint *
163 cpGrooveJointAlloc()
164 {
165     return cast(cpGrooveJoint*)cpcalloc(1, cpGrooveJoint.sizeof);
166 }
167 
168 cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint* joint, cpBody* a, cpBody* b, cpVect groove_a, cpVect groove_b, cpVect anchr2)
169 {
170     cpConstraintInit(cast(cpConstraint*)joint, &klass, a, b);
171 
172     joint.grv_a  = groove_a;
173     joint.grv_b  = groove_b;
174     joint.grv_n  = cpvperp(cpvnormalize(cpvsub(groove_b, groove_a)));
175     joint.anchr2 = anchr2;
176 
177     joint.jAcc = cpvzero;
178 
179     return joint;
180 }
181 
182 cpConstraint* cpGrooveJointNew(cpBody* a, cpBody* b, cpVect groove_a, cpVect groove_b, cpVect anchr2)
183 {
184     return cast(cpConstraint*)cpGrooveJointInit(cpGrooveJointAlloc(), a, b, groove_a, groove_b, anchr2);
185 }
186 
187 void cpGrooveJointSetGrooveA(cpConstraint* constraint, cpVect value)
188 {
189     cpGrooveJoint* g = cast(cpGrooveJoint*)constraint;
190     mixin(cpConstraintCheckCast("constraint", "cpGrooveJoint"));
191 
192     g.grv_a = value;
193     g.grv_n = cpvperp(cpvnormalize(cpvsub(g.grv_b, value)));
194 
195     cpConstraintActivateBodies(constraint);
196 }
197 
198 void cpGrooveJointSetGrooveB(cpConstraint* constraint, cpVect value)
199 {
200     cpGrooveJoint* g = cast(cpGrooveJoint*)constraint;
201     mixin(cpConstraintCheckCast("constraint", "cpGrooveJoint"));
202 
203     g.grv_b = value;
204     g.grv_n = cpvperp(cpvnormalize(cpvsub(value, g.grv_a)));
205 
206     cpConstraintActivateBodies(constraint);
207 }