mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-16 05:02:42 +00:00
184 lines
5.6 KiB
JavaScript
184 lines
5.6 KiB
JavaScript
/**
|
||
* @file
|
||
* @copyright 2020 Aleksej Komarov
|
||
* @license MIT
|
||
*/
|
||
|
||
import { clamp, round, toFixed } from 'common/math';
|
||
|
||
const SI_SYMBOLS = [
|
||
'f', // femto
|
||
'p', // pico
|
||
'n', // nano
|
||
'μ', // micro
|
||
'm', // milli
|
||
// NOTE: This is a space for a reason. When we right align si numbers,
|
||
// in monospace mode, we want to units and numbers stay in their respective
|
||
// columns. If rendering in HTML mode, this space will collapse into
|
||
// a single space anyway.
|
||
' ',
|
||
'k', // kilo
|
||
'M', // mega
|
||
'G', // giga
|
||
'T', // tera
|
||
'P', // peta
|
||
'E', // exa
|
||
'Z', // zetta
|
||
'Y', // yotta
|
||
'R', // ronna
|
||
'Q', // quecca
|
||
'F',
|
||
'N',
|
||
'H',
|
||
];
|
||
|
||
const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' ');
|
||
|
||
/**
|
||
* Formats a number to a human readable form, by reducing it to SI units.
|
||
* TODO: This is quite a shit code and shit math, needs optimization.
|
||
*/
|
||
export const formatSiUnit = (value, minBase1000 = -SI_BASE_INDEX, unit = '') => {
|
||
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||
return value;
|
||
}
|
||
const realBase10 = Math.floor(Math.log10(value));
|
||
const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10));
|
||
const realBase1000 = Math.floor(realBase10 / 3);
|
||
const base1000 = Math.floor(base10 / 3);
|
||
const symbolIndex = clamp(SI_BASE_INDEX + base1000, 0, SI_SYMBOLS.length);
|
||
const symbol = SI_SYMBOLS[symbolIndex];
|
||
const scaledNumber = value / Math.pow(1000, base1000);
|
||
const scaledPrecision = realBase1000 > minBase1000 ? 2 + base1000 * 3 - base10 : 0;
|
||
// TODO: Make numbers bigger than precision value show
|
||
// up to 2 decimal numbers.
|
||
const finalString = toFixed(scaledNumber, scaledPrecision) + ' ' + symbol + unit;
|
||
return finalString.trim();
|
||
};
|
||
|
||
export const formatPower = (value, minBase1000 = 0) => {
|
||
return formatSiUnit(value, minBase1000, 'W');
|
||
};
|
||
|
||
export const formatMoney = (value, precision = 0) => {
|
||
if (!Number.isFinite(value)) {
|
||
return value;
|
||
}
|
||
// Round the number and make it fixed precision
|
||
let fixed = round(value, precision);
|
||
if (precision > 0) {
|
||
fixed = toFixed(value, precision);
|
||
}
|
||
fixed = String(fixed);
|
||
// Place thousand separators
|
||
const length = fixed.length;
|
||
let indexOfPoint = fixed.indexOf('.');
|
||
if (indexOfPoint === -1) {
|
||
indexOfPoint = length;
|
||
}
|
||
let result = '';
|
||
for (let i = 0; i < length; i++) {
|
||
if (i > 0 && i < indexOfPoint && (indexOfPoint - i) % 3 === 0) {
|
||
// Thin space
|
||
result += '\u2009';
|
||
}
|
||
result += fixed.charAt(i);
|
||
}
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* Formats a floating point number as a number on the decibel scale.
|
||
*/
|
||
export const formatDb = (value) => {
|
||
const db = (20 * Math.log(value)) / Math.log(10);
|
||
const sign = db >= 0 ? '+' : '–';
|
||
let formatted = Math.abs(db);
|
||
if (formatted === Infinity) {
|
||
formatted = 'Inf';
|
||
} else {
|
||
formatted = toFixed(formatted, 2);
|
||
}
|
||
return sign + formatted + ' dB';
|
||
};
|
||
|
||
const SI_BASE_TEN_UNIT = [
|
||
'',
|
||
'· 10³', // kilo
|
||
'· 10⁶', // mega
|
||
'· 10⁹', // giga
|
||
'· 10¹²', // tera
|
||
'· 10¹⁵', // peta
|
||
'· 10¹⁸', // exa
|
||
'· 10²¹', // zetta
|
||
'· 10²⁴', // yotta
|
||
'· 10²⁷', // ronna
|
||
'· 10³⁰', // quecca
|
||
'· 10³³',
|
||
'· 10³⁶',
|
||
'· 10³⁹',
|
||
];
|
||
|
||
const SI_BASE_TEN_INDEX = SI_BASE_TEN_UNIT.indexOf(' ');
|
||
|
||
/**
|
||
* Formats a number to a human readable form, by reducing it to SI units.
|
||
* TODO: This is quite a shit code and shit math, needs optimization.
|
||
*/
|
||
export const formatSiBaseTenUnit = (value, minBase1000 = -SI_BASE_TEN_INDEX, unit = '') => {
|
||
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||
return value;
|
||
}
|
||
const realBase10 = Math.floor(Math.log10(value));
|
||
const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10));
|
||
const realBase1000 = Math.floor(realBase10 / 3);
|
||
const base1000 = Math.floor(base10 / 3);
|
||
const symbolIndex = clamp(SI_BASE_TEN_INDEX + base1000, 0, SI_BASE_TEN_UNIT.length);
|
||
const symbol = SI_BASE_TEN_UNIT[symbolIndex];
|
||
const scaledNumber = value / Math.pow(1000, base1000);
|
||
const scaledPrecision = realBase1000 > minBase1000 ? 2 + base1000 * 3 - base10 : 0;
|
||
// TODO: Make numbers bigger than precision value show
|
||
// up to 2 decimal numbers.
|
||
const finalString = toFixed(scaledNumber, scaledPrecision) + ' ' + symbol + ' ' + unit;
|
||
return finalString.trim();
|
||
};
|
||
|
||
/**
|
||
* Formats decisecond count into HH::MM::SS display by default
|
||
* "short" format does not pad and adds hms suffixes
|
||
*/
|
||
export const formatTime = (val, formatType) => {
|
||
// THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER
|
||
// HH:MM:SS
|
||
// 00:02:13
|
||
const seconds = toFixed(Math.floor((val / 10) % 60));
|
||
const minutes = toFixed(Math.floor((val / (10 * 60)) % 60));
|
||
const hours = toFixed(Math.floor((val / (10 * 60 * 60)) % 24));
|
||
switch (formatType) {
|
||
case 'short': {
|
||
const hours_truncated = hours > 0 ? `${hours}h` : '';
|
||
const minutes_truncated = minutes > 0 ? `${minutes}m` : '';
|
||
const seconds_truncated = seconds > 0 ? `${seconds}s` : '';
|
||
return `${hours_truncated}${minutes_truncated}${seconds_truncated}`;
|
||
}
|
||
default: {
|
||
const seconds_padded = seconds.padStart(2, '0');
|
||
const minutes_padded = minutes.padStart(2, '0');
|
||
const hours_padded = hours.padStart(2, '0');
|
||
return `${hours_padded}:${minutes_padded}:${seconds_padded}`;
|
||
}
|
||
}
|
||
};
|
||
|
||
/* VOREStation Addition Start */
|
||
export const formatCommaNumber = (value) => {
|
||
if (!Number.isFinite(value)) {
|
||
return value;
|
||
}
|
||
// From http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
|
||
let parts = value.toString().split('.');
|
||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||
return parts.join('.');
|
||
};
|
||
/* VOREStation Addition End */
|