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.cpPinJoint;
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* cpPinJointGetClass();
34 
35 /// @private
36 struct cpPinJoint
37 {
38     cpConstraint constraint;
39     cpVect anchr1, anchr2;
40     cpFloat dist = 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!("cpPinJoint", cpVect, "anchr1", "Anchr1");
51 mixin CP_DefineConstraintProperty!("cpPinJoint", cpVect, "anchr2", "Anchr2");
52 mixin CP_DefineConstraintProperty!("cpPinJoint", cpFloat, "dist", "Dist");
53 
54 void preStep(cpPinJoint* joint, cpFloat dt)
55 {
56     cpBody* a = joint.constraint.a;
57     cpBody* b = joint.constraint.b;
58 
59     joint.r1 = cpvrotate(joint.anchr1, a.rot);
60     joint.r2 = cpvrotate(joint.anchr2, b.rot);
61 
62     cpVect  delta = cpvsub(cpvadd(b.p, joint.r2), cpvadd(a.p, joint.r1));
63     cpFloat dist  = cpvlength(delta);
64     joint.n = cpvmult(delta, 1.0f / (dist ? dist : cast(cpFloat)INFINITY));
65 
66     // calculate mass normal
67     joint.nMass = 1.0f / k_scalar(a, b, joint.r1, joint.r2, joint.n);
68 
69     // calculate bias velocity
70     cpFloat maxBias = joint.constraint.maxBias;
71     joint.bias = cpfclamp(-bias_coef(joint.constraint.errorBias, dt) * (dist - joint.dist) / dt, -maxBias, maxBias);
72 }
73 
74 void applyCachedImpulse(cpPinJoint* joint, cpFloat dt_coef)
75 {
76     cpBody* a = joint.constraint.a;
77     cpBody* b = joint.constraint.b;
78 
79     cpVect j = cpvmult(joint.n, joint.jnAcc * dt_coef);
80     apply_impulses(a, b, joint.r1, joint.r2, j);
81 }
82 
83 void applyImpulse(cpPinJoint* joint, cpFloat dt)
84 {
85     cpBody* a = joint.constraint.a;
86     cpBody* b = joint.constraint.b;
87     cpVect  n = joint.n;
88 
89     // compute relative velocity
90     cpFloat vrn = normal_relative_velocity(a, b, joint.r1, joint.r2, n);
91 
92     cpFloat jnMax = joint.constraint.maxForce * dt;
93 
94     // compute normal impulse
95     cpFloat jn    = (joint.bias - vrn) * joint.nMass;
96     cpFloat jnOld = joint.jnAcc;
97     joint.jnAcc = cpfclamp(jnOld + jn, -jnMax, jnMax);
98     jn = joint.jnAcc - jnOld;
99 
100     // apply impulse
101     apply_impulses(a, b, joint.r1, joint.r2, cpvmult(n, jn));
102 }
103 
104 cpFloat getImpulse(cpPinJoint* joint)
105 {
106     return cpfabs(joint.jnAcc);
107 }
108 
109 
110 __gshared cpConstraintClass klass;
111 
112 void _initModuleCtor_cpPinJoint()
113 {
114     klass = cpConstraintClass(
115         cast(cpConstraintPreStepImpl)&preStep,
116         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
117         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
118         cast(cpConstraintGetImpulseImpl)&getImpulse,
119     );
120 }
121 
122 const(cpConstraintClass *) cpPinJointGetClass()
123 {
124     return cast(cpConstraintClass*)&klass;
125 }
126 
127 cpPinJoint *
128 cpPinJointAlloc()
129 {
130     return cast(cpPinJoint*)cpcalloc(1, cpPinJoint.sizeof);
131 }
132 
133 cpPinJoint* cpPinJointInit(cpPinJoint* joint, cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2)
134 {
135     cpConstraintInit(cast(cpConstraint*)joint, &klass, a, b);
136 
137     joint.anchr1 = anchr1;
138     joint.anchr2 = anchr2;
139 
140     // STATIC_BODY_CHECK
141     cpVect p1 = (a ? cpvadd(a.p, cpvrotate(anchr1, a.rot)) : anchr1);
142     cpVect p2 = (b ? cpvadd(b.p, cpvrotate(anchr2, b.rot)) : anchr2);
143     joint.dist = cpvlength(cpvsub(p2, p1));
144 
145     cpAssertWarn(joint.dist > 0.0, "You created a 0 length pin joint. A pivot joint will be much more stable.");
146 
147     joint.jnAcc = 0.0f;
148 
149     return joint;
150 }
151 
152 cpConstraint* cpPinJointNew(cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2)
153 {
154     return cast(cpConstraint*)cpPinJointInit(cpPinJointAlloc(), a, b, anchr1, anchr2);
155 }