@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
71 lines (56 loc) • 2.5 kB
JavaScript
import { v2_distance_sqr } from "../../../core/geom/vec2/v2_distance_sqr.js";
import { inverseLerp } from "../../../core/math/inverseLerp.js";
import { lerp } from "../../../core/math/lerp.js";
import { spline3_hermite } from "../../../core/math/spline/spline3_hermite.js";
import { spline3_hermite_nearest_point } from "../../../core/math/spline/spline3_hermite_nearest_point.js";
/**
* NOTE: this operation has O(n) complexity, where n is the number of keys in the curve.
* @param {AnimationCurve} curve
* @param {number} ref_time
* @param {number} ref_value
* @returns {number} time of the nearest point on the curve
* @see AnimationCurve
* @example
* const nearest_time = animation_curve_nearest_point(curve, 0.5, 10);
* const nearest_value = curve.evaluate(nearest_time);
*/
export function animation_curve_nearest_point(curve, ref_time, ref_value) {
const keys = curve.keys;
const key_count = keys.length;
if (key_count === 0) {
// special case: empty curve
return ref_time;
}
if (key_count === 1) {
// special case: single key
return keys[0].time;
}
let nearest_time = keys[0].time;
let nearest_value = keys[0].value;
let nearest_distance_sqr = v2_distance_sqr(nearest_time, nearest_value, ref_time, ref_value);
for (let i = 0; i <= key_count - 2; i++) {
const key0 = keys[i];
const key1 = keys[i + 1];
const time_delta = key1.time - key0.time;
// Normalize reference time to the segment's [0, 1] space
const ref_t = inverseLerp(key0.time, key1.time, ref_time);
// Scale tangents by the segment duration
const m0 = key0.outTangent * time_delta;
const m1 = key1.inTangent * time_delta;
const t = spline3_hermite_nearest_point(
ref_t, ref_value,
key0.value, key1.value,
m0, m1
);
// De-normalize the resulting time back to the global timeline
const curve_time = lerp(key0.time, key1.time, t);
const curve_value = spline3_hermite(t, key0.value, key1.value, m0, m1);
const distance_sqr = v2_distance_sqr(curve_time, curve_value, ref_time, ref_value);
if (distance_sqr < nearest_distance_sqr) {
nearest_time = curve_time;
nearest_value = curve_value;
nearest_distance_sqr = distance_sqr;
}
}
return nearest_time;
}