@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
128 lines (115 loc) • 3.32 kB
JavaScript
/**
* SI decimal prefixes, ordered from smallest to largest. Indexed by {@link magnitude_prefix_index}.
*
* Covers the full SI prefix range: `q` (quecto, 1e-30) through `Q` (quetta, 1e30).
*
* @type {string[]}
*/
export const MAGNITUDE_PREFIXES = [
'q', // 1e-30 quecto
'r', // 1e-27 ronto
'y', // 1e-24 yocto
'z', // 1e-21 zepto
'a', // 1e-18 atto
'f', // 1e-15 femto
'p', // 1e-12 pico
'n', // 1e-9 nano
'µ', // 1e-6 micro
'm', // 1e-3 milli
'', // 1
'k', // 1e3 kilo
'M', // 1e6 mega
'G', // 1e9 giga
'T', // 1e12 tera
'P', // 1e15 peta
'E', // 1e18 exa
'Z', // 1e21 zetta
'Y', // 1e24 yotta
'R', // 1e27 ronna
'Q' // 1e30 quetta
];
/**
* Decimal scale that matches each prefix in {@link MAGNITUDE_PREFIXES}, same indexing.
*
* A non-prefixed value at index `i` corresponds to scale `10^(3*(i - 10))`.
*
* @type {number[]}
*/
export const MAGNITUDE_SCALES = [
1e-30, 1e-27, 1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-9, 1e-6, 1e-3,
1,
1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24, 1e27, 1e30
];
/**
* Index of the unit-scale (`1`) entry in {@link MAGNITUDE_PREFIXES} / {@link MAGNITUDE_SCALES}.
* @type {number}
*/
const NEUTRAL_INDEX = 10;
/**
* Smallest representable scale index.
* @type {number}
*/
const MIN_INDEX = 0;
/**
* Largest representable scale index.
* @type {number}
*/
const MAX_INDEX = 20;
/**
* Pick the index into {@link MAGNITUDE_PREFIXES} / {@link MAGNITUDE_SCALES} that best represents
* `value`.
*
* "Best" here means: when `value` is divided by `MAGNITUDE_SCALES[i]`, the result lies in
* `[1, 1000)` whenever possible. The sign of `value` is ignored — magnitude is what matters.
*
* Edge cases:
* - `0`, `NaN` or any non-finite value returns the neutral index (no prefix, scale = 1)
* - values smaller than `MAGNITUDE_SCALES[0]` clamp to the smallest prefix
* - values larger than `MAGNITUDE_SCALES[MAX_INDEX]` clamp to the largest prefix
*
* @param {number} value
* @returns {number}
*/
export function magnitude_prefix_index(value) {
if (!Number.isFinite(value) || value === 0) {
return NEUTRAL_INDEX;
}
const abs = value < 0 ? -value : value;
// log1000(abs) -> shift relative to the neutral index (which represents 10^0)
const exponent_step = Math.floor(Math.log10(abs) / 3);
let index = NEUTRAL_INDEX + exponent_step;
if (index < MIN_INDEX) {
index = MIN_INDEX;
} else if (index > MAX_INDEX) {
index = MAX_INDEX;
}
return index;
}
/**
* Get the SI prefix string that best represents `value`.
*
* @example
* magnitude_prefix(1000) // 'k'
* magnitude_prefix(0.001) // 'm'
* magnitude_prefix(1) // ''
* magnitude_prefix(1.5e9) // 'G'
*
* @param {number} value
* @returns {string}
*/
export function magnitude_prefix(value) {
return MAGNITUDE_PREFIXES[magnitude_prefix_index(value)];
}
/**
* Get the decimal scale that pairs with {@link magnitude_prefix} for the same input.
*
* @example
* magnitude_scale(1000) // 1000
* magnitude_scale(0.001) // 0.001
*
* @param {number} value
* @returns {number}
*/
export function magnitude_scale(value) {
return MAGNITUDE_SCALES[magnitude_prefix_index(value)];
}