This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/anatomy/conventions/numeric-io-utilities.html
T
2026-09-09 17:35:23 +08:00

203 lines
10 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Numeric Input/Output | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Numeric Input/Output | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-NumericIoUtilities">
<h1 id="numeric-inputoutput">Numeric Input/Output</h1>
<p><code>NaN</code>, <code>Infinity</code> and <code>-Infinity</code> 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.</p>
<h2 id="the-server-half">The Server Half</h2>
<p>One line of configuration does it. <code>Program.cs</code> sets</p>
<pre><code class="lang-csharp">options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals;
</code></pre>
<p>so the ASP.NET Core serializer reads and writes the three values as the JSON string literals
<code>&quot;NaN&quot;</code>, <code>&quot;Infinity&quot;</code> and <code>&quot;-Infinity&quot;</code> instead of failing. It sits on the <code>AddControllers</code> JSON
options, so every controller inherits it and no DTO, converter or endpoint has to opt in. Without it
a <code>double.NaN</code> anywhere in a response throws during serialization, which is why the setting is
load-bearing rather than a convenience.</p>
<p>The reach stops at the controllers. Hub payloads are serialized by SignalR's own protocol options,
and nothing configures them: <code>AddSignalR()</code> is registered bare and the service contains no
<code>AddJsonProtocol</code> call, so the named literals are a controller-JSON contract rather than a
service-wide one.</p>
<h2 id="the-client-half">The Client Half</h2>
<p>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
<code>wwwroot-src/src/api/mission.ts</code> 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 <code>NaN</code> returned by those endpoints resolves to the caller's supplied default instead.</p>
<table>
<thead>
<tr>
<th></th>
<th><code>NumericInput.vue</code></th>
<th><code>Vec3Input.vue</code></th>
<th><code>Mat4Input.vue</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>NaN</code> displays as</td>
<td>empty field</td>
<td><code>NaN</code></td>
<td><code>0</code></td>
</tr>
<tr>
<td><code>Infinity</code> / <code>-Infinity</code> display as</td>
<td>literal text</td>
<td>literal text</td>
<td>literal text</td>
</tr>
<tr>
<td>An empty field parses to</td>
<td><code>null</code>, under the default <code>allowEmpty</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
</tr>
<tr>
<td>Unparseable text on blur</td>
<td>stays in the box under an error message</td>
<td>reverts to the last valid value</td>
<td>reverts to the last valid value</td>
</tr>
</tbody>
</table>
<p>All three accept the same spellings on the way in: <code>infinity</code>, <code>-infinity</code> and <code>nan</code>
case-insensitively. The <code>∞</code> and <code>-∞</code> 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.</p>
<p>The consequence worth knowing: <strong><code>NaN</code> does not survive a round trip through the matrix editor.</strong>
<code>Mat4Input.vue</code> renders it as <code>0</code>, so re-committing a cell that held <code>NaN</code> writes a real zero.</p>
<p><code>NumericInput.vue</code> 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
<a class="xref" href="../widget/numeric-input.html">Numeric Input</a>.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-page-application-source-code-path">Web Page Application Source Code Path</h3>
<ul>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the shared single-value numeric field: local <code>formatValue</code> / <code>parseValue</code>, blur-and-Enter commit, and the rejected text left standing under an error message when a commit fails.</li>
<li><code>wwwroot-src/src/components/widgets/Vec3Input.vue</code> — the three-axis editor; carries its own copy of the same format and parse pair, plus a <code>(x, y, z)</code> text form.</li>
<li><code>wwwroot-src/src/components/widgets/Mat4Input.vue</code> — the sixteen-cell matrix grid; the same pair again without the glyph spellings, and with the <code>NaN</code>-to-zero behaviour above.</li>
<li><code>wwwroot-src/src/api/mission.ts</code> — a non-widget reader of the same boundary: <code>numericToApiString</code> writes all three literals for the mission command fields, <code>parseMaybeInfiniteNumber</code> reads only the two infinity spellings back.</li>
<li><code>Program.cs</code> — the <code>AllowNamedFloatingPointLiterals</code> setting on the controller JSON options that lets the three values cross as JSON at all, and the bare <code>AddSignalR()</code> registration that does not share it.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../widget/vec3d-control.html">Vec3dControl Component</a> — the three-axis editor, one of the three inputs that carries its own format and parse</li>
<li><a class="xref" href="../widget/mat4d-control.html">Mat4dControl Component</a> — the sixteen-cell grid, the one input that renders NaN as zero</li>
<li><a class="xref" href="../widget/numeric-input.html">Numeric Input</a> — the single-value field, and the widget that carries this contract to the most callers</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>