package org.openautonomousconnection.oacswing.animated; import lombok.Getter; import lombok.Setter; import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class AnimationPath extends ArrayList { @Getter @Setter private int inbetweens; private int animationIterator = 0; private int subFrameIterator = 0; public AnimationPath(int inbetweens, Collection keyFrames) { this.addAll(keyFrames); this.inbetweens = inbetweens; } public AnimationPath(int inbetweens, KeyFrame... keyFrames) { this.addAll(Arrays.stream(keyFrames).toList()); this.inbetweens = inbetweens; } public AnimationPath(int inbetweens) { this.inbetweens = inbetweens; } /** * Get next keyframe according to current path iterator, depending on the current frame * @return next keyframe in order, depending on the current frame */ public KeyFrame getNext() { if(this.subFrameIterator >= this.inbetweens) { this.subFrameIterator = 0; this.animationIterator++; } if(this.animationIterator >= this.size()) return null; this.subFrameIterator++; KeyFrame current = this.get(this.animationIterator); KeyFrame next = this.getNextInOrder(); // How far the transition should be finished double transition = (double) this.subFrameIterator / this.inbetweens; return inBetween(current, next, transition); } /** * Get next keyframe according to current path iterator, not depending on the current frame * @return next keyframe in order, not depending on the current frame */ public KeyFrame getNextInOrder() { int i = this.animationIterator + 1; if(i >= this.size()) i--; return this.get(i); } /** * Reset animation path iterator to start */ public void reset() { this.subFrameIterator = 0; this.animationIterator = 0; } /** * Utility method needed to get in-betweens * @param point point to multiply * @param scalar scalar to multiply with * @return scalar product point */ private static Point multiply(Point point, double scalar) { int x = Math.toIntExact(Math.round(point.x * scalar)); int y = Math.toIntExact(Math.round(point.y * scalar)); return new Point(x, y); } /** * Add two points together; also needed for in-betweens * @param point augend * @param addend addend * @return sum of both points */ private static Point add(Point point, Point addend) { return new Point(point.x + addend.x, point.y + addend.y); } /** * Subtracts one point from another; also needed for in-betweens * @param point minuend * @param subtrahend subtrahend * @return sum of both points */ private static Point subtract(Point point, Point subtrahend) { return new Point(point.x - subtrahend.x, point.y - subtrahend.y); } /** * Find in-between with given scalar * @param kf1 first frame * @param kf2 next frame * @param scalar factor (ideally between 0 and 1, representing 0% transition to 100% transition) * @return in-between frame */ private static KeyFrame inBetween(KeyFrame kf1, KeyFrame kf2, double scalar) { // position Point difference = subtract(kf2.position(), kf1.position()); Point pos = add(kf1.position(), multiply(difference, scalar)); // scale int width, height; int dW = kf2.width() - kf1.width(); int dH = kf2.height() - kf1.height(); width = Math.toIntExact(Math.round(kf1.width() + dW * scalar)); height = Math.toIntExact(Math.round(kf1.height() + dH * scalar)); return new KeyFrame(pos, width, height); } }