Files
feuerwerk/src/ui/math.ts
T

51 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Clamps `value` into `[lo, hi]`. When `lo > hi` (e.g. a popup wider than the screen) `lo` wins. */
export function clamp(value: number, lo: number, hi: number): number {
return Math.max(lo, Math.min(hi, value));
}
/** Whole-pixel top offset that centres an item of `itemHeight` inside `containerHeight`. */
export function centreY(containerHeight: number, itemHeight: number): number {
return Math.round((containerHeight - itemHeight) / 2);
}
/** Decimal places a number field shows unless it asks for its own (`NumberFieldOptions.decimals`). */
export const NUMBER_DECIMALS = 3;
/**
* Rounds to `decimals` places and drops trailing zeros: `100`, `0.5`, `9.091`. A value that rounds
* to zero never carries a minus sign.
*/
export function formatNumber(value: number, decimals = NUMBER_DECIMALS): string {
const text = value.toFixed(decimals);
const trimmed = text.includes('.') ? text.replace(/\.?0+$/, '') : text;
return trimmed === '-0' ? '0' : trimmed;
}
/** Rounds to `decimals` places, which also removes binary-float noise (0.1 × 3 = 0.30000000000000004). */
export function roundTo(value: number, decimals = NUMBER_DECIMALS): number {
return Number.isFinite(value) ? Number(value.toFixed(decimals)) : value;
}
/**
* Rounds `value` onto the grid of `stepSize`, so scrubbing lands on clean values even though
* pointer deltas are fractional.
*/
export function snapToStep(value: number, stepSize: number): number {
if (!(stepSize > 0) || !Number.isFinite(value)) return value;
return roundTo(Math.round(value / stepSize) * stepSize);
}
/**
* Zoom multiplier for one wheel event. Exponential, so scrolling up and back down by the same
* amount lands exactly where it started. The caller clamps into its own range.
*/
export function zoomFactor(deltaY: number): number {
return Math.exp(-deltaY * 0.0015);
}
export function formatBytes(bytes: number): string {
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
// A small sprite is a few hundred bytes, and "0 KB" reads like a broken file.
return bytes >= 1024 ? `${Math.round(bytes / 1024)} KB` : `${bytes} B`;
}