AxisPhysicsModel.cpp (3636B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "AxisPhysicsModel.h" 8 9 namespace mozilla { 10 namespace layers { 11 12 /** 13 * The simulation is advanced forward in time with a fixed time step to ensure 14 * that it remains deterministic given variable framerates. To determine the 15 * position at any variable time, two samples are interpolated. 16 * 17 * kFixedtimestep is set to 120hz in order to ensure that every frame in a 18 * common 60hz refresh rate display will have at least one physics simulation 19 * sample. More accuracy can be obtained by reducing kFixedTimestep to smaller 20 * intervals, such as 240hz or 1000hz, at the cost of more CPU cycles. If 21 * kFixedTimestep is increased to much longer intervals, interpolation will 22 * become less effective at reducing temporal jitter and the simulation will 23 * lose accuracy. 24 */ 25 const double AxisPhysicsModel::kFixedTimestep = 1.0 / 120.0; // 120hz 26 27 /** 28 * Constructs an AxisPhysicsModel with initial values for state. 29 * 30 * @param aInitialPosition sets the initial position of the simulation, 31 * in AppUnits. 32 * @param aInitialVelocity sets the initial velocity of the simulation, 33 * in AppUnits / second. 34 */ 35 AxisPhysicsModel::AxisPhysicsModel(double aInitialPosition, 36 double aInitialVelocity) 37 : mProgress(1.0), 38 mPrevState(aInitialPosition, aInitialVelocity), 39 mNextState(aInitialPosition, aInitialVelocity) {} 40 41 AxisPhysicsModel::~AxisPhysicsModel() = default; 42 43 double AxisPhysicsModel::GetVelocity() const { 44 return LinearInterpolate(mPrevState.v, mNextState.v, mProgress); 45 } 46 47 double AxisPhysicsModel::GetPosition() const { 48 return LinearInterpolate(mPrevState.p, mNextState.p, mProgress); 49 } 50 51 void AxisPhysicsModel::SetVelocity(double aVelocity) { 52 mNextState.v = aVelocity; 53 mNextState.p = GetPosition(); 54 mProgress = 1.0; 55 } 56 57 void AxisPhysicsModel::SetPosition(double aPosition) { 58 mNextState.v = GetVelocity(); 59 mNextState.p = aPosition; 60 mProgress = 1.0; 61 } 62 63 void AxisPhysicsModel::Simulate(const TimeDuration& aDeltaTime) { 64 for (mProgress += aDeltaTime.ToSeconds() / kFixedTimestep; mProgress > 1.0; 65 mProgress -= 1.0) { 66 Integrate(kFixedTimestep); 67 } 68 } 69 70 void AxisPhysicsModel::Integrate(double aDeltaTime) { 71 mPrevState = mNextState; 72 73 // RK4 (Runge-Kutta method) Integration 74 // http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods 75 Derivative a = Evaluate(mNextState, 0.0, Derivative()); 76 Derivative b = Evaluate(mNextState, aDeltaTime * 0.5, a); 77 Derivative c = Evaluate(mNextState, aDeltaTime * 0.5, b); 78 Derivative d = Evaluate(mNextState, aDeltaTime, c); 79 80 double dpdt = 1.0 / 6.0 * (a.dp + 2.0 * (b.dp + c.dp) + d.dp); 81 double dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv); 82 83 mNextState.p += dpdt * aDeltaTime; 84 mNextState.v += dvdt * aDeltaTime; 85 } 86 87 AxisPhysicsModel::Derivative AxisPhysicsModel::Evaluate( 88 const State& aInitState, double aDeltaTime, const Derivative& aDerivative) { 89 State state(aInitState.p + aDerivative.dp * aDeltaTime, 90 aInitState.v + aDerivative.dv * aDeltaTime); 91 92 return Derivative(state.v, Acceleration(state)); 93 } 94 95 double AxisPhysicsModel::LinearInterpolate(double aV1, double aV2, 96 double aBlend) { 97 return aV1 * (1.0 - aBlend) + aV2 * aBlend; 98 } 99 100 } // namespace layers 101 } // namespace mozilla