blob: fbe12a104e6dcc14e94dba1a54fd275dee8b7528 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.replica.replicaisland;
/**
* Helper class for interpolating velocity over time given a target velocity and acceleration.
* The current velocity will be accelerated towards the target until the target is reached.
* Note that acceleration is effectively an absolute value--it always points in the direction of
* the target velocity.
*/
public class Interpolator extends AllocationGuard {
private float mCurrent;
private float mTarget;
private float mAcceleration;
public Interpolator() {
super();
}
// Rather than simply interpolating acceleration and velocity for each time step
// (as in, position += (velocity * time); velocity += (acceleration * time);),
// we actually perform the work needed to calculate the integral of velocity with respect to
// time.
//
// The integral of velocity is:
//
// integral[(v + aT)dT]
//
// Simplified to:
//
// vT + 1/2 * aT^2
//
// Thus:
// change in position = velocity * time + (0.5 * acceleration * (time^2))
// change in velocity = acceleration * time
public void set(float current, float target, float acceleration) {
mCurrent = current;
mTarget = target;
mAcceleration = acceleration;
}
// While this function writes directly to velocity, it doesn't affect
// position. Instead, the position offset is returned so that it can be blended.
public float interpolate(float secondsDelta) {
float oldVelocity = mCurrent;
// point the acceleration at the target, or zero it if we are already
// there
float directionalAcceleration = calculateAcceleration(oldVelocity, mAcceleration, mTarget);
// calculate scaled acceleration (0.5 * acceleration * (time^2))
float scaledAcceleration;
scaledAcceleration = scaleAcceleration(directionalAcceleration, secondsDelta);
// calculate the change in position
float positionOffset = (oldVelocity * secondsDelta) + scaledAcceleration;
// change in velocity = v + aT
float newVelocity = oldVelocity + (directionalAcceleration * secondsDelta);
// check to see if we've passed our target velocity since the last time
// step. If so, clamp to the target
if (passedTarget(oldVelocity, newVelocity, mTarget)) {
newVelocity = mTarget;
}
mCurrent = newVelocity;
return positionOffset;
}
public float getCurrent() {
return mCurrent;
}
private boolean passedTarget(float oldVelocity, float newVelocity, float targetVelocity) {
boolean result = false;
if (oldVelocity < targetVelocity && newVelocity > targetVelocity) {
result = true;
} else if (oldVelocity > targetVelocity && newVelocity < targetVelocity) {
result = true;
}
return result;
}
// find the magnitude and direction of acceleration.
// in this system, acceleration always points toward target velocity
private float calculateAcceleration(float velocity, float acceleration, float target) {
if (Math.abs(velocity - target) < 0.0001f) {
// no accel needed
acceleration = 0.0f;
} else if (velocity > target) {
// accel must be negative
acceleration *= -1.0f;
}
return acceleration;
}
// calculates 1/2 aT^2
private float scaleAcceleration(float acceleration, float secondsDelta) {
float timeSquared = (secondsDelta * secondsDelta);
float scaledAccel = acceleration * timeSquared;
scaledAccel *= 0.5f;
return scaledAccel;
}
}