UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

71 lines (56 loc) 2.5 kB
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; }