Numeric Input/Output
NaN, Infinity and -Infinity are ordinary values in a geometry model and ordinary things for a
user to type, but JSON has no encoding for any of them. This page describes how the web application
carries them across the boundary in both directions.
The Server Half
One line of configuration does it. Program.cs sets
options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals;
so the ASP.NET Core serializer reads and writes the three values as the JSON string literals
"NaN", "Infinity" and "-Infinity" instead of failing. It sits on the AddControllers JSON
options, so every controller inherits it and no DTO, converter or endpoint has to opt in. Without it
a double.NaN anywhere in a response throws during serialization, which is why the setting is
load-bearing rather than a convenience.
The reach stops at the controllers. Hub payloads are serialized by SignalR's own protocol options,
and nothing configures them: AddSignalR() is registered bare and the service contains no
AddJsonProtocol call, so the named literals are a controller-JSON contract rather than a
service-wide one.
The Client Half
There is no shared numeric module. Each input widget formats and parses the special values itself,
and the three that ship do not agree on all of it. The widgets are also not the only readers of this
boundary: an API module that has to put a non-finite number on the wire converts it in place, as
wwwroot-src/src/api/mission.ts does for the mission command fields. That conversion is not
symmetric — it writes all three literals but recognises only the two infinity spellings on the way
back — so a NaN returned by those endpoints resolves to the caller's supplied default instead.
NumericInput.vue |
Vec3Input.vue |
Mat4Input.vue |
|
|---|---|---|---|
NaN displays as |
empty field | NaN |
0 |
Infinity / -Infinity display as |
literal text | literal text | literal text |
| An empty field parses to | null, under the default allowEmpty |
0 |
0 |
| Unparseable text on blur | stays in the box under an error message | reverts to the last valid value | reverts to the last valid value |
All three accept the same spellings on the way in: infinity, -infinity and nan
case-insensitively. The ∞ and -∞ glyphs are where they part: the single-value field and the
three-axis editor take them, the matrix grid does not. All three commit on blur or Enter rather than
per keystroke, but only the vector and matrix editors compare the parsed value against the model
before emitting. The single-value field carries no such guard and emits on every accepted commit, so
committing with Enter and then leaving the field writes the same value twice.
The consequence worth knowing: NaN does not survive a round trip through the matrix editor.
Mat4Input.vue renders it as 0, so re-committing a cell that held NaN writes a real zero.
NumericInput.vue is the input the rest of the app reaches for: no other component in the client is
embedded by anywhere near as many editors, so a new numeric field should embed it rather than repeat
the parse. Its own props, bounds and validation messages are documented at
Numeric Input.
Source Code Path
See HiNC App Anatomy for git repository links.
Web Page Application Source Code Path
wwwroot-src/src/components/widgets/NumericInput.vue— the shared single-value numeric field: localformatValue/parseValue, blur-and-Enter commit, and the rejected text left standing under an error message when a commit fails.wwwroot-src/src/components/widgets/Vec3Input.vue— the three-axis editor; carries its own copy of the same format and parse pair, plus a(x, y, z)text form.wwwroot-src/src/components/widgets/Mat4Input.vue— the sixteen-cell matrix grid; the same pair again without the glyph spellings, and with theNaN-to-zero behaviour above.wwwroot-src/src/api/mission.ts— a non-widget reader of the same boundary:numericToApiStringwrites all three literals for the mission command fields,parseMaybeInfiniteNumberreads only the two infinity spellings back.Program.cs— theAllowNamedFloatingPointLiteralssetting on the controller JSON options that lets the three values cross as JSON at all, and the bareAddSignalR()registration that does not share it.
See Also
- Vec3dControl Component — the three-axis editor, one of the three inputs that carries its own format and parse
- Mat4dControl Component — the sixteen-cell grid, the one input that renders NaN as zero
- Numeric Input — the single-value field, and the widget that carries this contract to the most callers