@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
57 lines (46 loc) • 1.72 kB
JavaScript
import { assert } from "../../assert.js";
import { v3_dot } from "./v3_dot.js";
import { v3_lerp } from "./v3_lerp.js";
/**
* Spherical linear interpolation
* NOTE: vectors are assumed to be normalized
* @see https://en.wikipedia.org/wiki/Slerp
* @param {Vector3|{set(x:number,y:number,z:number)}} result Rotated vector is written here
* @param {number} a_x First vector
* @param {number} a_y First vector
* @param {number} a_z First vector
* @param {number} b_x Second vector
* @param {number} b_y Second vector
* @param {number} b_z Second vector
* @param {number} fraction Interpolation factor
*/
export function v3_slerp(
result,
a_x, a_y, a_z,
b_x, b_y, b_z,
fraction
) {
assert.isNumber(fraction, 'fraction');
assert.notNaN(fraction, "fraction");
assert.greaterThanOrEqual(fraction, 0, "fraction");
const dot = v3_dot(
a_x, a_y, a_z,
b_x, b_y, b_z
);
if (dot >= 1 || dot <= -1) {
// avoid division by 0, both vectors are pointing in the same direction, use lerp instead
v3_lerp(result, a_x, a_y, a_z, b_x, b_y, b_z, fraction);
return;
}
// compute angle between two vectors
const theta = Math.acos(dot);
const sin_theta = Math.sin(theta);
const inv_sin_theta = 1 / sin_theta;
// TODO check for sin_theta == 0 to avoid division by 0
const scale_0 = Math.sin((1 - fraction) * theta) * inv_sin_theta;
const scale_1 = Math.sin((fraction) * theta) * inv_sin_theta;
const x = a_x * scale_0 + b_x * scale_1;
const y = a_y * scale_0 + b_y * scale_1;
const z = a_z * scale_0 + b_z * scale_1;
result.set(x, y, z);
}