@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
JavaScript
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