Round number fields to the precision they display

`decimals` only reached `formatNumber`, so it changed the label and nothing else: typing 640.5 into
a field with `decimals: 0` stored 640.5 and displayed 641. The two existing users got away with it
because rounding happened outside the widget (`normalizeFrameRect`, the slice dialog), which their
comments had to point out.

`NumberField.clampValue` now rounds before it clamps, and every path that sets a value goes through
it (typing, scrubbing, arrow keys, the constructor), so the stored number matches the shown one and
`min`/`max` stay exact. The rounding is `roundTo` in math.ts, which `snapToStep` already did inline.

With that safe, camera `View width`/`View height` and text `Font size` become integers – they were
showing 640.000 and 32.000. Everything else that looked integral is not: positions, sprite and
collider sizes and zoom are all scrubbed, and `z` is a summed coordinate (walk.ts), not a layer.

Verified remotely against the RTX 4070 host: 270 checks, all passed (step 10 gained a check that the
field text has no decimal point and survives a reopen). `npm run check` green, 407 tests.
This commit is contained in:
root committed 2026-09-09 10:11:06 +02:00
1 parent 6cf47fd2ae
commit 1b16a55ffc
8 files changed
+80 -19

No files matched your search

+7 -3
View File
@@ -21,14 +21,18 @@ export function formatNumber(value: number, decimals = NUMBER_DECIMALS): string
return text === '-' + zero ? zero : text;
}
/** 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. The `toFixed` pass removes binary-float noise (0.1 × 3 =
* 0.30000000000000004).
* pointer deltas are fractional.
*/
export function snapToStep(value: number, stepSize: number): number {
if (!(stepSize > 0) || !Number.isFinite(value)) return value;
return Number((Math.round(value / stepSize) * stepSize).toFixed(NUMBER_DECIMALS));
return roundTo(Math.round(value / stepSize) * stepSize);
}
/**