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.cpDampedSpring;
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 alias cpDampedSpringForceFunc = cpFloat function(cpConstraint* spring, cpFloat dist);
34 
35 //~ const cpConstraintClass* cpDampedSpringGetClass();
36 
37 /// @private
38 struct cpDampedSpring
39 {
40     cpConstraint constraint;
41     cpVect anchr1, anchr2;
42     cpFloat restLength = 0;
43     cpFloat stiffness = 0;
44     cpFloat damping = 0;
45     cpDampedSpringForceFunc springForceFunc;
46 
47     cpFloat target_vrn = 0;
48     cpFloat v_coef = 0;
49 
50     cpVect r1, r2;
51     cpFloat nMass = 0;
52     cpVect n;
53 
54     cpFloat jAcc = 0;
55 }
56 
57 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpVect, "anchr1", "Anchr1");
58 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpVect, "anchr2", "Anchr2");
59 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpFloat, "restLength", "RestLength");
60 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpFloat, "stiffness", "Stiffness");
61 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpFloat, "damping", "Damping");
62 mixin CP_DefineConstraintProperty!("cpDampedSpring", cpDampedSpringForceFunc, "springForceFunc", "SpringForceFunc");
63 
64 
65 cpFloat defaultSpringForce(cpDampedSpring* spring, cpFloat dist)
66 {
67     return (spring.restLength - dist) * spring.stiffness;
68 }
69 
70 void preStep(cpDampedSpring* spring, cpFloat dt)
71 {
72     cpBody* a = spring.constraint.a;
73     cpBody* b = spring.constraint.b;
74 
75     spring.r1 = cpvrotate(spring.anchr1, a.rot);
76     spring.r2 = cpvrotate(spring.anchr2, b.rot);
77 
78     cpVect  delta = cpvsub(cpvadd(b.p, spring.r2), cpvadd(a.p, spring.r1));
79     cpFloat dist  = cpvlength(delta);
80     spring.n = cpvmult(delta, 1.0f / (dist ? dist : INFINITY));
81 
82     cpFloat k = k_scalar(a, b, spring.r1, spring.r2, spring.n);
83     cpAssertSoft(k != 0.0, "Unsolvable spring.");
84     spring.nMass = 1.0f / k;
85 
86     spring.target_vrn = 0.0f;
87     spring.v_coef     = 1.0f - cpfexp(-spring.damping * dt * k);
88 
89     // apply spring force
90     cpFloat f_spring = spring.springForceFunc(cast(cpConstraint*)spring, dist);
91     cpFloat j_spring = spring.jAcc = f_spring * dt;
92     apply_impulses(a, b, spring.r1, spring.r2, cpvmult(spring.n, j_spring));
93 }
94 
95 void applyCachedImpulse(cpDampedSpring* spring, cpFloat dt_coef)
96 {
97 }
98 
99 void applyImpulse(cpDampedSpring* spring, cpFloat dt)
100 {
101     cpBody* a = spring.constraint.a;
102     cpBody* b = spring.constraint.b;
103 
104     cpVect n  = spring.n;
105     cpVect r1 = spring.r1;
106     cpVect r2 = spring.r2;
107 
108     // compute relative velocity
109     cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n);
110 
111     // compute velocity loss from drag
112     cpFloat v_damp = (spring.target_vrn - vrn) * spring.v_coef;
113     spring.target_vrn = vrn + v_damp;
114 
115     cpFloat j_damp = v_damp * spring.nMass;
116     spring.jAcc += j_damp;
117     apply_impulses(a, b, spring.r1, spring.r2, cpvmult(spring.n, j_damp));
118 }
119 
120 cpFloat getImpulse(cpDampedSpring* spring)
121 {
122     return spring.jAcc;
123 }
124 
125 __gshared cpConstraintClass klass;
126 
127 void _initModuleCtor_cpDampedSpring()
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 *) cpDampedSpringGetClass()
138 {
139     return cast(cpConstraintClass*)&klass;
140 }
141 
142 cpDampedSpring *
143 cpDampedSpringAlloc()
144 {
145     return cast(cpDampedSpring*)cpcalloc(1, cpDampedSpring.sizeof);
146 }
147 
148 cpDampedSpring* cpDampedSpringInit(cpDampedSpring* spring, cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping)
149 {
150     cpConstraintInit(cast(cpConstraint*)spring, cpDampedSpringGetClass(), a, b);
151 
152     spring.anchr1 = anchr1;
153     spring.anchr2 = anchr2;
154 
155     spring.restLength      = restLength;
156     spring.stiffness       = stiffness;
157     spring.damping         = damping;
158     spring.springForceFunc = cast(cpDampedSpringForceFunc)&defaultSpringForce;
159 
160     spring.jAcc = 0.0f;
161 
162     return spring;
163 }
164 
165 cpConstraint* cpDampedSpringNew(cpBody* a, cpBody* b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiffness, cpFloat damping)
166 {
167     return cast(cpConstraint*)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchr1, anchr2, restLength, stiffness, damping);
168 }