Archived
203 lines
10 KiB
HTML
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 "{query}"">
|
|
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
|
<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>"NaN"</code>, <code>"Infinity"</code> and <code>"-Infinity"</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>
|