Further work on fixing keyframe path methods (still not working), and declared "momentum" functionality for replaying AnimationPaths through AnimatedComponents (not implemented yet). Added Javadocs.

This commit is contained in:
Tinglyyy
2026-02-09 22:15:18 +01:00
parent e61dbfa531
commit 59d72537b9
3 changed files with 166 additions and 24 deletions

View File

@@ -84,28 +84,95 @@ public class AnimationPath extends ArrayList<KeyFrame> {
double transition = this.linear(subIterator);
KeyFrame.PathMethod method;
CombinedPathMethod method;
KeyFrame.PathMethod
currentMethod = current.pathMethod(),
nextMethod = next.pathMethod();
// Translate EASE_IN_AND_OUT to its respective currently relevant counterpart
//TODO: non-linear keyframes can't behandled this way. This just makes it bug around
if(currentMethod.equals(KeyFrame.PathMethod.EASE_OUT) || currentMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
currentMethod = KeyFrame.PathMethod.EASE_OUT;
if(transition < 0.5)
method = current.pathMethod().equals(KeyFrame.PathMethod.EASE_IN_AND_OUT) ?
KeyFrame.PathMethod.EASE_OUT : current.pathMethod();
else
method = next.pathMethod().equals(KeyFrame.PathMethod.EASE_IN_AND_OUT) ?
KeyFrame.PathMethod.EASE_OUT : next.pathMethod();
if(nextMethod.equals(KeyFrame.PathMethod.EASE_OUT) || nextMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
nextMethod = KeyFrame.PathMethod.EASE_OUT;
// Else-case would be linear, which doesn't change anything
method = switch (currentMethod) {
case LINEAR -> switch (nextMethod) {
case LINEAR -> CombinedPathMethod.LINEAR;
case EASE_IN -> CombinedPathMethod.LINEAR_EASE_IN;
case EASE_OUT -> CombinedPathMethod.LINEAR_EASE_OUT;
default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
};
if(method.equals(KeyFrame.PathMethod.EASE_IN))
transition = this.easeIn(subIterator);
case EASE_IN -> switch (nextMethod) {
case LINEAR -> CombinedPathMethod.EASE_IN_LINEAR;
case EASE_IN -> CombinedPathMethod.EASE_IN;
case EASE_OUT -> CombinedPathMethod.EASE_OUT_LINEAR;
default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
};
else if(method.equals(KeyFrame.PathMethod.EASE_OUT))
transition = this.easeOut(subIterator);
case EASE_OUT -> switch (nextMethod) {
case LINEAR -> CombinedPathMethod.EASE_OUT_LINEAR;
case EASE_IN -> CombinedPathMethod.EASE_OUT_AND_IN;
case EASE_OUT -> CombinedPathMethod.EASE_OUT;
default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
};
default -> throw new IllegalStateException("Unexpected value: " + currentMethod);
};
return inBetween(current, next, transition, method);
boolean overHalf = transition > 0.5;
if(overHalf)
transition = 2 * transition - 1;
transition = switch (method) {
case LINEAR -> transition;
case LINEAR_EASE_IN -> overHalf ? easeIn(subIterator) : transition;
case EASE_IN -> easeIn(subIterator);
case EASE_IN_LINEAR -> overHalf ? transition : easeIn(subIterator);
case LINEAR_EASE_OUT -> overHalf ? easeOut(subIterator) : transition;
case EASE_OUT -> easeOut(subIterator);
case EASE_OUT_LINEAR -> overHalf ? transition : easeOut(subIterator);
case EASE_OUT_AND_IN -> overHalf ? easeOut(subIterator) : easeIn(subIterator);
};
System.out.println(method + " " + transition + " - linear: " + linear(subIterator));
return inBetween(current, next, transition, overHalf);
// KeyFrame.PathMethod method;
//
// // Translate EASE_IN_AND_OUT to its respective currently relevant counterpart
//
// //TODO: non-linear keyframes can't behandled this way. This just makes it bug around
//
// if(transition < 0.5)
// method = current.pathMethod().equals(KeyFrame.PathMethod.EASE_IN_AND_OUT) ?
// KeyFrame.PathMethod.EASE_OUT : current.pathMethod();
// else
// method = next.pathMethod().equals(KeyFrame.PathMethod.EASE_IN_AND_OUT) ?
// KeyFrame.PathMethod.EASE_OUT : next.pathMethod();
//
// // Else-case would be linear, which doesn't change anything
//
// if(method.equals(KeyFrame.PathMethod.EASE_IN))
// transition = this.easeIn(subIterator);
//
// else if(method.equals(KeyFrame.PathMethod.EASE_OUT))
// transition = this.easeOut(subIterator);
//
// return inBetween(current, next, transition, method);
}
@@ -122,6 +189,19 @@ public class AnimationPath extends ArrayList<KeyFrame> {
return this.get(i);
}
/**
* Get previous keyframe according to current path iterator, not depending on the current frame
* @return previous keyframe in order, not depending on the current frame
*/
public KeyFrame getPreviousInOrder() {
int i = this.animationIterator - 1;
if(i < 0)
i++;
return this.get(i);
}
/**
* @return true if there are any more frames coming
*/
@@ -171,14 +251,36 @@ public class AnimationPath extends ArrayList<KeyFrame> {
return new Point(point.x - subtrahend.x, point.y - subtrahend.y);
}
/**
* Calculate point between both points
* @param p1 first point
* @param p2 second point
* @return point in the middle between both points
*/
private static Point middle(Point p1, Point p2) {
return new Point(
multiply(
add(p1, p2),
0.5
)
);
}
/**
* 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)
* @param overHalf if this inbetween is over halfway through the transition process
* @return in-between frame
*/
private static KeyFrame inBetween(KeyFrame kf1, KeyFrame kf2, double scalar, KeyFrame.PathMethod method) {
private static KeyFrame inBetween(KeyFrame kf1, KeyFrame kf2, double scalar, boolean overHalf) {
// create halfway marked point
if(overHalf)
kf1.position().setLocation(
middle(kf1.position(), kf2.position())
);
// position
Point difference = subtract(kf2.position(), kf1.position());
@@ -196,7 +298,7 @@ public class AnimationPath extends ArrayList<KeyFrame> {
height = Math.toIntExact(Math.round(kf1.height() + dH * scalar));
return new KeyFrame(pos, width, height, method);
return new KeyFrame(pos, width, height, kf2.pathMethod());
}
@Override
@@ -208,11 +310,26 @@ public class AnimationPath extends ArrayList<KeyFrame> {
return (double) subIterator/2 / this.inbetweens;
}
private double easeIn(int subIterator) {
return this.linear(subIterator/2) * this.linear(subIterator/2);
private double easeIn(int subIterator) {
return this.linear(subIterator*2) * this.linear(subIterator*2);
}
public double easeOut(int subIterator) {
return Math.sqrt(this.linear(subIterator*2));
}
/**
* Enumerator purely to describe two combined PathMethods, like a 2-dimensional PathMethod enum
*/
private enum CombinedPathMethod {
LINEAR,
LINEAR_EASE_IN,
EASE_IN,
EASE_IN_LINEAR,
LINEAR_EASE_OUT,
EASE_OUT,
EASE_OUT_LINEAR,
EASE_OUT_AND_IN;
}
}