package schule.ngb.zm.anim; import schule.ngb.zm.Constants; import schule.ngb.zm.Updatable; import schule.ngb.zm.util.Validator; import schule.ngb.zm.util.events.EventDispatcher; import java.util.function.DoubleUnaryOperator; public abstract class Animation extends Constants implements Updatable { protected int runtime; protected int elapsedTime = 0; protected boolean running = false, finished = false; protected DoubleUnaryOperator easing; public Animation() { this.runtime = Constants.DEFAULT_ANIM_RUNTIME; this.easing = Constants.DEFAULT_EASING; } public Animation( DoubleUnaryOperator easing ) { this.runtime = Constants.DEFAULT_ANIM_RUNTIME; this.easing = Validator.requireNotNull(easing); } public Animation( int runtime ) { this.runtime = runtime; this.easing = Constants.DEFAULT_EASING; } public Animation( int runtime, DoubleUnaryOperator easing ) { this.runtime = runtime; this.easing = Validator.requireNotNull(easing); } public int getRuntime() { return runtime; } public void setRuntime( int pRuntime ) { this.runtime = pRuntime; } public DoubleUnaryOperator getEasing() { return easing; } public void setEasing( DoubleUnaryOperator pEasing ) { this.easing = pEasing; } public abstract T getAnimationTarget(); public final void start() { this.initialize(); elapsedTime = 0; running = true; finished = false; animate(easing.applyAsDouble(0.0)); initializeEventDispatcher().dispatchEvent("start", this); } public final void stop() { running = false; // Make sure the last animation frame was interpolated correctly animate(easing.applyAsDouble((double) elapsedTime / (double) runtime)); this.finish(); finished = true; initializeEventDispatcher().dispatchEvent("stop", this); } public void initialize() { // Intentionally left blank } public void finish() { // Intentionally left blank } public final void await() { while( !finished ) { Thread.yield(); } } @Override public boolean isActive() { return running; } @Override public void update( double delta ) { elapsedTime += (int) (delta * 1000); if( elapsedTime > runtime ) elapsedTime = runtime; double t = (double) elapsedTime / (double) runtime; if( t >= 1.0 ) { running = false; stop(); } else { animate(easing.applyAsDouble(t)); } } /** * Setzt den Fortschritt der Animation auf den angegebenen Wert. *

* {@code e} liegt in der Regel zwischen 0 und 1. Je nach verwendeten * {@link Easing} Funktion kann der Wert aber in Ausnahmefällen unter 0 oder * über 1 liegen. Die {@code step()} Methode muss dem nicht Rechnung tragen * und kann wenn sinnvoll den {@code e} Wert auf [0, 1] limitieren: *


	 * e = Constants.limit(e, 0, 1);
	 * 
* * @param e Fortschritt der Animation nachdem die Easingfunktion angewandt * wurde. */ public abstract void animate( double e ); EventDispatcher eventDispatcher; private EventDispatcher initializeEventDispatcher() { if( eventDispatcher == null ) { eventDispatcher = new EventDispatcher<>(); eventDispatcher.registerEventType("start", ( a, l ) -> l.animationStarted(a)); eventDispatcher.registerEventType("stop", ( a, l ) -> l.animationStopped(a)); } return eventDispatcher; } public void addListener( AnimationListener listener ) { initializeEventDispatcher().addListener(listener); } public void removeListener( AnimationListener listener ) { initializeEventDispatcher().removeListener(listener); } }