modules_math_math.js

/**
 * @module Math
 */

/**
 * Constrain a number between two limits.
 * @param {Number} value - Any number
 * @param {Number} min - Minimal limit for the value
 * @param {Number} max - Maximal limit for the value
 * @return {Number}
 * @example
 * constrain(999, 0, 50); // => 50
 * constrain(-999, 0, 50); // => 0
 */
export const constrain = (value, min, max) => Math.min(Math.max(value, min), max);

/**
 * Determine if two number can considered equals accounting for JS precision.
 * @param {Number} number1 - Any number
 * @param {Number} number2 - Any number
 * @param {Number} [epsilon=Number.EPSILON] - Maximum difference to consider two number equal.
 * @return {Boolean}
 * @example
 * equals(0.1 + 0.2, 0.3); // => true
 */
export const equals = (number1, number2, epsilon = Number.EPSILON) => Math.abs(number1 - number2) < epsilon;

/**
 * Return a random number between limits
 * @param {Number} [min=1] - Lower limit, or upper limit if max is omitted
 * @param {Number} [max] - Upper limit, can be omitted
 * @return {Number}
 * @example
 * random(); // => 0.5918807307648482
 * random(10); // => 4.4856764978326735
 * random(100, 200); // => 134.57047268453047
 */
export const random = (min = 1, max) => {
    let from = min;
    let to = max;
    if (max === undefined) {
        from = 0;
        to = min;
    }
    return (Math.random() * (to - from)) + from;
};

/**
 * Truncate a number to its integer part.
 * @param {Number} value - Any number
 * @return {Number}
 * @example
 * truncate(12.3); // => 12
 * truncate(-4.9); // => -4
 */
// eslint-disable-next-line no-bitwise
export const truncate = value => value << 0;

/**
 * Full rotation on radian circle
 * @type {Number}
 */
export const radianCircle = Math.PI * 2;

/**
 * Full rotation on degree circle
 * @type {Number}
 */
export const degreeCircle = 360;

/**
 * Golden ratio number
 * @type {Number}
 */
export const phi = (Math.sqrt(5) + 1) / 2;

/**
 * Return modulo with the same sign as the divisor (Floored division)
 * @param {Number} value - Dividend
 * @param {Number} divisor - Divisor
 * @return {Number}
 * @example
 * modulo(10, 3); // => 1
 * modulo(10, -3); // => -2
 */
export const modulo = (value, divisor) => {
    const remainder = value % divisor;
    return !value || Math.sign(value) === Math.sign(divisor) ? remainder : remainder + divisor;
};

/**
 * Returns an array of evenly distributed value across a range
 * @param {Number} nbValue - Number of value to generate
 * @param {Number} [min=0] - Starting value of the range
 * @param {Number} [max=1] - Ending value of the range
 * @return {Array<Number>}
 * @example
 * distribute(10); // => Number[10] evenly distributed across [0, 1]
 * distribute(5, 10, 20); // => Number[5] evenly distributed across [10, 20]
 */
export const distribute = (nbValue, min = 0, max = 1) => {
    const start = random(min, max);
    const diff = max - min;
    return [...new Array(nbValue)].map((_, i) => min + modulo(start + (i * phi * diff), diff));
};

/**
 * Add up all values passed as argument
 * @param {...Number} values - Any set of number
 * @return {Number}
 * @example
 * sum(1, 2, 3); // => 6
 */
export const sum = (...values) => values.reduce((acc, value) => acc + value, 0);

/**
 * Return the average of all values
 * @param {...Number} values - Any set of number
 * @return {Number}
 * @example
 * sum(1, 2, 3); // => 2
 */
export const average = (...values) => sum(...values) / values.length;

/**
 * Return the equivalent of a value from a scale to another
 * @param {Number} value - Value to use
 * @param {Number} fromMin - Start of the origin scale
 * @param {Number} fromMax - End of the origin scale
 * @param {Number} [toMin=0] - Start of the target scale
 * @param {Number} [toMax=1] - End of the target scale
 * @return {Number}
 * @example
 * map(5, 0, 10, 100, 200); // => 150
 */
export const map = (value, fromMin, fromMax, toMin = 0, toMax = 1) => (
    ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin) + toMin
);

/**
 * Linear extrapolation computation, useful when doing animation
 * @param {Number} from - Starting value
 * @param {Number} to - Ending value
 * @param {Number} ratio - Extrapolation ratio, 0 is the starting value and 1 the ending value
 * @return {Number}
 */
export const lerp = (from, to, ratio) => from + ((to - from) * ratio);