34 lines
1.6 KiB
TypeScript
34 lines
1.6 KiB
TypeScript
// Pure mapping helpers for the time-scale slider (v0.9.2, values reworked in v0.14.0) — no
|
||
// game logic, unit-testable in node. The slider has 10 discrete positions on a mixed
|
||
// ×2/×2.5 ladder: 1× → 2× → 5× → 10× → 25× → 50× → 100× → 250× → 500× → 1000×, so each
|
||
// step roughly doubles the speed (matching POST /api/sim's clamp range [1, 1000]).
|
||
|
||
export const TIME_SCALES = [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000] as const;
|
||
export const MIN_SCALE: number = TIME_SCALES[0];
|
||
export const MAX_SCALE: number = TIME_SCALES[TIME_SCALES.length - 1];
|
||
|
||
const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));
|
||
|
||
// t → scale: the position maps to the nearest ladder step. Exact at the endpoints:
|
||
// sliderToScale(0) === 1, sliderToScale(1) === 1000.
|
||
export function sliderToScale(t: number): number {
|
||
const tt = clamp(Number.isFinite(t) ? t : 0, 0, 1);
|
||
return TIME_SCALES[clamp(Math.round(tt * (TIME_SCALES.length - 1)), 0, TIME_SCALES.length - 1)];
|
||
}
|
||
|
||
// scale → t: the nearest ladder position by log distance. Exact at the endpoints:
|
||
// scaleToSlider(1) === 0, scaleToSlider(1000) === 1; round-trips every value in TIME_SCALES.
|
||
export function scaleToSlider(scale: number): number {
|
||
const s = clamp(Number.isFinite(scale) ? scale : MIN_SCALE, MIN_SCALE, MAX_SCALE);
|
||
let best = 0;
|
||
let bestDist = Infinity;
|
||
for (let i = 0; i < TIME_SCALES.length; i++) {
|
||
const d = Math.abs(Math.log(s) - Math.log(TIME_SCALES[i]));
|
||
if (d < bestDist) {
|
||
bestDist = d;
|
||
best = i;
|
||
}
|
||
}
|
||
return best / (TIME_SCALES.length - 1);
|
||
}
|