/src/org/mt4j/util/animation/MultiPurposeInterpolator.java
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}