PageRenderTime 108ms CodeModel.GetById 85ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/src/org/mt4j/util/animation/MultiPurposeInterpolator.java

http://mt4j.googlecode.com/
Java | 466 lines | 188 code | 73 blank | 205 comment | 53 complexity | befe8eaf80d03e133907e1d52af16816 MD5 | raw file
  1/***********************************************************************
  2 * mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3 *  
  4 *   This program is free software: you can redistribute it and/or modify
  5 *   it under the terms of the GNU General Public License as published by
  6 *   the Free Software Foundation, either version 3 of the License, or
  7 *   (at your option) any later version.
  8 *
  9 *   This program is distributed in the hope that it will be useful,
 10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 *   GNU General Public License for more details.
 13 *
 14 *   You should have received a copy of the GNU General Public License
 15 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16 *
 17 ***********************************************************************/
 18package org.mt4j.util.animation;
 19
 20/**
 21 * The Class MultiPurposeInterpolator.
 22 * 
 23 * <br><br>
 24 * Interpolator class, used to get interpolated values between
 25 * start and the destination values
 26 * 
 27 * @author Christopher Ruff
 28 */
 29public class MultiPurposeInterpolator implements Iinterpolator { 
 30	
 31	/** The real from. */
 32	private float
 33		normalizedFrom,
 34		normalizedValue,
 35		velocity,
 36		v0,
 37		normalizedLastStepDelta,
 38		normalizedTarget,
 39		normalizedRemainingTime,
 40//		normalizedTotalTime,
 41		realTarget,
 42		realFrom;
 43		
 44	/** The normalized tfactor. */
 45	private float normalizedTfactor;
 46	
 47	/** The normalized dfactor. */
 48	private float normalizedDfactor;
 49
 50	/** The t. */
 51	private float t;
 52	
 53	/** The t1. */
 54	private float t1;
 55	
 56	/** The t2. */
 57	private float t2;
 58		
 59	/** The time taken. */
 60	private long
 61		startTime,
 62		timeTaken;
 63	
 64	/** The original loop count. */
 65	private int loopCount, originalLoopCount;
 66	
 67	/** The debug. */
 68	private boolean debug;
 69	
 70	/** The alternating. */
 71	private boolean alternating;
 72	
 73	/** The alternate factor. */
 74	private int alternateFactor;
 75	
 76	/**
 77	 * Initializes a new Interpolator object, used to get interpolated values between
 78	 * the start and the destination.
 79	 * 
 80	 * @param from the value to start the interpolation from
 81	 * @param to the value to interpolate to
 82	 * @param interpolationDuration the duration of interpolation
 83	 * @param accelerationEndTime defines the normalized time until when the Easing IN takes place (normalized, value from 0..1) (i.e. 0.25f)
 84	 * @param decelerationStartTime defines the time from when the Easing OUT takes place (normalized, value from 0..1) (i.e. 0.75f)
 85	 * @param loopCount how often to loop the interpolation, Value of "-1" loops infinitely
 86	 */
 87	public MultiPurposeInterpolator(float from, float to, float interpolationDuration, float accelerationEndTime, float decelerationStartTime, int loopCount){ 
 88		if (interpolationDuration <=0) {
 89			throw new RuntimeException("You have to specify a time value greater than 0ms");
 90		} else if (loopCount == 0) {
 91			throw new RuntimeException("You have to specify a loopCount value that is not '0'");
 92		} else if (this.t1 > this.t2) {
 93			throw new RuntimeException("Value of t1 has to be smaller or equal to the value of t2");
 94		} else if (this.t1 > 1 || this.t1 < 0 || this.t2 > 1 || this.t2 < 0) {
 95			throw new RuntimeException("Values of t1 and t2 have to be: 0.0 < t1,t2 < 1.0");
 96		}
 97		
 98		this.debug = false;
 99		
100		this.normalizedTfactor = 1/interpolationDuration;
101		this.normalizedDfactor = 1/(to-from);
102		
103		if (this.debug){
104			System.out.println("Normalized TotalTime Factor: " + this.normalizedTfactor );
105			System.out.println("Normalized Total Distance Factor: " + this.normalizedDfactor );
106		}
107		
108		this.alternating = false;
109		
110		this.alternateFactor = -1;
111		
112		//these are also normalized values
113		//Defines the time until when the Easing IN takes place (normalized, value from 0..1);
114		this.t1 						= accelerationEndTime;
115		//Defines the time from when the Easing OUT takes place (normalized, value from 0..1);
116		this.t2 						= decelerationStartTime;
117		//Marks the normalized time we are at form 0..1, of the begining to the finish of interpolation
118		this.t  						= 0;
119		
120		this.normalizedFrom 			= 0;
121		this.normalizedTarget 			= 1;
122		this.realFrom 					= from;
123		this.realTarget 				= to;
124		
125//		this.normalizedTotalTime 		= 1;
126		this.loopCount					= loopCount;
127		this.originalLoopCount			= loopCount;
128		
129		this.normalizedRemainingTime	= 1;
130		this.normalizedValue 			= 0;
131		
132		this.timeTaken 					= 0;
133		this.startTime 					= 0;
134		
135		/*
136		 * calculate the maximum velocity at the middle part of the velocity/time curve
137		 */
138		this.v0 				= 2 / (1 + this.t2 - this.t1); //jedes mal hier ausrechnen oder in construktor?
139		
140		this.resetForNextLoop();
141	}
142	
143	/**
144	 * resets the interpolator for the next loop.
145	 */
146	private void resetForNextLoop(){
147		/*
148		 * reset values
149		 */
150		this.alternateFactor			*=	-1;
151		
152		this.startTime 					= System.currentTimeMillis(); 
153		
154		this.t  						= 0;
155		this.normalizedRemainingTime	= 1;
156		
157		this.normalizedLastStepDelta 	= 0.0f;
158		this.normalizedValue 			= this.normalizedFrom;
159		
160		this.velocity 					= 0;
161	}
162	
163	
164	/* (non-Javadoc)
165	 * @see util.animation.Iinterpolator#resetInterpolator()
166	 */
167	public void resetInterpolator(){
168		/*
169		 * reset values
170		 */
171		this.startTime 					= System.currentTimeMillis(); 
172		
173		this.alternateFactor 			= 1;
174		
175		this.t  						= 0;
176		this.normalizedRemainingTime	= 1;
177		
178		this.normalizedLastStepDelta 	= 0.0f;
179		this.normalizedValue 			= this.normalizedFrom;
180		
181		this.timeTaken 					= 0;
182		
183		this.v0 						= 2 / (1 + this.t2 - this.t1); 
184		
185		this.loopCount 					= this.originalLoopCount;
186		
187		this.velocity 					= 0;
188	}
189	
190	/**
191	 * Does the next interpolation step, taking the timeDelta into account
192	 * <p>
193	 * This implementation makes sure the destination is reached exactly, and sets the value
194	 * to the target value if the next interpolation step would surpass the target value, or if the
195	 * time is up.
196	 * 
197	 * @param deltaTime amount of time to interpolate
198	 * 
199	 * @return true, if interpolate
200	 * 
201	 * <b>false</b> if this call to interpolate() would lead to a higher value than the desired
202	 * target value or the time is up, <b>true</b> if the target isnt reached in this interpolation step
203	 * and there is still remaining time
204	 */
205	public boolean interpolate(float deltaTime) {
206		/*
207		 * Check if the interpolation has finished 
208		 * -> restart again if there are loops left and the target was reached from previous interpolation
209		 * -> do nothing if there are no more loops 
210		*/
211		if (this.isTargetReached() && (this.loopCount == -1 || this.loopCount > 0 )){
212			if(this.debug){
213				System.out.println("Target reached or infinitely looped, or still more loops to go -> resetting the values before interpolating again");
214			}
215			this.resetForNextLoop();
216		}else if (this.isTargetReached() && this.loopCount == 0){
217			return false;
218		}
219		
220		/*
221		 * calculate normalized timeDelta and add it to our current time t,
222		 * calculate the remaining time
223		 */
224		float normalizeDeltaTime = (deltaTime * this.normalizedTfactor);
225		
226		this.t += normalizeDeltaTime; //map timeDelta to 0..1
227		this.normalizedRemainingTime -= normalizeDeltaTime;
228		
229		if(this.debug){
230			System.out.println("Normalized deltatime: " + normalizeDeltaTime);
231			System.out.println("T: " + this.t + " Remaining: " + this.normalizedRemainingTime);
232		}
233		
234		
235		/*
236		 * calucalte velocity
237		 */
238		if (this.t < this.t1){ //anfang, beschleunigung
239//			d = velocity *t*t/(2*t1); 
240			this.velocity = this.v0 * (this.t / this.t1);
241		} 
242		else{ 
243//			d = velocity * (t1/2); 
244			if (this.t < this.t2){ //mitte constante geschwindigkeit  
245//				d += (t-t1)*velocity; 
246				this.velocity = this.v0;
247			} 
248			else{ //letztes stück geschwindigkeit linear abnehmen lassen
249//				d += (t2-t1)*velocity;
250//				d += (t-t*t/2-t2+t2*b/2) * velocity/(1-t2); 
251				this.velocity = this.v0 * (1 - (this.t - this.t2) / (1 - this.t2) );
252				
253				/*
254				if (t2 == 1 || t == 1){ //FIXME added to avoid infinity velocity when t==t2
255					this.velocity = 0;
256				}
257				*/
258			} 
259		} 
260		
261		if (Float.isInfinite(velocity) 
262			|| Float.isNaN(velocity)){
263//			System.out.println("Velocity: " + velocity);
264			this.velocity = 0;
265		}
266		
267		/*
268		 * calculate the normalized step and overall value by multiplying
269		 * the velocity with the normalized deltaTime
270		 */
271		float normalizedTmpStepDelta	= (this.velocity * normalizeDeltaTime);
272		float normalizedTmpValue 		= this.normalizedValue + normalizedTmpStepDelta;
273		
274		if(this.debug){
275			float tmpRealValue = normalizedTmpValue *(this.realTarget - this.realFrom); 
276			System.out.println("Velocity: " + this.velocity);
277			System.out.println("Normalized TMP Value: " + normalizedTmpValue);
278			System.out.println("-> current value: " + tmpRealValue);
279		}
280		
281		this.timeTaken = System.currentTimeMillis() - this.startTime;
282		
283		/*
284		 * Checks if the target would be exceeded by this step, or if the 
285		 * time is up - if so, sets the value to match the target so we 
286		 * always end up at the desired target value 
287		 * -> one loop is complete
288		 */
289		// Checken ob der berechnete wert über den target wert hinausschiesst -> loop beenden
290		if (normalizedTmpValue >= this.normalizedTarget){ 
291			this.normalizedLastStepDelta 	= this.normalizedTarget - this.normalizedValue;
292			this.normalizedValue 			= this.normalizedTarget;
293			
294			/*
295			if (Float.isInfinite(normalizedLastStepDelta)
296					|| Float.isNaN(normalizedLastStepDelta)
297				){
298					System.out.println("Stepdelta malformed! trying to correct..");
299					System.out.println("Velocity: " + velocity);
300					System.out.println();
301					float normStepDelta = (this.normalizedTarget - this.normalizedValue);
302					this.normalizedLastStepDelta = normStepDelta;
303				}
304			*/
305			
306			if (this.loopCount != -1) {
307				this.loopCount--;
308			}
309			
310			if(this.debug) {
311				System.out.println("Interpolation duration: " + this.timeTaken);
312			}
313			return false;
314		}
315		// Checken ob die Zeit, die die animation hatte abgelaufen ist -> loop benden
316		else if (this.normalizedRemainingTime <= 0){
317			this.normalizedLastStepDelta 	= this.normalizedTarget - this.normalizedValue;
318			this.normalizedValue 			= this.normalizedTarget;
319			
320			
321			/*
322			// FIXME
323			// WHAT THE F*CK !?? WARUM KOMMT MANCHMAL NAN ODER INFINITE RAUS 
324			// BEIM RECHNEN VON 1.0 - 1.0 und speichern in normalizedLastStepDelta
325			// BEIM NEUEN BERECHNEN STIMMT DER WERT DANN??
326			if (Float.isInfinite(normalizedLastStepDelta)
327				|| Float.isNaN(normalizedLastStepDelta)
328			){
329				System.out.println("Stepdelta malformed! trying to correct..");
330				System.out.println("Velocity: " + velocity);
331				System.out.println();
332				float normStepDelta = (this.normalizedTarget - this.normalizedValue);
333				this.normalizedLastStepDelta = normStepDelta;
334			}
335			*/
336			
337			if (this.loopCount != -1) {
338				this.loopCount--;
339			}
340			
341			if(this.debug) {
342				System.out.println("Interpolation duration: " + this.timeTaken);
343			}
344			return false;
345		}
346		// Just save the values for the next step -> loop nicht beenden
347		else{ 
348			this.normalizedLastStepDelta = normalizedTmpStepDelta;
349			this.normalizedValue 		 = normalizedTmpValue;
350			
351			/*
352			if (Float.isInfinite(normalizedLastStepDelta)
353					|| Float.isNaN(normalizedLastStepDelta)
354				){
355					System.out.println("Stepdelta malformed! trying to correct..");
356					System.out.println("Velocity: " + velocity);
357					System.out.println();
358					float normStepDelta = (this.normalizedTarget - this.normalizedValue);
359					this.normalizedLastStepDelta = normStepDelta;
360//					this.normalizedLastStepDelta = 0;
361//					this.normalizedLastStepDelta = this.normalizedTarget - this.normalizedValue;
362				}
363				*/
364			
365			return true;
366		}
367	}
368
369	/**
370	 * checks if the interpolation has reached its target
371	 * and there are no more loops left.
372	 * 
373	 * @return true: if the interpolation is finished, then you shouldnt call interpolate() again,
374	 * false: if there are still loops to do, and/or the target hasnt been reached
375	 */
376	public boolean isFinished(){
377		if (this.normalizedTarget != this.normalizedValue || this.loopCount == -1 ) {
378			return false;
379		} else if (this.normalizedTarget == this.normalizedValue && this.loopCount > 0) {
380			return false;
381		} else if (this.normalizedTarget == this.normalizedValue && this.loopCount == 0) {
382			return true;
383		} else {
384			return false;
385		}
386	}
387	
388	/**
389	 * Checks if is target reached.
390	 * 
391	 * @return true, if is target reached
392	 */
393	private boolean isTargetReached(){
394		return this.normalizedValue == this.normalizedTarget;
395	}
396	
397	/**
398	 * Gets the time taken.
399	 * 
400	 * @return the time taken
401	 */
402	public float getTimeTaken() {
403		return this.timeTaken;
404	}
405	
406	
407	/**
408	 * Checks if is alternating.
409	 * 
410	 * @return true, if is alternating
411	 */
412	public boolean isAlternating() {
413		return this.alternating;
414	}
415
416	/**
417	 * Sets the alternating.
418	 * 
419	 * @param alternating the new alternating
420	 */
421	public void setAlternating(boolean alternating) {
422		this.alternating = alternating;
423	}
424
425	/**
426	 * Returns the un-normalized value of the current interpolation.
427	 * 
428	 * @return the current value
429	 */
430	public float getCurrentValue() { //un-normalize the value to get the real value
431		float currentValue = (this.normalizedValue * (this.realTarget - this.realFrom)) + this.realFrom;
432		if (this.isAlternating()) {
433			return this.alternateFactor * currentValue;
434		} else {
435			return currentValue;
436		}
437	}
438	
439	
440	/**
441	 * Returns the unnormalized delta value from the last interpolated value to the current,
442	 * after a interpolationstep
443	 * - useful to determine how much the value increased/decreased.
444	 * 
445	 * @return the current step delta
446	 */
447	public float getCurrentStepDelta() {//un-normalize the step to get the real step
448		float stepDelta = this.normalizedLastStepDelta * (this.realTarget - this.realFrom);
449		
450//		if (Float.isNaN(stepDelta)){
451//			System.out.println("Stepdelta is NAN! :" + stepDelta );
452//		}
453//		if (stepDelta > 50 || stepDelta < 0){
454//			System.out.println(stepDelta);
455//			System.out.println("Stepdelta malformed: " + stepDelta);
456//		}
457		
458		if (this.isAlternating()) {
459			return this.alternateFactor * stepDelta;
460		} else{ 
461			return stepDelta;
462		}
463	}
464	
465	
466}