UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

83 lines (70 loc) 2.84 kB
import { assert } from "../assert.js"; import { EPSILON } from "./EPSILON.js"; import { solveQuadratic } from "./solveQuadratic.js"; const ONE_THIRD = 1.0 / 3.0; const TWO_THIRDS_PI = 2.0 * Math.PI / 3.0; /** * Return real solutions for a cubic polynomial: ax³ + bx² + cx + d * Repeated roots are written once each — multiplicity is not duplicated in the output. * Imaginary roots are not provided. * * @param {number[]|Float32Array|Float64Array} result solutions are written here * @param {number} result_offset offset into result array where solutions are written to * @param {number} a * @param {number} b * @param {number} c * @param {number} d * @returns {number} number of real roots found (0, 1, 2, or 3) */ export function solveCubic(result, result_offset, a, b, c, d) { assert.isNumber(a, 'a'); assert.isNumber(b, 'b'); assert.isNumber(c, 'c'); assert.isNumber(d, 'd'); if (Math.abs(a) < EPSILON) { // degrade to quadratic return solveQuadratic(result, result_offset, b, c, d); } // normalize: x³ + B·x² + C·x + D = 0 const inv_a = 1 / a; const B = b * inv_a; const C = c * inv_a; const D = d * inv_a; // depress via x = y - B/3 → y³ + p·y + q = 0 const B_third = B * ONE_THIRD; const p = C - B * B_third; const q = 2 * B_third * B_third * B_third - C * B_third + D; if (Math.abs(p) < EPSILON && Math.abs(q) < EPSILON) { // triple root at y = 0 result[result_offset] = -B_third; return 1; } // Cardano's discriminant criterion. ratio = (q/2)² + (p/3)³. // ratio > 0 → one real root, two complex // ratio = 0 → repeated real roots (single + double, or triple covered above) // ratio < 0 → three distinct real roots const half_q = q * 0.5; const third_p = p * ONE_THIRD; const ratio = half_q * half_q + third_p * third_p * third_p; if (ratio > EPSILON) { const sqrt_ratio = Math.sqrt(ratio); const u = Math.cbrt(-half_q + sqrt_ratio); const v = Math.cbrt(-half_q - sqrt_ratio); result[result_offset] = u + v - B_third; return 1; } if (ratio < -EPSILON) { // p must be negative for ratio < 0; sqrt(-p/3) is real const m = 2 * Math.sqrt(-third_p); const theta_third = ONE_THIRD * Math.acos(3 * q / (p * m)); result[result_offset] = m * Math.cos(theta_third) - B_third; result[result_offset + 1] = m * Math.cos(theta_third - TWO_THIRDS_PI) - B_third; result[result_offset + 2] = m * Math.cos(theta_third - 2 * TWO_THIRDS_PI) - B_third; return 3; } // ratio ≈ 0: simple + double root const u = Math.cbrt(-half_q); result[result_offset] = 2 * u - B_third; result[result_offset + 1] = -u - B_third; return 2; }