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.cpDampedRotarySpring;
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 alias cpDampedRotarySpringTorqueFunc = cpFloat function(cpConstraint* spring, cpFloat relativeAngle);
33 
34 //~ const cpConstraintClass* cpDampedRotarySpringGetClass();
35 
36 /// @private
37 struct cpDampedRotarySpring
38 {
39     cpConstraint constraint;
40     cpFloat restAngle = 0;
41     cpFloat stiffness = 0;
42     cpFloat damping = 0;
43     cpDampedRotarySpringTorqueFunc springTorqueFunc;
44 
45     cpFloat target_wrn = 0;
46     cpFloat w_coef = 0;
47 
48     cpFloat iSum = 0;
49     cpFloat jAcc = 0;
50 }
51 
52 /// Allocate a damped rotary spring.
53 cpDampedRotarySpring* cpDampedRotarySpringAlloc();
54 
55 /// Initialize a damped rotary spring.
56 cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring* joint, cpBody* a, cpBody* b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
57 
58 /// Allocate and initialize a damped rotary spring.
59 cpConstraint* cpDampedRotarySpringNew(cpBody* a, cpBody* b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
60 
61 mixin CP_DefineConstraintProperty!("cpDampedRotarySpring", cpFloat, "restAngle", "RestAngle");
62 mixin CP_DefineConstraintProperty!("cpDampedRotarySpring", cpFloat, "stiffness", "Stiffness");
63 mixin CP_DefineConstraintProperty!("cpDampedRotarySpring", cpFloat, "damping", "Damping");
64 mixin CP_DefineConstraintProperty!("cpDampedRotarySpring", cpDampedRotarySpringTorqueFunc, "springTorqueFunc", "SpringTorqueFunc");
65 
66 cpFloat defaultSpringTorque(cpDampedRotarySpring* spring, cpFloat relativeAngle)
67 {
68     return (relativeAngle - spring.restAngle) * spring.stiffness;
69 }
70 
71 void preStep(cpDampedRotarySpring* spring, cpFloat dt)
72 {
73     cpBody* a = spring.constraint.a;
74     cpBody* b = spring.constraint.b;
75 
76     cpFloat moment = a.i_inv + b.i_inv;
77     cpAssertSoft(moment != 0.0, "Unsolvable spring.");
78     spring.iSum = 1.0f / moment;
79 
80     spring.w_coef     = 1.0f - cpfexp(-spring.damping * dt * moment);
81     spring.target_wrn = 0.0f;
82 
83     // apply spring torque
84     cpFloat j_spring = spring.springTorqueFunc(cast(cpConstraint*)spring, a.a - b.a) * dt;
85     spring.jAcc = j_spring;
86 
87     a.w -= j_spring * a.i_inv;
88     b.w += j_spring * b.i_inv;
89 }
90 
91 void applyCachedImpulse(cpDampedRotarySpring* spring, cpFloat dt_coef)
92 {
93 }
94 
95 void applyImpulse(cpDampedRotarySpring* spring, cpFloat dt)
96 {
97     cpBody* a = spring.constraint.a;
98     cpBody* b = spring.constraint.b;
99 
100     // compute relative velocity
101     cpFloat wrn = a.w - b.w;    //normal_relative_velocity(a, b, r1, r2, n) - spring.target_vrn;
102 
103     // compute velocity loss from drag
104     // not 100% certain this is derived correctly, though it makes sense
105     cpFloat w_damp = (spring.target_wrn - wrn) * spring.w_coef;
106     spring.target_wrn = wrn + w_damp;
107 
108     //apply_impulses(a, b, spring.r1, spring.r2, cpvmult(spring.n, v_damp*spring.nMass));
109     cpFloat j_damp = w_damp * spring.iSum;
110     spring.jAcc += j_damp;
111 
112     a.w += j_damp * a.i_inv;
113     b.w -= j_damp * b.i_inv;
114 }
115 
116 cpFloat getImpulse(cpDampedRotarySpring* spring)
117 {
118     return spring.jAcc;
119 }
120 
121 __gshared cpConstraintClass klass;
122 
123 void _initModuleCtor_cpDampedRotarySpring()
124 {
125     klass = cpConstraintClass(
126         cast(cpConstraintPreStepImpl)&preStep,
127         cast(cpConstraintApplyCachedImpulseImpl)&applyCachedImpulse,
128         cast(cpConstraintApplyImpulseImpl)&applyImpulse,
129         cast(cpConstraintGetImpulseImpl)&getImpulse,
130     );
131 };
132 
133 const(cpConstraintClass *) cpDampedRotarySpringGetClass()
134 {
135     return cast(cpConstraintClass*)&klass;
136 }
137 
138 cpDampedRotarySpring *
139 cpDampedRotarySpringAlloc()
140 {
141     return cast(cpDampedRotarySpring*)cpcalloc(1, cpDampedRotarySpring.sizeof);
142 }
143 
144 cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring* spring, cpBody* a, cpBody* b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)
145 {
146     cpConstraintInit(cast(cpConstraint*)spring, &klass, a, b);
147 
148     spring.restAngle        = restAngle;
149     spring.stiffness        = stiffness;
150     spring.damping          = damping;
151     spring.springTorqueFunc = cast(cpDampedRotarySpringTorqueFunc)&defaultSpringTorque;
152 
153     spring.jAcc = 0.0f;
154 
155     return spring;
156 }
157 
158 cpConstraint* cpDampedRotarySpringNew(cpBody* a, cpBody* b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)
159 {
160     return cast(cpConstraint*)cpDampedRotarySpringInit(cpDampedRotarySpringAlloc(), a, b, restAngle, stiffness, damping);
161 }