From ecbe2b4f6b698768fbb6bae5cd785cae90bce5b3 Mon Sep 17 00:00:00 2001 From: "J. Neugebauer" Date: Mon, 25 Jul 2022 17:42:06 +0200 Subject: [PATCH] interpolate zu animate umbenannt Animation erbt zur Vereinfachung nun auch von Constants und dort gibt es schon eine interpolate Methode. --- .../java/schule/ngb/zm/anim/Animation.java | 33 ++--- .../schule/ngb/zm/anim/AnimationFacade.java | 4 +- .../schule/ngb/zm/anim/AnimationGroup.java | 134 ++++++++++++------ .../schule/ngb/zm/anim/CircleAnimation.java | 39 +++++ .../ngb/zm/anim/ContinousAnimation.java | 74 ++++++++++ .../schule/ngb/zm/anim/FadeAnimation.java | 2 +- .../schule/ngb/zm/anim/FillAnimation.java | 2 +- .../schule/ngb/zm/anim/MorphAnimation.java | 2 +- .../schule/ngb/zm/anim/MoveAnimation.java | 2 +- .../schule/ngb/zm/anim/RotateAnimation.java | 2 +- .../schule/ngb/zm/anim/StrokeAnimation.java | 2 +- .../schule/ngb/zm/anim/WaveAnimation.java | 37 +++++ 12 files changed, 260 insertions(+), 73 deletions(-) create mode 100644 src/main/java/schule/ngb/zm/anim/CircleAnimation.java create mode 100644 src/main/java/schule/ngb/zm/anim/ContinousAnimation.java create mode 100644 src/main/java/schule/ngb/zm/anim/WaveAnimation.java diff --git a/src/main/java/schule/ngb/zm/anim/Animation.java b/src/main/java/schule/ngb/zm/anim/Animation.java index afda8cb..100b656 100644 --- a/src/main/java/schule/ngb/zm/anim/Animation.java +++ b/src/main/java/schule/ngb/zm/anim/Animation.java @@ -2,15 +2,16 @@ 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 implements Updatable { +public abstract class Animation extends Constants implements Updatable { protected int runtime; - protected int elapsed_time = 0; + protected int elapsedTime = 0; protected boolean running = false, finished = false; @@ -23,7 +24,7 @@ public abstract class Animation implements Updatable { public Animation( DoubleUnaryOperator easing ) { this.runtime = Constants.DEFAULT_ANIM_RUNTIME; - this.easing = easing; + this.easing = Validator.requireNotNull(easing); } public Animation( int runtime ) { @@ -33,7 +34,7 @@ public abstract class Animation implements Updatable { public Animation( int runtime, DoubleUnaryOperator easing ) { this.runtime = runtime; - this.easing = easing; + this.easing = Validator.requireNotNull(easing); } public int getRuntime() { @@ -56,17 +57,17 @@ public abstract class Animation implements Updatable { public final void start() { this.initialize(); - elapsed_time = 0; + elapsedTime = 0; running = true; finished = false; - interpolate(easing.applyAsDouble(0.0)); + animate(easing.applyAsDouble(0.0)); initializeEventDispatcher().dispatchEvent("start", this); } public final void stop() { running = false; // Make sure the last animation frame was interpolated correctly - interpolate(easing.applyAsDouble((double) elapsed_time / (double) runtime)); + animate(easing.applyAsDouble((double) elapsedTime / (double) runtime)); this.finish(); finished = true; initializeEventDispatcher().dispatchEvent("stop", this); @@ -82,11 +83,7 @@ public abstract class Animation implements Updatable { public final void await() { while( !finished ) { - try { - Thread.sleep(1); - } catch( InterruptedException ex ) { - // Keep waiting - } + Thread.yield(); } } @@ -97,16 +94,16 @@ public abstract class Animation implements Updatable { @Override public void update( double delta ) { - elapsed_time += (int) (delta * 1000); - if( elapsed_time > runtime ) - elapsed_time = runtime; + elapsedTime += (int) (delta * 1000); + if( elapsedTime > runtime ) + elapsedTime = runtime; - double t = (double) elapsed_time / (double) runtime; + double t = (double) elapsedTime / (double) runtime; if( t >= 1.0 ) { running = false; stop(); } else { - interpolate(easing.applyAsDouble(t)); + animate(easing.applyAsDouble(t)); } } @@ -124,7 +121,7 @@ public abstract class Animation implements Updatable { * @param e Fortschritt der Animation nachdem die Easingfunktion angewandt * wurde. */ - public abstract void interpolate( double e ); + public abstract void animate( double e ); EventDispatcher eventDispatcher; diff --git a/src/main/java/schule/ngb/zm/anim/AnimationFacade.java b/src/main/java/schule/ngb/zm/anim/AnimationFacade.java index 192b540..1a0ca6c 100644 --- a/src/main/java/schule/ngb/zm/anim/AnimationFacade.java +++ b/src/main/java/schule/ngb/zm/anim/AnimationFacade.java @@ -19,8 +19,8 @@ public class AnimationFacade extends Animation { } @Override - public void interpolate( double e ) { - anim.interpolate(e); + public void animate( double e ) { + anim.animate(e); } @Override diff --git a/src/main/java/schule/ngb/zm/anim/AnimationGroup.java b/src/main/java/schule/ngb/zm/anim/AnimationGroup.java index ea2e244..16b24ac 100644 --- a/src/main/java/schule/ngb/zm/anim/AnimationGroup.java +++ b/src/main/java/schule/ngb/zm/anim/AnimationGroup.java @@ -1,76 +1,116 @@ package schule.ngb.zm.anim; -import schule.ngb.zm.shapes.Shape; - -import java.util.Arrays; +import java.util.Collection; +import java.util.List; import java.util.function.DoubleUnaryOperator; -public class AnimationGroup extends Animation { +@SuppressWarnings( "unused" ) +public class AnimationGroup extends Animation { - Animation[] anims; - - private boolean overrideRuntime = false; + List> anims; - public AnimationGroup( DoubleUnaryOperator easing, Animation... anims ) { - super(easing); - this.anims = anims; + private boolean overrideEasing = false; - int maxRuntime = Arrays.stream(this.anims).mapToInt((a) -> a.getRuntime()).reduce(0, Integer::max); - setRuntime(maxRuntime); + private int overrideRuntime = -1; + + private int lag = 0; + + private int active = 0; + + public AnimationGroup( Collection> anims ) { + this(0, -1, null, anims); } - public AnimationGroup( int runtime, DoubleUnaryOperator easing, Animation... anims ) { - super(runtime, easing); - this.anims = anims; - overrideRuntime = true; + public AnimationGroup( int lag, Collection> anims ) { + this(lag, -1, null, anims); + } + + public AnimationGroup( DoubleUnaryOperator easing, Collection> anims ) { + this(0, -1, easing, anims); + } + + public AnimationGroup( int lag, DoubleUnaryOperator easing, Collection> anims ) { + this(lag, -1, easing, anims); + } + + public AnimationGroup( int lag, int runtime, DoubleUnaryOperator easing, Collection> anims ) { + super(); + + this.anims = List.copyOf(anims); + this.lag = lag; + + if( easing != null ) { + this.easing = easing; + overrideEasing = true; + } + + if( runtime > 0 ) { + this.runtime = anims.size() * lag + runtime; + this.overrideRuntime = runtime; + } else { + this.runtime = 0; + for( int i = 0; i < this.anims.size(); i++ ) { + if( i * lag + this.anims.get(i).getRuntime() > this.runtime ) { + this.runtime = i * lag + this.anims.get(i).getRuntime(); + } + } + } } @Override - public Shape getAnimationTarget() { - return null; + public T getAnimationTarget() { + for( Animation anim : anims ) { + if( anim.isActive() ) { + return anim.getAnimationTarget(); + } + } + return anims.get(anims.size() - 1).getAnimationTarget(); } @Override public void update( double delta ) { - if( overrideRuntime ) { - synchronized( anims ) { - for( Animation anim: anims ) { - if( anim.isActive() ) { - anim.update(delta); - } + elapsedTime += (int) (delta * 1000); + // Animation is done. Stop all Animations. + if( elapsedTime > runtime ) { + for( int i = 0; i < anims.size(); i++ ) { + if( anims.get(i).isActive() ) { + anims.get(i).elapsedTime = anims.get(i).runtime; + anims.get(i).stop(); } } - } else { - super.update(delta); + running = false; + this.stop(); } - } - @Override - public void interpolate( double e ) { - synchronized( anims ) { - for( Animation anim: anims ) { - anim.interpolate(e); + while( active < anims.size() && elapsedTime >= active * lag ) { + anims.get(active).start(); + active += 1; + } + + for( int i = 0; i < active; i++ ) { + double t = 0.0; + if( overrideRuntime > 0 ) { + t = (double) (elapsedTime - i*lag) / (double) overrideRuntime; + } else { + t = (double) (elapsedTime - i*lag) / (double) anims.get(i).getRuntime(); + } + + if( t >= 1.0 ) { + anims.get(i).elapsedTime = anims.get(i).runtime; + anims.get(i).stop(); + } else { + double e = overrideEasing ? + easing.applyAsDouble(t) : + anims.get(i).easing.applyAsDouble(t); + + anims.get(i).animate(e); } } } @Override - public void initialize() { - synchronized( anims ) { - for( Animation anim: anims ) { - anim.initialize(); - } - } - } - - @Override - public void finish() { - synchronized( anims ) { - for( Animation anim: anims ) { - anim.finish(); - } - } + public void animate( double e ) { } } diff --git a/src/main/java/schule/ngb/zm/anim/CircleAnimation.java b/src/main/java/schule/ngb/zm/anim/CircleAnimation.java new file mode 100644 index 0000000..4c0cdb8 --- /dev/null +++ b/src/main/java/schule/ngb/zm/anim/CircleAnimation.java @@ -0,0 +1,39 @@ +package schule.ngb.zm.anim; + + +import schule.ngb.zm.Constants; +import schule.ngb.zm.Vector; +import schule.ngb.zm.shapes.Shape; + +import java.util.function.DoubleUnaryOperator; + +public class CircleAnimation extends Animation { + + private Shape object; + + private double centerx, centery, radius, startangle; + + public CircleAnimation( Shape target, double cx, double cy, int runtime, DoubleUnaryOperator easing ) { + super(runtime, easing); + object = target; + centerx = cx; + centery = cy; + Vector vec = new Vector(target.getX(), target.getY()).sub(cx, cy); + startangle = vec.heading(); + radius = vec.length(); + } + + @Override + public Shape getAnimationTarget() { + return object; + } + + @Override + public void animate( double e ) { + double angle = startangle + Constants.radians(Constants.interpolate(0, 360, e)); + double x = centerx + radius * Constants.cos(angle); + double y = centery + radius * Constants.sin(angle); + object.moveTo(x, y); + } + +} diff --git a/src/main/java/schule/ngb/zm/anim/ContinousAnimation.java b/src/main/java/schule/ngb/zm/anim/ContinousAnimation.java new file mode 100644 index 0000000..924adbe --- /dev/null +++ b/src/main/java/schule/ngb/zm/anim/ContinousAnimation.java @@ -0,0 +1,74 @@ +package schule.ngb.zm.anim; + +@SuppressWarnings( "unused" ) +public class ContinousAnimation extends Animation { + + private final Animation baseAnimation; + + private int lag = 0; + + /** + * Speichert eine Approximation der aktuellen Steigung der Easing-Funktion, + * um im Fall {@code easeInOnly == true} nach dem ersten Durchlauf die + * passende Geschwindigkeit beizubehalten. + */ + private double m = 1.0, lastEase = 0.0; + + private boolean easeInOnly = false; + + public ContinousAnimation( Animation baseAnimation ) { + this(baseAnimation, 0, false); + } + + public ContinousAnimation( Animation baseAnimation, int lag ) { + this(baseAnimation, lag, false); + } + + public ContinousAnimation( Animation baseAnimation, boolean easeInOnly ) { + this(baseAnimation, 0, easeInOnly); + } + + private ContinousAnimation( Animation baseAnimation, int lag, boolean easeInOnly ) { + super(baseAnimation.getRuntime(), baseAnimation.getEasing()); + this.baseAnimation = baseAnimation; + this.lag = lag; + this.easeInOnly = easeInOnly; + } + + @Override + public T getAnimationTarget() { + return baseAnimation.getAnimationTarget(); + } + + @Override + public void update( double delta ) { + elapsedTime += (int) (delta * 1000); + if( elapsedTime >= runtime + lag ) { + elapsedTime %= (runtime + lag); + + if( easeInOnly && easing != null ) { + easing = null; + // runtime = (int)((1.0/m)*(runtime + lag)); + } + } + + double t = (double) elapsedTime / (double) runtime; + if( t >= 1.0 ) { + t = 1.0; + } + if( easing != null ) { + double e = easing.applyAsDouble(t); + animate(e); + m = (e-lastEase)/(delta*1000/(asDouble(runtime))); + lastEase = e; + } else { + animate(t); + } + } + + @Override + public void animate( double e ) { + baseAnimation.animate(e); + } + +} diff --git a/src/main/java/schule/ngb/zm/anim/FadeAnimation.java b/src/main/java/schule/ngb/zm/anim/FadeAnimation.java index ba230bd..4895eda 100644 --- a/src/main/java/schule/ngb/zm/anim/FadeAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/FadeAnimation.java @@ -36,7 +36,7 @@ public class FadeAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.setFillColor(new Color(fill, (int) Constants.interpolate(fillAlpha, tAlpha, e))); object.setStrokeColor(new Color(stroke, (int) Constants.interpolate(strokeAlpha, tAlpha, e))); } diff --git a/src/main/java/schule/ngb/zm/anim/FillAnimation.java b/src/main/java/schule/ngb/zm/anim/FillAnimation.java index f10c375..92c51d5 100644 --- a/src/main/java/schule/ngb/zm/anim/FillAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/FillAnimation.java @@ -26,7 +26,7 @@ public class FillAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.setFillColor(Color.interpolate(oFill, tFill, e)); } diff --git a/src/main/java/schule/ngb/zm/anim/MorphAnimation.java b/src/main/java/schule/ngb/zm/anim/MorphAnimation.java index 2e04e40..2681d7c 100644 --- a/src/main/java/schule/ngb/zm/anim/MorphAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/MorphAnimation.java @@ -27,7 +27,7 @@ public class MorphAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.setX(Constants.interpolate(original.getX(), target.getX(), e)); object.setY(Constants.interpolate(original.getY(), target.getY(), e)); object.setFillColor(Color.interpolate(original.getFillColor(), target.getFillColor(), e)); diff --git a/src/main/java/schule/ngb/zm/anim/MoveAnimation.java b/src/main/java/schule/ngb/zm/anim/MoveAnimation.java index a068a0f..3a826ce 100644 --- a/src/main/java/schule/ngb/zm/anim/MoveAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/MoveAnimation.java @@ -31,7 +31,7 @@ public class MoveAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.setX(Constants.interpolate(oX, tX, e)); object.setY(Constants.interpolate(oY, tY, e)); } diff --git a/src/main/java/schule/ngb/zm/anim/RotateAnimation.java b/src/main/java/schule/ngb/zm/anim/RotateAnimation.java index 70d1c93..aea03f4 100644 --- a/src/main/java/schule/ngb/zm/anim/RotateAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/RotateAnimation.java @@ -25,7 +25,7 @@ public class RotateAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.rotateTo(Constants.interpolate(oA, tA, e)); } diff --git a/src/main/java/schule/ngb/zm/anim/StrokeAnimation.java b/src/main/java/schule/ngb/zm/anim/StrokeAnimation.java index ea46455..ea2d5e5 100644 --- a/src/main/java/schule/ngb/zm/anim/StrokeAnimation.java +++ b/src/main/java/schule/ngb/zm/anim/StrokeAnimation.java @@ -25,7 +25,7 @@ public class StrokeAnimation extends Animation { } @Override - public void interpolate( double e ) { + public void animate( double e ) { object.setStrokeColor(Color.interpolate(oFill, tFill, e)); } diff --git a/src/main/java/schule/ngb/zm/anim/WaveAnimation.java b/src/main/java/schule/ngb/zm/anim/WaveAnimation.java new file mode 100644 index 0000000..e551e2f --- /dev/null +++ b/src/main/java/schule/ngb/zm/anim/WaveAnimation.java @@ -0,0 +1,37 @@ +package schule.ngb.zm.anim; + +import schule.ngb.zm.Constants; +import schule.ngb.zm.Options; +import schule.ngb.zm.shapes.Shape; + +import java.util.function.DoubleUnaryOperator; + +public class WaveAnimation extends Animation { + + private Shape object; + + private double strength, sinOffset, previousDelta = 0.0; + + private Options.Direction dir; + + public WaveAnimation( Shape target, double strength, Options.Direction dir, double sinOffset, int runtime, DoubleUnaryOperator easing ) { + super(runtime, easing); + this.object = target; + this.dir = dir; + this.strength = strength; + this.sinOffset = sinOffset; + } + + @Override + public Shape getAnimationTarget() { + return object; + } + + @Override + public void animate( double e ) { + double delta = this.strength * Constants.sin(Constants.interpolate(0.0, Constants.TWO_PI, e) + sinOffset); + object.move((delta - previousDelta) * dir.x, (delta - previousDelta) * dir.y); + previousDelta = delta; + } + +}