UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

143 lines (131 loc) 4.29 kB
import {ChannelData, KeyframeData, ChannelInterpolation, SetCurveCallback, GetValueCallback} from './KeyframeCommon'; import {setCurveFromKeyframePairCubic, getValueCubic} from './channel/Cubic'; import {setCurveFromKeyframePairLinear, getValueLinear} from './channel/Linear'; import {mix} from '../math/_Module'; import {pushOnArrayAtEntry} from '../MapUtils'; import {copyKeyframeData, createKeyframeData} from './KeyframeSerialize'; interface KeyframePair { start: KeyframeData; end: KeyframeData; } // const _v2 = new Vector2(); export class Channel { private _valuesByPos: Map<number, number> = new Map(); protected _setCurveCallback: SetCurveCallback = setCurveFromKeyframePairLinear; protected _getValueCallback: GetValueCallback = getValueLinear; constructor(public readonly data: ChannelData) { Channel.validate(this.data); this.compute(); } static fromJSON(data: ChannelData) { return new Channel(data); } value(t: number): number { if (t == Math.floor(t)) { const value = this._valuesByPos.get(t); if (value != null) { return value; } const keyframes = this.data.keyframes; if (keyframes.length == 0) { return 0; } const firstPos = keyframes[0].pos; const lastPos = keyframes[keyframes.length - 1].pos; if (t < firstPos) { return this._valuesByPos.get(firstPos) || 0; } if (t > lastPos) { return this._valuesByPos.get(lastPos) || 0; } } const t0 = Math.floor(t); const t1 = Math.ceil(t); const v0 = this._valuesByPos.get(t0) || 0; const v1 = this._valuesByPos.get(t1) || 0; return mix(v0, v1, t - t0); } static validate(data: ChannelData) { let keyframes = data.keyframes; const keyframesByPos: Map<number, KeyframeData[]> = new Map(); for (const keyframe of keyframes) { pushOnArrayAtEntry(keyframesByPos, keyframe.pos, keyframe); } // if we have keyframes that need to be merged, adjust the sizes of the arrays if (keyframes.length != keyframesByPos.size) { const expectedSize = keyframesByPos.size; const keptKeyframes: KeyframeData[] = new Array(expectedSize); let i = 0; keyframesByPos.forEach((keyframes, pos) => { keptKeyframes[i] = keyframes[0]; i++; }); keyframes = keptKeyframes; data.keyframes.splice(expectedSize, data.keyframes.length - expectedSize); } // sort by replacing the values, not just swapping the keyframe data objects const sortedKeyframes = keyframes .sort((k1, k2) => k1.pos - k2.pos) .map((k) => { const target = createKeyframeData(); copyKeyframeData(k, target); return target; }); let i = 0; for (const keyframe of sortedKeyframes) { copyKeyframeData(keyframe, data.keyframes[i]); i++; } } private _setCallbacks() { switch (this.data.interpolation) { case ChannelInterpolation.CUBIC: { this._setCurveCallback = setCurveFromKeyframePairCubic; this._getValueCallback = getValueCubic; return; } case ChannelInterpolation.LINEAR: { this._setCurveCallback = setCurveFromKeyframePairLinear; this._getValueCallback = getValueLinear; return; } } } compute() { Channel.validate(this.data); const keyframes = this.data.keyframes; if (keyframes.length == 0) { return; } this._setCallbacks(); const firstPos = keyframes[0].pos; const lastPos = keyframes[keyframes.length - 1].pos; this._valuesByPos.clear(); if (keyframes.length == 0) { return; } if (keyframes.length == 1) { this._valuesByPos.set(keyframes[0].pos, keyframes[0].value); return; } let segmentIndex = 0; const keyframePair: KeyframePair = {start: keyframes[0], end: keyframes[1]}; this._setCurveCallback(keyframePair.start, keyframePair.end); for (let pos = firstPos; pos <= lastPos; pos++) { if (pos > keyframePair.end.pos) { segmentIndex++; keyframePair.start = keyframePair.end; keyframePair.end = keyframes[segmentIndex + 1]; this._setCurveCallback(keyframePair.start, keyframePair.end); } const value = this._getValueCallback(pos); this._valuesByPos.set(pos, value); } } // private _computeValue(pos: number, curve: CubicBezierCurve): number { // const t = this._findTForXCallback(pos, curve); // curve.getPoint(t, _v2); // console.log(pos, t, _v2.x, _v2.y); // return _v2.y; // } }