1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.replica.replicaisland;
18
19/**
20 * Helper class for interpolating velocity over time given a target velocity and acceleration.
21 * The current velocity will be accelerated towards the target until the target is reached.
22 * Note that acceleration is effectively an absolute value--it always points in the direction of
23 * the target velocity.
24 */
25public class Interpolator extends AllocationGuard {
26
27    private float mCurrent;
28    private float mTarget;
29    private float mAcceleration;
30
31    public Interpolator() {
32        super();
33    }
34
35    // Rather than simply interpolating acceleration and velocity for each time step
36    // (as in, position += (velocity * time); velocity += (acceleration * time);),
37    // we actually perform the work needed to calculate the integral of velocity with respect to
38    // time.
39    //
40    // The integral of velocity is:
41    //
42    // integral[(v + aT)dT]
43    //
44    // Simplified to:
45    //
46    // vT + 1/2 * aT^2
47    //
48    // Thus:
49    // change in position = velocity * time + (0.5 * acceleration * (time^2))
50    // change in velocity = acceleration * time
51
52    public void set(float current, float target, float acceleration) {
53        mCurrent = current;
54        mTarget = target;
55        mAcceleration = acceleration;
56    }
57
58    // While this function writes directly to velocity, it doesn't affect
59    // position.  Instead, the position offset is returned so that it can be blended.
60    public float interpolate(float secondsDelta) {
61        float oldVelocity = mCurrent;
62
63        // point the acceleration at the target, or zero it if we are already
64        // there
65        float directionalAcceleration = calculateAcceleration(oldVelocity, mAcceleration, mTarget);
66
67        // calculate scaled acceleration (0.5 * acceleration * (time^2))
68        float scaledAcceleration;
69        scaledAcceleration = scaleAcceleration(directionalAcceleration, secondsDelta);
70
71        // calculate the change in position
72        float positionOffset = (oldVelocity * secondsDelta) + scaledAcceleration;
73
74        // change in velocity = v + aT
75        float newVelocity = oldVelocity + (directionalAcceleration * secondsDelta);
76
77        // check to see if we've passed our target velocity since the last time
78        // step.  If so, clamp to the target
79        if (passedTarget(oldVelocity, newVelocity, mTarget)) {
80            newVelocity = mTarget;
81        }
82
83        mCurrent = newVelocity;
84
85        return positionOffset;
86    }
87
88    public float getCurrent() {
89        return mCurrent;
90    }
91
92    private boolean passedTarget(float oldVelocity, float newVelocity, float targetVelocity) {
93        boolean result = false;
94
95        if (oldVelocity < targetVelocity && newVelocity > targetVelocity) {
96            result = true;
97        } else if (oldVelocity > targetVelocity && newVelocity < targetVelocity) {
98            result = true;
99        }
100
101        return result;
102    }
103
104    // find the magnitude and direction of acceleration.
105    // in this system, acceleration always points toward target velocity
106    private float calculateAcceleration(float velocity, float acceleration, float target) {
107        if (Math.abs(velocity - target) < 0.0001f) {
108            // no accel needed
109            acceleration = 0.0f;
110        } else if (velocity > target) {
111            // accel must be negative
112            acceleration *= -1.0f;
113        }
114
115        return acceleration;
116    }
117
118    // calculates 1/2 aT^2
119    private float scaleAcceleration(float acceleration, float secondsDelta) {
120        float timeSquared = (secondsDelta * secondsDelta);
121        float scaledAccel = acceleration * timeSquared;
122        scaledAccel *= 0.5f;
123
124        return scaledAccel;
125    }
126}
127