function* animation(spec, frame_pace = 1) {
const OUT = 1e30;
let variables = Object.keys(spec);
let frames = [];
let lastFrame = 0;
for (let v of variables) {
let anim = spec[v];
anim.sort((a, b) => a[0] - b[0]);
lastFrame = Math.max(lastFrame, anim[anim.length - 1][0]);
}
let state = {};
for (let v of variables) {
state[v] = {
interpolator: (f) => OUT,
nextSpecFrame: 0,
prevFrame: 0,
prevValue: OUT
};
}
for (let f = 0; f <= lastFrame; f += frame_pace) {
for (let v of variables) {
let nextSpec = spec[v][state[v].nextSpecFrame];
if (f >= nextSpec[0]) {
let prevValue = nextSpec[nextSpec.length - 1];
if (state[v].nextSpecFrame < spec[v].length - 1)
state[v].nextSpecFrame++;
let prevFrame = f;
nextSpec = spec[v][state[v].nextSpecFrame];
let nextFrame = nextSpec[0];
if (f == nextFrame) state[v].interpolator = (frame) => prevValue;
else {
let bezier = casteljau(prevValue, ...nextSpec.slice(1));
state[v].interpolator = (frame) =>
bezier((frame - prevFrame) / (nextFrame - prevFrame));
}
state[v].prevFrame = f;
state[v].prevValue = prevValue;
}
}
let varValues = { frame: f };
for (let v of variables) varValues[v] = state[v].interpolator(f);
yield varValues;
}
}