mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
Improve number fields/labels in the UI to filter out floating point noise and truncate at correct precision (#4363)
* Improve the displayed precision of float number labels * Show actual tiny values in high-precision number fields
This commit is contained in:
committed by
Dennis Kobert
parent
31239f77c6
commit
f4609e0e3b
@@ -3,6 +3,7 @@
|
||||
import { preventEscapeClosingParentFloatingMenu } from "/src/components/layout/FloatingMenu.svelte";
|
||||
import FieldInput from "/src/components/widgets/inputs/FieldInput.svelte";
|
||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "/src/managers/input";
|
||||
import { roundAwayFloatNoise } from "/src/utility-functions/numbers";
|
||||
import { browserVersion } from "/src/utility-functions/platform";
|
||||
import type { ActionShortcut, EditorWrapper, NumberInputIncrementBehavior, NumberInputMode } from "/wrapper/pkg/graphite_wasm_wrapper";
|
||||
|
||||
@@ -213,9 +214,19 @@
|
||||
function displayText(displayValue: number | undefined, unit: string): string {
|
||||
if (displayValue === undefined) return "-";
|
||||
|
||||
const roundingPower = 10 ** Math.max(displayDecimalPlaces, 0);
|
||||
const decimalPlaces = Math.max(displayDecimalPlaces, 0);
|
||||
const roundingPower = 10 ** decimalPlaces;
|
||||
|
||||
// Values within floating point noise of zero (including -0) display as unsigned zero, unless the field's decimal precision is fine enough to display them
|
||||
const effectiveValue = Math.abs(displayValue) < Math.min(1e-12, 0.5 / roundingPower) ? 0 : roundAwayFloatNoise(displayValue);
|
||||
const unitlessDisplayValue = Math.round(effectiveValue * roundingPower) / roundingPower;
|
||||
|
||||
// Trailing zeros are trimmed only when the display is exact, so a truncated value keeps its decimal places (like "0.00" or "3.10") to indicate the truncation
|
||||
if (unitlessDisplayValue !== effectiveValue) {
|
||||
const sign = unitlessDisplayValue === 0 && effectiveValue < 0 ? "-" : "";
|
||||
return `${sign}${unitlessDisplayValue.toFixed(decimalPlaces)}${unPluralize(unit, displayValue)}`;
|
||||
}
|
||||
|
||||
const unitlessDisplayValue = Math.round(displayValue * roundingPower) / roundingPower;
|
||||
return `${unitlessDisplayValue}${unPluralize(unit, displayValue)}`;
|
||||
}
|
||||
|
||||
@@ -230,9 +241,8 @@
|
||||
// ===========================
|
||||
|
||||
function onTextFocused() {
|
||||
// The degree of precision allowed in the number that's shown when editing the number field, where additional precision is removed to round out floating point errors.
|
||||
const MAX_PRECISION = 12;
|
||||
const noFloatingImprecisionValue = value === undefined ? undefined : Number(value.toPrecision(MAX_PRECISION));
|
||||
// The number shown when editing the field, with floating point imprecision noise removed
|
||||
const noFloatingImprecisionValue = value === undefined ? undefined : roundAwayFloatNoise(value);
|
||||
|
||||
if (value === undefined) text = "";
|
||||
else if (unitIsHiddenWhenEditing) text = `${noFloatingImprecisionValue}`;
|
||||
|
||||
15
frontend/src/utility-functions/numbers.ts
Normal file
15
frontend/src/utility-functions/numbers.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// Recovers the intended number from floating point imprecision noise when that can be done reliably, e.g. 0.30000000000000004 -> 0.3.
|
||||
// Rounding to each significant digit count from 1 to 12, the first candidate within a relative 1e-13 of the original is accepted.
|
||||
// Actual high-precision values (like 0.3333333333333333) never pass the tolerance and are returned unchanged.
|
||||
export function roundAwayFloatNoise(value: number): number {
|
||||
if (value === 0 || !Number.isFinite(value)) return value === 0 ? 0 : value;
|
||||
|
||||
const exponent = Math.floor(Math.log10(Math.abs(value)));
|
||||
for (let significantDigits = 1; significantDigits <= 12; significantDigits += 1) {
|
||||
const scale = 10 ** (significantDigits - 1 - exponent);
|
||||
const rounded = Math.round(value * scale) / scale;
|
||||
if (Math.abs((rounded - value) / value) < 1e-13) return rounded;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user