package graphics; //wraps keyframe data public class Animation { public boolean looping = true; public boolean linear = true; private double[][] keyframes; private int lastFrame = -1; //index of last frame in animation //for spline interpolation BezierCurve[] splines; public Animation(int numFrames) { keyframes = new double[numFrames][2]; } //returns number of frames actually added public int numFrames() { return lastFrame + 1; } //how many seconds is this animation? public double totalTime() { if (lastFrame < 0) return 0; return keyframes[lastFrame][0]; } public void addKeyFrame(double time, double value) { if (numFrames() > 0 && keyframes[lastFrame][0] > time) { System.err.println("Cannot add keyframe - new time is earlier than existing time"); return; } if (numFrames() == keyframes.length) { System.err.println("Cannot add keyframe - keyframe array is full"); return; } lastFrame++; keyframes[lastFrame][0] = time; keyframes[lastFrame][1] = value; } //interpolates keyframe value public double get(double time) { if (linear == false && splines == null) generateCurves(); //if animation loops, wrap around total animation time //N.B. THIS OVERRIDES LAST FRAME - CHECK FOR CONTINUITY @ ENDPTS if (looping) { time %= totalTime(); if (keyframes[0][1] != keyframes[lastFrame][1]) System.err.println("Warning: possible continuity error at endpoints"); } //index of keyframe before or at specified time int frameIndex = -1; //find index where time falls in between //check edges - before first keyframe or after last? if (time <= keyframes[0][0]) return keyframes[0][1]; //value of first frame if (time >= keyframes[lastFrame][0]) return keyframes[lastFrame][1]; //value of last frame for (int i=0; i time) { frameIndex = i; break; } } if (frameIndex < 0) //should never happen { System.err.println("Could not find a value in animation for time = "+time); return -1; } //start frame is really first control point for spline if (!linear) frameIndex -= frameIndex%4; double t0 = keyframes[frameIndex][0]; double v0 = keyframes[frameIndex][1]; double t1 = keyframes[frameIndex+1][0]; double v1 = keyframes[frameIndex+1][1]; //end frame is really last control point for spline if (!linear) { t1 = keyframes[frameIndex+3][0]; v1 = keyframes[frameIndex+3][1]; } double dt = t1 - t0; //t = [0, 1] double t = (time - t0)/dt; if (linear) return lerp(t, v0, v1); else { BezierCurve spline = splines[frameIndex/4]; return spline.get(t); } } //linear interpolation double lerp(double t, double a, double b) { return a + (b-a)*t; } public void generateCurves() { if (numFrames()%4 != 0) { System.err.println("Warning: wrong # of keyframes to generate curves: " + numFrames()); return; } splines = new BezierCurve[numFrames()/4]; for (int i=0; i