1package aurelienribon.tweenengine;
2
3/**
4 * BaseTween is the base class of Tween and Timeline. It defines the
5 * iteration engine used to play animations for any number of times, and in
6 * any direction, at any speed.
7 * <p/>
8 *
9 * It is responsible for calling the different callbacks at the right moments,
10 * and for making sure that every callbacks are triggered, even if the update
11 * engine gets a big delta time at once.
12 *
13 * @see Tween
14 * @see Timeline
15 * @author Aurelien Ribon | http://www.aurelienribon.com/
16 */
17public abstract class BaseTween<T> {
18	// General
19	private int step;
20	private int repeatCnt;
21	private boolean isIterationStep;
22	private boolean isYoyo;
23
24	// Timings
25	protected float delay;
26	protected float duration;
27	private float repeatDelay;
28	private float currentTime;
29	private float deltaTime;
30	private boolean isStarted; // true when the object is started
31	private boolean isInitialized; // true after the delay
32	private boolean isFinished; // true when all repetitions are done
33	private boolean isKilled; // true if kill() was called
34	private boolean isPaused; // true if pause() was called
35
36	// Misc
37	private TweenCallback callback;
38	private int callbackTriggers;
39	private Object userData;
40
41	// Package access
42	boolean isAutoRemoveEnabled;
43	boolean isAutoStartEnabled;
44
45	// -------------------------------------------------------------------------
46
47	protected void reset() {
48		step = -2;
49		repeatCnt = 0;
50		isIterationStep = isYoyo = false;
51
52		delay = duration = repeatDelay = currentTime = deltaTime = 0;
53		isStarted = isInitialized = isFinished = isKilled = isPaused = false;
54
55		callback = null;
56		callbackTriggers = TweenCallback.COMPLETE;
57		userData = null;
58
59		isAutoRemoveEnabled = isAutoStartEnabled = true;
60	}
61
62	// -------------------------------------------------------------------------
63	// Public API
64	// -------------------------------------------------------------------------
65
66	/**
67	 * Builds and validates the object. Only needed if you want to finalize a
68	 * tween or timeline without starting it, since a call to ".start()" also
69	 * calls this method.
70	 *
71	 * @return The current object, for chaining instructions.
72	 */
73	public T build() {
74		return (T) this;
75	}
76
77	/**
78	 * Starts or restarts the object unmanaged. You will need to take care of
79	 * its life-cycle. If you want the tween to be managed for you, use a
80	 * {@link TweenManager}.
81	 *
82	 * @return The current object, for chaining instructions.
83	 */
84	public T start() {
85		build();
86		currentTime = 0;
87		isStarted = true;
88		return (T) this;
89	}
90
91	/**
92	 * Convenience method to add an object to a manager. Its life-cycle will be
93	 * handled for you. Relax and enjoy the animation.
94	 *
95	 * @return The current object, for chaining instructions.
96	 */
97	public T start(TweenManager manager) {
98		manager.add(this);
99		return (T) this;
100	}
101
102	/**
103	 * Adds a delay to the tween or timeline.
104	 *
105	 * @param delay A duration.
106	 * @return The current object, for chaining instructions.
107	 */
108	public T delay(float delay) {
109		this.delay += delay;
110		return (T) this;
111	}
112
113	/**
114	 * Kills the tween or timeline. If you are using a TweenManager, this object
115	 * will be removed automatically.
116	 */
117	public void kill() {
118		isKilled = true;
119	}
120
121	/**
122	 * Stops and resets the tween or timeline, and sends it to its pool, for
123+	 * later reuse. Note that if you use a {@link TweenManager}, this method
124+	 * is automatically called once the animation is finished.
125	 */
126	public void free() {
127	}
128
129	/**
130	 * Pauses the tween or timeline. Further update calls won't have any effect.
131	 */
132	public void pause() {
133		isPaused = true;
134	}
135
136	/**
137	 * Resumes the tween or timeline. Has no effect is it was no already paused.
138	 */
139	public void resume() {
140		isPaused = false;
141	}
142
143	/**
144	 * Repeats the tween or timeline for a given number of times.
145	 * @param count The number of repetitions. For infinite repetition,
146	 * use Tween.INFINITY, or a negative number.
147	 *
148	 * @param delay A delay between each iteration.
149	 * @return The current tween or timeline, for chaining instructions.
150	 */
151	public T repeat(int count, float delay) {
152		if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started");
153		repeatCnt = count;
154		repeatDelay = delay >= 0 ? delay : 0;
155		isYoyo = false;
156		return (T) this;
157	}
158
159	/**
160	 * Repeats the tween or timeline for a given number of times.
161	 * Every two iterations, it will be played backwards.
162	 *
163	 * @param count The number of repetitions. For infinite repetition,
164	 * use Tween.INFINITY, or '-1'.
165	 * @param delay A delay before each repetition.
166	 * @return The current tween or timeline, for chaining instructions.
167	 */
168	public T repeatYoyo(int count, float delay) {
169		if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started");
170		repeatCnt = count;
171		repeatDelay = delay >= 0 ? delay : 0;
172		isYoyo = true;
173		return (T) this;
174	}
175
176	/**
177	 * Sets the callback. By default, it will be fired at the completion of the
178	 * tween or timeline (event COMPLETE). If you want to change this behavior
179	 * and add more triggers, use the {@link setCallbackTriggers()} method.
180	 *
181	 * @see TweenCallback
182	 */
183	public T setCallback(TweenCallback callback) {
184		this.callback = callback;
185		return (T) this;
186	}
187
188	/**
189	 * Changes the triggers of the callback. The available triggers, listed as
190	 * members of the {@link TweenCallback} interface, are:
191	 * <p/>
192	 *
193	 * <b>BEGIN</b>: right after the delay (if any)<br/>
194	 * <b>START</b>: at each iteration beginning<br/>
195	 * <b>END</b>: at each iteration ending, before the repeat delay<br/>
196	 * <b>COMPLETE</b>: at last END event<br/>
197	 * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/>
198	 * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/>
199	 * <b>BACK_END</b>: at each backward iteration ending<br/>
200	 * <b>BACK_COMPLETE</b>: at last BACK_END event
201	 * <p/>
202	 *
203	 * <pre> {@code
204	 * forward :      BEGIN                                   COMPLETE
205	 * forward :      START    END      START    END      START    END
206	 * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX]
207	 * backward:      bEND  bSTART      bEND  bSTART      bEND  bSTART
208	 * backward:      bCOMPLETE                                 bBEGIN
209	 * }</pre>
210	 *
211	 * @param flags one or more triggers, separated by the '|' operator.
212	 * @see TweenCallback
213	 */
214	public T setCallbackTriggers(int flags) {
215		this.callbackTriggers = flags;
216		return (T) this;
217	}
218
219	/**
220	 * Attaches an object to this tween or timeline. It can be useful in order
221	 * to retrieve some data from a TweenCallback.
222	 *
223	 * @param data Any kind of object.
224	 * @return The current tween or timeline, for chaining instructions.
225	 */
226	public T setUserData(Object data) {
227		userData = data;
228		return (T) this;
229	}
230
231	// -------------------------------------------------------------------------
232	// Getters
233	// -------------------------------------------------------------------------
234
235	/**
236	 * Gets the delay of the tween or timeline. Nothing will happen before
237	 * this delay.
238	 */
239	public float getDelay() {
240		return delay;
241	}
242
243	/**
244	 * Gets the duration of a single iteration.
245	 */
246	public float getDuration() {
247		return duration;
248	}
249
250	/**
251	 * Gets the number of iterations that will be played.
252	 */
253	public int getRepeatCount() {
254		return repeatCnt;
255	}
256
257	/**
258	 * Gets the delay occuring between two iterations.
259	 */
260	public float getRepeatDelay() {
261		return repeatDelay;
262	}
263
264	/**
265	 * Returns the complete duration, including initial delay and repetitions.
266	 * The formula is as follows:
267	 * <pre>
268	 * fullDuration = delay + duration + (repeatDelay + duration) * repeatCnt
269	 * </pre>
270	 */
271	public float getFullDuration() {
272		if (repeatCnt < 0) return -1;
273		return delay + duration + (repeatDelay + duration) * repeatCnt;
274	}
275
276	/**
277	 * Gets the attached data, or null if none.
278	 */
279	public Object getUserData() {
280		return userData;
281	}
282
283	/**
284	 * Gets the id of the current step. Values are as follows:<br/>
285	 * <ul>
286	 * <li>even numbers mean that an iteration is playing,<br/>
287	 * <li>odd numbers mean that we are between two iterations,<br/>
288	 * <li>-2 means that the initial delay has not ended,<br/>
289	 * <li>-1 means that we are before the first iteration,<br/>
290	 * <li>repeatCount*2 + 1 means that we are after the last iteration
291	 */
292	public int getStep() {
293		return step;
294	}
295
296	/**
297	 * Gets the local time.
298	 */
299	public float getCurrentTime() {
300		return currentTime;
301	}
302
303	/**
304	 * Returns true if the tween or timeline has been started.
305	 */
306	public boolean isStarted() {
307		return isStarted;
308	}
309
310	/**
311	 * Returns true if the tween or timeline has been initialized. Starting
312	 * values for tweens are stored at initialization time. This initialization
313	 * takes place right after the initial delay, if any.
314	 */
315	public boolean isInitialized() {
316		return isInitialized;
317	}
318
319	/**
320	 * Returns true if the tween is finished (i.e. if the tween has reached
321	 * its end or has been killed). If you don't use a TweenManager, you may
322	 * want to call {@link free()} to reuse the object later.
323	 */
324	public boolean isFinished() {
325		return isFinished || isKilled;
326	}
327
328	/**
329	 * Returns true if the iterations are played as yoyo. Yoyo means that
330	 * every two iterations, the animation will be played backwards.
331	 */
332	public boolean isYoyo() {
333		return isYoyo;
334	}
335
336	/**
337	 * Returns true if the tween or timeline is currently paused.
338	 */
339	public boolean isPaused() {
340		return isPaused;
341	}
342
343	// -------------------------------------------------------------------------
344	// Abstract API
345	// -------------------------------------------------------------------------
346
347	protected abstract void forceStartValues();
348	protected abstract void forceEndValues();
349
350	protected abstract boolean containsTarget(Object target);
351	protected abstract boolean containsTarget(Object target, int tweenType);
352
353	// -------------------------------------------------------------------------
354	// Protected API
355	// -------------------------------------------------------------------------
356
357	protected void initializeOverride() {
358	}
359
360	protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) {
361	}
362
363	protected void forceToStart() {
364		currentTime = -delay;
365		step = -1;
366		isIterationStep = false;
367		if (isReverse(0)) forceEndValues();
368		else forceStartValues();
369	}
370
371	protected void forceToEnd(float time) {
372		currentTime = time - getFullDuration();
373		step = repeatCnt*2 + 1;
374		isIterationStep = false;
375		if (isReverse(repeatCnt*2)) forceStartValues();
376		else forceEndValues();
377	}
378
379	protected void callCallback(int type) {
380		if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this);
381	}
382
383	protected boolean isReverse(int step) {
384		return isYoyo && Math.abs(step%4) == 2;
385	}
386
387	protected boolean isValid(int step) {
388		return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0;
389	}
390
391	protected void killTarget(Object target) {
392		if (containsTarget(target)) kill();
393	}
394
395	protected void killTarget(Object target, int tweenType) {
396		if (containsTarget(target, tweenType)) kill();
397	}
398
399	// -------------------------------------------------------------------------
400	// Update engine
401	// -------------------------------------------------------------------------
402
403	/**
404	 * Updates the tween or timeline state. <b>You may want to use a
405	 * TweenManager to update objects for you.</b>
406	 *
407	 * Slow motion, fast motion and backward play can be easily achieved by
408	 * tweaking this delta time. Multiply it by -1 to play the animation
409	 * backward, or by 0.5 to play it twice slower than its normal speed.
410	 *
411	 * @param delta A delta time between now and the last call.
412	 */
413	public void update(float delta) {
414		if (!isStarted || isPaused || isKilled) return;
415
416		deltaTime = delta;
417
418		if (!isInitialized) {
419			initialize();
420		}
421
422		if (isInitialized) {
423			testRelaunch();
424			updateStep();
425			testCompletion();
426		}
427
428		currentTime += deltaTime;
429		deltaTime = 0;
430	}
431
432	private void initialize() {
433		if (currentTime+deltaTime >= delay) {
434			initializeOverride();
435			isInitialized = true;
436			isIterationStep = true;
437			step = 0;
438			deltaTime -= delay-currentTime;
439			currentTime = 0;
440			callCallback(TweenCallback.BEGIN);
441			callCallback(TweenCallback.START);
442		}
443	}
444
445	private void testRelaunch() {
446		if (!isIterationStep && repeatCnt >= 0 && step < 0 && currentTime+deltaTime >= 0) {
447			assert step == -1;
448			isIterationStep = true;
449			step = 0;
450			float delta = 0-currentTime;
451			deltaTime -= delta;
452			currentTime = 0;
453			callCallback(TweenCallback.BEGIN);
454			callCallback(TweenCallback.START);
455			updateOverride(step, step-1, isIterationStep, delta);
456
457		} else if (!isIterationStep && repeatCnt >= 0 && step > repeatCnt*2 && currentTime+deltaTime < 0) {
458			assert step == repeatCnt*2 + 1;
459			isIterationStep = true;
460			step = repeatCnt*2;
461			float delta = 0-currentTime;
462			deltaTime -= delta;
463			currentTime = duration;
464			callCallback(TweenCallback.BACK_BEGIN);
465			callCallback(TweenCallback.BACK_START);
466			updateOverride(step, step+1, isIterationStep, delta);
467		}
468	}
469
470	private void updateStep() {
471		while (isValid(step)) {
472			if (!isIterationStep && currentTime+deltaTime <= 0) {
473				isIterationStep = true;
474				step -= 1;
475
476				float delta = 0-currentTime;
477				deltaTime -= delta;
478				currentTime = duration;
479
480				if (isReverse(step)) forceStartValues(); else forceEndValues();
481				callCallback(TweenCallback.BACK_START);
482				updateOverride(step, step+1, isIterationStep, delta);
483
484			} else if (!isIterationStep && currentTime+deltaTime >= repeatDelay) {
485				isIterationStep = true;
486				step += 1;
487
488				float delta = repeatDelay-currentTime;
489				deltaTime -= delta;
490				currentTime = 0;
491
492				if (isReverse(step)) forceEndValues(); else forceStartValues();
493				callCallback(TweenCallback.START);
494				updateOverride(step, step-1, isIterationStep, delta);
495
496			} else if (isIterationStep && currentTime+deltaTime < 0) {
497				isIterationStep = false;
498				step -= 1;
499
500				float delta = 0-currentTime;
501				deltaTime -= delta;
502				currentTime = 0;
503
504				updateOverride(step, step+1, isIterationStep, delta);
505				callCallback(TweenCallback.BACK_END);
506
507				if (step < 0 && repeatCnt >= 0) callCallback(TweenCallback.BACK_COMPLETE);
508				else currentTime = repeatDelay;
509
510			} else if (isIterationStep && currentTime+deltaTime > duration) {
511				isIterationStep = false;
512				step += 1;
513
514				float delta = duration-currentTime;
515				deltaTime -= delta;
516				currentTime = duration;
517
518				updateOverride(step, step-1, isIterationStep, delta);
519				callCallback(TweenCallback.END);
520
521				if (step > repeatCnt*2 && repeatCnt >= 0) callCallback(TweenCallback.COMPLETE);
522				currentTime = 0;
523
524			} else if (isIterationStep) {
525				float delta = deltaTime;
526				deltaTime -= delta;
527				currentTime += delta;
528				updateOverride(step, step, isIterationStep, delta);
529				break;
530
531			} else {
532				float delta = deltaTime;
533				deltaTime -= delta;
534				currentTime += delta;
535				break;
536			}
537		}
538	}
539
540	private void testCompletion() {
541		isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0);
542	}
543}
544