/*********************************************************************** * mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***********************************************************************/ package org.mt4j.util.animation; /** * The Class MultiPurposeInterpolator. * * <br><br> * Interpolator class, used to get interpolated values between * start and the destination values * * @author Christopher Ruff */ public class MultiPurposeInterpolator implements Iinterpolator { /** The real from. */ private float normalizedFrom, normalizedValue, velocity, v0, normalizedLastStepDelta, normalizedTarget, normalizedRemainingTime, // normalizedTotalTime, realTarget, realFrom; /** The normalized tfactor. */ private float normalizedTfactor; /** The normalized dfactor. */ private float normalizedDfactor; /** The t. */ private float t; /** The t1. */ private float t1; /** The t2. */ private float t2; /** The time taken. */ private long startTime, timeTaken; /** The original loop count. */ private int loopCount, originalLoopCount; /** The debug. */ private boolean debug; /** The alternating. */ private boolean alternating; /** The alternate factor. */ private int alternateFactor; /** * Initializes a new Interpolator object, used to get interpolated values between * the start and the destination. * * @param from the value to start the interpolation from * @param to the value to interpolate to * @param interpolationDuration the duration of interpolation * @param accelerationEndTime defines the normalized time until when the Easing IN takes place (normalized, value from 0..1) (i.e. 0.25f) * @param decelerationStartTime defines the time from when the Easing OUT takes place (normalized, value from 0..1) (i.e. 0.75f) * @param loopCount how often to loop the interpolation, Value of "-1" loops infinitely */ public MultiPurposeInterpolator(float from, float to, float interpolationDuration, float accelerationEndTime, float decelerationStartTime, int loopCount){ if (interpolationDuration <=0) { throw new RuntimeException("You have to specify a time value greater than 0ms"); } else if (loopCount == 0) { throw new RuntimeException("You have to specify a loopCount value that is not '0'"); } else if (this.t1 > this.t2) { throw new RuntimeException("Value of t1 has to be smaller or equal to the value of t2"); } else if (this.t1 > 1 || this.t1 < 0 || this.t2 > 1 || this.t2 < 0) { throw new RuntimeException("Values of t1 and t2 have to be: 0.0 < t1,t2 < 1.0"); } this.debug = false; this.normalizedTfactor = 1/interpolationDuration; this.normalizedDfactor = 1/(to-from); if (this.debug){ System.out.println("Normalized TotalTime Factor: " + this.normalizedTfactor ); System.out.println("Normalized Total Distance Factor: " + this.normalizedDfactor ); } this.alternating = false; this.alternateFactor = -1; //these are also normalized values //Defines the time until when the Easing IN takes place (normalized, value from 0..1); this.t1 = accelerationEndTime; //Defines the time from when the Easing OUT takes place (normalized, value from 0..1); this.t2 = decelerationStartTime; //Marks the normalized time we are at form 0..1, of the begining to the finish of interpolation this.t = 0; this.normalizedFrom = 0; this.normalizedTarget = 1; this.realFrom = from; this.realTarget = to; // this.normalizedTotalTime = 1; this.loopCount = loopCount; this.originalLoopCount = loopCount; this.normalizedRemainingTime = 1; this.normalizedValue = 0; this.timeTaken = 0; this.startTime = 0; /* * calculate the maximum velocity at the middle part of the velocity/time curve */ this.v0 = 2 / (1 + this.t2 - this.t1); //jedes mal hier ausrechnen oder in construktor? this.resetForNextLoop(); } /** * resets the interpolator for the next loop. */ private void resetForNextLoop(){ /* * reset values */ this.alternateFactor *= -1; this.startTime = System.currentTimeMillis(); this.t = 0; this.normalizedRemainingTime = 1; this.normalizedLastStepDelta = 0.0f; this.normalizedValue = this.normalizedFrom; this.velocity = 0; } /* (non-Javadoc) * @see util.animation.Iinterpolator#resetInterpolator() */ public void resetInterpolator(){ /* * reset values */ this.startTime = System.currentTimeMillis(); this.alternateFactor = 1; this.t = 0; this.normalizedRemainingTime = 1; this.normalizedLastStepDelta = 0.0f; this.normalizedValue = this.normalizedFrom; this.timeTaken = 0; this.v0 = 2 / (1 + this.t2 - this.t1); this.loopCount = this.originalLoopCount; this.velocity = 0; } /** * Does the next interpolation step, taking the timeDelta into account * <p> * This implementation makes sure the destination is reached exactly, and sets the value * to the target value if the next interpolation step would surpass the target value, or if the * time is up. * * @param deltaTime amount of time to interpolate * * @return true, if interpolate * * <b>false</b> if this call to interpolate() would lead to a higher value than the desired * target value or the time is up, <b>true</b> if the target isnt reached in this interpolation step * and there is still remaining time */ public boolean interpolate(float deltaTime) { /* * Check if the interpolation has finished * -> restart again if there are loops left and the target was reached from previous interpolation * -> do nothing if there are no more loops */ if (this.isTargetReached() && (this.loopCount == -1 || this.loopCount > 0 )){ if(this.debug){ System.out.println("Target reached or infinitely looped, or still more loops to go -> resetting the values before interpolating again"); } this.resetForNextLoop(); }else if (this.isTargetReached() && this.loopCount == 0){ return false; } /* * calculate normalized timeDelta and add it to our current time t, * calculate the remaining time */ float normalizeDeltaTime = (deltaTime * this.normalizedTfactor); this.t += normalizeDeltaTime; //map timeDelta to 0..1 this.normalizedRemainingTime -= normalizeDeltaTime; if(this.debug){ System.out.println("Normalized deltatime: " + normalizeDeltaTime); System.out.println("T: " + this.t + " Remaining: " + this.normalizedRemainingTime); } /* * calucalte velocity */ if (this.t < this.t1){ //anfang, beschleunigung // d = velocity *t*t/(2*t1); this.velocity = this.v0 * (this.t / this.t1); } else{ // d = velocity * (t1/2); if (this.t < this.t2){ //mitte constante geschwindigkeit // d += (t-t1)*velocity; this.velocity = this.v0; } else{ //letztes st�ck geschwindigkeit linear abnehmen lassen // d += (t2-t1)*velocity; // d += (t-t*t/2-t2+t2*b/2) * velocity/(1-t2); this.velocity = this.v0 * (1 - (this.t - this.t2) / (1 - this.t2) ); /* if (t2 == 1 || t == 1){ //FIXME added to avoid infinity velocity when t==t2 this.velocity = 0; } */ } } if (Float.isInfinite(velocity) || Float.isNaN(velocity)){ // System.out.println("Velocity: " + velocity); this.velocity = 0; } /* * calculate the normalized step and overall value by multiplying * the velocity with the normalized deltaTime */ float normalizedTmpStepDelta = (this.velocity * normalizeDeltaTime); float normalizedTmpValue = this.normalizedValue + normalizedTmpStepDelta; if(this.debug){ float tmpRealValue = normalizedTmpValue *(this.realTarget - this.realFrom); System.out.println("Velocity: " + this.velocity); System.out.println("Normalized TMP Value: " + normalizedTmpValue); System.out.println("-> current value: " + tmpRealValue); } this.timeTaken = System.currentTimeMillis() - this.startTime; /* * Checks if the target would be exceeded by this step, or if the * time is up - if so, sets the value to match the target so we * always end up at the desired target value * -> one loop is complete */ // Checken ob der berechnete wert �ber den target wert hinausschiesst -> loop beenden if (normalizedTmpValue >= this.normalizedTarget){ this.normalizedLastStepDelta = this.normalizedTarget - this.normalizedValue; this.normalizedValue = this.normalizedTarget; /* if (Float.isInfinite(normalizedLastStepDelta) || Float.isNaN(normalizedLastStepDelta) ){ System.out.println("Stepdelta malformed! trying to correct.."); System.out.println("Velocity: " + velocity); System.out.println(); float normStepDelta = (this.normalizedTarget - this.normalizedValue); this.normalizedLastStepDelta = normStepDelta; } */ if (this.loopCount != -1) { this.loopCount--; } if(this.debug) { System.out.println("Interpolation duration: " + this.timeTaken); } return false; } // Checken ob die Zeit, die die animation hatte abgelaufen ist -> loop benden else if (this.normalizedRemainingTime <= 0){ this.normalizedLastStepDelta = this.normalizedTarget - this.normalizedValue; this.normalizedValue = this.normalizedTarget; /* // FIXME // WHAT THE F*CK !?? WARUM KOMMT MANCHMAL NAN ODER INFINITE RAUS // BEIM RECHNEN VON 1.0 - 1.0 und speichern in normalizedLastStepDelta // BEIM NEUEN BERECHNEN STIMMT DER WERT DANN?? if (Float.isInfinite(normalizedLastStepDelta) || Float.isNaN(normalizedLastStepDelta) ){ System.out.println("Stepdelta malformed! trying to correct.."); System.out.println("Velocity: " + velocity); System.out.println(); float normStepDelta = (this.normalizedTarget - this.normalizedValue); this.normalizedLastStepDelta = normStepDelta; } */ if (this.loopCount != -1) { this.loopCount--; } if(this.debug) { System.out.println("Interpolation duration: " + this.timeTaken); } return false; } // Just save the values for the next step -> loop nicht beenden else{ this.normalizedLastStepDelta = normalizedTmpStepDelta; this.normalizedValue = normalizedTmpValue; /* if (Float.isInfinite(normalizedLastStepDelta) || Float.isNaN(normalizedLastStepDelta) ){ System.out.println("Stepdelta malformed! trying to correct.."); System.out.println("Velocity: " + velocity); System.out.println(); float normStepDelta = (this.normalizedTarget - this.normalizedValue); this.normalizedLastStepDelta = normStepDelta; // this.normalizedLastStepDelta = 0; // this.normalizedLastStepDelta = this.normalizedTarget - this.normalizedValue; } */ return true; } } /** * checks if the interpolation has reached its target * and there are no more loops left. * * @return true: if the interpolation is finished, then you shouldnt call interpolate() again, * false: if there are still loops to do, and/or the target hasnt been reached */ public boolean isFinished(){ if (this.normalizedTarget != this.normalizedValue || this.loopCount == -1 ) { return false; } else if (this.normalizedTarget == this.normalizedValue && this.loopCount > 0) { return false; } else if (this.normalizedTarget == this.normalizedValue && this.loopCount == 0) { return true; } else { return false; } } /** * Checks if is target reached. * * @return true, if is target reached */ private boolean isTargetReached(){ return this.normalizedValue == this.normalizedTarget; } /** * Gets the time taken. * * @return the time taken */ public float getTimeTaken() { return this.timeTaken; } /** * Checks if is alternating. * * @return true, if is alternating */ public boolean isAlternating() { return this.alternating; } /** * Sets the alternating. * * @param alternating the new alternating */ public void setAlternating(boolean alternating) { this.alternating = alternating; } /** * Returns the un-normalized value of the current interpolation. * * @return the current value */ public float getCurrentValue() { //un-normalize the value to get the real value float currentValue = (this.normalizedValue * (this.realTarget - this.realFrom)) + this.realFrom; if (this.isAlternating()) { return this.alternateFactor * currentValue; } else { return currentValue; } } /** * Returns the unnormalized delta value from the last interpolated value to the current, * after a interpolationstep * - useful to determine how much the value increased/decreased. * * @return the current step delta */ public float getCurrentStepDelta() {//un-normalize the step to get the real step float stepDelta = this.normalizedLastStepDelta * (this.realTarget - this.realFrom); // if (Float.isNaN(stepDelta)){ // System.out.println("Stepdelta is NAN! :" + stepDelta ); // } // if (stepDelta > 50 || stepDelta < 0){ // System.out.println(stepDelta); // System.out.println("Stepdelta malformed: " + stepDelta); // } if (this.isAlternating()) { return this.alternateFactor * stepDelta; } else{ return stepDelta; } } }