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.cpSlideJoint;
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* cpSlideJointGetClass();
34 
35 /// @private
36 struct cpSlideJoint
37 {
38     cpConstraint constraint;
39     cpVect anchr1, anchr2;
40     cpFloat min = 0, max = 0;
41 
42     cpVect r1, r2;
43     cpVect n;
44     cpFloat nMass = 0;
45 
46     cpFloat jnAcc = 0;
47     cpFloat bias = 0;
48 }
49 
50 mixin CP_DefineConstraintProperty!("cpSlideJoint", cpVect, "anchr1", "Anchr1");
51 mixin CP_DefineConstraintProperty!("cpSlideJoint", cpVect, "anchr2", "Anchr2");
52 mixin CP_DefineConstraintProperty!("cpSlideJoint", cpFloat, "min", "Min");
53 mixin CP_DefineConstraintProperty!("cpSlideJoint", cpFloat, "max", "Max");
54 
55 void preStep(cpSlideJoint* joint, cpFloat dt)
56 {
57     cpBody* a = joint.constraint.a;
58     cpBody* b = joint.constraint.b;
59 
60     joint.r1 = cpvrotate(joint.anchr1, a.rot);
61     joint.r2 = cpvrotate(joint.anchr2, b.rot);
62 
63     cpVect  delta = cpvsub(cpvadd(b.p, joint.r2), cpvadd(a.p, joint.r1));
64     cpFloat dist  = cpvlength(delta);
65     cpFloat pdist = 0.0f;
66 
67     if (dist > joint.max)
68     {
69         pdist    = dist - joint.max;
70         joint.n = cpvnormalize_safe(delta);
71     }
72     else if (dist < joint.min)
73     {
74         pdist    = joint.min - dist;
75         joint.n = cpvneg(cpvnormalize_safe(delta));
76     }
77     else
78     {
79         joint.n     = cpvzero;
80         joint.jnAcc = 0.0f;
81     }
82 
83     // calculate mass normal
84     joint.nMass = 1.0f / k_scalar(a, b, joint.r1, joint.r2, joint.n);
85 
86     // calculate bias velocity
87     cpFloat maxBias = joint.constraint.maxBias;
88     joint.bias = cpfclamp(-bias_coef(joint.constraint.errorBias, dt) * pdist / dt, -maxBias, maxBias);
89 }
90 
91 void applyCachedImpulse(cpSlideJoint* joint, cpFloat dt_coef)
92 {
93     cpBody* a = joint.constraint.a;
94     cpBody* b = joint.constraint.b;
95 
96     cpVect j = cpvmult(joint.n, joint.jnAcc * dt_coef);
97     apply_impulses(a, b, joint.r1, joint.r2, j);
98 }
99 
100 void applyImpulse(cpSlideJoint* joint, cpFloat dt)
101 {
102     if (cpveql(joint.n, cpvzero))
103         return;                                // early exit
104 
105     cpBody* a = joint.constraint.a;
106     cpBody* b = joint.constraint.b;
107 
108     cpVect n  = joint.n;
109     cpVect r1 = joint.r1;
110     cpVect r2 = joint.r2;
111 
112     // compute relative velocity
113     cpVect  vr  = relative_velocity(a, b, r1, r2);
114     cpFloat vrn = cpvdot(vr, n);
115 
116     // compute normal impulse
117     cpFloat jn    = (joint.bias - vrn) * joint.nMass;
118     cpFloat jnOld = joint.jnAcc;
119     joint.jnAcc = cpfclamp(jnOld + jn, -joint.constraint.maxForce * dt, 0.0f);
120     jn = joint.jnAcc - jnOld;
121 
122     // apply impulse
123     apply_impulses(a, b, joint.r1, joint.r2, cpvmult(n, jn));
124 }
125 
126 cpFloat getImpulse(cpConstraint* joint)
127 {
128     return cpfabs((cast(cpSlideJoint*)joint).jnAcc);
129 }
130 
131 __gshared cpConstraintClass klass;
132 
133 void _initModuleCtor_cpSlideJoint()
134 {
135     klass = cpConstraintClass(
136         cast(cpConstraintPreStepImpl)&preStep,
137         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
138         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
139         cast(cpConstraintGetImpulseImpl)&getImpulse,
140     );
141 }
142 
143 const(cpConstraintClass *) cpSlideJointGetClass()
144 {
145     return cast(cpConstraintClass*)&klass;
146 }
147 
148 cpSlideJoint *
149 cpSlideJointAlloc()
150 {
151     return cast(cpSlideJoint*)cpcalloc(1, cpSlideJoint.sizeof);
152 }
153 
154 cpSlideJoint* cpSlideJointInit(cpSlideJoint* joint, cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2, cpFloat min, cpFloat max)
155 {
156     cpConstraintInit(cast(cpConstraint*)joint, &klass, a, b);
157 
158     joint.anchr1 = anchr1;
159     joint.anchr2 = anchr2;
160     joint.min    = min;
161     joint.max    = max;
162 
163     joint.jnAcc = 0.0f;
164 
165     return joint;
166 }
167 
168 cpConstraint* cpSlideJointNew(cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2, cpFloat min, cpFloat max)
169 {
170     return cast(cpConstraint*)cpSlideJointInit(cpSlideJointAlloc(), a, b, anchr1, anchr2, min, max);
171 }