UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

160 lines 6.35 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { serializable } from "../engine/engine_serialization_decorator.js"; /** * Keyframe is a representation of a keyframe in an AnimationCurve. */ export class Keyframe { time = 0; value = 0; inTangent = Infinity; inWeight; outTangent = Infinity; outWeight; weightedMode; constructor(time = 0, value = 0) { this.time = time; this.value = value; } } __decorate([ serializable() ], Keyframe.prototype, "time", void 0); __decorate([ serializable() ], Keyframe.prototype, "value", void 0); __decorate([ serializable() ], Keyframe.prototype, "inTangent", void 0); __decorate([ serializable() ], Keyframe.prototype, "inWeight", void 0); __decorate([ serializable() ], Keyframe.prototype, "outTangent", void 0); __decorate([ serializable() ], Keyframe.prototype, "outWeight", void 0); __decorate([ serializable() ], Keyframe.prototype, "weightedMode", void 0); /** * AnimationCurve is a representation of a curve that can be used to animate values over time. */ export class AnimationCurve { /** * Creates an animation curve that goes from the `from` value to the `to` value over the given `duration`. */ static linearFromTo(from, to, duration) { const curve = new AnimationCurve(); const keyframe1 = new Keyframe(); keyframe1.time = 0; keyframe1.value = from; const keyframe2 = new Keyframe(); keyframe2.time = duration; keyframe2.value = to; curve.keys.push(keyframe1, keyframe2); return curve; } /** Creates an animation curve with just one keyframe */ static constant(value) { const curve = new AnimationCurve(); const keyframe = new Keyframe(); keyframe.time = 0; keyframe.value = value; curve.keys.push(keyframe); return curve; } /** * The keyframes that define the curve. */ keys = []; /** * Clones this AnimationCurve and returns a new instance with the same keyframes (the keyframes are also cloned). */ clone() { const curve = new AnimationCurve(); curve.keys = this.keys?.map(k => { const key = new Keyframe(); key.time = k.time; key.value = k.value; key.inTangent = k.inTangent; key.inWeight = k.inWeight; key.outTangent = k.outTangent; key.outWeight = k.outWeight; key.weightedMode = k.weightedMode; return key; }) || []; return curve; } /** The duration of the curve, which is the time of the last keyframe. */ get duration() { if (!this.keys || this.keys.length == 0) return 0; return this.keys[this.keys.length - 1].time; } /** Evaluates the curve at the given time and returns the value of the curve at that time. * @param time The time at which to evaluate the curve. * @returns The value of the curve at the given time. */ evaluate(time) { if (!this.keys || this.keys.length == 0) return 0; if (this.keys.length === 1) { return this.keys[0].value; } // if the first keyframe time is already greater than the time we want to evaluate // then we dont need to iterate if (this.keys[0].time >= time) { return this.keys[0].value; } for (let i = 0; i < this.keys.length; i++) { const kf = this.keys[i]; if (kf.time <= time) { const hasNextKeyframe = i + 1 < this.keys.length; if (hasNextKeyframe) { const nextKf = this.keys[i + 1]; // if the next if (nextKf.time < time) continue; // tangents are set to Infinity if interpolation is set to constant - in that case we should always return the floored value if (!isFinite(kf.outTangent) || !isFinite(nextKf.inTangent)) return kf.value; return AnimationCurve.interpolateValue(time, kf, nextKf); } else { return kf.value; } } } return this.keys[this.keys.length - 1].value; } static interpolateValue(time, keyframe1, keyframe2) { const startTime1 = keyframe1.time; const startValue1 = keyframe1.value; const outTangent1 = keyframe1.outTangent; const startTime2 = keyframe2.time; const startValue2 = keyframe2.value; const inTangent2 = keyframe2.inTangent; // could be precomputed and stored in the keyframes maybe const timeDifference = startTime2 - startTime1; const timeDifferenceSquared = timeDifference * timeDifference; const timeDifferenceCubed = timeDifferenceSquared * timeDifference; const a = ((outTangent1 + inTangent2) * timeDifference - 2 * (startValue2 - startValue1)) / timeDifferenceCubed; const b = (3 * (startValue2 - startValue1) - (inTangent2 + 2 * outTangent1) * timeDifference) / timeDifferenceSquared; const c = outTangent1; const d = startValue1; const timeDelta = time - startTime1; const timeDeltaSquared = timeDelta * timeDelta; const timeDeltaCubed = timeDeltaSquared * timeDelta; return a * timeDeltaCubed + b * timeDeltaSquared + c * timeDelta + d; } } __decorate([ serializable(Keyframe) ], AnimationCurve.prototype, "keys", void 0); //# sourceMappingURL=AnimationCurve.js.map