1 package aurelienribon.tweenengine; 2 3 import aurelienribon.tweenengine.equations.Quad; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 /** 8 * Core class of the Tween Engine. A Tween is basically an interpolation 9 * between two values of an object attribute. However, the main interest of a 10 * Tween is that you can apply an easing formula on this interpolation, in 11 * order to smooth the transitions or to achieve cool effects like springs or 12 * bounces. 13 * <p/> 14 * 15 * The Universal Tween Engine is called "universal" because it is able to apply 16 * interpolations on every attribute from every possible object. Therefore, 17 * every object in your application can be animated with cool effects: it does 18 * not matter if your application is a game, a desktop interface or even a 19 * console program! If it makes sense to animate something, then it can be 20 * animated through this engine. 21 * <p/> 22 * 23 * This class contains many static factory methods to create and instantiate 24 * new interpolations easily. The common way to create a Tween is by using one 25 * of these factories: 26 * <p/> 27 * 28 * - Tween.to(...)<br/> 29 * - Tween.from(...)<br/> 30 * - Tween.set(...)<br/> 31 * - Tween.call(...) 32 * <p/> 33 * 34 * <h2>Example - firing a Tween</h2> 35 * 36 * The following example will move the target horizontal position from its 37 * current value to x=200 and y=300, during 500ms, but only after a delay of 38 * 1000ms. The animation will also be repeated 2 times (the starting position 39 * is registered at the end of the delay, so the animation will automatically 40 * restart from this registered position). 41 * <p/> 42 * 43 * <pre> {@code 44 * Tween.to(myObject, POSITION_XY, 0.5f) 45 * .target(200, 300) 46 * .ease(Quad.INOUT) 47 * .delay(1.0f) 48 * .repeat(2, 0.2f) 49 * .start(myManager); 50 * }</pre> 51 * 52 * Tween life-cycles can be automatically managed for you, thanks to the 53 * {@link TweenManager} class. If you choose to manage your tween when you start 54 * it, then you don't need to care about it anymore. <b>Tweens are 55 * <i>fire-and-forget</i>: don't think about them anymore once you started 56 * them (if they are managed of course).</b> 57 * <p/> 58 * 59 * You need to periodicaly update the tween engine, in order to compute the new 60 * values. If your tweens are managed, only update the manager; else you need 61 * to call {@link #update()} on your tweens periodically. 62 * <p/> 63 * 64 * <h2>Example - setting up the engine</h2> 65 * 66 * The engine cannot directly change your objects attributes, since it doesn't 67 * know them. Therefore, you need to tell him how to get and set the different 68 * attributes of your objects: <b>you need to implement the {@link 69 * TweenAccessor} interface for each object class you will animate</b>. Once 70 * done, don't forget to register these implementations, using the static method 71 * {@link registerAccessor()}, when you start your application. 72 * 73 * @see TweenAccessor 74 * @see TweenManager 75 * @see TweenEquation 76 * @see Timeline 77 * @author Aurelien Ribon | http://www.aurelienribon.com/ 78 */ 79 public final class Tween extends BaseTween<Tween> { 80 // ------------------------------------------------------------------------- 81 // Static -- misc 82 // ------------------------------------------------------------------------- 83 84 /** 85 * Used as parameter in {@link #repeat(int, float)} and 86 * {@link #repeatYoyo(int, float)} methods. 87 */ 88 public static final int INFINITY = -1; 89 90 private static int combinedAttrsLimit = 3; 91 private static int waypointsLimit = 0; 92 93 /** 94 * Changes the limit for combined attributes. Defaults to 3 to reduce 95 * memory footprint. 96 */ 97 public static void setCombinedAttributesLimit(int limit) { 98 Tween.combinedAttrsLimit = limit; 99 } 100 101 /** 102 * Changes the limit of allowed waypoints for each tween. Defaults to 0 to 103 * reduce memory footprint. 104 */ 105 public static void setWaypointsLimit(int limit) { 106 Tween.waypointsLimit = limit; 107 } 108 109 /** 110 * Gets the version number of the library. 111 */ 112 public static String getVersion() { 113 return "6.3.3"; 114 } 115 116 // ------------------------------------------------------------------------- 117 // Static -- pool 118 // ------------------------------------------------------------------------- 119 120 private static final Pool.Callback<Tween> poolCallback = new Pool.Callback<Tween>() { 121 @Override public void onPool(Tween obj) {obj.reset();} 122 @Override public void onUnPool(Tween obj) {obj.reset();} 123 }; 124 125 private static final Pool<Tween> pool = new Pool<Tween>(20, poolCallback) { 126 @Override protected Tween create() {return new Tween();} 127 }; 128 129 /** 130 * Used for debug purpose. Gets the current number of objects that are 131 * waiting in the Tween pool. 132 */ 133 public static int getPoolSize() { 134 return pool.size(); 135 } 136 137 /** 138 * Increases the minimum capacity of the pool. Capacity defaults to 20. 139 */ 140 public static void ensurePoolCapacity(int minCapacity) { 141 pool.ensureCapacity(minCapacity); 142 } 143 144 // ------------------------------------------------------------------------- 145 // Static -- tween accessors 146 // ------------------------------------------------------------------------- 147 148 private static final Map<Class<?>, TweenAccessor<?>> registeredAccessors = new HashMap<Class<?>, TweenAccessor<?>>(); 149 150 /** 151 * Registers an accessor with the class of an object. This accessor will be 152 * used by tweens applied to every objects implementing the registered 153 * class, or inheriting from it. 154 * 155 * @param someClass An object class. 156 * @param defaultAccessor The accessor that will be used to tween any 157 * object of class "someClass". 158 */ 159 public static void registerAccessor(Class<?> someClass, TweenAccessor<?> defaultAccessor) { 160 registeredAccessors.put(someClass, defaultAccessor); 161 } 162 163 /** 164 * Gets the registered TweenAccessor associated with the given object class. 165 * 166 * @param someClass An object class. 167 */ 168 public static TweenAccessor<?> getRegisteredAccessor(Class<?> someClass) { 169 return registeredAccessors.get(someClass); 170 } 171 172 // ------------------------------------------------------------------------- 173 // Static -- factories 174 // ------------------------------------------------------------------------- 175 176 /** 177 * Factory creating a new standard interpolation. This is the most common 178 * type of interpolation. The starting values are retrieved automatically 179 * after the delay (if any). 180 * <br/><br/> 181 * 182 * <b>You need to set the target values of the interpolation by using one 183 * of the target() methods</b>. The interpolation will run from the 184 * starting values to these target values. 185 * <br/><br/> 186 * 187 * The common use of Tweens is "fire-and-forget": you do not need to care 188 * for tweens once you added them to a TweenManager, they will be updated 189 * automatically, and cleaned once finished. Common call: 190 * <br/><br/> 191 * 192 * <pre> {@code 193 * Tween.to(myObject, POSITION, 1.0f) 194 * .target(50, 70) 195 * .ease(Quad.INOUT) 196 * .start(myManager); 197 * }</pre> 198 * 199 * Several options such as delay, repetitions and callbacks can be added to 200 * the tween. 201 * 202 * @param target The target object of the interpolation. 203 * @param tweenType The desired type of interpolation. 204 * @param duration The duration of the interpolation, in milliseconds. 205 * @return The generated Tween. 206 */ 207 public static Tween to(Object target, int tweenType, float duration) { 208 Tween tween = pool.get(); 209 tween.setup(target, tweenType, duration); 210 tween.ease(Quad.INOUT); 211 tween.path(TweenPaths.catmullRom); 212 return tween; 213 } 214 215 /** 216 * Factory creating a new reversed interpolation. The ending values are 217 * retrieved automatically after the delay (if any). 218 * <br/><br/> 219 * 220 * <b>You need to set the starting values of the interpolation by using one 221 * of the target() methods</b>. The interpolation will run from the 222 * starting values to these target values. 223 * <br/><br/> 224 * 225 * The common use of Tweens is "fire-and-forget": you do not need to care 226 * for tweens once you added them to a TweenManager, they will be updated 227 * automatically, and cleaned once finished. Common call: 228 * <br/><br/> 229 * 230 * <pre> {@code 231 * Tween.from(myObject, POSITION, 1.0f) 232 * .target(0, 0) 233 * .ease(Quad.INOUT) 234 * .start(myManager); 235 * }</pre> 236 * 237 * Several options such as delay, repetitions and callbacks can be added to 238 * the tween. 239 * 240 * @param target The target object of the interpolation. 241 * @param tweenType The desired type of interpolation. 242 * @param duration The duration of the interpolation, in milliseconds. 243 * @return The generated Tween. 244 */ 245 public static Tween from(Object target, int tweenType, float duration) { 246 Tween tween = pool.get(); 247 tween.setup(target, tweenType, duration); 248 tween.ease(Quad.INOUT); 249 tween.path(TweenPaths.catmullRom); 250 tween.isFrom = true; 251 return tween; 252 } 253 254 /** 255 * Factory creating a new instantaneous interpolation (thus this is not 256 * really an interpolation). 257 * <br/><br/> 258 * 259 * <b>You need to set the target values of the interpolation by using one 260 * of the target() methods</b>. The interpolation will set the target 261 * attribute to these values after the delay (if any). 262 * <br/><br/> 263 * 264 * The common use of Tweens is "fire-and-forget": you do not need to care 265 * for tweens once you added them to a TweenManager, they will be updated 266 * automatically, and cleaned once finished. Common call: 267 * <br/><br/> 268 * 269 * <pre> {@code 270 * Tween.set(myObject, POSITION) 271 * .target(50, 70) 272 * .delay(1.0f) 273 * .start(myManager); 274 * }</pre> 275 * 276 * Several options such as delay, repetitions and callbacks can be added to 277 * the tween. 278 * 279 * @param target The target object of the interpolation. 280 * @param tweenType The desired type of interpolation. 281 * @return The generated Tween. 282 */ 283 public static Tween set(Object target, int tweenType) { 284 Tween tween = pool.get(); 285 tween.setup(target, tweenType, 0); 286 tween.ease(Quad.INOUT); 287 return tween; 288 } 289 290 /** 291 * Factory creating a new timer. The given callback will be triggered on 292 * each iteration start, after the delay. 293 * <br/><br/> 294 * 295 * The common use of Tweens is "fire-and-forget": you do not need to care 296 * for tweens once you added them to a TweenManager, they will be updated 297 * automatically, and cleaned once finished. Common call: 298 * <br/><br/> 299 * 300 * <pre> {@code 301 * Tween.call(myCallback) 302 * .delay(1.0f) 303 * .repeat(10, 1000) 304 * .start(myManager); 305 * }</pre> 306 * 307 * @param callback The callback that will be triggered on each iteration 308 * start. 309 * @return The generated Tween. 310 * @see TweenCallback 311 */ 312 public static Tween call(TweenCallback callback) { 313 Tween tween = pool.get(); 314 tween.setup(null, -1, 0); 315 tween.setCallback(callback); 316 tween.setCallbackTriggers(TweenCallback.START); 317 return tween; 318 } 319 320 /** 321 * Convenience method to create an empty tween. Such object is only useful 322 * when placed inside animation sequences (see {@link Timeline}), in which 323 * it may act as a beacon, so you can set a callback on it in order to 324 * trigger some action at the right moment. 325 * 326 * @return The generated Tween. 327 * @see Timeline 328 */ 329 public static Tween mark() { 330 Tween tween = pool.get(); 331 tween.setup(null, -1, 0); 332 return tween; 333 } 334 335 // ------------------------------------------------------------------------- 336 // Attributes 337 // ------------------------------------------------------------------------- 338 339 // Main 340 private Object target; 341 private Class<?> targetClass; 342 private TweenAccessor<Object> accessor; 343 private int type; 344 private TweenEquation equation; 345 private TweenPath path; 346 347 // General 348 private boolean isFrom; 349 private boolean isRelative; 350 private int combinedAttrsCnt; 351 private int waypointsCnt; 352 353 // Values 354 private final float[] startValues = new float[combinedAttrsLimit]; 355 private final float[] targetValues = new float[combinedAttrsLimit]; 356 private final float[] waypoints = new float[waypointsLimit * combinedAttrsLimit]; 357 358 // Buffers 359 private float[] accessorBuffer = new float[combinedAttrsLimit]; 360 private float[] pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit]; 361 362 // ------------------------------------------------------------------------- 363 // Setup 364 // ------------------------------------------------------------------------- 365 366 private Tween() { 367 reset(); 368 } 369 370 @Override 371 protected void reset() { 372 super.reset(); 373 374 target = null; 375 targetClass = null; 376 accessor = null; 377 type = -1; 378 equation = null; 379 path = null; 380 381 isFrom = isRelative = false; 382 combinedAttrsCnt = waypointsCnt = 0; 383 384 if (accessorBuffer.length != combinedAttrsLimit) { 385 accessorBuffer = new float[combinedAttrsLimit]; 386 } 387 388 if (pathBuffer.length != (2+waypointsLimit)*combinedAttrsLimit) { 389 pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit]; 390 } 391 } 392 393 private void setup(Object target, int tweenType, float duration) { 394 if (duration < 0) throw new RuntimeException("Duration can't be negative"); 395 396 this.target = target; 397 this.targetClass = target != null ? findTargetClass() : null; 398 this.type = tweenType; 399 this.duration = duration; 400 } 401 402 private Class<?> findTargetClass() { 403 if (registeredAccessors.containsKey(target.getClass())) return target.getClass(); 404 if (target instanceof TweenAccessor) return target.getClass(); 405 406 Class<?> parentClass = target.getClass().getSuperclass(); 407 while (parentClass != null && !registeredAccessors.containsKey(parentClass)) 408 parentClass = parentClass.getSuperclass(); 409 410 return parentClass; 411 } 412 413 // ------------------------------------------------------------------------- 414 // Public API 415 // ------------------------------------------------------------------------- 416 417 /** 418 * Sets the easing equation of the tween. Existing equations are located in 419 * <i>aurelienribon.tweenengine.equations</i> package, but you can of course 420 * implement your owns, see {@link TweenEquation}. You can also use the 421 * {@link TweenEquations} static instances to quickly access all the 422 * equations. Default equation is Quad.INOUT. 423 * <p/> 424 * 425 * <b>Proposed equations are:</b><br/> 426 * - Linear.INOUT,<br/> 427 * - Quad.IN | OUT | INOUT,<br/> 428 * - Cubic.IN | OUT | INOUT,<br/> 429 * - Quart.IN | OUT | INOUT,<br/> 430 * - Quint.IN | OUT | INOUT,<br/> 431 * - Circ.IN | OUT | INOUT,<br/> 432 * - Sine.IN | OUT | INOUT,<br/> 433 * - Expo.IN | OUT | INOUT,<br/> 434 * - Back.IN | OUT | INOUT,<br/> 435 * - Bounce.IN | OUT | INOUT,<br/> 436 * - Elastic.IN | OUT | INOUT 437 * 438 * @return The current tween, for chaining instructions. 439 * @see TweenEquation 440 * @see TweenEquations 441 */ 442 public Tween ease(TweenEquation easeEquation) { 443 this.equation = easeEquation; 444 return this; 445 } 446 447 /** 448 * Forces the tween to use the TweenAccessor registered with the given 449 * target class. Useful if you want to use a specific accessor associated 450 * to an interface, for instance. 451 * 452 * @param targetClass A class registered with an accessor. 453 * @return The current tween, for chaining instructions. 454 */ 455 public Tween cast(Class<?> targetClass) { 456 if (isStarted()) throw new RuntimeException("You can't cast the target of a tween once it is started"); 457 this.targetClass = targetClass; 458 return this; 459 } 460 461 /** 462 * Sets the target value of the interpolation. The interpolation will run 463 * from the <b>value at start time (after the delay, if any)</b> to this 464 * target value. 465 * <p/> 466 * 467 * To sum-up:<br/> 468 * - start value: value at start time, after delay<br/> 469 * - end value: param 470 * 471 * @param targetValue The target value of the interpolation. 472 * @return The current tween, for chaining instructions. 473 */ 474 public Tween target(float targetValue) { 475 targetValues[0] = targetValue; 476 return this; 477 } 478 479 /** 480 * Sets the target values of the interpolation. The interpolation will run 481 * from the <b>values at start time (after the delay, if any)</b> to these 482 * target values. 483 * <p/> 484 * 485 * To sum-up:<br/> 486 * - start values: values at start time, after delay<br/> 487 * - end values: params 488 * 489 * @param targetValue1 The 1st target value of the interpolation. 490 * @param targetValue2 The 2nd target value of the interpolation. 491 * @return The current tween, for chaining instructions. 492 */ 493 public Tween target(float targetValue1, float targetValue2) { 494 targetValues[0] = targetValue1; 495 targetValues[1] = targetValue2; 496 return this; 497 } 498 499 /** 500 * Sets the target values of the interpolation. The interpolation will run 501 * from the <b>values at start time (after the delay, if any)</b> to these 502 * target values. 503 * <p/> 504 * 505 * To sum-up:<br/> 506 * - start values: values at start time, after delay<br/> 507 * - end values: params 508 * 509 * @param targetValue1 The 1st target value of the interpolation. 510 * @param targetValue2 The 2nd target value of the interpolation. 511 * @param targetValue3 The 3rd target value of the interpolation. 512 * @return The current tween, for chaining instructions. 513 */ 514 public Tween target(float targetValue1, float targetValue2, float targetValue3) { 515 targetValues[0] = targetValue1; 516 targetValues[1] = targetValue2; 517 targetValues[2] = targetValue3; 518 return this; 519 } 520 521 /** 522 * Sets the target values of the interpolation. The interpolation will run 523 * from the <b>values at start time (after the delay, if any)</b> to these 524 * target values. 525 * <p/> 526 * 527 * To sum-up:<br/> 528 * - start values: values at start time, after delay<br/> 529 * - end values: params 530 * 531 * @param targetValues The target values of the interpolation. 532 * @return The current tween, for chaining instructions. 533 */ 534 public Tween target(float... targetValues) { 535 if (targetValues.length > combinedAttrsLimit) throwCombinedAttrsLimitReached(); 536 System.arraycopy(targetValues, 0, this.targetValues, 0, targetValues.length); 537 return this; 538 } 539 540 /** 541 * Sets the target value of the interpolation, relatively to the <b>value 542 * at start time (after the delay, if any)</b>. 543 * <p/> 544 * 545 * To sum-up:<br/> 546 * - start value: value at start time, after delay<br/> 547 * - end value: param + value at start time, after delay 548 * 549 * @param targetValue The relative target value of the interpolation. 550 * @return The current tween, for chaining instructions. 551 */ 552 public Tween targetRelative(float targetValue) { 553 isRelative = true; 554 targetValues[0] = isInitialized() ? targetValue + startValues[0] : targetValue; 555 return this; 556 } 557 558 /** 559 * Sets the target values of the interpolation, relatively to the <b>values 560 * at start time (after the delay, if any)</b>. 561 * <p/> 562 * 563 * To sum-up:<br/> 564 * - start values: values at start time, after delay<br/> 565 * - end values: params + values at start time, after delay 566 * 567 * @param targetValue1 The 1st relative target value of the interpolation. 568 * @param targetValue2 The 2nd relative target value of the interpolation. 569 * @return The current tween, for chaining instructions. 570 */ 571 public Tween targetRelative(float targetValue1, float targetValue2) { 572 isRelative = true; 573 targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1; 574 targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2; 575 return this; 576 } 577 578 /** 579 * Sets the target values of the interpolation, relatively to the <b>values 580 * at start time (after the delay, if any)</b>. 581 * <p/> 582 * 583 * To sum-up:<br/> 584 * - start values: values at start time, after delay<br/> 585 * - end values: params + values at start time, after delay 586 * 587 * @param targetValue1 The 1st relative target value of the interpolation. 588 * @param targetValue2 The 2nd relative target value of the interpolation. 589 * @param targetValue3 The 3rd relative target value of the interpolation. 590 * @return The current tween, for chaining instructions. 591 */ 592 public Tween targetRelative(float targetValue1, float targetValue2, float targetValue3) { 593 isRelative = true; 594 targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1; 595 targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2; 596 targetValues[2] = isInitialized() ? targetValue3 + startValues[2] : targetValue3; 597 return this; 598 } 599 600 /** 601 * Sets the target values of the interpolation, relatively to the <b>values 602 * at start time (after the delay, if any)</b>. 603 * <p/> 604 * 605 * To sum-up:<br/> 606 * - start values: values at start time, after delay<br/> 607 * - end values: params + values at start time, after delay 608 * 609 * @param targetValues The relative target values of the interpolation. 610 * @return The current tween, for chaining instructions. 611 */ 612 public Tween targetRelative(float... targetValues) { 613 if (targetValues.length > combinedAttrsLimit) throwCombinedAttrsLimitReached(); 614 for (int i=0; i<targetValues.length; i++) { 615 this.targetValues[i] = isInitialized() ? targetValues[i] + startValues[i] : targetValues[i]; 616 } 617 618 isRelative = true; 619 return this; 620 } 621 622 /** 623 * Adds a waypoint to the path. The default path runs from the start values 624 * to the end values linearly. If you add waypoints, the default path will 625 * use a smooth catmull-rom spline to navigate between the waypoints, but 626 * you can change this behavior by using the {@link #path(TweenPath)} 627 * method. 628 * 629 * @param targetValue The target of this waypoint. 630 * @return The current tween, for chaining instructions. 631 */ 632 public Tween waypoint(float targetValue) { 633 if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); 634 waypoints[waypointsCnt] = targetValue; 635 waypointsCnt += 1; 636 return this; 637 } 638 639 /** 640 * Adds a waypoint to the path. The default path runs from the start values 641 * to the end values linearly. If you add waypoints, the default path will 642 * use a smooth catmull-rom spline to navigate between the waypoints, but 643 * you can change this behavior by using the {@link #path(TweenPath)} 644 * method. 645 * <p/> 646 * Note that if you want waypoints relative to the start values, use one of 647 * the .targetRelative() methods to define your target. 648 * 649 * @param targetValue1 The 1st target of this waypoint. 650 * @param targetValue2 The 2nd target of this waypoint. 651 * @return The current tween, for chaining instructions. 652 */ 653 public Tween waypoint(float targetValue1, float targetValue2) { 654 if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); 655 waypoints[waypointsCnt*2] = targetValue1; 656 waypoints[waypointsCnt*2+1] = targetValue2; 657 waypointsCnt += 1; 658 return this; 659 } 660 661 /** 662 * Adds a waypoint to the path. The default path runs from the start values 663 * to the end values linearly. If you add waypoints, the default path will 664 * use a smooth catmull-rom spline to navigate between the waypoints, but 665 * you can change this behavior by using the {@link #path(TweenPath)} 666 * method. 667 * <p/> 668 * Note that if you want waypoints relative to the start values, use one of 669 * the .targetRelative() methods to define your target. 670 * 671 * @param targetValue1 The 1st target of this waypoint. 672 * @param targetValue2 The 2nd target of this waypoint. 673 * @param targetValue3 The 3rd target of this waypoint. 674 * @return The current tween, for chaining instructions. 675 */ 676 public Tween waypoint(float targetValue1, float targetValue2, float targetValue3) { 677 if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); 678 waypoints[waypointsCnt*3] = targetValue1; 679 waypoints[waypointsCnt*3+1] = targetValue2; 680 waypoints[waypointsCnt*3+2] = targetValue3; 681 waypointsCnt += 1; 682 return this; 683 } 684 685 /** 686 * Adds a waypoint to the path. The default path runs from the start values 687 * to the end values linearly. If you add waypoints, the default path will 688 * use a smooth catmull-rom spline to navigate between the waypoints, but 689 * you can change this behavior by using the {@link #path(TweenPath)} 690 * method. 691 * <p/> 692 * Note that if you want waypoints relative to the start values, use one of 693 * the .targetRelative() methods to define your target. 694 * 695 * @param targetValues The targets of this waypoint. 696 * @return The current tween, for chaining instructions. 697 */ 698 public Tween waypoint(float... targetValues) { 699 if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); 700 System.arraycopy(targetValues, 0, waypoints, waypointsCnt*targetValues.length, targetValues.length); 701 waypointsCnt += 1; 702 return this; 703 } 704 705 /** 706 * Sets the algorithm that will be used to navigate through the waypoints, 707 * from the start values to the end values. Default is a catmull-rom spline, 708 * but you can find other paths in the {@link TweenPaths} class. 709 * 710 * @param path A TweenPath implementation. 711 * @return The current tween, for chaining instructions. 712 * @see TweenPath 713 * @see TweenPaths 714 */ 715 public Tween path(TweenPath path) { 716 this.path = path; 717 return this; 718 } 719 720 // ------------------------------------------------------------------------- 721 // Getters 722 // ------------------------------------------------------------------------- 723 724 /** 725 * Gets the target object. 726 */ 727 public Object getTarget() { 728 return target; 729 } 730 731 /** 732 * Gets the type of the tween. 733 */ 734 public int getType() { 735 return type; 736 } 737 738 /** 739 * Gets the easing equation. 740 */ 741 public TweenEquation getEasing() { 742 return equation; 743 } 744 745 /** 746 * Gets the target values. The returned buffer is as long as the maximum 747 * allowed combined values. Therefore, you're surely not interested in all 748 * its content. Use {@link #getCombinedTweenCount()} to get the number of 749 * interesting slots. 750 */ 751 public float[] getTargetValues() { 752 return targetValues; 753 } 754 755 /** 756 * Gets the number of combined animations. 757 */ 758 public int getCombinedAttributesCount() { 759 return combinedAttrsCnt; 760 } 761 762 /** 763 * Gets the TweenAccessor used with the target. 764 */ 765 public TweenAccessor<?> getAccessor() { 766 return accessor; 767 } 768 769 /** 770 * Gets the class that was used to find the associated TweenAccessor. 771 */ 772 public Class<?> getTargetClass() { 773 return targetClass; 774 } 775 776 // ------------------------------------------------------------------------- 777 // Overrides 778 // ------------------------------------------------------------------------- 779 780 @Override 781 public Tween build() { 782 if (target == null) return this; 783 784 accessor = (TweenAccessor<Object>) registeredAccessors.get(targetClass); 785 if (accessor == null && target instanceof TweenAccessor) accessor = (TweenAccessor<Object>) target; 786 if (accessor != null) combinedAttrsCnt = accessor.getValues(target, type, accessorBuffer); 787 else throw new RuntimeException("No TweenAccessor was found for the target"); 788 789 if (combinedAttrsCnt > combinedAttrsLimit) throwCombinedAttrsLimitReached(); 790 return this; 791 } 792 793 @Override 794 public void free() { 795 pool.free(this); 796 } 797 798 @Override 799 protected void initializeOverride() { 800 if (target == null) return; 801 802 accessor.getValues(target, type, startValues); 803 804 for (int i=0; i<combinedAttrsCnt; i++) { 805 targetValues[i] += isRelative ? startValues[i] : 0; 806 807 for (int ii=0; ii<waypointsCnt; ii++) { 808 waypoints[ii*combinedAttrsCnt+i] += isRelative ? startValues[i] : 0; 809 } 810 811 if (isFrom) { 812 float tmp = startValues[i]; 813 startValues[i] = targetValues[i]; 814 targetValues[i] = tmp; 815 } 816 } 817 } 818 819 @Override 820 protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { 821 if (target == null || equation == null) return; 822 823 // Case iteration end has been reached 824 825 if (!isIterationStep && step > lastStep) { 826 accessor.setValues(target, type, isReverse(lastStep) ? startValues : targetValues); 827 return; 828 } 829 830 if (!isIterationStep && step < lastStep) { 831 accessor.setValues(target, type, isReverse(lastStep) ? targetValues : startValues); 832 return; 833 } 834 835 // Validation 836 837 assert isIterationStep; 838 assert getCurrentTime() >= 0; 839 assert getCurrentTime() <= duration; 840 841 // Case duration equals zero 842 843 if (duration < 0.00000000001f && delta > -0.00000000001f) { 844 accessor.setValues(target, type, isReverse(step) ? targetValues : startValues); 845 return; 846 } 847 848 if (duration < 0.00000000001f && delta < 0.00000000001f) { 849 accessor.setValues(target, type, isReverse(step) ? startValues : targetValues); 850 return; 851 } 852 853 // Normal behavior 854 855 float time = isReverse(step) ? duration - getCurrentTime() : getCurrentTime(); 856 float t = equation.compute(time/duration); 857 858 if (waypointsCnt == 0 || path == null) { 859 for (int i=0; i<combinedAttrsCnt; i++) { 860 accessorBuffer[i] = startValues[i] + t * (targetValues[i] - startValues[i]); 861 } 862 863 } else { 864 for (int i=0; i<combinedAttrsCnt; i++) { 865 pathBuffer[0] = startValues[i]; 866 pathBuffer[1+waypointsCnt] = targetValues[i]; 867 for (int ii=0; ii<waypointsCnt; ii++) { 868 pathBuffer[ii+1] = waypoints[ii*combinedAttrsCnt+i]; 869 } 870 871 accessorBuffer[i] = path.compute(t, pathBuffer, waypointsCnt+2); 872 } 873 } 874 875 accessor.setValues(target, type, accessorBuffer); 876 } 877 878 // ------------------------------------------------------------------------- 879 // BaseTween impl. 880 // ------------------------------------------------------------------------- 881 882 @Override 883 protected void forceStartValues() { 884 if (target == null) return; 885 accessor.setValues(target, type, startValues); 886 } 887 888 @Override 889 protected void forceEndValues() { 890 if (target == null) return; 891 accessor.setValues(target, type, targetValues); 892 } 893 894 @Override 895 protected boolean containsTarget(Object target) { 896 return this.target == target; 897 } 898 899 @Override 900 protected boolean containsTarget(Object target, int tweenType) { 901 return this.target == target && this.type == tweenType; 902 } 903 904 // ------------------------------------------------------------------------- 905 // Helpers 906 // ------------------------------------------------------------------------- 907 908 private void throwCombinedAttrsLimitReached() { 909 String msg = "You cannot combine more than " + combinedAttrsLimit + " " 910 + "attributes in a tween. You can raise this limit with " 911 + "Tween.setCombinedAttributesLimit(), which should be called once " 912 + "in application initialization code."; 913 throw new RuntimeException(msg); 914 } 915 916 private void throwWaypointsLimitReached() { 917 String msg = "You cannot add more than " + waypointsLimit + " " 918 + "waypoints to a tween. You can raise this limit with " 919 + "Tween.setWaypointsLimit(), which should be called once in " 920 + "application initialization code."; 921 throw new RuntimeException(msg); 922 } 923 } 924