Skip to content

Instantly share code, notes, and snippets.

@RustyKnight
Last active May 27, 2018 04:03
Show Gist options
  • Save RustyKnight/ec65186aa1f931d453f604010a602ae4 to your computer and use it in GitHub Desktop.
Save RustyKnight/ec65186aa1f931d453f604010a602ae4 to your computer and use it in GitHub Desktop.
A concept of a generic, reusable TimeLine which can be used to calculate a value between key frames based on the progression through the timeline
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class TimeLine<T> {
private Map<Float, KeyFrame<T>> mapEvents;
private Blender<T> blender;
private SplineInterpolator splineInterpolator;
public TimeLine(Blender<T> blender) {
this(blender, null);
}
public TimeLine(Blender<T> blender, SplineInterpolator splineInterpolator) {
mapEvents = new TreeMap<>();
this.blender = blender;
this.splineInterpolator = splineInterpolator;
}
public void setBlender(Blender<T> blender) {
this.blender = blender;
}
public Blender<T> getBlender() {
return blender;
}
public void setSplineInterpolator(SplineInterpolator splineInterpolator) {
this.splineInterpolator = splineInterpolator;
}
public SplineInterpolator getSplineInterpolator() {
return splineInterpolator;
}
public void add(float progress, T value) {
mapEvents.put(progress, new KeyFrame<T>(progress, value));
}
public T getValueAt(float progress) {
if (progress < 0) {
progress = 0;
} else if (progress > 1) {
progress = 1;
}
SplineInterpolator interpolator = getSplineInterpolator();
if (interpolator != null) {
progress = (float)interpolator.interpolate(progress);
}
List<KeyFrame<T>> keyFrames = getKeyFramesBetween(progress);
float max = keyFrames.get(1).progress - keyFrames.get(0).progress;
float value = progress - keyFrames.get(0).progress;
float weight = value / max;
T blend = blend(keyFrames.get(0).getValue(), keyFrames.get(1).getValue(), 1f - weight);
return blend;
}
public List<KeyFrame<T>> getKeyFramesBetween(float progress) {
List<KeyFrame<T>> frames = new ArrayList<>(2);
int startAt = 0;
Float[] keyFrames = mapEvents.keySet().toArray(new Float[mapEvents.size()]);
while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
startAt++;
}
if (startAt >= keyFrames.length) {
startAt = keyFrames.length - 1;
}
frames.add(mapEvents.get(keyFrames[startAt - 1]));
frames.add(mapEvents.get(keyFrames[startAt]));
return frames;
}
protected T blend(T start, T end, float ratio) {
return blender.blend(start, end, ratio);
}
public static interface Blender<T> {
public T blend(T start, T end, float ratio);
}
public class KeyFrame<T> {
private float progress;
private T value;
public KeyFrame(float progress, T value) {
this.progress = progress;
this.value = value;
}
public float getProgress() {
return progress;
}
public T getValue() {
return value;
}
@Override
public String toString() {
return "KeyFrame progress = " + getProgress() + "; value = " + getValue();
}
}
}
@RustyKnight
Copy link
Author

RustyKnight commented May 26, 2018

A sample implementation of a Blender for "float" values

	public class FloatBlender implements TimeLine.Blender<Float> {

		@Override
		public Float blend(Float start, Float end, float ratio) {
			float ir = (float) 1.0 - ratio;
			return (float) (start * ratio + end * ir);
		}

	}

A simple timeline can be established with a series of "key frames", which specify key values at given points along the timeline

timeLine = new DurationTimeLine<>(new FloatBlender());
timeLine.add(0.0f, 0.0f);
timeLine.add(0.5f, 1.0f);
timeLine.add(1.0f, 0.0f);

A value can then be extracted from the TimeLine using getValueAt(float) (where at is a normalised value between 0-1) which will return the value for the specified progress

The overall concept (and partial motivation) is demonstrated at https://stackoverflow.com/questions/50539835/how-can-i-make-a-polygon-object-in-java-pulsate-like-the-chalice-in-the-atari-g/50540173#50540173

The "concept" is also used at

@RustyKnight
Copy link
Author

The TimeLine makes use of a SplineInterpolator which can be found at https://gist.github.com/RustyKnight/4da7747831e172dbb6f77d8310ee0023

This allows the TimeLine to support interpolations of the value based on the time curve - that is, things like slow-in, slow-out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment