1 |
|
2 |
|
3 |
|
4 | let generateSpringRK4 = (function(){
|
5 | function springAccelerationForState( state ){
|
6 | return (-state.tension * state.x) - (state.friction * state.v);
|
7 | }
|
8 |
|
9 | function springEvaluateStateWithDerivative( initialState, dt, derivative ){
|
10 | let state = {
|
11 | x: initialState.x + derivative.dx * dt,
|
12 | v: initialState.v + derivative.dv * dt,
|
13 | tension: initialState.tension,
|
14 | friction: initialState.friction
|
15 | };
|
16 |
|
17 | return { dx: state.v, dv: springAccelerationForState( state ) };
|
18 | }
|
19 |
|
20 | function springIntegrateState( state, dt ){
|
21 | let a = {
|
22 | dx: state.v,
|
23 | dv: springAccelerationForState( state )
|
24 | },
|
25 | b = springEvaluateStateWithDerivative( state, dt * 0.5, a ),
|
26 | c = springEvaluateStateWithDerivative( state, dt * 0.5, b ),
|
27 | d = springEvaluateStateWithDerivative( state, dt, c ),
|
28 | dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
|
29 | dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
|
30 |
|
31 | state.x = state.x + dxdt * dt;
|
32 | state.v = state.v + dvdt * dt;
|
33 |
|
34 | return state;
|
35 | }
|
36 |
|
37 | return function springRK4Factory( tension, friction, duration ){
|
38 |
|
39 | let initState = {
|
40 | x: -1,
|
41 | v: 0,
|
42 | tension: null,
|
43 | friction: null
|
44 | },
|
45 | path = [0],
|
46 | time_lapsed = 0,
|
47 | tolerance = 1 / 10000,
|
48 | DT = 16 / 1000,
|
49 | have_duration, dt, last_state;
|
50 |
|
51 | tension = parseFloat( tension ) || 500;
|
52 | friction = parseFloat( friction ) || 20;
|
53 | duration = duration || null;
|
54 |
|
55 | initState.tension = tension;
|
56 | initState.friction = friction;
|
57 |
|
58 | have_duration = duration !== null;
|
59 |
|
60 |
|
61 | if( have_duration ){
|
62 |
|
63 | time_lapsed = springRK4Factory( tension, friction );
|
64 |
|
65 | dt = time_lapsed / duration * DT;
|
66 | } else {
|
67 | dt = DT;
|
68 | }
|
69 |
|
70 | for(;;){
|
71 |
|
72 | last_state = springIntegrateState( last_state || initState, dt );
|
73 |
|
74 | path.push( 1 + last_state.x );
|
75 | time_lapsed += 16;
|
76 |
|
77 | if( !(Math.abs( last_state.x ) > tolerance && Math.abs( last_state.v ) > tolerance) ){
|
78 | break;
|
79 | }
|
80 | }
|
81 |
|
82 | |
83 |
|
84 | return !have_duration ? time_lapsed : function( percentComplete ){ return path[ (percentComplete * (path.length - 1)) | 0 ]; };
|
85 | };
|
86 | }());
|
87 |
|
88 | export default generateSpringRK4;
|