diff options
Diffstat (limited to 'java/api')
28 files changed, 3101 insertions, 0 deletions
diff --git a/java/api/src/aurelienribon/tweenengine.gwt.xml b/java/api/src/aurelienribon/tweenengine.gwt.xml new file mode 100755 index 0000000..3b4e623 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine.gwt.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" ?> +<module rename-to="aurelienribon.tweenengine"> + <source path="tweenengine" /> +</module>
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/BaseTween.java b/java/api/src/aurelienribon/tweenengine/BaseTween.java new file mode 100755 index 0000000..d8cf0e6 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/BaseTween.java @@ -0,0 +1,543 @@ +package aurelienribon.tweenengine; + +/** + * BaseTween is the base class of Tween and Timeline. It defines the + * iteration engine used to play animations for any number of times, and in + * any direction, at any speed. + * <p/> + * + * It is responsible for calling the different callbacks at the right moments, + * and for making sure that every callbacks are triggered, even if the update + * engine gets a big delta time at once. + * + * @see Tween + * @see Timeline + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class BaseTween<T> { + // General + private int step; + private int repeatCnt; + private boolean isIterationStep; + private boolean isYoyo; + + // Timings + protected float delay; + protected float duration; + private float repeatDelay; + private float currentTime; + private float deltaTime; + private boolean isStarted; // true when the object is started + private boolean isInitialized; // true after the delay + private boolean isFinished; // true when all repetitions are done + private boolean isKilled; // true if kill() was called + private boolean isPaused; // true if pause() was called + + // Misc + private TweenCallback callback; + private int callbackTriggers; + private Object userData; + + // Package access + boolean isAutoRemoveEnabled; + boolean isAutoStartEnabled; + + // ------------------------------------------------------------------------- + + protected void reset() { + step = -2; + repeatCnt = 0; + isIterationStep = isYoyo = false; + + delay = duration = repeatDelay = currentTime = deltaTime = 0; + isStarted = isInitialized = isFinished = isKilled = isPaused = false; + + callback = null; + callbackTriggers = TweenCallback.COMPLETE; + userData = null; + + isAutoRemoveEnabled = isAutoStartEnabled = true; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + /** + * Builds and validates the object. Only needed if you want to finalize a + * tween or timeline without starting it, since a call to ".start()" also + * calls this method. + * + * @return The current object, for chaining instructions. + */ + public T build() { + return (T) this; + } + + /** + * Starts or restarts the object unmanaged. You will need to take care of + * its life-cycle. If you want the tween to be managed for you, use a + * {@link TweenManager}. + * + * @return The current object, for chaining instructions. + */ + public T start() { + build(); + currentTime = 0; + isStarted = true; + return (T) this; + } + + /** + * Convenience method to add an object to a manager. Its life-cycle will be + * handled for you. Relax and enjoy the animation. + * + * @return The current object, for chaining instructions. + */ + public T start(TweenManager manager) { + manager.add(this); + return (T) this; + } + + /** + * Adds a delay to the tween or timeline. + * + * @param delay A duration. + * @return The current object, for chaining instructions. + */ + public T delay(float delay) { + this.delay += delay; + return (T) this; + } + + /** + * Kills the tween or timeline. If you are using a TweenManager, this object + * will be removed automatically. + */ + public void kill() { + isKilled = true; + } + + /** + * Stops and resets the tween or timeline, and sends it to its pool, for ++ * later reuse. Note that if you use a {@link TweenManager}, this method ++ * is automatically called once the animation is finished. + */ + public void free() { + } + + /** + * Pauses the tween or timeline. Further update calls won't have any effect. + */ + public void pause() { + isPaused = true; + } + + /** + * Resumes the tween or timeline. Has no effect is it was no already paused. + */ + public void resume() { + isPaused = false; + } + + /** + * Repeats the tween or timeline for a given number of times. + * @param count The number of repetitions. For infinite repetition, + * use Tween.INFINITY, or a negative number. + * + * @param delay A delay between each iteration. + * @return The current tween or timeline, for chaining instructions. + */ + public T repeat(int count, float delay) { + if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); + repeatCnt = count; + repeatDelay = delay >= 0 ? delay : 0; + isYoyo = false; + return (T) this; + } + + /** + * Repeats the tween or timeline for a given number of times. + * Every two iterations, it will be played backwards. + * + * @param count The number of repetitions. For infinite repetition, + * use Tween.INFINITY, or '-1'. + * @param delay A delay before each repetition. + * @return The current tween or timeline, for chaining instructions. + */ + public T repeatYoyo(int count, float delay) { + if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); + repeatCnt = count; + repeatDelay = delay >= 0 ? delay : 0; + isYoyo = true; + return (T) this; + } + + /** + * Sets the callback. By default, it will be fired at the completion of the + * tween or timeline (event COMPLETE). If you want to change this behavior + * and add more triggers, use the {@link setCallbackTriggers()} method. + * + * @see TweenCallback + */ + public T setCallback(TweenCallback callback) { + this.callback = callback; + return (T) this; + } + + /** + * Changes the triggers of the callback. The available triggers, listed as + * members of the {@link TweenCallback} interface, are: + * <p/> + * + * <b>BEGIN</b>: right after the delay (if any)<br/> + * <b>START</b>: at each iteration beginning<br/> + * <b>END</b>: at each iteration ending, before the repeat delay<br/> + * <b>COMPLETE</b>: at last END event<br/> + * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/> + * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/> + * <b>BACK_END</b>: at each backward iteration ending<br/> + * <b>BACK_COMPLETE</b>: at last BACK_END event + * <p/> + * + * <pre> {@code + * forward : BEGIN COMPLETE + * forward : START END START END START END + * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX] + * backward: bEND bSTART bEND bSTART bEND bSTART + * backward: bCOMPLETE bBEGIN + * }</pre> + * + * @param flags one or more triggers, separated by the '|' operator. + * @see TweenCallback + */ + public T setCallbackTriggers(int flags) { + this.callbackTriggers = flags; + return (T) this; + } + + /** + * Attaches an object to this tween or timeline. It can be useful in order + * to retrieve some data from a TweenCallback. + * + * @param data Any kind of object. + * @return The current tween or timeline, for chaining instructions. + */ + public T setUserData(Object data) { + userData = data; + return (T) this; + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + /** + * Gets the delay of the tween or timeline. Nothing will happen before + * this delay. + */ + public float getDelay() { + return delay; + } + + /** + * Gets the duration of a single iteration. + */ + public float getDuration() { + return duration; + } + + /** + * Gets the number of iterations that will be played. + */ + public int getRepeatCount() { + return repeatCnt; + } + + /** + * Gets the delay occuring between two iterations. + */ + public float getRepeatDelay() { + return repeatDelay; + } + + /** + * Returns the complete duration, including initial delay and repetitions. + * The formula is as follows: + * <pre> + * fullDuration = delay + duration + (repeatDelay + duration) * repeatCnt + * </pre> + */ + public float getFullDuration() { + if (repeatCnt < 0) return -1; + return delay + duration + (repeatDelay + duration) * repeatCnt; + } + + /** + * Gets the attached data, or null if none. + */ + public Object getUserData() { + return userData; + } + + /** + * Gets the id of the current step. Values are as follows:<br/> + * <ul> + * <li>even numbers mean that an iteration is playing,<br/> + * <li>odd numbers mean that we are between two iterations,<br/> + * <li>-2 means that the initial delay has not ended,<br/> + * <li>-1 means that we are before the first iteration,<br/> + * <li>repeatCount*2 + 1 means that we are after the last iteration + */ + public int getStep() { + return step; + } + + /** + * Gets the local time. + */ + public float getCurrentTime() { + return currentTime; + } + + /** + * Returns true if the tween or timeline has been started. + */ + public boolean isStarted() { + return isStarted; + } + + /** + * Returns true if the tween or timeline has been initialized. Starting + * values for tweens are stored at initialization time. This initialization + * takes place right after the initial delay, if any. + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * Returns true if the tween is finished (i.e. if the tween has reached + * its end or has been killed). If you don't use a TweenManager, you may + * want to call {@link free()} to reuse the object later. + */ + public boolean isFinished() { + return isFinished || isKilled; + } + + /** + * Returns true if the iterations are played as yoyo. Yoyo means that + * every two iterations, the animation will be played backwards. + */ + public boolean isYoyo() { + return isYoyo; + } + + /** + * Returns true if the tween or timeline is currently paused. + */ + public boolean isPaused() { + return isPaused; + } + + // ------------------------------------------------------------------------- + // Abstract API + // ------------------------------------------------------------------------- + + protected abstract void forceStartValues(); + protected abstract void forceEndValues(); + + protected abstract boolean containsTarget(Object target); + protected abstract boolean containsTarget(Object target, int tweenType); + + // ------------------------------------------------------------------------- + // Protected API + // ------------------------------------------------------------------------- + + protected void initializeOverride() { + } + + protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { + } + + protected void forceToStart() { + currentTime = -delay; + step = -1; + isIterationStep = false; + if (isReverse(0)) forceEndValues(); + else forceStartValues(); + } + + protected void forceToEnd(float time) { + currentTime = time - getFullDuration(); + step = repeatCnt*2 + 1; + isIterationStep = false; + if (isReverse(repeatCnt*2)) forceStartValues(); + else forceEndValues(); + } + + protected void callCallback(int type) { + if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this); + } + + protected boolean isReverse(int step) { + return isYoyo && Math.abs(step%4) == 2; + } + + protected boolean isValid(int step) { + return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0; + } + + protected void killTarget(Object target) { + if (containsTarget(target)) kill(); + } + + protected void killTarget(Object target, int tweenType) { + if (containsTarget(target, tweenType)) kill(); + } + + // ------------------------------------------------------------------------- + // Update engine + // ------------------------------------------------------------------------- + + /** + * Updates the tween or timeline state. <b>You may want to use a + * TweenManager to update objects for you.</b> + * + * Slow motion, fast motion and backward play can be easily achieved by + * tweaking this delta time. Multiply it by -1 to play the animation + * backward, or by 0.5 to play it twice slower than its normal speed. + * + * @param delta A delta time between now and the last call. + */ + public void update(float delta) { + if (!isStarted || isPaused || isKilled) return; + + deltaTime = delta; + + if (!isInitialized) { + initialize(); + } + + if (isInitialized) { + testRelaunch(); + updateStep(); + testCompletion(); + } + + currentTime += deltaTime; + deltaTime = 0; + } + + private void initialize() { + if (currentTime+deltaTime >= delay) { + initializeOverride(); + isInitialized = true; + isIterationStep = true; + step = 0; + deltaTime -= delay-currentTime; + currentTime = 0; + callCallback(TweenCallback.BEGIN); + callCallback(TweenCallback.START); + } + } + + private void testRelaunch() { + if (!isIterationStep && repeatCnt >= 0 && step < 0 && currentTime+deltaTime >= 0) { + assert step == -1; + isIterationStep = true; + step = 0; + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = 0; + callCallback(TweenCallback.BEGIN); + callCallback(TweenCallback.START); + updateOverride(step, step-1, isIterationStep, delta); + + } else if (!isIterationStep && repeatCnt >= 0 && step > repeatCnt*2 && currentTime+deltaTime < 0) { + assert step == repeatCnt*2 + 1; + isIterationStep = true; + step = repeatCnt*2; + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = duration; + callCallback(TweenCallback.BACK_BEGIN); + callCallback(TweenCallback.BACK_START); + updateOverride(step, step+1, isIterationStep, delta); + } + } + + private void updateStep() { + while (isValid(step)) { + if (!isIterationStep && currentTime+deltaTime <= 0) { + isIterationStep = true; + step -= 1; + + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = duration; + + if (isReverse(step)) forceStartValues(); else forceEndValues(); + callCallback(TweenCallback.BACK_START); + updateOverride(step, step+1, isIterationStep, delta); + + } else if (!isIterationStep && currentTime+deltaTime >= repeatDelay) { + isIterationStep = true; + step += 1; + + float delta = repeatDelay-currentTime; + deltaTime -= delta; + currentTime = 0; + + if (isReverse(step)) forceEndValues(); else forceStartValues(); + callCallback(TweenCallback.START); + updateOverride(step, step-1, isIterationStep, delta); + + } else if (isIterationStep && currentTime+deltaTime < 0) { + isIterationStep = false; + step -= 1; + + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = 0; + + updateOverride(step, step+1, isIterationStep, delta); + callCallback(TweenCallback.BACK_END); + + if (step < 0 && repeatCnt >= 0) callCallback(TweenCallback.BACK_COMPLETE); + else currentTime = repeatDelay; + + } else if (isIterationStep && currentTime+deltaTime > duration) { + isIterationStep = false; + step += 1; + + float delta = duration-currentTime; + deltaTime -= delta; + currentTime = duration; + + updateOverride(step, step-1, isIterationStep, delta); + callCallback(TweenCallback.END); + + if (step > repeatCnt*2 && repeatCnt >= 0) callCallback(TweenCallback.COMPLETE); + currentTime = 0; + + } else if (isIterationStep) { + float delta = deltaTime; + deltaTime -= delta; + currentTime += delta; + updateOverride(step, step, isIterationStep, delta); + break; + + } else { + float delta = deltaTime; + deltaTime -= delta; + currentTime += delta; + break; + } + } + } + + private void testCompletion() { + isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0); + } +} diff --git a/java/api/src/aurelienribon/tweenengine/Pool.java b/java/api/src/aurelienribon/tweenengine/Pool.java new file mode 100755 index 0000000..0b7b2fc --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/Pool.java @@ -0,0 +1,55 @@ +package aurelienribon.tweenengine; + +import java.util.ArrayList; + +/** + * A light pool of objects that can be resused to avoid allocation. + * Based on Nathan Sweet pool implementation + */ +abstract class Pool<T> { + private final ArrayList<T> objects; + private final Callback<T> callback; + + protected abstract T create(); + + public Pool(int initCapacity, Callback<T> callback) { + this.objects = new ArrayList<T>(initCapacity); + this.callback = callback; + } + + public T get() { + T obj = null; + try { + obj = objects.isEmpty() ? create() : objects.remove(0); + } catch (Exception e) {} + if (obj == null) obj = create(); + if (callback != null) callback.onUnPool(obj); + return obj; + } + + public void free(T obj) { + if (obj == null) return; + + if (!objects.contains(obj)) { + if (callback != null) callback.onPool(obj); + objects.add(obj); + } + } + + public void clear() { + objects.clear(); + } + + public int size() { + return objects.size(); + } + + public void ensureCapacity(int minCapacity) { + objects.ensureCapacity(minCapacity); + } + + public interface Callback<T> { + public void onPool(T obj); + public void onUnPool(T obj); + } +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/Timeline.java b/java/api/src/aurelienribon/tweenengine/Timeline.java new file mode 100755 index 0000000..5e1f765 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/Timeline.java @@ -0,0 +1,363 @@ +package aurelienribon.tweenengine; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A Timeline can be used to create complex animations made of sequences and + * parallel sets of Tweens. + * <p/> + * + * The following example will create an animation sequence composed of 5 parts: + * <p/> + * + * 1. First, opacity and scale are set to 0 (with Tween.set() calls).<br/> + * 2. Then, opacity and scale are animated in parallel.<br/> + * 3. Then, the animation is paused for 1s.<br/> + * 4. Then, position is animated to x=100.<br/> + * 5. Then, rotation is animated to 360°. + * <p/> + * + * This animation will be repeated 5 times, with a 500ms delay between each + * iteration: + * <br/><br/> + * + * <pre> {@code + * Timeline.createSequence() + * .push(Tween.set(myObject, OPACITY).target(0)) + * .push(Tween.set(myObject, SCALE).target(0, 0)) + * .beginParallel() + * .push(Tween.to(myObject, OPACITY, 0.5f).target(1).ease(Quad.INOUT)) + * .push(Tween.to(myObject, SCALE, 0.5f).target(1, 1).ease(Quad.INOUT)) + * .end() + * .pushPause(1.0f) + * .push(Tween.to(myObject, POSITION_X, 0.5f).target(100).ease(Quad.INOUT)) + * .push(Tween.to(myObject, ROTATION, 0.5f).target(360).ease(Quad.INOUT)) + * .repeat(5, 0.5f) + * .start(myManager); + * }</pre> + * + * @see Tween + * @see TweenManager + * @see TweenCallback + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public final class Timeline extends BaseTween<Timeline> { + // ------------------------------------------------------------------------- + // Static -- pool + // ------------------------------------------------------------------------- + + private static final Pool.Callback<Timeline> poolCallback = new Pool.Callback<Timeline>() { + @Override public void onPool(Timeline obj) {obj.reset();} + @Override public void onUnPool(Timeline obj) {obj.reset();} + }; + + static final Pool<Timeline> pool = new Pool<Timeline>(10, poolCallback) { + @Override protected Timeline create() {return new Timeline();} + }; + + /** + * Used for debug purpose. Gets the current number of empty timelines that + * are waiting in the Timeline pool. + */ + public static int getPoolSize() { + return pool.size(); + } + + /** + * Increases the minimum capacity of the pool. Capacity defaults to 10. + */ + public static void ensurePoolCapacity(int minCapacity) { + pool.ensureCapacity(minCapacity); + } + + // ------------------------------------------------------------------------- + // Static -- factories + // ------------------------------------------------------------------------- + + /** + * Creates a new timeline with a 'sequence' behavior. Its children will + * be delayed so that they are triggered one after the other. + */ + public static Timeline createSequence() { + Timeline tl = pool.get(); + tl.setup(Modes.SEQUENCE); + return tl; + } + + /** + * Creates a new timeline with a 'parallel' behavior. Its children will be + * triggered all at once. + */ + public static Timeline createParallel() { + Timeline tl = pool.get(); + tl.setup(Modes.PARALLEL); + return tl; + } + + // ------------------------------------------------------------------------- + // Attributes + // ------------------------------------------------------------------------- + + private enum Modes {SEQUENCE, PARALLEL} + + private final List<BaseTween<?>> children = new ArrayList<BaseTween<?>>(10); + private Timeline current; + private Timeline parent; + private Modes mode; + private boolean isBuilt; + + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + private Timeline() { + reset(); + } + + @Override + protected void reset() { + super.reset(); + + children.clear(); + current = parent = null; + + isBuilt = false; + } + + private void setup(Modes mode) { + this.mode = mode; + this.current = this; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + /** + * Adds a Tween to the current timeline. + * + * @return The current timeline, for chaining instructions. + */ + public Timeline push(Tween tween) { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + current.children.add(tween); + return this; + } + + /** + * Nests a Timeline in the current one. + * + * @return The current timeline, for chaining instructions. + */ + public Timeline push(Timeline timeline) { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + if (timeline.current != timeline) throw new RuntimeException("You forgot to call a few 'end()' statements in your pushed timeline"); + timeline.parent = current; + current.children.add(timeline); + return this; + } + + /** + * Adds a pause to the timeline. The pause may be negative if you want to + * overlap the preceding and following children. + * + * @param time A positive or negative duration. + * @return The current timeline, for chaining instructions. + */ + public Timeline pushPause(float time) { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + current.children.add(Tween.mark().delay(time)); + return this; + } + + /** + * Starts a nested timeline with a 'sequence' behavior. Don't forget to + * call {@link end()} to close this nested timeline. + * + * @return The current timeline, for chaining instructions. + */ + public Timeline beginSequence() { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + Timeline tl = pool.get(); + tl.parent = current; + tl.mode = Modes.SEQUENCE; + current.children.add(tl); + current = tl; + return this; + } + + /** + * Starts a nested timeline with a 'parallel' behavior. Don't forget to + * call {@link end()} to close this nested timeline. + * + * @return The current timeline, for chaining instructions. + */ + public Timeline beginParallel() { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + Timeline tl = pool.get(); + tl.parent = current; + tl.mode = Modes.PARALLEL; + current.children.add(tl); + current = tl; + return this; + } + + /** + * Closes the last nested timeline. + * + * @return The current timeline, for chaining instructions. + */ + public Timeline end() { + if (isBuilt) throw new RuntimeException("You can't push anything to a timeline once it is started"); + if (current == this) throw new RuntimeException("Nothing to end..."); + current = current.parent; + return this; + } + + /** + * Gets a list of the timeline children. If the timeline is started, the + * list will be immutable. + */ + public List<BaseTween<?>> getChildren() { + if (isBuilt) return Collections.unmodifiableList(current.children); + else return current.children; + } + + // ------------------------------------------------------------------------- + // Overrides + // ------------------------------------------------------------------------- + + @Override + public Timeline build() { + if (isBuilt) return this; + + duration = 0; + + for (int i=0; i<children.size(); i++) { + BaseTween<?> obj = children.get(i); + + if (obj.getRepeatCount() < 0) throw new RuntimeException("You can't push an object with infinite repetitions in a timeline"); + obj.build(); + + switch (mode) { + case SEQUENCE: + float tDelay = duration; + duration += obj.getFullDuration(); + obj.delay += tDelay; + break; + + case PARALLEL: + duration = Math.max(duration, obj.getFullDuration()); + break; + } + } + + isBuilt = true; + return this; + } + + @Override + public Timeline start() { + super.start(); + + for (int i=0; i<children.size(); i++) { + BaseTween<?> obj = children.get(i); + obj.start(); + } + + return this; + } + + @Override + public void free() { + for (int i=children.size()-1; i>=0; i--) { + BaseTween<?> obj = children.remove(i); + obj.free(); + } + + pool.free(this); + } + + @Override + protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { + if (!isIterationStep && step > lastStep) { + assert delta >= 0; + float dt = isReverse(lastStep) ? -delta-1 : delta+1; + for (int i=0, n=children.size(); i<n; i++) children.get(i).update(dt); + return; + } + + if (!isIterationStep && step < lastStep) { + assert delta <= 0; + float dt = isReverse(lastStep) ? -delta-1 : delta+1; + for (int i=children.size()-1; i>=0; i--) children.get(i).update(dt); + return; + } + + assert isIterationStep; + + if (step > lastStep) { + if (isReverse(step)) { + forceEndValues(); + for (int i=0, n=children.size(); i<n; i++) children.get(i).update(delta); + } else { + forceStartValues(); + for (int i=0, n=children.size(); i<n; i++) children.get(i).update(delta); + } + + } else if (step < lastStep) { + if (isReverse(step)) { + forceStartValues(); + for (int i=children.size()-1; i>=0; i--) children.get(i).update(delta); + } else { + forceEndValues(); + for (int i=children.size()-1; i>=0; i--) children.get(i).update(delta); + } + + } else { + float dt = isReverse(step) ? -delta : delta; + if (delta >= 0) for (int i=0, n=children.size(); i<n; i++) children.get(i).update(dt); + else for (int i=children.size()-1; i>=0; i--) children.get(i).update(dt); + } + } + + // ------------------------------------------------------------------------- + // BaseTween impl. + // ------------------------------------------------------------------------- + + @Override + protected void forceStartValues() { + for (int i=children.size()-1; i>=0; i--) { + BaseTween<?> obj = children.get(i); + obj.forceToStart(); + } + } + + @Override + protected void forceEndValues() { + for (int i=0, n=children.size(); i<n; i++) { + BaseTween<?> obj = children.get(i); + obj.forceToEnd(duration); + } + } + + @Override + protected boolean containsTarget(Object target) { + for (int i=0, n=children.size(); i<n; i++) { + BaseTween<?> obj = children.get(i); + if (obj.containsTarget(target)) return true; + } + return false; + } + + @Override + protected boolean containsTarget(Object target, int tweenType) { + for (int i=0, n=children.size(); i<n; i++) { + BaseTween<?> obj = children.get(i); + if (obj.containsTarget(target, tweenType)) return true; + } + return false; + } +} diff --git a/java/api/src/aurelienribon/tweenengine/Tween.java b/java/api/src/aurelienribon/tweenengine/Tween.java new file mode 100755 index 0000000..911182e --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/Tween.java @@ -0,0 +1,923 @@ +package aurelienribon.tweenengine; + +import aurelienribon.tweenengine.equations.Quad; +import java.util.HashMap; +import java.util.Map; + +/** + * Core class of the Tween Engine. A Tween is basically an interpolation + * between two values of an object attribute. However, the main interest of a + * Tween is that you can apply an easing formula on this interpolation, in + * order to smooth the transitions or to achieve cool effects like springs or + * bounces. + * <p/> + * + * The Universal Tween Engine is called "universal" because it is able to apply + * interpolations on every attribute from every possible object. Therefore, + * every object in your application can be animated with cool effects: it does + * not matter if your application is a game, a desktop interface or even a + * console program! If it makes sense to animate something, then it can be + * animated through this engine. + * <p/> + * + * This class contains many static factory methods to create and instantiate + * new interpolations easily. The common way to create a Tween is by using one + * of these factories: + * <p/> + * + * - Tween.to(...)<br/> + * - Tween.from(...)<br/> + * - Tween.set(...)<br/> + * - Tween.call(...) + * <p/> + * + * <h2>Example - firing a Tween</h2> + * + * The following example will move the target horizontal position from its + * current value to x=200 and y=300, during 500ms, but only after a delay of + * 1000ms. The animation will also be repeated 2 times (the starting position + * is registered at the end of the delay, so the animation will automatically + * restart from this registered position). + * <p/> + * + * <pre> {@code + * Tween.to(myObject, POSITION_XY, 0.5f) + * .target(200, 300) + * .ease(Quad.INOUT) + * .delay(1.0f) + * .repeat(2, 0.2f) + * .start(myManager); + * }</pre> + * + * Tween life-cycles can be automatically managed for you, thanks to the + * {@link TweenManager} class. If you choose to manage your tween when you start + * it, then you don't need to care about it anymore. <b>Tweens are + * <i>fire-and-forget</i>: don't think about them anymore once you started + * them (if they are managed of course).</b> + * <p/> + * + * You need to periodicaly update the tween engine, in order to compute the new + * values. If your tweens are managed, only update the manager; else you need + * to call {@link #update()} on your tweens periodically. + * <p/> + * + * <h2>Example - setting up the engine</h2> + * + * The engine cannot directly change your objects attributes, since it doesn't + * know them. Therefore, you need to tell him how to get and set the different + * attributes of your objects: <b>you need to implement the {@link + * TweenAccessor} interface for each object class you will animate</b>. Once + * done, don't forget to register these implementations, using the static method + * {@link registerAccessor()}, when you start your application. + * + * @see TweenAccessor + * @see TweenManager + * @see TweenEquation + * @see Timeline + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public final class Tween extends BaseTween<Tween> { + // ------------------------------------------------------------------------- + // Static -- misc + // ------------------------------------------------------------------------- + + /** + * Used as parameter in {@link #repeat(int, float)} and + * {@link #repeatYoyo(int, float)} methods. + */ + public static final int INFINITY = -1; + + private static int combinedAttrsLimit = 3; + private static int waypointsLimit = 0; + + /** + * Changes the limit for combined attributes. Defaults to 3 to reduce + * memory footprint. + */ + public static void setCombinedAttributesLimit(int limit) { + Tween.combinedAttrsLimit = limit; + } + + /** + * Changes the limit of allowed waypoints for each tween. Defaults to 0 to + * reduce memory footprint. + */ + public static void setWaypointsLimit(int limit) { + Tween.waypointsLimit = limit; + } + + /** + * Gets the version number of the library. + */ + public static String getVersion() { + return "6.3.3"; + } + + // ------------------------------------------------------------------------- + // Static -- pool + // ------------------------------------------------------------------------- + + private static final Pool.Callback<Tween> poolCallback = new Pool.Callback<Tween>() { + @Override public void onPool(Tween obj) {obj.reset();} + @Override public void onUnPool(Tween obj) {obj.reset();} + }; + + private static final Pool<Tween> pool = new Pool<Tween>(20, poolCallback) { + @Override protected Tween create() {return new Tween();} + }; + + /** + * Used for debug purpose. Gets the current number of objects that are + * waiting in the Tween pool. + */ + public static int getPoolSize() { + return pool.size(); + } + + /** + * Increases the minimum capacity of the pool. Capacity defaults to 20. + */ + public static void ensurePoolCapacity(int minCapacity) { + pool.ensureCapacity(minCapacity); + } + + // ------------------------------------------------------------------------- + // Static -- tween accessors + // ------------------------------------------------------------------------- + + private static final Map<Class<?>, TweenAccessor<?>> registeredAccessors = new HashMap<Class<?>, TweenAccessor<?>>(); + + /** + * Registers an accessor with the class of an object. This accessor will be + * used by tweens applied to every objects implementing the registered + * class, or inheriting from it. + * + * @param someClass An object class. + * @param defaultAccessor The accessor that will be used to tween any + * object of class "someClass". + */ + public static void registerAccessor(Class<?> someClass, TweenAccessor<?> defaultAccessor) { + registeredAccessors.put(someClass, defaultAccessor); + } + + /** + * Gets the registered TweenAccessor associated with the given object class. + * + * @param someClass An object class. + */ + public static TweenAccessor<?> getRegisteredAccessor(Class<?> someClass) { + return registeredAccessors.get(someClass); + } + + // ------------------------------------------------------------------------- + // Static -- factories + // ------------------------------------------------------------------------- + + /** + * Factory creating a new standard interpolation. This is the most common + * type of interpolation. The starting values are retrieved automatically + * after the delay (if any). + * <br/><br/> + * + * <b>You need to set the target values of the interpolation by using one + * of the target() methods</b>. The interpolation will run from the + * starting values to these target values. + * <br/><br/> + * + * The common use of Tweens is "fire-and-forget": you do not need to care + * for tweens once you added them to a TweenManager, they will be updated + * automatically, and cleaned once finished. Common call: + * <br/><br/> + * + * <pre> {@code + * Tween.to(myObject, POSITION, 1.0f) + * .target(50, 70) + * .ease(Quad.INOUT) + * .start(myManager); + * }</pre> + * + * Several options such as delay, repetitions and callbacks can be added to + * the tween. + * + * @param target The target object of the interpolation. + * @param tweenType The desired type of interpolation. + * @param duration The duration of the interpolation, in milliseconds. + * @return The generated Tween. + */ + public static Tween to(Object target, int tweenType, float duration) { + Tween tween = pool.get(); + tween.setup(target, tweenType, duration); + tween.ease(Quad.INOUT); + tween.path(TweenPaths.catmullRom); + return tween; + } + + /** + * Factory creating a new reversed interpolation. The ending values are + * retrieved automatically after the delay (if any). + * <br/><br/> + * + * <b>You need to set the starting values of the interpolation by using one + * of the target() methods</b>. The interpolation will run from the + * starting values to these target values. + * <br/><br/> + * + * The common use of Tweens is "fire-and-forget": you do not need to care + * for tweens once you added them to a TweenManager, they will be updated + * automatically, and cleaned once finished. Common call: + * <br/><br/> + * + * <pre> {@code + * Tween.from(myObject, POSITION, 1.0f) + * .target(0, 0) + * .ease(Quad.INOUT) + * .start(myManager); + * }</pre> + * + * Several options such as delay, repetitions and callbacks can be added to + * the tween. + * + * @param target The target object of the interpolation. + * @param tweenType The desired type of interpolation. + * @param duration The duration of the interpolation, in milliseconds. + * @return The generated Tween. + */ + public static Tween from(Object target, int tweenType, float duration) { + Tween tween = pool.get(); + tween.setup(target, tweenType, duration); + tween.ease(Quad.INOUT); + tween.path(TweenPaths.catmullRom); + tween.isFrom = true; + return tween; + } + + /** + * Factory creating a new instantaneous interpolation (thus this is not + * really an interpolation). + * <br/><br/> + * + * <b>You need to set the target values of the interpolation by using one + * of the target() methods</b>. The interpolation will set the target + * attribute to these values after the delay (if any). + * <br/><br/> + * + * The common use of Tweens is "fire-and-forget": you do not need to care + * for tweens once you added them to a TweenManager, they will be updated + * automatically, and cleaned once finished. Common call: + * <br/><br/> + * + * <pre> {@code + * Tween.set(myObject, POSITION) + * .target(50, 70) + * .delay(1.0f) + * .start(myManager); + * }</pre> + * + * Several options such as delay, repetitions and callbacks can be added to + * the tween. + * + * @param target The target object of the interpolation. + * @param tweenType The desired type of interpolation. + * @return The generated Tween. + */ + public static Tween set(Object target, int tweenType) { + Tween tween = pool.get(); + tween.setup(target, tweenType, 0); + tween.ease(Quad.INOUT); + return tween; + } + + /** + * Factory creating a new timer. The given callback will be triggered on + * each iteration start, after the delay. + * <br/><br/> + * + * The common use of Tweens is "fire-and-forget": you do not need to care + * for tweens once you added them to a TweenManager, they will be updated + * automatically, and cleaned once finished. Common call: + * <br/><br/> + * + * <pre> {@code + * Tween.call(myCallback) + * .delay(1.0f) + * .repeat(10, 1000) + * .start(myManager); + * }</pre> + * + * @param callback The callback that will be triggered on each iteration + * start. + * @return The generated Tween. + * @see TweenCallback + */ + public static Tween call(TweenCallback callback) { + Tween tween = pool.get(); + tween.setup(null, -1, 0); + tween.setCallback(callback); + tween.setCallbackTriggers(TweenCallback.START); + return tween; + } + + /** + * Convenience method to create an empty tween. Such object is only useful + * when placed inside animation sequences (see {@link Timeline}), in which + * it may act as a beacon, so you can set a callback on it in order to + * trigger some action at the right moment. + * + * @return The generated Tween. + * @see Timeline + */ + public static Tween mark() { + Tween tween = pool.get(); + tween.setup(null, -1, 0); + return tween; + } + + // ------------------------------------------------------------------------- + // Attributes + // ------------------------------------------------------------------------- + + // Main + private Object target; + private Class<?> targetClass; + private TweenAccessor<Object> accessor; + private int type; + private TweenEquation equation; + private TweenPath path; + + // General + private boolean isFrom; + private boolean isRelative; + private int combinedAttrsCnt; + private int waypointsCnt; + + // Values + private final float[] startValues = new float[combinedAttrsLimit]; + private final float[] targetValues = new float[combinedAttrsLimit]; + private final float[] waypoints = new float[waypointsLimit * combinedAttrsLimit]; + + // Buffers + private float[] accessorBuffer = new float[combinedAttrsLimit]; + private float[] pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit]; + + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + private Tween() { + reset(); + } + + @Override + protected void reset() { + super.reset(); + + target = null; + targetClass = null; + accessor = null; + type = -1; + equation = null; + path = null; + + isFrom = isRelative = false; + combinedAttrsCnt = waypointsCnt = 0; + + if (accessorBuffer.length != combinedAttrsLimit) { + accessorBuffer = new float[combinedAttrsLimit]; + } + + if (pathBuffer.length != (2+waypointsLimit)*combinedAttrsLimit) { + pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit]; + } + } + + private void setup(Object target, int tweenType, float duration) { + if (duration < 0) throw new RuntimeException("Duration can't be negative"); + + this.target = target; + this.targetClass = target != null ? findTargetClass() : null; + this.type = tweenType; + this.duration = duration; + } + + private Class<?> findTargetClass() { + if (registeredAccessors.containsKey(target.getClass())) return target.getClass(); + if (target instanceof TweenAccessor) return target.getClass(); + + Class<?> parentClass = target.getClass().getSuperclass(); + while (parentClass != null && !registeredAccessors.containsKey(parentClass)) + parentClass = parentClass.getSuperclass(); + + return parentClass; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + /** + * Sets the easing equation of the tween. Existing equations are located in + * <i>aurelienribon.tweenengine.equations</i> package, but you can of course + * implement your owns, see {@link TweenEquation}. You can also use the + * {@link TweenEquations} static instances to quickly access all the + * equations. Default equation is Quad.INOUT. + * <p/> + * + * <b>Proposed equations are:</b><br/> + * - Linear.INOUT,<br/> + * - Quad.IN | OUT | INOUT,<br/> + * - Cubic.IN | OUT | INOUT,<br/> + * - Quart.IN | OUT | INOUT,<br/> + * - Quint.IN | OUT | INOUT,<br/> + * - Circ.IN | OUT | INOUT,<br/> + * - Sine.IN | OUT | INOUT,<br/> + * - Expo.IN | OUT | INOUT,<br/> + * - Back.IN | OUT | INOUT,<br/> + * - Bounce.IN | OUT | INOUT,<br/> + * - Elastic.IN | OUT | INOUT + * + * @return The current tween, for chaining instructions. + * @see TweenEquation + * @see TweenEquations + */ + public Tween ease(TweenEquation easeEquation) { + this.equation = easeEquation; + return this; + } + + /** + * Forces the tween to use the TweenAccessor registered with the given + * target class. Useful if you want to use a specific accessor associated + * to an interface, for instance. + * + * @param targetClass A class registered with an accessor. + * @return The current tween, for chaining instructions. + */ + public Tween cast(Class<?> targetClass) { + if (isStarted()) throw new RuntimeException("You can't cast the target of a tween once it is started"); + this.targetClass = targetClass; + return this; + } + + /** + * Sets the target value of the interpolation. The interpolation will run + * from the <b>value at start time (after the delay, if any)</b> to this + * target value. + * <p/> + * + * To sum-up:<br/> + * - start value: value at start time, after delay<br/> + * - end value: param + * + * @param targetValue The target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween target(float targetValue) { + targetValues[0] = targetValue; + return this; + } + + /** + * Sets the target values of the interpolation. The interpolation will run + * from the <b>values at start time (after the delay, if any)</b> to these + * target values. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + * + * @param targetValue1 The 1st target value of the interpolation. + * @param targetValue2 The 2nd target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween target(float targetValue1, float targetValue2) { + targetValues[0] = targetValue1; + targetValues[1] = targetValue2; + return this; + } + + /** + * Sets the target values of the interpolation. The interpolation will run + * from the <b>values at start time (after the delay, if any)</b> to these + * target values. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + * + * @param targetValue1 The 1st target value of the interpolation. + * @param targetValue2 The 2nd target value of the interpolation. + * @param targetValue3 The 3rd target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween target(float targetValue1, float targetValue2, float targetValue3) { + targetValues[0] = targetValue1; + targetValues[1] = targetValue2; + targetValues[2] = targetValue3; + return this; + } + + /** + * Sets the target values of the interpolation. The interpolation will run + * from the <b>values at start time (after the delay, if any)</b> to these + * target values. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + * + * @param targetValues The target values of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween target(float... targetValues) { + if (targetValues.length > combinedAttrsLimit) throwCombinedAttrsLimitReached(); + System.arraycopy(targetValues, 0, this.targetValues, 0, targetValues.length); + return this; + } + + /** + * Sets the target value of the interpolation, relatively to the <b>value + * at start time (after the delay, if any)</b>. + * <p/> + * + * To sum-up:<br/> + * - start value: value at start time, after delay<br/> + * - end value: param + value at start time, after delay + * + * @param targetValue The relative target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween targetRelative(float targetValue) { + isRelative = true; + targetValues[0] = isInitialized() ? targetValue + startValues[0] : targetValue; + return this; + } + + /** + * Sets the target values of the interpolation, relatively to the <b>values + * at start time (after the delay, if any)</b>. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + values at start time, after delay + * + * @param targetValue1 The 1st relative target value of the interpolation. + * @param targetValue2 The 2nd relative target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween targetRelative(float targetValue1, float targetValue2) { + isRelative = true; + targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1; + targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2; + return this; + } + + /** + * Sets the target values of the interpolation, relatively to the <b>values + * at start time (after the delay, if any)</b>. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + values at start time, after delay + * + * @param targetValue1 The 1st relative target value of the interpolation. + * @param targetValue2 The 2nd relative target value of the interpolation. + * @param targetValue3 The 3rd relative target value of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween targetRelative(float targetValue1, float targetValue2, float targetValue3) { + isRelative = true; + targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1; + targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2; + targetValues[2] = isInitialized() ? targetValue3 + startValues[2] : targetValue3; + return this; + } + + /** + * Sets the target values of the interpolation, relatively to the <b>values + * at start time (after the delay, if any)</b>. + * <p/> + * + * To sum-up:<br/> + * - start values: values at start time, after delay<br/> + * - end values: params + values at start time, after delay + * + * @param targetValues The relative target values of the interpolation. + * @return The current tween, for chaining instructions. + */ + public Tween targetRelative(float... targetValues) { + if (targetValues.length > combinedAttrsLimit) throwCombinedAttrsLimitReached(); + for (int i=0; i<targetValues.length; i++) { + this.targetValues[i] = isInitialized() ? targetValues[i] + startValues[i] : targetValues[i]; + } + + isRelative = true; + return this; + } + + /** + * Adds a waypoint to the path. The default path runs from the start values + * to the end values linearly. If you add waypoints, the default path will + * use a smooth catmull-rom spline to navigate between the waypoints, but + * you can change this behavior by using the {@link #path(TweenPath)} + * method. + * + * @param targetValue The target of this waypoint. + * @return The current tween, for chaining instructions. + */ + public Tween waypoint(float targetValue) { + if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); + waypoints[waypointsCnt] = targetValue; + waypointsCnt += 1; + return this; + } + + /** + * Adds a waypoint to the path. The default path runs from the start values + * to the end values linearly. If you add waypoints, the default path will + * use a smooth catmull-rom spline to navigate between the waypoints, but + * you can change this behavior by using the {@link #path(TweenPath)} + * method. + * <p/> + * Note that if you want waypoints relative to the start values, use one of + * the .targetRelative() methods to define your target. + * + * @param targetValue1 The 1st target of this waypoint. + * @param targetValue2 The 2nd target of this waypoint. + * @return The current tween, for chaining instructions. + */ + public Tween waypoint(float targetValue1, float targetValue2) { + if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); + waypoints[waypointsCnt*2] = targetValue1; + waypoints[waypointsCnt*2+1] = targetValue2; + waypointsCnt += 1; + return this; + } + + /** + * Adds a waypoint to the path. The default path runs from the start values + * to the end values linearly. If you add waypoints, the default path will + * use a smooth catmull-rom spline to navigate between the waypoints, but + * you can change this behavior by using the {@link #path(TweenPath)} + * method. + * <p/> + * Note that if you want waypoints relative to the start values, use one of + * the .targetRelative() methods to define your target. + * + * @param targetValue1 The 1st target of this waypoint. + * @param targetValue2 The 2nd target of this waypoint. + * @param targetValue3 The 3rd target of this waypoint. + * @return The current tween, for chaining instructions. + */ + public Tween waypoint(float targetValue1, float targetValue2, float targetValue3) { + if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); + waypoints[waypointsCnt*3] = targetValue1; + waypoints[waypointsCnt*3+1] = targetValue2; + waypoints[waypointsCnt*3+2] = targetValue3; + waypointsCnt += 1; + return this; + } + + /** + * Adds a waypoint to the path. The default path runs from the start values + * to the end values linearly. If you add waypoints, the default path will + * use a smooth catmull-rom spline to navigate between the waypoints, but + * you can change this behavior by using the {@link #path(TweenPath)} + * method. + * <p/> + * Note that if you want waypoints relative to the start values, use one of + * the .targetRelative() methods to define your target. + * + * @param targetValues The targets of this waypoint. + * @return The current tween, for chaining instructions. + */ + public Tween waypoint(float... targetValues) { + if (waypointsCnt == waypointsLimit) throwWaypointsLimitReached(); + System.arraycopy(targetValues, 0, waypoints, waypointsCnt*targetValues.length, targetValues.length); + waypointsCnt += 1; + return this; + } + + /** + * Sets the algorithm that will be used to navigate through the waypoints, + * from the start values to the end values. Default is a catmull-rom spline, + * but you can find other paths in the {@link TweenPaths} class. + * + * @param path A TweenPath implementation. + * @return The current tween, for chaining instructions. + * @see TweenPath + * @see TweenPaths + */ + public Tween path(TweenPath path) { + this.path = path; + return this; + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + /** + * Gets the target object. + */ + public Object getTarget() { + return target; + } + + /** + * Gets the type of the tween. + */ + public int getType() { + return type; + } + + /** + * Gets the easing equation. + */ + public TweenEquation getEasing() { + return equation; + } + + /** + * Gets the target values. The returned buffer is as long as the maximum + * allowed combined values. Therefore, you're surely not interested in all + * its content. Use {@link #getCombinedTweenCount()} to get the number of + * interesting slots. + */ + public float[] getTargetValues() { + return targetValues; + } + + /** + * Gets the number of combined animations. + */ + public int getCombinedAttributesCount() { + return combinedAttrsCnt; + } + + /** + * Gets the TweenAccessor used with the target. + */ + public TweenAccessor<?> getAccessor() { + return accessor; + } + + /** + * Gets the class that was used to find the associated TweenAccessor. + */ + public Class<?> getTargetClass() { + return targetClass; + } + + // ------------------------------------------------------------------------- + // Overrides + // ------------------------------------------------------------------------- + + @Override + public Tween build() { + if (target == null) return this; + + accessor = (TweenAccessor<Object>) registeredAccessors.get(targetClass); + if (accessor == null && target instanceof TweenAccessor) accessor = (TweenAccessor<Object>) target; + if (accessor != null) combinedAttrsCnt = accessor.getValues(target, type, accessorBuffer); + else throw new RuntimeException("No TweenAccessor was found for the target"); + + if (combinedAttrsCnt > combinedAttrsLimit) throwCombinedAttrsLimitReached(); + return this; + } + + @Override + public void free() { + pool.free(this); + } + + @Override + protected void initializeOverride() { + if (target == null) return; + + accessor.getValues(target, type, startValues); + + for (int i=0; i<combinedAttrsCnt; i++) { + targetValues[i] += isRelative ? startValues[i] : 0; + + for (int ii=0; ii<waypointsCnt; ii++) { + waypoints[ii*combinedAttrsCnt+i] += isRelative ? startValues[i] : 0; + } + + if (isFrom) { + float tmp = startValues[i]; + startValues[i] = targetValues[i]; + targetValues[i] = tmp; + } + } + } + + @Override + protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { + if (target == null || equation == null) return; + + // Case iteration end has been reached + + if (!isIterationStep && step > lastStep) { + accessor.setValues(target, type, isReverse(lastStep) ? startValues : targetValues); + return; + } + + if (!isIterationStep && step < lastStep) { + accessor.setValues(target, type, isReverse(lastStep) ? targetValues : startValues); + return; + } + + // Validation + + assert isIterationStep; + assert getCurrentTime() >= 0; + assert getCurrentTime() <= duration; + + // Case duration equals zero + + if (duration < 0.00000000001f && delta > -0.00000000001f) { + accessor.setValues(target, type, isReverse(step) ? targetValues : startValues); + return; + } + + if (duration < 0.00000000001f && delta < 0.00000000001f) { + accessor.setValues(target, type, isReverse(step) ? startValues : targetValues); + return; + } + + // Normal behavior + + float time = isReverse(step) ? duration - getCurrentTime() : getCurrentTime(); + float t = equation.compute(time/duration); + + if (waypointsCnt == 0 || path == null) { + for (int i=0; i<combinedAttrsCnt; i++) { + accessorBuffer[i] = startValues[i] + t * (targetValues[i] - startValues[i]); + } + + } else { + for (int i=0; i<combinedAttrsCnt; i++) { + pathBuffer[0] = startValues[i]; + pathBuffer[1+waypointsCnt] = targetValues[i]; + for (int ii=0; ii<waypointsCnt; ii++) { + pathBuffer[ii+1] = waypoints[ii*combinedAttrsCnt+i]; + } + + accessorBuffer[i] = path.compute(t, pathBuffer, waypointsCnt+2); + } + } + + accessor.setValues(target, type, accessorBuffer); + } + + // ------------------------------------------------------------------------- + // BaseTween impl. + // ------------------------------------------------------------------------- + + @Override + protected void forceStartValues() { + if (target == null) return; + accessor.setValues(target, type, startValues); + } + + @Override + protected void forceEndValues() { + if (target == null) return; + accessor.setValues(target, type, targetValues); + } + + @Override + protected boolean containsTarget(Object target) { + return this.target == target; + } + + @Override + protected boolean containsTarget(Object target, int tweenType) { + return this.target == target && this.type == tweenType; + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private void throwCombinedAttrsLimitReached() { + String msg = "You cannot combine more than " + combinedAttrsLimit + " " + + "attributes in a tween. You can raise this limit with " + + "Tween.setCombinedAttributesLimit(), which should be called once " + + "in application initialization code."; + throw new RuntimeException(msg); + } + + private void throwWaypointsLimitReached() { + String msg = "You cannot add more than " + waypointsLimit + " " + + "waypoints to a tween. You can raise this limit with " + + "Tween.setWaypointsLimit(), which should be called once in " + + "application initialization code."; + throw new RuntimeException(msg); + } +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenAccessor.java b/java/api/src/aurelienribon/tweenengine/TweenAccessor.java new file mode 100755 index 0000000..780fb3c --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenAccessor.java @@ -0,0 +1,82 @@ +package aurelienribon.tweenengine; + +/** + * The TweenAccessor interface lets you interpolate any attribute from any + * object. Just implement it as you want and register it to the engine by + * calling {@link Tween#registerAccessor}. + * <p/> + * + * <h2>Example</h2> + * + * The following code snippet presents an example of implementation for tweening + * a Particle class. This Particle class is supposed to only define a position + * with an "x" and an "y" fields, and their associated getters and setters. + * <p/> + * + * <pre> {@code + * public class ParticleAccessor implements TweenAccessor<Particle> { + * public static final int X = 1; + * public static final int Y = 2; + * public static final int XY = 3; + * + * public int getValues(Particle target, int tweenType, float[] returnValues) { + * switch (tweenType) { + * case X: returnValues[0] = target.getX(); return 1; + * case Y: returnValues[0] = target.getY(); return 1; + * case XY: + * returnValues[0] = target.getX(); + * returnValues[1] = target.getY(); + * return 2; + * default: assert false; return 0; + * } + * } + * + * public void setValues(Particle target, int tweenType, float[] newValues) { + * switch (tweenType) { + * case X: target.setX(newValues[0]); break; + * case Y: target.setY(newValues[1]); break; + * case XY: + * target.setX(newValues[0]); + * target.setY(newValues[1]); + * break; + * default: assert false; break; + * } + * } + * } + * }</pre> + * + * Once done, you only need to register this TweenAccessor once to be able to + * use it for every Particle objects in your application: + * <p/> + * + * <pre> {@code + * Tween.registerAccessor(Particle.class, new ParticleAccessor()); + * }</pre> + * + * And that's all, the Tween Engine can no work with all your particles! + * + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public interface TweenAccessor<T> { + /** + * Gets one or many values from the target object associated to the + * given tween type. It is used by the Tween Engine to determine starting + * values. + * + * @param target The target object of the tween. + * @param tweenType An integer representing the tween type. + * @param returnValues An array which should be modified by this method. + * @return The count of modified slots from the returnValues array. + */ + public int getValues(T target, int tweenType, float[] returnValues); + + /** + * This method is called by the Tween Engine each time a running tween + * associated with the current target object has been updated. + * + * @param target The target object of the tween. + * @param tweenType An integer representing the tween type. + * @param newValues The new values determined by the Tween Engine. + */ + public void setValues(T target, int tweenType, float[] newValues); +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenCallback.java b/java/api/src/aurelienribon/tweenengine/TweenCallback.java new file mode 100755 index 0000000..5a733cb --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenCallback.java @@ -0,0 +1,45 @@ +package aurelienribon.tweenengine; + +/** + * TweenCallbacks are used to trigger actions at some specific times. They are + * used in both Tweens and Timelines. The moment when the callback is + * triggered depends on its registered triggers: + * <p/> + * + * <b>BEGIN</b>: right after the delay (if any)<br/> + * <b>START</b>: at each iteration beginning<br/> + * <b>END</b>: at each iteration ending, before the repeat delay<br/> + * <b>COMPLETE</b>: at last END event<br/> + * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/> + * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/> + * <b>BACK_END</b>: at each backward iteration ending<br/> + * <b>BACK_COMPLETE</b>: at last BACK_END event + * <p/> + * + * <pre> {@code + * forward : BEGIN COMPLETE + * forward : START END START END START END + * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX] + * backward: bEND bSTART bEND bSTART bEND bSTART + * backward: bCOMPLETE bBEGIN + * }</pre> + * + * @see Tween + * @see Timeline + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public interface TweenCallback { + public static final int BEGIN = 0x01; + public static final int START = 0x02; + public static final int END = 0x04; + public static final int COMPLETE = 0x08; + public static final int BACK_BEGIN = 0x10; + public static final int BACK_START = 0x20; + public static final int BACK_END = 0x40; + public static final int BACK_COMPLETE = 0x80; + public static final int ANY_FORWARD = 0x0F; + public static final int ANY_BACKWARD = 0xF0; + public static final int ANY = 0xFF; + + public void onEvent(int type, BaseTween<?> source); +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenEquation.java b/java/api/src/aurelienribon/tweenengine/TweenEquation.java new file mode 100755 index 0000000..d550703 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenEquation.java @@ -0,0 +1,28 @@ +package aurelienribon.tweenengine; + +/** + * Base class for every easing equation. You can create your own equations + * and directly use them in the Tween engine by inheriting from this class. + * + * @see Tween + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class TweenEquation { + + /** + * Computes the next value of the interpolation. + * + * @param t The current time, between 0 and 1. + * @return The current value. + */ + public abstract float compute(float t); + + /** + * Returns true if the given string is the name of this equation (the name + * is returned in the toString() method, don't forget to override it). + * This method is usually used to save/load a tween to/from a text file. + */ + public boolean isValueOf(String str) { + return str.equals(toString()); + } +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenEquations.java b/java/api/src/aurelienribon/tweenengine/TweenEquations.java new file mode 100755 index 0000000..115fc32 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenEquations.java @@ -0,0 +1,52 @@ +package aurelienribon.tweenengine; + +import aurelienribon.tweenengine.equations.Back; +import aurelienribon.tweenengine.equations.Bounce; +import aurelienribon.tweenengine.equations.Circ; +import aurelienribon.tweenengine.equations.Cubic; +import aurelienribon.tweenengine.equations.Elastic; +import aurelienribon.tweenengine.equations.Expo; +import aurelienribon.tweenengine.equations.Linear; +import aurelienribon.tweenengine.equations.Quad; +import aurelienribon.tweenengine.equations.Quart; +import aurelienribon.tweenengine.equations.Quint; +import aurelienribon.tweenengine.equations.Sine; + +/** + * Collection of built-in easing equations + * + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public interface TweenEquations { + public static final Linear easeNone = Linear.INOUT; + public static final Quad easeInQuad = Quad.IN; + public static final Quad easeOutQuad = Quad.OUT; + public static final Quad easeInOutQuad = Quad.INOUT; + public static final Cubic easeInCubic = Cubic.IN; + public static final Cubic easeOutCubic = Cubic.OUT; + public static final Cubic easeInOutCubic = Cubic.INOUT; + public static final Quart easeInQuart = Quart.IN; + public static final Quart easeOutQuart = Quart.OUT; + public static final Quart easeInOutQuart = Quart.INOUT; + public static final Quint easeInQuint = Quint.IN; + public static final Quint easeOutQuint = Quint.OUT; + public static final Quint easeInOutQuint = Quint.INOUT; + public static final Circ easeInCirc = Circ.IN; + public static final Circ easeOutCirc = Circ.OUT; + public static final Circ easeInOutCirc = Circ.INOUT; + public static final Sine easeInSine = Sine.IN; + public static final Sine easeOutSine = Sine.OUT; + public static final Sine easeInOutSine = Sine.INOUT; + public static final Expo easeInExpo = Expo.IN; + public static final Expo easeOutExpo = Expo.OUT; + public static final Expo easeInOutExpo = Expo.INOUT; + public static final Back easeInBack = Back.IN; + public static final Back easeOutBack = Back.OUT; + public static final Back easeInOutBack = Back.INOUT; + public static final Bounce easeInBounce = Bounce.IN; + public static final Bounce easeOutBounce = Bounce.OUT; + public static final Bounce easeInOutBounce = Bounce.INOUT; + public static final Elastic easeInElastic = Elastic.IN; + public static final Elastic easeOutElastic = Elastic.OUT; + public static final Elastic easeInOutElastic = Elastic.INOUT; +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenManager.java b/java/api/src/aurelienribon/tweenengine/TweenManager.java new file mode 100755 index 0000000..bc6ec7b --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenManager.java @@ -0,0 +1,237 @@ +package aurelienribon.tweenengine; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A TweenManager updates all your tweens and timelines at once. + * Its main interest is that it handles the tween/timeline life-cycles for you, + * as well as the pooling constraints (if object pooling is enabled). + * <p/> + * + * Just give it a bunch of tweens or timelines and call update() periodically, + * you don't need to care for anything else! Relax and enjoy your animations. + * + * @see Tween + * @see Timeline + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class TweenManager { + // ------------------------------------------------------------------------- + // Static API + // ------------------------------------------------------------------------- + + /** + * Disables or enables the "auto remove" mode of any tween manager for a + * particular tween or timeline. This mode is activated by default. The + * interest of desactivating it is to prevent some tweens or timelines from + * being automatically removed from a manager once they are finished. + * Therefore, if you update a manager backwards, the tweens or timelines + * will be played again, even if they were finished. + */ + public static void setAutoRemove(BaseTween<?> object, boolean value) { + object.isAutoRemoveEnabled = value; + } + + /** + * Disables or enables the "auto start" mode of any tween manager for a + * particular tween or timeline. This mode is activated by default. If it + * is not enabled, add a tween or timeline to any manager won't start it + * automatically, and you'll need to call .start() manually on your object. + */ + public static void setAutoStart(BaseTween<?> object, boolean value) { + object.isAutoStartEnabled = value; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + private final ArrayList<BaseTween<?>> objects = new ArrayList<BaseTween<?>>(20); + private boolean isPaused = false; + + /** + * Adds a tween or timeline to the manager and starts or restarts it. + * + * @return The manager, for instruction chaining. + */ + public TweenManager add(BaseTween<?> object) { + if (!objects.contains(object)) objects.add(object); + if (object.isAutoStartEnabled) object.start(); + return this; + } + + /** + * Returns true if the manager contains any valid interpolation associated + * to the given target object. + */ + public boolean containsTarget(Object target) { + for (int i=0, n=objects.size(); i<n; i++) { + BaseTween<?> obj = objects.get(i); + if (obj.containsTarget(target)) return true; + } + return false; + } + + /** + * Returns true if the manager contains any valid interpolation associated + * to the given target object and to the given tween type. + */ + public boolean containsTarget(Object target, int tweenType) { + for (int i=0, n=objects.size(); i<n; i++) { + BaseTween<?> obj = objects.get(i); + if (obj.containsTarget(target, tweenType)) return true; + } + return false; + } + + /** + * Kills every managed tweens and timelines. + */ + public void killAll() { + for (int i=0, n=objects.size(); i<n; i++) { + BaseTween<?> obj = objects.get(i); + obj.kill(); + } + } + + /** + * Kills every tweens associated to the given target. Will also kill every + * timelines containing a tween associated to the given target. + */ + public void killTarget(Object target) { + for (int i=0, n=objects.size(); i<n; i++) { + BaseTween<?> obj = objects.get(i); + obj.killTarget(target); + } + } + + /** + * Kills every tweens associated to the given target and tween type. Will + * also kill every timelines containing a tween associated to the given + * target and tween type. + */ + public void killTarget(Object target, int tweenType) { + for (int i=0, n=objects.size(); i<n; i++) { + BaseTween<?> obj = objects.get(i); + obj.killTarget(target, tweenType); + } + } + + /** + * Increases the minimum capacity of the manager. Defaults to 20. + */ + public void ensureCapacity(int minCapacity) { + objects.ensureCapacity(minCapacity); + } + + /** + * Pauses the manager. Further update calls won't have any effect. + */ + public void pause() { + isPaused = true; + } + + /** + * Resumes the manager, if paused. + */ + public void resume() { + isPaused = false; + } + + /** + * Updates every tweens with a delta time ang handles the tween life-cycles + * automatically. If a tween is finished, it will be removed from the + * manager. The delta time represents the elapsed time between now and the + * last update call. Each tween or timeline manages its local time, and adds + * this delta to its local time to update itself. + * <p/> + * + * Slow motion, fast motion and backward play can be easily achieved by + * tweaking this delta time. Multiply it by -1 to play the animation + * backward, or by 0.5 to play it twice slower than its normal speed. + */ + public void update(float delta) { + for (int i=objects.size()-1; i>=0; i--) { + BaseTween<?> obj = objects.get(i); + if (obj.isFinished() && obj.isAutoRemoveEnabled) { + objects.remove(i); + obj.free(); + } + } + + if (!isPaused) { + if (delta >= 0) { + for (int i=0, n=objects.size(); i<n; i++) objects.get(i).update(delta); + } else { + for (int i=objects.size()-1; i>=0; i--) objects.get(i).update(delta); + } + } + } + + /** + * Gets the number of managed objects. An object may be a tween or a + * timeline. Note that a timeline only counts for 1 object, since it + * manages its children itself. + * <p/> + * To get the count of running tweens, see {@link #getRunningTweensCount()}. + */ + public int size() { + return objects.size(); + } + + /** + * Gets the number of running tweens. This number includes the tweens + * located inside timelines (and nested timelines). + * <p/> + * <b>Provided for debug purpose only.</b> + */ + public int getRunningTweensCount() { + return getTweensCount(objects); + } + + /** + * Gets the number of running timelines. This number includes the timelines + * nested inside other timelines. + * <p/> + * <b>Provided for debug purpose only.</b> + */ + public int getRunningTimelinesCount() { + return getTimelinesCount(objects); + } + + /** + * Gets an immutable list of every managed object. + * <p/> + * <b>Provided for debug purpose only.</b> + */ + public List<BaseTween<?>> getObjects() { + return Collections.unmodifiableList(objects); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private static int getTweensCount(List<BaseTween<?>> objs) { + int cnt = 0; + for (int i=0, n=objs.size(); i<n; i++) { + BaseTween<?> obj = objs.get(i); + if (obj instanceof Tween) cnt += 1; + else cnt += getTweensCount(((Timeline)obj).getChildren()); + } + return cnt; + } + + private static int getTimelinesCount(List<BaseTween<?>> objs) { + int cnt = 0; + for (int i=0, n=objs.size(); i<n; i++) { + BaseTween<?> obj = objs.get(i); + if (obj instanceof Timeline) { + cnt += 1 + getTimelinesCount(((Timeline)obj).getChildren()); + } + } + return cnt; + } +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenPath.java b/java/api/src/aurelienribon/tweenengine/TweenPath.java new file mode 100755 index 0000000..724741b --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenPath.java @@ -0,0 +1,22 @@ +package aurelienribon.tweenengine; + +/** + * Base class for every paths. You can create your own paths and directly use + * them in the Tween engine by inheriting from this class. + * + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public interface TweenPath { + + /** + * Computes the next value of the interpolation, based on its waypoints and + * the current progress. + * + * @param t The progress of the interpolation, between 0 and 1. May be out + * of these bounds if the easing equation involves some kind of rebounds. + * @param points The waypoints of the tween, from start to target values. + * @param pointsCnt The number of valid points in the array. + * @return The next value of the interpolation. + */ + public float compute(float t, float[] points, int pointsCnt); +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenPaths.java b/java/api/src/aurelienribon/tweenengine/TweenPaths.java new file mode 100755 index 0000000..dbea075 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenPaths.java @@ -0,0 +1,14 @@ +package aurelienribon.tweenengine; + +import aurelienribon.tweenengine.paths.CatmullRom; +import aurelienribon.tweenengine.paths.Linear; + +/** + * Collection of built-in paths. + * + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public interface TweenPaths { + public static final Linear linear = new Linear(); + public static final CatmullRom catmullRom = new CatmullRom(); +} diff --git a/java/api/src/aurelienribon/tweenengine/TweenUtils.java b/java/api/src/aurelienribon/tweenengine/TweenUtils.java new file mode 100755 index 0000000..6c1b12c --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/TweenUtils.java @@ -0,0 +1,53 @@ +package aurelienribon.tweenengine; + +import aurelienribon.tweenengine.equations.Back; +import aurelienribon.tweenengine.equations.Bounce; +import aurelienribon.tweenengine.equations.Circ; +import aurelienribon.tweenengine.equations.Cubic; +import aurelienribon.tweenengine.equations.Elastic; +import aurelienribon.tweenengine.equations.Expo; +import aurelienribon.tweenengine.equations.Linear; +import aurelienribon.tweenengine.equations.Quad; +import aurelienribon.tweenengine.equations.Quart; +import aurelienribon.tweenengine.equations.Quint; +import aurelienribon.tweenengine.equations.Sine; + +/** + * Collection of miscellaneous utilities. + * + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class TweenUtils { + private static TweenEquation[] easings; + + /** + * Takes an easing name and gives you the corresponding TweenEquation. + * You probably won't need this, but tools will love that. + * + * @param easingName The name of an easing, like "Quad.INOUT". + * @return The parsed equation, or null if there is no match. + */ + public static TweenEquation parseEasing(String easingName) { + if (easings == null) { + easings = new TweenEquation[] {Linear.INOUT, + Quad.IN, Quad.OUT, Quad.INOUT, + Cubic.IN, Cubic.OUT, Cubic.INOUT, + Quart.IN, Quart.OUT, Quart.INOUT, + Quint.IN, Quint.OUT, Quint.INOUT, + Circ.IN, Circ.OUT, Circ.INOUT, + Sine.IN, Sine.OUT, Sine.INOUT, + Expo.IN, Expo.OUT, Expo.INOUT, + Back.IN, Back.OUT, Back.INOUT, + Bounce.IN, Bounce.OUT, Bounce.INOUT, + Elastic.IN, Elastic.OUT, Elastic.INOUT + }; + } + + for (int i=0; i<easings.length; i++) { + if (easingName.equals(easings[i].toString())) + return easings[i]; + } + + return null; + } +} diff --git a/java/api/src/aurelienribon/tweenengine/equations/Back.java b/java/api/src/aurelienribon/tweenengine/equations/Back.java new file mode 100755 index 0000000..46385ed --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Back.java @@ -0,0 +1,59 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Back extends TweenEquation { + public static final Back IN = new Back() { + @Override + public final float compute(float t) { + float s = param_s; + return t*t*((s+1)*t - s); + } + + @Override + public String toString() { + return "Back.IN"; + } + }; + + public static final Back OUT = new Back() { + @Override + public final float compute(float t) { + float s = param_s; + return (t-=1)*t*((s+1)*t + s) + 1; + } + + @Override + public String toString() { + return "Back.OUT"; + } + }; + + public static final Back INOUT = new Back() { + @Override + public final float compute(float t) { + float s = param_s; + if ((t*=2) < 1) return 0.5f*(t*t*(((s*=(1.525f))+1)*t - s)); + return 0.5f*((t-=2)*t*(((s*=(1.525f))+1)*t + s) + 2); + } + + @Override + public String toString() { + return "Back.INOUT"; + } + }; + + // ------------------------------------------------------------------------- + + protected float param_s = 1.70158f; + + public Back s(float s) { + param_s = s; + return this; + } +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Bounce.java b/java/api/src/aurelienribon/tweenengine/equations/Bounce.java new file mode 100755 index 0000000..3cfc4e3 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Bounce.java @@ -0,0 +1,55 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Bounce extends TweenEquation { + public static final Bounce IN = new Bounce() { + @Override + public final float compute(float t) { + return 1 - OUT.compute(1-t); + } + + @Override + public String toString() { + return "Bounce.IN"; + } + }; + + public static final Bounce OUT = new Bounce() { + @Override + public final float compute(float t) { + if (t < (1/2.75)) { + return 7.5625f*t*t; + } else if (t < (2/2.75)) { + return 7.5625f*(t-=(1.5f/2.75f))*t + .75f; + } else if (t < (2.5/2.75)) { + return 7.5625f*(t-=(2.25f/2.75f))*t + .9375f; + } else { + return 7.5625f*(t-=(2.625f/2.75f))*t + .984375f; + } + } + + @Override + public String toString() { + return "Bounce.OUT"; + } + }; + + public static final Bounce INOUT = new Bounce() { + @Override + public final float compute(float t) { + if (t < 0.5f) return IN.compute(t*2) * .5f; + else return OUT.compute(t*2-1) * .5f + 0.5f; + } + + @Override + public String toString() { + return "Bounce.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Circ.java b/java/api/src/aurelienribon/tweenengine/equations/Circ.java new file mode 100755 index 0000000..63a0145 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Circ.java @@ -0,0 +1,47 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Circ extends TweenEquation { + public static final Circ IN = new Circ() { + @Override + public final float compute(float t) { + return (float) -Math.sqrt(1 - t*t) - 1; + } + + @Override + public String toString() { + return "Circ.IN"; + } + }; + + public static final Circ OUT = new Circ() { + @Override + public final float compute(float t) { + return (float) Math.sqrt(1 - (t-=1)*t); + } + + @Override + public String toString() { + return "Circ.OUT"; + } + }; + + public static final Circ INOUT = new Circ() { + @Override + public final float compute(float t) { + if ((t*=2) < 1) return -0.5f * ((float)Math.sqrt(1 - t*t) - 1); + return 0.5f * ((float)Math.sqrt(1 - (t-=2)*t) + 1); + } + + @Override + public String toString() { + return "Circ.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Cubic.java b/java/api/src/aurelienribon/tweenengine/equations/Cubic.java new file mode 100755 index 0000000..9d86988 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Cubic.java @@ -0,0 +1,47 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Cubic extends TweenEquation { + public static final Cubic IN = new Cubic() { + @Override + public final float compute(float t) { + return t*t*t; + } + + @Override + public String toString() { + return "Cubic.IN"; + } + }; + + public static final Cubic OUT = new Cubic() { + @Override + public final float compute(float t) { + return (t-=1)*t*t + 1; + } + + @Override + public String toString() { + return "Cubic.OUT"; + } + }; + + public static final Cubic INOUT = new Cubic() { + @Override + public final float compute(float t) { + if ((t*=2) < 1) return 0.5f*t*t*t; + return 0.5f * ((t-=2)*t*t + 2); + } + + @Override + public String toString() { + return "Cubic.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Elastic.java b/java/api/src/aurelienribon/tweenengine/equations/Elastic.java new file mode 100755 index 0000000..925993d --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Elastic.java @@ -0,0 +1,86 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Elastic extends TweenEquation { + private static final float PI = 3.14159265f; + + public static final Elastic IN = new Elastic() { + @Override + public final float compute(float t) { + float a = param_a; + float p = param_p; + if (t==0) return 0; if (t==1) return 1; if (!setP) p=.3f; + float s; + if (!setA || a < 1) { a=1; s=p/4; } + else s = p/(2*PI) * (float)Math.asin(1/a); + return -(a*(float)Math.pow(2,10*(t-=1)) * (float)Math.sin( (t-s)*(2*PI)/p )); + } + + @Override + public String toString() { + return "Elastic.IN"; + } + }; + + public static final Elastic OUT = new Elastic() { + @Override + public final float compute(float t) { + float a = param_a; + float p = param_p; + if (t==0) return 0; if (t==1) return 1; if (!setP) p=.3f; + float s; + if (!setA || a < 1) { a=1; s=p/4; } + else s = p/(2*PI) * (float)Math.asin(1/a); + return a*(float)Math.pow(2,-10*t) * (float)Math.sin( (t-s)*(2*PI)/p ) + 1; + } + + @Override + public String toString() { + return "Elastic.OUT"; + } + }; + + public static final Elastic INOUT = new Elastic() { + @Override + public final float compute(float t) { + float a = param_a; + float p = param_p; + if (t==0) return 0; if ((t*=2)==2) return 1; if (!setP) p=.3f*1.5f; + float s; + if (!setA || a < 1) { a=1; s=p/4; } + else s = p/(2*PI) * (float)Math.asin(1/a); + if (t < 1) return -.5f*(a*(float)Math.pow(2,10*(t-=1)) * (float)Math.sin( (t-s)*(2*PI)/p )); + return a*(float)Math.pow(2,-10*(t-=1)) * (float)Math.sin( (t-s)*(2*PI)/p )*.5f + 1; + } + + @Override + public String toString() { + return "Elastic.INOUT"; + } + }; + + // ------------------------------------------------------------------------- + + protected float param_a; + protected float param_p; + protected boolean setA = false; + protected boolean setP = false; + + public Elastic a(float a) { + param_a = a; + this.setA = true; + return this; + } + + public Elastic p(float p) { + param_p = p; + this.setP = true; + return this; + } +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Expo.java b/java/api/src/aurelienribon/tweenengine/equations/Expo.java new file mode 100755 index 0000000..38a6a00 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Expo.java @@ -0,0 +1,49 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Expo extends TweenEquation { + public static final Expo IN = new Expo() { + @Override + public final float compute(float t) { + return (t==0) ? 0 : (float) Math.pow(2, 10 * (t - 1)); + } + + @Override + public String toString() { + return "Expo.IN"; + } + }; + + public static final Expo OUT = new Expo() { + @Override + public final float compute(float t) { + return (t==1) ? 1 : -(float) Math.pow(2, -10 * t) + 1; + } + + @Override + public String toString() { + return "Expo.OUT"; + } + }; + + public static final Expo INOUT = new Expo() { + @Override + public final float compute(float t) { + if (t==0) return 0; + if (t==1) return 1; + if ((t*=2) < 1) return 0.5f * (float) Math.pow(2, 10 * (t - 1)); + return 0.5f * (-(float)Math.pow(2, -10 * --t) + 2); + } + + @Override + public String toString() { + return "Expo.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Linear.java b/java/api/src/aurelienribon/tweenengine/equations/Linear.java new file mode 100755 index 0000000..1f95103 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Linear.java @@ -0,0 +1,22 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Linear extends TweenEquation { + public static final Linear INOUT = new Linear() { + @Override + public float compute(float t) { + return t; + } + + @Override + public String toString() { + return "Linear.INOUT"; + } + }; +} diff --git a/java/api/src/aurelienribon/tweenengine/equations/Quad.java b/java/api/src/aurelienribon/tweenengine/equations/Quad.java new file mode 100755 index 0000000..d015112 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Quad.java @@ -0,0 +1,47 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Quad extends TweenEquation { + public static final Quad IN = new Quad() { + @Override + public final float compute(float t) { + return t*t; + } + + @Override + public String toString() { + return "Quad.IN"; + } + }; + + public static final Quad OUT = new Quad() { + @Override + public final float compute(float t) { + return -t*(t-2); + } + + @Override + public String toString() { + return "Quad.OUT"; + } + }; + + public static final Quad INOUT = new Quad() { + @Override + public final float compute(float t) { + if ((t*=2) < 1) return 0.5f*t*t; + return -0.5f * ((--t)*(t-2) - 1); + } + + @Override + public String toString() { + return "Quad.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Quart.java b/java/api/src/aurelienribon/tweenengine/equations/Quart.java new file mode 100755 index 0000000..826eebb --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Quart.java @@ -0,0 +1,47 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Quart extends TweenEquation { + public static final Quart IN = new Quart() { + @Override + public final float compute(float t) { + return t*t*t*t; + } + + @Override + public String toString() { + return "Quart.IN"; + } + }; + + public static final Quart OUT = new Quart() { + @Override + public final float compute(float t) { + return -((t-=1)*t*t*t - 1); + } + + @Override + public String toString() { + return "Quart.OUT"; + } + }; + + public static final Quart INOUT = new Quart() { + @Override + public final float compute(float t) { + if ((t*=2) < 1) return 0.5f*t*t*t*t; + return -0.5f * ((t-=2)*t*t*t - 2); + } + + @Override + public String toString() { + return "Quart.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Quint.java b/java/api/src/aurelienribon/tweenengine/equations/Quint.java new file mode 100755 index 0000000..80e2412 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Quint.java @@ -0,0 +1,47 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Quint extends TweenEquation { + public static final Quint IN = new Quint() { + @Override + public final float compute(float t) { + return t*t*t*t*t; + } + + @Override + public String toString() { + return "Quint.IN"; + } + }; + + public static final Quint OUT = new Quint() { + @Override + public final float compute(float t) { + return (t-=1)*t*t*t*t + 1; + } + + @Override + public String toString() { + return "Quint.OUT"; + } + }; + + public static final Quint INOUT = new Quint() { + @Override + public final float compute(float t) { + if ((t*=2) < 1) return 0.5f*t*t*t*t*t; + return 0.5f*((t-=2)*t*t*t*t + 2); + } + + @Override + public String toString() { + return "Quint.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/equations/Sine.java b/java/api/src/aurelienribon/tweenengine/equations/Sine.java new file mode 100755 index 0000000..a4aec5c --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/equations/Sine.java @@ -0,0 +1,48 @@ +package aurelienribon.tweenengine.equations; + +import aurelienribon.tweenengine.TweenEquation; + +/** + * Easing equation based on Robert Penner's work: + * http://robertpenner.com/easing/ + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class Sine extends TweenEquation { + private static final float PI = 3.14159265f; + + public static final Sine IN = new Sine() { + @Override + public final float compute(float t) { + return (float) -Math.cos(t * (PI/2)) + 1; + } + + @Override + public String toString() { + return "Sine.IN"; + } + }; + + public static final Sine OUT = new Sine() { + @Override + public final float compute(float t) { + return (float) Math.sin(t * (PI/2)); + } + + @Override + public String toString() { + return "Sine.OUT"; + } + }; + + public static final Sine INOUT = new Sine() { + @Override + public final float compute(float t) { + return -0.5f * ((float) Math.cos(PI*t) - 1); + } + + @Override + public String toString() { + return "Sine.INOUT"; + } + }; +}
\ No newline at end of file diff --git a/java/api/src/aurelienribon/tweenengine/paths/CatmullRom.java b/java/api/src/aurelienribon/tweenengine/paths/CatmullRom.java new file mode 100755 index 0000000..5dedae4 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/paths/CatmullRom.java @@ -0,0 +1,39 @@ +package aurelienribon.tweenengine.paths; + +import aurelienribon.tweenengine.TweenPath; + +/** + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class CatmullRom implements TweenPath { + @Override + public float compute(float t, float[] points, int pointsCnt) { + int segment = (int) Math.floor((pointsCnt-1) * t); + segment = Math.max(segment, 0); + segment = Math.min(segment, pointsCnt-2); + + t = t * (pointsCnt-1) - segment; + + if (segment == 0) { + return catmullRomSpline(points[0], points[0], points[1], points[2], t); + } + + if (segment == pointsCnt-2) { + return catmullRomSpline(points[pointsCnt-3], points[pointsCnt-2], points[pointsCnt-1], points[pointsCnt-1], t); + } + + return catmullRomSpline(points[segment-1], points[segment], points[segment+1], points[segment+2], t); + } + + private float catmullRomSpline(float a, float b, float c, float d, float t) { + float t1 = (c - a) * 0.5f; + float t2 = (d - b) * 0.5f; + + float h1 = +2 * t * t * t - 3 * t * t + 1; + float h2 = -2 * t * t * t + 3 * t * t; + float h3 = t * t * t - 2 * t * t + t; + float h4 = t * t * t - t * t; + + return b * h1 + c * h2 + t1 * h3 + t2 * h4; + } +} diff --git a/java/api/src/aurelienribon/tweenengine/paths/Linear.java b/java/api/src/aurelienribon/tweenengine/paths/Linear.java new file mode 100755 index 0000000..a21b7b5 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/paths/Linear.java @@ -0,0 +1,19 @@ +package aurelienribon.tweenengine.paths; + +import aurelienribon.tweenengine.TweenPath; + +/** + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class Linear implements TweenPath { + @Override + public float compute(float t, float[] points, int pointsCnt) { + int segment = (int) Math.floor((pointsCnt-1) * t); + segment = Math.max(segment, 0); + segment = Math.min(segment, pointsCnt-2); + + t = t * (pointsCnt-1) - segment; + + return points[segment] + t * (points[segment+1] - points[segment]); + } +} diff --git a/java/api/src/aurelienribon/tweenengine/primitives/MutableFloat.java b/java/api/src/aurelienribon/tweenengine/primitives/MutableFloat.java new file mode 100755 index 0000000..a2683b9 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/primitives/MutableFloat.java @@ -0,0 +1,34 @@ +package aurelienribon.tweenengine.primitives; + +import aurelienribon.tweenengine.TweenAccessor; + +/** + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class MutableFloat extends Number implements TweenAccessor<MutableFloat> { + private float value; + + public MutableFloat(float value) { + this.value = value; + } + + public void setValue(float value) { + this.value = value; + } + + @Override public int intValue() {return (int) value;} + @Override public long longValue() {return (long) value;} + @Override public float floatValue() {return (float) value;} + @Override public double doubleValue() {return (double) value;} + + @Override + public int getValues(MutableFloat target, int tweenType, float[] returnValues) { + returnValues[0] = target.value; + return 1; + } + + @Override + public void setValues(MutableFloat target, int tweenType, float[] newValues) { + target.value = newValues[0]; + } +} diff --git a/java/api/src/aurelienribon/tweenengine/primitives/MutableInteger.java b/java/api/src/aurelienribon/tweenengine/primitives/MutableInteger.java new file mode 100755 index 0000000..a7ab932 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/primitives/MutableInteger.java @@ -0,0 +1,34 @@ +package aurelienribon.tweenengine.primitives; + +import aurelienribon.tweenengine.TweenAccessor; + +/** + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public class MutableInteger extends Number implements TweenAccessor<MutableInteger> { + private int value; + + public MutableInteger(int value) { + this.value = value; + } + + public void setValue(int value) { + this.value = value; + } + + @Override public int intValue() {return (int) value;} + @Override public long longValue() {return (long) value;} + @Override public float floatValue() {return (float) value;} + @Override public double doubleValue() {return (double) value;} + + @Override + public int getValues(MutableInteger target, int tweenType, float[] returnValues) { + returnValues[0] = target.value; + return 1; + } + + @Override + public void setValues(MutableInteger target, int tweenType, float[] newValues) { + target.value = (int) newValues[0]; + } +} |