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