UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

424 lines (391 loc) 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeyframeTrack = void 0; var _constants = require("../constants.js"); var _CubicInterpolant = require("../math/interpolants/CubicInterpolant.js"); var _LinearInterpolant = require("../math/interpolants/LinearInterpolant.js"); var _DiscreteInterpolant = require("../math/interpolants/DiscreteInterpolant.js"); var AnimationUtils = _interopRequireWildcard(require("./AnimationUtils.js")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /** * Represents s a timed sequence of keyframes, which are composed of lists of * times and related values, and which are used to animate a specific property * of an object. */ class KeyframeTrack { /** * Constructs a new keyframe track. * * @param {string} name - The keyframe track's name. * @param {Array<number>} times - A list of keyframe times. * @param {Array<number>} values - A list of keyframe values. * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type. */ constructor(name, times, values, interpolation) { if (name === undefined) throw new Error('THREE.KeyframeTrack: track name is undefined'); if (times === undefined || times.length === 0) throw new Error('THREE.KeyframeTrack: no keyframes in track named ' + name); /** * The track's name can refer to morph targets or bones or * possibly other values within an animated object. See {@link PropertyBinding#parseTrackName} * for the forms of strings that can be parsed for property binding. * * @type {string} */ this.name = name; /** * The keyframe times. * * @type {Float32Array} */ this.times = AnimationUtils.convertArray(times, this.TimeBufferType); /** * The keyframe values. * * @type {Float32Array} */ this.values = AnimationUtils.convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } /** * Converts the keyframe track to JSON. * * @static * @param {KeyframeTrack} track - The keyframe track to serialize. * @return {Object} The serialized keyframe track as JSON. */ static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { 'name': track.name, 'times': AnimationUtils.convertArray(track.times, Array), 'values': AnimationUtils.convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } /** * Factory method for creating a new discrete interpolant. * * @static * @param {TypedArray} [result] - The result buffer. * @return {DiscreteInterpolant} The new interpolant. */ InterpolantFactoryMethodDiscrete(result) { return new _DiscreteInterpolant.DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } /** * Factory method for creating a new linear interpolant. * * @static * @param {TypedArray} [result] - The result buffer. * @return {LinearInterpolant} The new interpolant. */ InterpolantFactoryMethodLinear(result) { return new _LinearInterpolant.LinearInterpolant(this.times, this.values, this.getValueSize(), result); } /** * Factory method for creating a new smooth interpolant. * * @static * @param {TypedArray} [result] - The result buffer. * @return {CubicInterpolant} The new interpolant. */ InterpolantFactoryMethodSmooth(result) { return new _CubicInterpolant.CubicInterpolant(this.times, this.values, this.getValueSize(), result); } /** * Defines the interpolation factor method for this keyframe track. * * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} interpolation - The interpolation type. * @return {KeyframeTrack} A reference to this keyframe track. */ setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case _constants.InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case _constants.InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case _constants.InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = 'unsupported interpolation for ' + this.ValueTypeName + ' keyframe track named ' + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn('THREE.KeyframeTrack:', message); return this; } this.createInterpolant = factoryMethod; return this; } /** * Returns the current interpolation type. * * @return {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} The interpolation type. */ getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return _constants.InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return _constants.InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return _constants.InterpolateSmooth; } } /** * Returns the value size. * * @return {number} The value size. */ getValueSize() { return this.values.length / this.times.length; } /** * Moves all keyframes either forward or backward in time. * * @param {number} timeOffset - The offset to move the time values. * @return {KeyframeTrack} A reference to this keyframe track. */ shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } /** * Scale all keyframe times by a factor (useful for frame - seconds conversions). * * @param {number} timeScale - The time scale. * @return {KeyframeTrack} A reference to this keyframe track. */ scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } /** * Removes keyframes before and after animation without changing any values within the defined time range. * * Note: The method does not shift around keys to the start of the track time, because for interpolated * keys this will change their values * * @param {number} startTime - The start time. * @param {number} endTime - The end time. * @return {KeyframeTrack} A reference to this keyframe track. */ trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = times.slice(from, to); this.values = this.values.slice(from * stride, to * stride); } return this; } /** * Performs minimal validation on the keyframe track. Returns `true` if the values * are valid. * * @return {boolean} Whether the keyframes are valid or not. */ validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error('THREE.KeyframeTrack: Invalid value size in track.', this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error('THREE.KeyframeTrack: Track is empty.', this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === 'number' && isNaN(currTime)) { console.error('THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error('THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (AnimationUtils.isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error('THREE.KeyframeTrack: Value is not a valid number.', this, i, value); valid = false; break; } } } } return valid; } /** * Optimizes this keyframe track by removing equivalent sequential keys (which are * common in morph target sequences). * * @return {AnimationClip} A reference to this animation clip. */ optimize() { // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) // times or values may be shared with other tracks, so overwriting is unsafe const times = this.times.slice(), values = this.values.slice(), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === _constants.InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = times.slice(0, writeIndex); this.values = values.slice(0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } /** * Returns a new keyframe track with copied values from this instance. * * @return {KeyframeTrack} A clone of this instance. */ clone() { const times = this.times.slice(); const values = this.values.slice(); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } /** * The value type name. * * @type {String} * @default '' */ exports.KeyframeTrack = KeyframeTrack; KeyframeTrack.prototype.ValueTypeName = ''; /** * The time buffer type of this keyframe track. * * @type {TypedArray|Array} * @default Float32Array.constructor */ KeyframeTrack.prototype.TimeBufferType = Float32Array; /** * The value buffer type of this keyframe track. * * @type {TypedArray|Array} * @default Float32Array.constructor */ KeyframeTrack.prototype.ValueBufferType = Float32Array; /** * The default interpolation type of this keyframe track. * * @type {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} * @default InterpolateLinear */ KeyframeTrack.prototype.DefaultInterpolation = _constants.InterpolateLinear;