import { assert } from "../../../assert.js";
import { clamp01 } from "../../../math/clamp01.js";

/**
 *
 * @param {number} a
 * @param {number} b
 * @return {number}
 */
function multiply_add_fraction(a, b) {
    const ab = a * b;
    return ab - Math.floor(ab);
}

const PHI = Math.sqrt(5) * 0.5 + 0.5;

/**
 * Generate a spherical fibonacci point
 * The points go from z = +1 down to z = -1 in a spiral. To generate samples on the +z hemisphere, just stop before i > N/2.
 *
 * @see http://lgdv.cs.fau.de/publications/publication/Pub.2015.tech.IMMD.IMMD9.spheri/
 *
 * @param {number[]|Float32Array} output 3d point
 * @param {number} output_offset
 * @param {number} i index of a point, from 0 to n-1
 * @param {number} n total number of points
 */
export function sphere_fibonacci_point(output, output_offset, i, n) {
    assert.isNonNegativeInteger(i, 'i');
    assert.isNonNegativeInteger(n, 'n');
    assert.isNonNegativeInteger(output_offset, 'output_offset');

    const phi = 2 * Math.PI * multiply_add_fraction(i, PHI - 1);

    const cosTheta = 1.0 - (2.0 * i + 1.0) * (1.0 / n);

    const sinTheta = Math.sqrt(clamp01(1.0 - cosTheta * cosTheta));

    const x = Math.cos(phi) * sinTheta;
    const y = Math.sin(phi) * sinTheta;
    const z = cosTheta;

    output[output_offset] = x;
    output[output_offset + 1] = y;
    output[output_offset + 2] = z;
}