Archived
deploy.
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Widgets | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Widgets | 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-Widgets">
|
||||
<h1 id="widgets">Widgets</h1>
|
||||
|
||||
<p>The reusable controls that pages embed rather than own. Each page here documents one control — most
|
||||
of them under the SPA's <code>components/widgets/</code> directory — together with its WPF counterpart where one
|
||||
exists, so a reader who meets the same control on four screens reads about it once.</p>
|
||||
<p>A control earns a page here when more than one screen embeds it. A control that only ever appears
|
||||
on one screen is documented on that screen's page instead.</p>
|
||||
<p>Ordered by how widely each control is embedded across the two clients, most-embedded first.</p>
|
||||
<h2 id="pages">Pages</h2>
|
||||
<ul>
|
||||
<li><a href="numeric-input.html">Numeric Input</a> — The single-value numeric field every editor embeds: blur-and-Enter commit, inclusive bounds, and the Infinity and NaN spellings; the most widely embedded control in the app</li>
|
||||
<li><a href="object-management-menu-button.html">Object Management Menu Button</a> — Load / Save / Paste / XML for any object that can produce an XML source</li>
|
||||
<li><a href="renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a> — The <code>View ▾</code> camera-preset menu every 3D canvas docks</li>
|
||||
<li><a href="vec3d-control.html">Vec3dControl</a> — The three-component vector editor: per-axis and single-field text modes, with local special-value handling</li>
|
||||
<li><a href="polar-resolution-2d-panel.html">Polar Resolution 2D Panel</a> — The editor for <a class="xref" href="../../api/Hi.Geom.Resolution.PolarResolution2d.html">PolarResolution2d</a>, embedded by the two holder editors that tessellate a revolved profile; the one control here with no SPA widget of its own</li>
|
||||
<li><a href="mat4d-control.html">Mat4dControl</a> — The sixteen-cell 4x4 matrix grid, with its Identity and Invert events</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a href="numeric-input.html">Numeric Input</a> — the single-value numeric field the other editors on this shelf are built beside</li>
|
||||
<li><a href="../conventions/index.html">Conventions</a> — the file-path and numeric-value contracts two of these controls implement</li>
|
||||
<li><a href="../shell/index.html">App Shell</a> — the frame these controls are embedded inside</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>
|
||||
@@ -0,0 +1,182 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mat4dControl Component | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Mat4dControl Component | 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-Mat4dControl">
|
||||
<h1 id="mat4dcontrol-component">Mat4dControl Component</h1>
|
||||
|
||||
<p>Mat4dControl edits and displays a 4x4 transform matrix. It is an embedded widget with no route and
|
||||
no Control-Tree node of its own, reached only through the editor that hosts it.</p>
|
||||
<h2 id="key-model">Key Model</h2>
|
||||
<p>The persisted model is <a class="xref" href="../../api/Hi.Geom.Mat4d.html">Mat4d</a>, exposed over <code>/api/Mat4d</code> by <code>Widget/Mat4dController.cs</code>.</p>
|
||||
<p>The widget's own model is a plain array of sixteen numbers, exported as <code>type Mat4</code> from
|
||||
<code>wwwroot-src/src/components/widgets/Mat4Input.vue</code>. The layout is column-major, matching the backend
|
||||
Mat4d: cell (row, col) resolves to index <code>col * 4 + row</code>, so indices 0 to 3 are the first column and
|
||||
12 to 15 the translation column. <code>wwwroot-src/src/api/transformer.ts</code> states the same column-major
|
||||
contract on the wire. A model shorter than sixteen numbers, or not an array at all, is padded out
|
||||
with zeros before display, and a longer one is truncated to sixteen.</p>
|
||||
<p>The widget carries no key and calls no endpoint. Its host,
|
||||
<code>wwwroot-src/src/components/topo/StaticFreeformEditor.vue</code>, binds it with <code>v-model</code> and persists
|
||||
each change through <code>updateStaticFreeformMat</code> from <code>wwwroot-src/src/api/transformer.ts</code>, which posts
|
||||
the whole sixteen-number array to <code>/api/StaticFreeform/Update</code>; <code>Mech/Topo/StaticFreeformController.cs</code>
|
||||
rejects a payload that is not exactly sixteen elements.</p>
|
||||
<p>The props are <code>modelValue</code>, <code>disable</code>, <code>readonly</code>, <code>identity</code> (on by default) and <code>invert</code> (off by
|
||||
default), and the emits are <code>update:modelValue</code>, <code>identity</code> and <code>invert</code>. <code>disable</code> both disables
|
||||
the cells and dims the whole block.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>Mat4dControl
|
||||
<ul>
|
||||
<li><code>Matrix Grid</code> — four rows of four numeric cells, each cell right-aligned</li>
|
||||
<li><code>Identity Button</code> — present when the identity flag is set, which is the default</li>
|
||||
<li><code>Invert Button</code> — present only when the invert flag is set</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>The cells and both buttons are inactive while the control is disabled or readonly.</p>
|
||||
<h2 id="feature">Feature</h2>
|
||||
<h3 id="single-input-mode">Single input mode</h3>
|
||||
<p>The grid of sixteen cells is the only editing surface, and each cell shows the full precision of its
|
||||
number with no rounding for display.</p>
|
||||
<h3 id="matrix-operations">Matrix operations</h3>
|
||||
<ul>
|
||||
<li>Identity — the button raises an <code>identity</code> event and writes nothing itself; the host supplies the
|
||||
matrix. StaticFreeformEditor pushes its own identity constant through <code>updateStaticFreeformMat</code>.</li>
|
||||
<li>Invert — the button raises an <code>invert</code> event and computes nothing itself. StaticFreeformEditor
|
||||
calls <code>invertStaticFreeformMat</code>, which posts to <code>/api/StaticFreeform/InvertMat</code> and feeds the
|
||||
sixteen numbers that come back into the model.</li>
|
||||
</ul>
|
||||
<h3 id="commit-and-special-values">Commit and special values</h3>
|
||||
<p>Each cell commits on blur or Enter, and the widget emits the entire sixteen-number array rather than
|
||||
a single index. An unparseable cell reverts to its previous value, an empty cell becomes 0, and a
|
||||
cell whose parsed value is unchanged emits nothing. Formatting and parsing are local to the
|
||||
component: Infinity and -Infinity are shown and parsed as literal text, while NaN and a missing cell
|
||||
both display as 0, so NaN is not round-tripped.</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>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/widgets/Mat4Input.vue</code> — the widget itself: the column-major
|
||||
sixteen-cell grid, the Identity and Invert buttons, and the local format, parse and commit helpers</li>
|
||||
<li><code>wwwroot-src/src/components/topo/StaticFreeformEditor.vue</code> — the host, and the full binding
|
||||
contract in one place: <code>v-model</code>, the two button flags, <code>@update:model-value</code>, <code>@identity</code>, <code>@invert</code></li>
|
||||
<li><code>wwwroot-src/src/api/transformer.ts</code> — <code>updateStaticFreeformMat</code> and <code>invertStaticFreeformMat</code></li>
|
||||
<li><code>Mech/Topo/StaticFreeformController.cs</code> — the backend behind those two endpoints: Update takes a
|
||||
sixteen-element array, InvertMat returns the inverted sixteen numbers</li>
|
||||
<li><code>Widget/Mat4dController.cs</code> — the <code>/api/Mat4d</code> REST surface: New, NewWithValue, Get, Update,
|
||||
UpdateAt, ParseAndUpdate, SetIdentity, Transpose and Inverse. The SPA reaches none of them; the
|
||||
widget's host persists through the StaticFreeform endpoints instead.</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/widgets.ts</code> — the Invert label and both button tooltips under <code>widgets.matInput</code></li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="vec3d-control.html">Vec3dControl Component</a> — the sibling numeric-geometry editor, and the one a reader usually wants next</li>
|
||||
<li><a class="xref" href="../conventions/numeric-io-utilities.html">Numeric Input/Output</a> — why NaN and the infinities are text on the wire, and why NaN does not survive this grid</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>
|
||||
@@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Numeric Input | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Numeric Input | 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-NumericInput">
|
||||
<h1 id="numeric-input">Numeric Input</h1>
|
||||
|
||||
<p>NumericInput is the single-value numeric field the rest of the web application embeds. It has no
|
||||
route and no Control-Tree node of its own: it is reached only through the editors that host it,
|
||||
and those span the equipment and mission trees, the Tool House tool editors, the Legacy Controller
|
||||
tabs, the transformer and geometry editors and the Preference menu. The rule for carrying <code>NaN</code> and
|
||||
the infinities across the JSON boundary is stated once in
|
||||
<a class="xref" href="../conventions/numeric-io-utilities.html">Numeric Input/Output</a>; this page is the widget that implements
|
||||
the client end of it.</p>
|
||||
<h2 id="the-binding-contract">The Binding Contract</h2>
|
||||
<p>The widget owns no value. It holds the raw text the user is typing and nothing else, and every
|
||||
committed number is handed straight back to the host, which decides what to persist and when.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Prop</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>modelValue</code></td>
|
||||
<td><code>number</code>, <code>null</code> or <code>undefined</code> — the value to show</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>label</code></td>
|
||||
<td>the field's label, inside the outline; floats to the top of it once the field has content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hint</code></td>
|
||||
<td>text below the field</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>unit</code></td>
|
||||
<td>a display-only suffix inside the field</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>readonly</code> / <code>disable</code></td>
|
||||
<td>passed through to the underlying field</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>min</code> / <code>max</code></td>
|
||||
<td>inclusive bounds, checked on commit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>allowEmpty</code></td>
|
||||
<td>whether a blank field commits <code>null</code>; defaults to on</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hideBottomSpace</code></td>
|
||||
<td>stop holding space below the field; a hint or message still renders when there is one</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>rules</code></td>
|
||||
<td>extra validation functions over the raw <strong>text</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Two events leave the widget.</p>
|
||||
<ul>
|
||||
<li><code>update:modelValue</code> carries a number or <code>null</code>. <code>null</code> is a real outcome rather than an error
|
||||
signal, so a host that must not receive one either turns <code>allowEmpty</code> off or filters what it
|
||||
gets: the cutter section panel returns early on <code>null</code> so that clearing a field cannot write a
|
||||
zero into the profile, and the graphic-cache menu rejects <code>null</code> and every non-finite value
|
||||
before it calls the server.</li>
|
||||
<li><code>parseError</code> carries the raw text that failed to parse. No shipped host listens for it, so a
|
||||
parse failure is visible only as the message under the field.</li>
|
||||
</ul>
|
||||
<h2 id="commit-semantics">Commit Semantics</h2>
|
||||
<p>Typing changes nothing but the text in the box. The commit runs on blur and on Enter, and on
|
||||
nothing else — there is no per-keystroke emit, no debounce and no timer. The underlying control is
|
||||
a plain text field rather than a browser number input, so there is no spinner, no step and no
|
||||
keystroke-level filtering: any text at all may sit in the box until the field is committed.</p>
|
||||
<p>A commit parses the text once and then takes one of three exits. On success the error strip is
|
||||
cleared, <code>update:modelValue</code> is emitted, and the box is rewritten from the parsed number. On text
|
||||
that does not parse, <code>parseError</code> is emitted and a message appears. On a number outside the bounds,
|
||||
a message appears. Neither failing exit emits <code>update:modelValue</code>, and neither rewrites the box.</p>
|
||||
<p>Enter commits without moving focus, so the value is committed again when the field is finally left.
|
||||
The widget carries no equality guard — it emits whenever the text parses and passes the bounds,
|
||||
whether or not the result differs from <code>modelValue</code> — so that second commit reaches the host as a
|
||||
second, identical write. Here the widget parts company with the vector and matrix editors, both of
|
||||
which compare against the model before emitting.</p>
|
||||
<h2 id="bounds-and-special-values">Bounds and Special Values</h2>
|
||||
<p><code>min</code> and <code>max</code> are inclusive and are enforced inside the widget, before anything is emitted: a
|
||||
number below <code>min</code> or above <code>max</code> is refused and the host never sees it.</p>
|
||||
<p><strong>The bounds apply to finite numbers only.</strong> <code>Infinity</code>, <code>-Infinity</code>, <code>NaN</code> and the <code>null</code> a blank
|
||||
field produces all skip the range check, so a field declared with a <code>min</code> of 0 still commits
|
||||
<code>-Infinity</code> and <code>NaN</code>. Where that matters, the host filters what it receives.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Typed</th>
|
||||
<th>Committed</th>
|
||||
<th>Shown afterwards</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Infinity</code> in any case, or <code>∞</code></td>
|
||||
<td><code>Infinity</code></td>
|
||||
<td><code>Infinity</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-Infinity</code> in any case, or <code>-∞</code></td>
|
||||
<td><code>-Infinity</code></td>
|
||||
<td><code>-Infinity</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>NaN</code> in any case</td>
|
||||
<td><code>NaN</code></td>
|
||||
<td><em>(empty)</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>blank, <code>allowEmpty</code> on</td>
|
||||
<td><code>null</code></td>
|
||||
<td><em>(empty)</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>blank, <code>allowEmpty</code> off</td>
|
||||
<td>nothing — parse error</td>
|
||||
<td>the blank stays</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>anything JavaScript's <code>Number()</code> reads as a finite number</td>
|
||||
<td>that number</td>
|
||||
<td>its default string form</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>anything else</td>
|
||||
<td>nothing — parse error</td>
|
||||
<td>the rejected text stays</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><code>Number()</code> is the whole numeric parser, so exponent notation and the <code>0x</code>, <code>0b</code> and <code>0o</code> integer
|
||||
literals are accepted alongside ordinary decimals, while a thousands separator is not. Its result
|
||||
must also come back finite to be accepted, so a literal that overflows the double — <code>1e400</code> — is a
|
||||
parse error rather than a second route to <code>Infinity</code>. The two spellings at the head of the table
|
||||
are the only way to reach one.</p>
|
||||
<p>Two consequences follow from <code>NaN</code> and <code>null</code> sharing the empty box. A <code>NaN</code> arriving from the
|
||||
server is indistinguishable from a blank field; and because a blank field commits <code>null</code> under the
|
||||
default, merely focusing such a field and leaving it replaces the <code>NaN</code> with <code>null</code>. With
|
||||
<code>allowEmpty</code> turned off the same field instead fails to parse on every blur until something is
|
||||
typed into it.</p>
|
||||
<p>The infinity spellings the widget writes and reads are the same strings the API layer puts on the
|
||||
wire for a non-finite number — see <a class="xref" href="../conventions/numeric-io-utilities.html">Numeric Input/Output</a>. The
|
||||
widget itself never touches the wire: it emits a JavaScript number, and converting a non-finite one
|
||||
into its string form belongs to the host's API module, as <code>wwwroot-src/src/api/mission.ts</code> does for
|
||||
the mission commands. That conversion is not symmetric for <code>NaN</code>: the mission module writes all
|
||||
three strings but recognises only the two infinity spellings when reading, so a <code>NaN</code> returned by
|
||||
the endpoint becomes the caller's supplied default instead.</p>
|
||||
<h2 id="unit-suffix-and-precision">Unit Suffix and Precision</h2>
|
||||
<p><code>unit</code> is rendered as a suffix inside the field, to the right of the text. It is decoration only:
|
||||
it is not part of the editable text, it is not parsed, and no unit conversion happens anywhere in
|
||||
the widget — the number committed is in whatever unit the host's model already uses. Hosts pass
|
||||
plain unit text, <code>mm</code> and <code>deg</code> being the common ones, alongside <code>mm/min</code>, <code>rpm</code>, <code>N</code>, <code>°C</code> and
|
||||
<code>MB</code>.</p>
|
||||
<p>There is no precision, decimals or step prop. A value is displayed through JavaScript's default
|
||||
number-to-string conversion, so it shows at full precision and switches to exponent notation at the
|
||||
magnitudes where that conversion does. Rounding, where a host wants it, is the host's own: the
|
||||
graphic-cache current-size field rounds in its commit handler, after the widget has handed the
|
||||
number over, while the two limit fields in the same menu send what they were given.</p>
|
||||
<h2 id="validation-messages">Validation Messages</h2>
|
||||
<p>Three messages can appear under the field, and all three are built inside the component in English:
|
||||
<code>Invalid number: "…"</code> quoting the rejected text, <code>Must be ≥ …</code> and <code>Must be ≤ …</code> quoting the bound.
|
||||
They are not keys and appear in no i18n bundle, so they do not follow the application's language
|
||||
setting — unlike <code>label</code> and <code>hint</code>, which hosts pass in already translated.</p>
|
||||
<p><code>rules</code> is a separate mechanism and differs in three ways: its functions receive the raw display
|
||||
<strong>text</strong> rather than the parsed number, they run on the underlying field's own validation pass —
|
||||
as the text changes, and again when focus leaves — rather than inside the commit, and <strong>they do
|
||||
not gate the commit</strong>: a value failing a caller rule is still parsed, still bounds-checked and
|
||||
still emitted. While the widget's own message is showing, it also masks the rule's.</p>
|
||||
<p>No shipped host passes <code>rules</code>, and none fills the <code>append</code> slot, so both are available rather
|
||||
than exercised. <code>hideBottomSpace</code> is the opposite — it is among the most-passed props on the
|
||||
widget, set on nearly every field the SoftNc controller panels, the spindle contour editor and the
|
||||
fluting span list drop into a table cell.</p>
|
||||
<h2 id="when-the-box-and-the-model-disagree">When the Box and the Model Disagree</h2>
|
||||
<p>A rejected commit leaves the typed text in the box. That text is replaced only when <code>modelValue</code>
|
||||
changes to something whose formatted form differs from what is showing; the widget does not revert
|
||||
on its own. That is the second thing the vector and matrix editors do that this one does not: both
|
||||
reinstate the last valid value the moment a cell fails to parse. Two situations follow, and both
|
||||
put a number on screen that was never stored:</p>
|
||||
<ul>
|
||||
<li>A number outside the bounds, or text that does not parse, stays in the box under its error
|
||||
message. Nothing restores it until the host pushes a different value.</li>
|
||||
<li>A commit the host accepts and the server then refuses leaves the box showing the refused number
|
||||
unless the host had already applied it locally, because assigning the unchanged value back to
|
||||
<code>modelValue</code> is not a change and moves nothing.</li>
|
||||
</ul>
|
||||
<p>The message is equally persistent: it is cleared only by the next successful commit, so it can
|
||||
outlive the value that caused it and sit under a field the host has since repopulated.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>Numeric Input Field
|
||||
<ul>
|
||||
<li>Label — the host's <code>label</code>, positioned inside the field's outline: on the input line while the
|
||||
field is empty and unfocused, floated to the top of the outline once it has a value or the
|
||||
focus; absent when none is given</li>
|
||||
<li>Text Box — one line, free text until committed</li>
|
||||
<li>Unit Suffix — the host's <code>unit</code>, inside the field at the right; display only</li>
|
||||
<li>Append Slot — an optional trailing slot for a host-supplied control</li>
|
||||
<li>Bottom Strip — the host's <code>hint</code>, replaced by the validation message while the field is in
|
||||
error. <code>hideBottomSpace</code> stops space being held below the field but does not suppress the
|
||||
strip: a hint or a validation message still renders, and with nothing reserved for it, it
|
||||
grows the field instead of filling a gap already left below.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<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-application">Web Application</h3>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the widget: the raw-text buffer, the
|
||||
local format and parse pair, the blur-and-Enter commit, the inclusive bounds check that skips
|
||||
non-finite values, and the three English validation strings.</li>
|
||||
<li><code>wwwroot-src/src/components/preference/GraphicCacheMenu.vue</code> — a compact host of three instances,
|
||||
one of them bounded by the values of the other two; every handler rejects <code>null</code> and non-finite
|
||||
numbers, and the current-size handler is the one that rounds before sending.</li>
|
||||
<li><code>wwwroot-src/src/components/controlTree/toolhouse/CutterSectionPanel.vue</code> — a host that returns
|
||||
early on the <code>null</code> a cleared field emits, so blanking a cutter dimension cannot write a zero.</li>
|
||||
<li><code>wwwroot-src/src/components/spindle/SpindleContourEditor.vue</code> — a host that turns <code>allowEmpty</code>
|
||||
off and keeps its own draft copy of the contour points around the blur commit.</li>
|
||||
<li><code>wwwroot-src/src/api/mission.ts</code> — the module that turns a committed non-finite number into the
|
||||
<code>Infinity</code>, <code>-Infinity</code> or <code>NaN</code> string the endpoint takes, and reads the two infinity spellings
|
||||
back; a <code>NaN</code> arriving from the endpoint falls through to the caller's default instead.</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="../conventions/numeric-io-utilities.html">Numeric Input/Output</a> — the cross-boundary rule this widget implements the client half of, and how the three numeric inputs differ from one another</li>
|
||||
<li><a class="xref" href="index.html">Widgets</a> — the other controls that pages embed rather than own</li>
|
||||
<li><a class="xref" href="../general-setup/controller/editing-contract.html">Editing Contract</a> — the branch that embeds this field most heavily, and the
|
||||
commit rules its panels inherit from it</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>
|
||||
@@ -0,0 +1,218 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Object Management Menu Button | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Object Management Menu Button | 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-ObjectManagementMenuButton">
|
||||
<h1 id="object-management-menu-button">Object Management Menu Button</h1>
|
||||
|
||||
<p>The menu button represent the target object with getter function and setter function. The target object generally is <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a>.</p>
|
||||
<p>the target object with getter function and setter function is such like:</p>
|
||||
<pre><code class="lang-csharp">object TargetObject{get=>TargetObjectGetter?.Invoke();set=>TargetObjectSetter?.Invoke(value);}
|
||||
Func<TargetObject> TargetObjectGetter{get;set;}
|
||||
Action<TargetObject> TargetObjectSetter{get;set;}
|
||||
</code></pre>
|
||||
<p>The target object has the following functions:</p>
|
||||
<ul>
|
||||
<li>File Save/Load
|
||||
<ul>
|
||||
<li>See <a class="xref" href="../conventions/gui-file-path-assignment.html">GUI File Path Assignment</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Object Copy/Paste</li>
|
||||
<li>Editor Panel Mode Selection
|
||||
<ul>
|
||||
<li>GUI (user-friendly)</li>
|
||||
<li>XML
|
||||
<ul>
|
||||
<li>Get XML by <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a> with <code>exhibitionOnly</code> true.</li>
|
||||
<li>Set XML by <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>If the target object is not <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a>, then the XML Editor Panel Mode should not appear. The other functions are still buildable.</p>
|
||||
<p>On <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a> functions, set <code>enableRebase</code> to <code>true</code> (which is the default). The rebase mode resolves relative file paths from the file's own directory, preventing silent inner exceptions.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>Object Management Menu Button
|
||||
<ul>
|
||||
<li>Load Button</li>
|
||||
<li>Load Resource Button</li>
|
||||
<li>Save As Button</li>
|
||||
<li>(splitter)</li>
|
||||
<li>Copy Button (with hotkey support if the Object Management Menu Button is focused)</li>
|
||||
<li>Paste Button (with hotkey support if the Object Management Menu Button is focused)</li>
|
||||
<li>(splitter)</li>
|
||||
<li>GUI Ratio Button</li>
|
||||
<li>XML Ratio Button</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="TIP">
|
||||
<h5>Tip</h5>
|
||||
<ul>
|
||||
<li>Since the Object Management Menu Button has special meaning, use icon instead of text label.</li>
|
||||
<li>Do not use icon on the other child buttons. The icons gains nothing but hard to keep style to the full application.</li>
|
||||
<li>If the model is selected, show a different style (may be color) on the menu button.</li>
|
||||
<li>The model should contain a ResourceDirectory property.</li>
|
||||
<li>Do not show Load Resource Button if Resource directory not explicitly gave.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h2 id="copy--paste">Copy & Paste</h2>
|
||||
<ul>
|
||||
<li>Object Copy/Paste (i.e. Select/Set or Duplicated-Set)
|
||||
<ul>
|
||||
<li>Copy (i.e. Select)
|
||||
Set the model to <code>UserService.SelectedItem</code>.</li>
|
||||
<li>Paste
|
||||
Set <code>UserService.SelectedItem</code> to the model.
|
||||
Set by reference is default. Apply Duplicated-Set if explicitly required.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>While a object is copied (selected) here, it can also be paste (drag) to:</p>
|
||||
<ul>
|
||||
<li>Text editor
|
||||
Use <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a>.<a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html#Hi_Common_XmlUtils_IMakeXmlSource_MakeXmlSource_System_String_System_String_System_Boolean_">MakeXmlSource(string, string, bool)</a> with <code>exhibitionOnly</code> false and the argument (<code>baseDirectory</code> and <code>relFile</code>) from object's host to paste the text, it should be the same text content by the host XML output.</li>
|
||||
<li>File browser
|
||||
Use <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a>.<a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html#Hi_Common_XmlUtils_IMakeXmlSource_MakeXmlSource_System_String_System_String_System_Boolean_">MakeXmlSource(string, string, bool)</a> with <code>exhibitionOnly</code> true, <code>baseDirectory</code> destination folder, <code>relFile</code> destination file name (maybe xxx.class-name) to paste the file.</li>
|
||||
</ul>
|
||||
<p>Show message when paste action success or failed. See <a class="xref" href="../conventions/index.html">Handle Message and Exception</a>.</p>
|
||||
<p>If the data type is not matched, show the un-matched message.</p>
|
||||
<h2 id="editor-panel-mode-ratio-button">Editor Panel Mode Ratio Button</h2>
|
||||
<p>The Editor Panel switched by the Editor Panel Mode Ratio Button.</p>
|
||||
<p>The BaseDirectory and RelFile properties should exist. Use the RelFile property if the object's host has the corresponding property like XXXFile.</p>
|
||||
<p>The Apply action should be well-set. Include <a class="xref" href="../../api/Hi.Common.XmlUtils.SetFileDelegate.html">SetFileDelegate</a> from <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html#Hi_Common_XmlUtils_XFactory_Gen_">Gen</a> function.
|
||||
The last argument should also be delivered by the host. So there must exist an property to pass the argument.</p>
|
||||
<h3 id="xml-editor-panel-layout">XML Editor Panel Layout</h3>
|
||||
<ul>
|
||||
<li>XML Editor Panel
|
||||
<ul>
|
||||
<li>Cancel Button</li>
|
||||
<li>Apply Button</li>
|
||||
<li>XML TextArea</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Shows error message if the xml-parsing or object creation failed on XML Editor Panel Apply Button applied.</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>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — Quasar dropdown + XML editor dialog. Emits <code>update:modelKey</code>, <code>objectLoaded</code>, <code>objectPasted</code>, <code>xmlApplied</code>, <code>error</code>. Supports Ctrl+C / Ctrl+V.</li>
|
||||
<li><code>wwwroot-src/src/api/objectManagement.ts</code> — typed wrapper over <code>/api/ObjectManagement/*</code>.</li>
|
||||
<li><code>Widget/ObjectManagementController.cs</code> — REST endpoints: <code>Load</code>, <code>LoadResource</code>, <code>SaveAs</code>, <code>Copy</code>, <code>Paste</code>, <code>GetXml</code>, <code>ApplyXml</code>, <code>SupportsXml</code>, <code>GetSelectedItem</code>.</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a href="../util/file-explorer.html">File Explorer</a> — reaches the same project / resource folders through a different endpoint family</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>
|
||||
@@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Polar Resolution 2D Panel | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Polar Resolution 2D Panel | 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-PolarResolution2dPanel">
|
||||
<h1 id="polar-resolution-2d-panel">Polar Resolution 2D Panel</h1>
|
||||
|
||||
<p>The model is <a class="xref" href="../../api/Hi.Geom.Resolution.PolarResolution2d.html">PolarResolution2d</a>: a linear step in millimetres plus an angular step, carried in radians and edited in degrees, that together tessellate a revolved shape into an STL mesh. Both clients edit it on a tool holder and nowhere else — the WPF client through this widget, the web client through the Cylindroid holder's Resolution section.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<h3 id="wpf-layout">WPF Layout</h3>
|
||||
<ul>
|
||||
<li>Polar Resolution 2D Panel
|
||||
<ul>
|
||||
<li>Enable Custom Resolution CheckBox
|
||||
<ul>
|
||||
<li>checked if the host model is not null; the checkbox itself is always clickable.</li>
|
||||
<li>unchecking sets the host model to null; checking a null model publishes a fresh <a class="xref" href="../../api/Hi.Geom.Resolution.PolarResolution2d.html">PolarResolution2d</a> of 0.5 mm / 5 deg through the setter.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Linear Resolution Input Field, with an <code>mm</code> suffix label
|
||||
<ul>
|
||||
<li>enabled if model not null</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Angle Resolution Input Field, with a degree-sign suffix label
|
||||
<ul>
|
||||
<li>enabled if model not null</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="web-layout">Web Layout</h3>
|
||||
<ul>
|
||||
<li>Holder Resolution Section
|
||||
<ul>
|
||||
<li>Resolution Hint Caption — names the STL tessellation used for display and collision meshing</li>
|
||||
<li>Linear Resolution (mm) Input Field</li>
|
||||
<li>Angle Resolution (deg) Input Field</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>There is no enable checkbox and no null state on the web: both fields are always editable, and each commits on blur or Enter, and only when both values are greater than zero.</p>
|
||||
<h2 id="feature">Feature</h2>
|
||||
<p>The Enable CheckBox is always shown. The host drives the WPF widget only through <code>GetterFunc</code> / <code>SetterFunc</code>; it calls <code>UpdateUI()</code> after its own model changes and subscribes to <code>OnModelChanged</code> to react to an edit.</p>
|
||||
<p>An edit republishes a whole new carrier through the setter instead of mutating the current one, in either client. A holder's <a class="xref" href="../../api/Hi.NcMech.Holders.CylindroidHolder.html#Hi_NcMech_Holders_CylindroidHolder_PolarResolution2d">PolarResolution2d</a> setter swaps the holder's solid for one born with the new value and disposes the old one, and skips a value-equal reassignment — which is what makes the WPF widget's per-keystroke setting affordable. The web client performs the same swap server-side inside <code>SetPolarResolution</code>, then clears the holder's cache.</p>
|
||||
<p>If the host model is null, it may mean the resolution applied the default value: <a class="xref" href="../../api/Hi.NcMech.Solids.Solid.html#Hi_NcMech_Solids_Solid_PolarResolution2d">PolarResolution2d</a> is a snapshot of the solid's identity, and a null carrier lets a parametric geometry apply its own default. Only the WPF client can reach that state. The service reports a null resolution as 0 mm / 0 deg, the two web fields display those zeros, and the commit guard then blocks any write until real numbers are typed.</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>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA): no standalone widget. The resolution is edited on the Cylindroid holder's Resolution section, at the Control-Tree id <code>toolhouse/tool-{id}/holder/resolution</code> and the route <code>/tool-house/:toolId/holder/resolution</code>. A Freeform holder grows no sections in the web client; its Holder panel shows a hint pointing at the WPF client instead.</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/controlTree/toolhouse/HolderSectionPanel.vue</code> — the <code>section === 'resolution'</code> branch: the two number fields, seeded from <code>Get</code> and committed on blur / Enter.</li>
|
||||
<li><code>wwwroot-src/src/components/controlTree/toolHouseItemTypes.ts</code> — registers that panel as the <code>HolderSection</code> item type and grows the Resolution node only for a <code>CylindroidHolder</code>.</li>
|
||||
<li><code>wwwroot-src/src/components/controlTree/toolhouse/ToolHolderPanel.vue</code> — the Holder stem above it, whose holder-type selector decides which sections appear.</li>
|
||||
<li><code>wwwroot-src/src/api/cylindroidHolder.ts</code> — <code>PolarResolutionDto</code> (<code>linear_mm</code> / <code>angle_deg</code>) and <code>setCylindroidHolderResolution</code>.</li>
|
||||
<li><code>wwwroot-src/src/router/treeRoutes.ts</code> — maps the holder's section ids onto the Tool House route params.</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/toolhouse.ts</code> — the two field labels and the tessellation hint caption.</li>
|
||||
<li><code>Mech/CylindroidHolderController.cs</code> — <code>POST /api/CylindroidHolder/SetPolarResolution</code>, which rejects a non-positive value, assigns a fresh <code>PolarResolution2d</code> and calls <code>ClearCache()</code>; <code>GET Get</code> reports a null resolution as 0 / 0.</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="../tool-house/holder/cylindroid-holder-panel.html">Cylindroid Holder Panel</a> — the holder editor that embeds this panel to tessellate its profile</li>
|
||||
<li><a class="xref" href="../tool-house/holder/freeform-holder-panel.html">Freeform Holder Panel</a> — the other holder editor that embeds it</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>
|
||||
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RenderingCanvas Tool Bar | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="RenderingCanvas Tool Bar | 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-RenderingCanvasToolBar">
|
||||
<h1 id="renderingcanvas-tool-bar">RenderingCanvas Tool Bar</h1>
|
||||
|
||||
<p>The RenderingCanvas Tool Bar is the camera-preset menu that every 3D canvas in the app carries. In the WPF client the control holds a <a class="xref" href="../../api/Hi.Disp.DispEngine.html">DispEngine</a> and drives it directly from each menu click. On the web it holds no engine: it takes a <code>canvas</code> prop and calls that component's exposed <code>setView(name)</code>, which invokes the <code>SetView</code> hub method, and the hub is what drives the per-connection <a class="xref" href="../../api/Hi.Disp.DispEngine.html">DispEngine</a>.</p>
|
||||
<h2 id="view-menu">View Menu</h2>
|
||||
<p>The bar is a single <code>View ▾</code> menu. Its seven entries, in shipped order, are Isometric, Front, Back, Right, Left, Top and Bottom. The engine also understands a <code>home</code> view and <code>RenderingCanvas.vue</code> exposes <code>setViewToHomeView</code>, but this menu does not offer it.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Entry</th>
|
||||
<th>API Method</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Isometric</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToIsometricView">SetViewToIsometricView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Front</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToFrontView">SetViewToFrontView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Back</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToFrontView">SetViewToFrontView()</a> + <a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_TurnBackView">TurnBackView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Right</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToRightView">SetViewToRightView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Left</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToRightView">SetViewToRightView()</a> + <a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_TurnBackView">TurnBackView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Top</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToTopView">SetViewToTopView()</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bottom</td>
|
||||
<td><a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToTopView">SetViewToTopView()</a> + <a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_TurnBackView">TurnBackView()</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="back-view-implementation">Back View Implementation</h3>
|
||||
<p>Back / Left / Bottom views are composed by first calling the corresponding forward-view method (<code>SetViewToFrontView</code> / <code>SetViewToRightView</code> / <code>SetViewToTopView</code>) and then invoking <a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_TurnBackView">TurnBackView()</a> to flip the camera about the view plane. Both clients compose them the same way — the web in the hub's <code>SetView</code> switch, WPF in the tool bar's click handlers.</p>
|
||||
<h3 id="canvas-binding">Canvas Binding</h3>
|
||||
<p>On the web the tool bar declares exactly one prop, a nullable <code>canvas</code>, typed structurally as an object exposing <code>setView(v: string): Promise<void></code> rather than as the <code>RenderingCanvas</code> component type. Every entry calls it optionally, so a tool bar with no canvas bound still opens and each entry is a no-op. The preset names travel to the hub untranslated; only the row labels are localized, from the <code>widgets.canvas.*</code> keys.</p>
|
||||
<h2 id="scene-menu">Scene Menu</h2>
|
||||
<p>Pages that sit next to a <code>RenderingCanvas</code> surface a per-page <code>Scene ▾</code> menu button — the <code>DisplayOptionsMenu</code> component, labelled from <code>widgets.canvas.scene</code> because no caller overrides the label. It chooses what the 3D scene draws (solids, coordinates, display aids), as distinct from the camera-oriented <code>View ▾</code> menu beside it. The layout (header + checkboxes + radio rows) is shared across four callers — the Execution page's extended tool bar, the General Setup equipment panel, the Tool House setup panel and the STL preview pane — so it is implemented once as a generic, schema-driven component.</p>
|
||||
<h3 id="schema">Schema</h3>
|
||||
<p>Each dropdown consumes a <code>DisplayGroup[]</code> array. A group has an optional header and a flat list of items:</p>
|
||||
<ul>
|
||||
<li><code>{ kind: 'check', label, modelValue, disable?, onUpdate }</code> — a checkbox row.</li>
|
||||
<li><code>{ kind: 'radio', label, groupValue, value, disable?, onUpdate }</code> — a radio row; rows sharing a <code>groupValue</code> behave as one group, each row bound to its own <code>value</code>.</li>
|
||||
</ul>
|
||||
<p>The component is generic over the radio <code>value</code> type, so <code>RenderingMode</code> / <code>HolderRenderingMode</code> / etc. stay fully typed at the call site. Besides <code>groups</code> it accepts <code>label</code>, <code>disable</code>, <code>title</code>, <code>minWidthPx</code> (default 220), <code>size</code> (default <code>sm</code>), <code>contentClass</code> and <code>compact</code>. <code>compact</code> is a tighter-than-dense row mode, used by the General Setup and Tool House panels for their long option lists; the General Setup, Tool House and Execution callers pass <code>content-class="bg-white"</code> to opt out of Quasar's transparent menu.</p>
|
||||
<h3 id="hinc-2025-webservice-quasar-cli-spa">HiNC-2025-webservice (Quasar CLI SPA)</h3>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/widgets/DisplayOptionsMenu.vue</code> — the shared <code><q-btn-dropdown></code> + <code><q-list></code> implementation behind four of the app's five Scene menus. The Controller viewer is the exception: <code>wwwroot-src/src/components/controller/ControllerExtendedToolBar.vue</code> builds its Scene dropdown inline against the same <code>/api/rendering-flags</code> surface.</li>
|
||||
<li>Callers (each reads a <code>DisplayGroup[]</code> computed from its page-local state and forwards <code>onUpdate</code> to its existing handlers):
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/execution/ExecutionExtendedToolBar.vue</code></li>
|
||||
<li><code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code></li>
|
||||
<li><code>wwwroot-src/src/components/toolhouse/ToolHouseSetupPanel.vue</code></li>
|
||||
<li><code>wwwroot-src/src/components/StlPreviewPane.vue</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Backends — one per caller, each a different surface: <code>ExecutionExtendedToolBar.vue</code> reaches <code>/api/rendering-flags</code> (<code>Common/RenderingFlagsController.cs</code>), which flips bits in <code>ExecutionDisplayee.RenderingFlagBitArray</code>; <code>EquipmentSetupPanel.vue</code> reaches <code>/api/mech/equipment-setup-display/*</code> (<code>Mech/EquipmentSetupDisplayController.cs</code>); <code>ToolHouseSetupPanel.vue</code> reaches <code>/api/mech/tool-house-display/*</code> (<code>Mech/ToolHouseDisplayController.cs</code>); <code>StlPreviewPane.vue</code> reaches <code>/api/stl-preview/set-coordinate/*</code> (<code>Disp/StlPreviewController.cs</code>). Those last three route each POST by the caller's <code>renderingConnectionId</code>, so the toggle lands on that canvas's own engine; the rendering-flags surface carries no connection id and acts on the project-wide <code>ExecutionDisplayee</code> instead. <code>DisplayOptionsMenu.vue</code> is UI-only; it does no REST work itself — the caller owns the <code>onUpdate</code> handlers, so optimistic update and error reversion stay page-local.</li>
|
||||
</ul>
|
||||
<h3 id="wpf">WPF</h3>
|
||||
<ul>
|
||||
<li><code>Play/RenderingFlagSubmenu.xaml</code> — the shared flag submenu, wrapped per surface by <code>Numerical/Controller/ControllerExtendedRenderingCanvasToolBar.xaml</code> and <code>Play/PlayerExtendedRenderingCanvasToolBar.xaml</code>. Behaviour parity with the web <code>Scene ▾</code> menu; no schema-driven composition.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/RenderingCanvasToolBar.vue</code> — the <code>View ▾</code> menu. Eight callers embed it, one per canvas: <code>wwwroot-src/src/pages/ExecutionPage.vue</code> (teleported into the canvas panel's header, beside <code>ExecutionExtendedToolBar</code>), <code>wwwroot-src/src/pages/ControllerPage.vue</code> (beside <code>ControllerExtendedToolBar</code>), <code>wwwroot-src/src/pages/MachineToolPage.vue</code>, <code>wwwroot-src/src/pages/MechBuilderPage.vue</code>, <code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code> (the General Setup canvas column, mounted by <code>wwwroot-src/src/pages/GeneralSetupPage.vue</code>), <code>wwwroot-src/src/components/toolhouse/ToolHouseSetupPanel.vue</code> (mounted by <code>wwwroot-src/src/pages/ToolHousePage.vue</code>), <code>wwwroot-src/src/components/StlPreviewPane.vue</code> (the File Explorer preview pane, mounted by <code>wwwroot-src/src/components/FileExplorer.vue</code>) and <code>wwwroot-src/src/components/execution/StepVolumePanel.vue</code> (the CWE footprint canvas, teleported into its host expansion's header). Four of those sit beside a <code>Scene ▾</code> menu — the Execution, General Setup, Tool House and STL preview canvases; the Machine Tool, Mechanism Builder and CWE canvases carry the <code>View ▾</code> menu alone, and the Controller viewer pairs it with an inlined Scene dropdown.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/DisplayOptionsMenu.vue</code> — schema-driven dropdown described above.</li>
|
||||
<li><code>wwwroot-src/src/components/RenderingCanvas.vue</code> — SignalR-hosted WebSocket canvas whose exposed <code>setView</code> / <code>setViewTo*View</code> methods the tool bar calls (see <a class="xref" href="../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service</a>).</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/widgets.ts</code> — the <code>widgets.canvas.*</code> keys: <code>view</code>, the seven preset labels, and <code>scene</code>.</li>
|
||||
<li>Backends:
|
||||
<ul>
|
||||
<li><code>Disp/RenderingHub.cs</code> — the <code>SetView(string)</code> hub method that maps each preset name onto engine calls.</li>
|
||||
<li><code>Disp/RenderingService.cs</code> — <code>GetOrCreateEngine(connectionId)</code>, the per-connection <code>DispEngine</code> store the hub resolves against.</li>
|
||||
<li><code>Common/RenderingFlagsController.cs</code> — the <code>/api/rendering-flags</code> surface (<code>GET</code>, <code>POST update</code>, <code>POST batch</code>) behind the Execution page's Scene menu and the Controller viewer's inlined one, wrapped by <code>wwwroot-src/src/api/renderingFlags.ts</code>. It reads and writes <code>ExecutionDisplayee.RenderingFlagBitArray</code> through <code>ProjectDisplayeeService</code>.</li>
|
||||
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code>, <code>wwwroot-src/src/api/toolHouse.ts</code> and <code>wwwroot-src/src/api/stlPreview.ts</code> — the typed wrappers the other three Scene menus post through.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="../execution/index.html">Execution Page</a> — the page whose canvas header adopts this tool bar</li>
|
||||
<li><a class="xref" href="../execution/execution-extended-renderingcanvas-tool-bar.html">Execution Extended RenderingCanvas Tool Bar</a> — the run-specific controls that sit beside it</li>
|
||||
<li><a class="xref" href="../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service Application</a> — the SignalR transport whose hub actually moves the camera this menu asks for</li>
|
||||
<li><a class="xref" href="../util/stl-preview-pane.html">STL Preview Pane</a> — the one caller whose canvas is a single file rather than a project scene</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>
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
<div id="sidetoggle">
|
||||
<div>
|
||||
<div class="sidefilter">
|
||||
<form class="toc-filter">
|
||||
<span class="glyphicon glyphicon-filter filter-icon"></span>
|
||||
<span class="glyphicon glyphicon-remove clear-icon" id="toc_filter_clear"></span>
|
||||
<input type="text" id="toc_filter_input" placeholder="Filter by title" onkeypress="if(event.keyCode==13) {return false;}">
|
||||
</form>
|
||||
</div>
|
||||
<div class="sidetoc">
|
||||
<div class="toc" id="toc">
|
||||
|
||||
<ul class="nav level1">
|
||||
<li>
|
||||
<a href="index.html" name="" title="Overview">Overview</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="object-management-menu-button.html" name="" title="Object Management Menu Button">Object Management Menu Button</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="renderingcanvas-tool-bar.html" name="" title="RenderingCanvas Tool Bar">RenderingCanvas Tool Bar</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="numeric-input.html" name="" title="Numeric Input">Numeric Input</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="polar-resolution-2d-panel.html" name="" title="Polar Resolution 2D Panel">Polar Resolution 2D Panel</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="vec3d-control.html" name="" title="Vec3dControl">Vec3dControl</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mat4d-control.html" name="" title="Mat4dControl">Mat4dControl</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Object Management Menu Button","href":"object-management-menu-button.html","topicHref":"object-management-menu-button.html"},{"name":"RenderingCanvas Tool Bar","href":"renderingcanvas-tool-bar.html","topicHref":"renderingcanvas-tool-bar.html"},{"name":"Numeric Input","href":"numeric-input.html","topicHref":"numeric-input.html"},{"name":"Polar Resolution 2D Panel","href":"polar-resolution-2d-panel.html","topicHref":"polar-resolution-2d-panel.html"},{"name":"Vec3dControl","href":"vec3d-control.html","topicHref":"vec3d-control.html"},{"name":"Mat4dControl","href":"mat4d-control.html","topicHref":"mat4d-control.html"}]}
|
||||
@@ -0,0 +1,660 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vec3dControl Component | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Vec3dControl Component | 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-Vec3dControl">
|
||||
<h1 id="vec3dcontrol-component">Vec3dControl Component</h1>
|
||||
|
||||
<p>Vec3dControl edits and displays a three-component vector. It is an embedded widget with no route
|
||||
and no Control-Tree node of its own, reached only through the editors that host it.</p>
|
||||
<h2 id="key-model">Key Model</h2>
|
||||
<p>The persisted model is <a class="xref" href="../../api/Hi.Geom.Vec3d.html">Vec3d</a>, exposed over <code>/api/Vec3d</code> by <code>Widget/Vec3dController.cs</code>.</p>
|
||||
<p>In the web application the widget's own model is the plain <code>{ x, y, z }</code> number triple exported as
|
||||
<code>interface Vec3</code> from <code>wwwroot-src/src/components/widgets/Vec3Input.vue</code>. The widget carries no key
|
||||
and calls no endpoint: the host binds it with <code>v-model</code> plus an <code>@update:model-value</code> handler, and
|
||||
that handler does the persisting, each host on its own terms. The four transformer editors call
|
||||
<code>updateVec3d</code> from <code>wwwroot-src/src/api/geometry.ts</code>, which posts all three axes together to
|
||||
<code>/api/Vec3d/Update</code>, while <code>wwwroot-src/src/components/geom/Box3dEditor.vue</code> folds the edited triple
|
||||
back into a min and max pair and posts that to <code>/api/Box3d/Update</code>.</p>
|
||||
<p>The WPF control likewise holds no value of its own. <code>GetterFunc</code> returns the live
|
||||
<a class="xref" href="../../api/Hi.Geom.Vec3d.html">Vec3d</a> instance, the text handlers write the parsed numbers straight onto it, and
|
||||
<code>UpdateByContentFunc</code> is awaited afterwards so the host can push the change on.</p>
|
||||
<p>The web props are <code>modelValue</code>, <code>disable</code>, <code>readonly</code>, <code>normalize</code>, <code>labels</code> and <code>textMode</code>, and the
|
||||
emits are <code>update:modelValue</code> and <code>normalize</code>. <code>labels</code> is a three-tuple defaulting to
|
||||
<code>['X', 'Y', 'Z']</code>, and carries units where the host has them, as in <code>['X (mm)', 'Y (mm)', 'Z (mm)']</code>.
|
||||
<code>textMode</code> sets the initial mode only and defaults to false. The corresponding WPF properties are
|
||||
<code>ShowNormalizeButton</code>, <code>IsTextMode</code> and <code>IsReadOnly</code>.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>Vec3dControl
|
||||
<ul>
|
||||
<li><code>Mode Toggle Button</code> — always present; switches between the per-axis columns and the
|
||||
single-field text form, and is highlighted while the text form is showing</li>
|
||||
<li><code>X</code>, <code>Y</code> and <code>Z</code> Input Fields — the per-axis form, three fields side by side</li>
|
||||
<li><code>Vector Text Field</code> — the single-field form, standing in place of the three axis fields</li>
|
||||
<li><code>Normalize Button</code> — present only when the normalize flag is set, and disabled while the
|
||||
control is disabled or readonly</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>In the WPF client the two forms are two panels, <code>StandardModePanel</code> and <code>TextModePanel</code>, of which
|
||||
exactly one is visible; each panel carries its own mode-toggle and normalize buttons.</p>
|
||||
<h2 id="feature">Feature</h2>
|
||||
<h3 id="per-axis-form">Per-axis form</h3>
|
||||
<p>X, Y and Z in three separate fields. The web widget renders every finite value at full precision;
|
||||
the WPF control renders each axis with a fixed three decimal places.</p>
|
||||
<h3 id="single-field-text-form">Single-field text form</h3>
|
||||
<p>The web form is <code>(x, y, z)</code>. Its parser strips surrounding brackets and splits on comma, semicolon
|
||||
or whitespace, requiring at least three parts, and entering the text form re-syncs the field from
|
||||
the current value so a stale in-progress edit is discarded. The WPF text box holds comma-separated
|
||||
<code>x,y,z</code> without brackets and accepts two further shapes: a pasted 4x4 matrix, from which it takes
|
||||
the translation column, and loose text, from which it takes the first three numbers it can read.</p>
|
||||
<h3 id="vector-normalization">Vector normalization</h3>
|
||||
<p>The normalize button is opt-in and off by default in both clients: the <code>normalize</code> prop in the web
|
||||
widget, the <code>ShowNormalizeButton</code> property in the WPF control.</p>
|
||||
<p>In the web widget the button only raises a <code>normalize</code> event, and the embedding editor performs the
|
||||
normalization on its own owning object. <code>wwwroot-src/src/components/topo/StaticRotationEditor.vue</code>
|
||||
posts to <code>/api/StaticRotation/NormalizeAxis</code>, while
|
||||
<code>wwwroot-src/src/components/topo/DynamicRotationEditor.vue</code> and
|
||||
<code>wwwroot-src/src/components/topo/DynamicTranslationEditor.vue</code> call <code>normalizeDynamicRotationAxis</code>
|
||||
and <code>normalizeDynamicTranslationAxis</code> from <code>wwwroot-src/src/api/transformer.ts</code>. The WPF control
|
||||
normalizes the vector in place and writes the result back at three decimal places.</p>
|
||||
<h3 id="commit-and-special-values">Commit and special values</h3>
|
||||
<p>Web edits commit on blur or Enter, never per keystroke, and a value is emitted only when it differs
|
||||
from the current model. An empty field parses to 0; any other unparseable text reverts the field to
|
||||
the last valid value. Formatting and parsing are local to the component: NaN renders as <code>NaN</code> and
|
||||
the infinities as <code>Infinity</code> and <code>-Infinity</code>, and the parser accepts those spellings
|
||||
case-insensitively as well as the <code>∞</code> and <code>-∞</code> glyphs. The WPF control commits on every keystroke
|
||||
instead, leaving the model untouched while the text is unparseable so that a half-typed number is
|
||||
not treated as an error.</p>
|
||||
<h2 id="refer-sample-code">Refer Sample Code</h2>
|
||||
<pre><code class="lang-csharp" name="SampleCode-xaml"><UserControl x:Class="HiNC_2025_win_desktop.Geom.Vec3dControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="30" d:DesignWidth="200">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Standard Mode Panel -->
|
||||
<StackPanel x:Name="StandardModePanel" Orientation="Horizontal" Grid.ColumnSpan="5">
|
||||
<TextBox x:Name="XTextBox" Width="60" Margin="0,0,2,0" TextChanged="XTextBox_TextChanged" IsReadOnly="{Binding IsReadOnly}"/>
|
||||
<TextBox x:Name="YTextBox" Width="60" Margin="0,0,2,0" TextChanged="YTextBox_TextChanged" IsReadOnly="{Binding IsReadOnly}"/>
|
||||
<TextBox x:Name="ZTextBox" Width="60" Margin="0,0,2,0" TextChanged="ZTextBox_TextChanged" IsReadOnly="{Binding IsReadOnly}"/>
|
||||
<Button Content="{DynamicResource Vec3d_TextMode_Toggle}" Width="20" Click="TextModeToggle_Click" Margin="0,0,2,0" ToolTip="{DynamicResource Vec3d_TextMode_Tooltip}"/>
|
||||
<Button Content="{DynamicResource Vec3d_Normalize}" Width="20" Click="NormalizeButton_Click" Visibility="{Binding ShowNormalizeButton, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTip="{DynamicResource Vec3d_Normalize_Tooltip}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Text Mode Panel -->
|
||||
<StackPanel x:Name="TextModePanel" Orientation="Horizontal" Grid.ColumnSpan="5" Visibility="Collapsed">
|
||||
<TextBox x:Name="VectorTextBox" Width="180" Margin="0,0,2,0" TextChanged="VectorTextBox_TextChanged" IsReadOnly="{Binding IsReadOnly}"
|
||||
ToolTip="{DynamicResource Vec3d_TextMode_Format_Tooltip}"/>
|
||||
<Button Content="{DynamicResource Vec3d_StandardMode_Toggle}" Width="20" Click="StandardModeToggle_Click" Margin="0,0,2,0" ToolTip="{DynamicResource Vec3d_StandardMode_Tooltip}"/>
|
||||
<Button Content="{DynamicResource Vec3d_Normalize}" Width="20" Click="NormalizeButton_Click" Visibility="{Binding ShowNormalizeButton, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTip="{DynamicResource Vec3d_Normalize_Tooltip}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</code></pre><pre><code class="lang-csharp" name="SampleCode-xaml.cs">using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
using Hi.Geom;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.ComponentModel;
|
||||
using Hi.Common;
|
||||
using Hi.Common.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace HiNC_2025_win_desktop.Geom
|
||||
{
|
||||
/// <summary>
|
||||
/// Vec3dControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class Vec3dControl : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
private bool _isUpdating = false;
|
||||
private Func<Vec3d> _getterFunc;
|
||||
private Func<Task> _updateByContentFunc;
|
||||
private bool _showNormalizeButton = false;
|
||||
private bool _isTextMode = false;
|
||||
private bool _isReadOnly = false;
|
||||
private readonly ILogger<Vec3dControl> _logger = App.ServiceProvider.GetRequiredService<ILogger<Vec3dControl>>();
|
||||
|
||||
private static readonly Regex VectorRegex = new Regex(@"^\s*[\(\[\{]?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*[\)\]\}]?\s*$", RegexOptions.Compiled);
|
||||
private static readonly Regex MatrixRegex = new Regex(@"\{(?:\s*\{?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*\}?\s*,){3}\s*\{?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*\}?\s*\}", RegexOptions.Compiled);
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public string ControlId { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public Func<Vec3d> GetterFunc
|
||||
{
|
||||
get => _getterFunc;
|
||||
set
|
||||
{
|
||||
_getterFunc = value;
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
public Func<Task> UpdateByContentFunc
|
||||
{
|
||||
get => _updateByContentFunc;
|
||||
set => _updateByContentFunc = value;
|
||||
}
|
||||
|
||||
public bool ShowNormalizeButton
|
||||
{
|
||||
get => _showNormalizeButton;
|
||||
set
|
||||
{
|
||||
if (_showNormalizeButton != value)
|
||||
{
|
||||
_showNormalizeButton = value;
|
||||
OnPropertyChanged(nameof(ShowNormalizeButton));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTextMode
|
||||
{
|
||||
get => _isTextMode;
|
||||
set
|
||||
{
|
||||
if (_isTextMode != value)
|
||||
{
|
||||
_isTextMode = value;
|
||||
UpdateModeVisibility();
|
||||
OnPropertyChanged(nameof(IsTextMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => _isReadOnly;
|
||||
set
|
||||
{
|
||||
if (_isReadOnly != value)
|
||||
{
|
||||
_isReadOnly = value;
|
||||
OnPropertyChanged(nameof(IsReadOnly));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public Vec3dControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
private void UpdateModeVisibility()
|
||||
{
|
||||
if (IsTextMode)
|
||||
{
|
||||
StandardModePanel.Visibility = Visibility.Collapsed;
|
||||
TextModePanel.Visibility = Visibility.Visible;
|
||||
UpdateVectorTextFromXYZ();
|
||||
}
|
||||
else
|
||||
{
|
||||
StandardModePanel.Visibility = Visibility.Visible;
|
||||
TextModePanel.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVectorTextFromXYZ()
|
||||
{
|
||||
if (_isUpdating) return;
|
||||
|
||||
if (double.TryParse(XTextBox.Text, out double x) &&
|
||||
double.TryParse(YTextBox.Text, out double y) &&
|
||||
double.TryParse(ZTextBox.Text, out double z))
|
||||
{
|
||||
VectorTextBox.Text = $"{x},{y},{z}";
|
||||
}
|
||||
}
|
||||
|
||||
private void TextModeToggle_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsTextMode = true;
|
||||
}
|
||||
|
||||
private void StandardModeToggle_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsTextMode = false;
|
||||
}
|
||||
|
||||
private async void NormalizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isUpdating || _getterFunc == null || IsReadOnly)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_isUpdating = true;
|
||||
|
||||
var vec = _getterFunc();
|
||||
if (vec != null)
|
||||
{
|
||||
vec.Normalize();
|
||||
|
||||
XTextBox.Text = vec.X.ToString("F3");
|
||||
YTextBox.Text = vec.Y.ToString("F3");
|
||||
ZTextBox.Text = vec.Z.ToString("F3");
|
||||
|
||||
if (IsTextMode)
|
||||
{
|
||||
VectorTextBox.Text = $"{vec.X:F3},{vec.Y:F3},{vec.Z:F3}";
|
||||
}
|
||||
if(_updateByContentFunc != null)
|
||||
await _updateByContentFunc();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(string.Format(Application.Current.FindResource("Vec3d_Update_Error").ToString(), ex.Message));
|
||||
_logger.LogError(ex, "{Message}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (_isUpdating || _getterFunc == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_isUpdating = true;
|
||||
var vec = _getterFunc();
|
||||
if (vec != null)
|
||||
{
|
||||
XTextBox.Text = vec.X.ToString("F3");
|
||||
YTextBox.Text = vec.Y.ToString("F3");
|
||||
ZTextBox.Text = vec.Z.ToString("F3");
|
||||
|
||||
if (IsTextMode)
|
||||
{
|
||||
VectorTextBox.Text = $"{vec.X},{vec.Y},{vec.Z}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
XTextBox.Text = "";
|
||||
YTextBox.Text = "";
|
||||
ZTextBox.Text = "";
|
||||
VectorTextBox.Text = "";
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void XTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
await HandleTextChanged();
|
||||
}
|
||||
|
||||
private async void YTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
await HandleTextChanged();
|
||||
}
|
||||
|
||||
private async void ZTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
await HandleTextChanged();
|
||||
}
|
||||
|
||||
private async void VectorTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (_isUpdating || _getterFunc == null || IsReadOnly)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
string text = VectorTextBox.Text.Trim();
|
||||
|
||||
// 如果文本为空或太短,不做处理
|
||||
if (string.IsNullOrWhiteSpace(text) || text.Length < 3)
|
||||
return;
|
||||
|
||||
_isUpdating = true;
|
||||
|
||||
// 尝试解析为向量格式
|
||||
if (TryParseVector(text, out double x, out double y, out double z))
|
||||
{
|
||||
await UpdateVectorValues(x, y, z);
|
||||
}
|
||||
// 尝试解析为变换矩阵格式(仅提取位移分量)
|
||||
else if (TryParseTransformMatrix(text, out double tx, out double ty, out double tz))
|
||||
{
|
||||
await UpdateVectorValues(tx, ty, tz);
|
||||
}
|
||||
// 尝试作为单值解析每个字段(宽松模式)
|
||||
else if (TryParseLooseVector(text, out double lx, out double ly, out double lz))
|
||||
{
|
||||
await UpdateVectorValues(lx, ly, lz);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(string.Format(Application.Current.FindResource("Vec3d_Update_Error").ToString(), ex.Message));
|
||||
_logger.LogError(ex, "{Message}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryParseVector(string text, out double x, out double y, out double z)
|
||||
{
|
||||
x = y = z = 0;
|
||||
|
||||
// 使用正则表达式匹配向量格式
|
||||
Match match = VectorRegex.Match(text);
|
||||
if (match.Success && match.Groups.Count >= 4)
|
||||
{
|
||||
if (double.TryParse(match.Groups[1].Value, out x) &&
|
||||
double.TryParse(match.Groups[2].Value, out y) &&
|
||||
double.TryParse(match.Groups[3].Value, out z))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryParseTransformMatrix(string text, out double tx, out double ty, out double tz)
|
||||
{
|
||||
tx = ty = tz = 0;
|
||||
|
||||
// 移除所有换行和多余空格,便于匹配
|
||||
text = Regex.Replace(text, @"\s+", " ");
|
||||
|
||||
// 尝试匹配变换矩阵格式
|
||||
Match match = MatrixRegex.Match(text);
|
||||
if (match.Success && match.Groups.Count >= 8)
|
||||
{
|
||||
// 变换矩阵的第4列通常是位移分量
|
||||
if (double.TryParse(match.Groups[4].Value, out tx) &&
|
||||
double.TryParse(match.Groups[8].Value, out ty) &&
|
||||
double.TryParse(match.Groups[12].Value, out tz))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试匹配更宽松的变换矩阵表示(例如从界面复制的数据)
|
||||
var numbers = Regex.Matches(text, @"(-?\d*\.?\d+)");
|
||||
if (numbers.Count >= 16)
|
||||
{
|
||||
// 假设这是一个4x4矩阵,提取位移分量(第4、8、12个数字)
|
||||
if (double.TryParse(numbers[3].Value, out tx) &&
|
||||
double.TryParse(numbers[7].Value, out ty) &&
|
||||
double.TryParse(numbers[11].Value, out tz))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryParseLooseVector(string text, out double x, out double y, out double z)
|
||||
{
|
||||
x = y = z = 0;
|
||||
|
||||
// 移除所有非数字和小数点以外的字符,然后按空白分割
|
||||
string cleanText = Regex.Replace(text, @"[^\d\.\-\s,;]+", " ");
|
||||
string[] parts = Regex.Split(cleanText, @"[\s,;]+");
|
||||
|
||||
// 尝试从分割后的部分获取三个数字
|
||||
var numbers = new System.Collections.Generic.List<double>();
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(part) && double.TryParse(part, out double value))
|
||||
{
|
||||
numbers.Add(value);
|
||||
if (numbers.Count >= 3) break; // 最多取3个数字
|
||||
}
|
||||
}
|
||||
|
||||
// 如果获取到了三个数字,就认为解析成功
|
||||
if (numbers.Count >= 3)
|
||||
{
|
||||
x = numbers[0];
|
||||
y = numbers[1];
|
||||
z = numbers[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task UpdateVectorValues(double x, double y, double z)
|
||||
{
|
||||
XTextBox.Text = x.ToString("F3");
|
||||
YTextBox.Text = y.ToString("F3");
|
||||
ZTextBox.Text = z.ToString("F3");
|
||||
|
||||
var vec = _getterFunc?.Invoke();
|
||||
if (vec != null)
|
||||
{
|
||||
vec.X = x;
|
||||
vec.Y = y;
|
||||
vec.Z = z;
|
||||
|
||||
if (_updateByContentFunc != null)
|
||||
{
|
||||
await _updateByContentFunc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleTextChanged()
|
||||
{
|
||||
if (_isUpdating || _getterFunc == null || IsReadOnly)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_isUpdating = true;
|
||||
|
||||
// 尝试解析每个文本框的值
|
||||
bool allValid = true;
|
||||
|
||||
allValid &= double.TryParse(XTextBox.Text, out double x);
|
||||
allValid &= double.TryParse(YTextBox.Text, out double y);
|
||||
allValid &= double.TryParse(ZTextBox.Text, out double z);
|
||||
|
||||
if (allValid)
|
||||
{
|
||||
if (IsTextMode)
|
||||
{
|
||||
VectorTextBox.Text = $"{x},{y},{z}";
|
||||
}
|
||||
|
||||
var vec = _getterFunc();
|
||||
if (vec != null)
|
||||
{
|
||||
vec.X = x;
|
||||
vec.Y = y;
|
||||
vec.Z = z;
|
||||
|
||||
if(_updateByContentFunc != null)
|
||||
await _updateByContentFunc();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果有无效输入,不进行更新但也不显示错误
|
||||
// 这允许用户在输入过程中有不完整的状态
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录异常但不中断用户操作
|
||||
_logger.LogWarning(string.Format(Application.Current.FindResource("Vec3d_Update_Error").ToString(), ex.Message));
|
||||
_logger.LogError(ex, "{Message}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><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>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/widgets/Vec3Input.vue</code> — the widget itself: mode toggle, per-axis and
|
||||
single-field inputs, normalize button, and the local format and parse helpers</li>
|
||||
<li><code>wwwroot-src/src/api/geometry.ts</code> — the <code>/api/Vec3d</code> client the transformer editors use, and the <code>Vec3dDto</code> shape</li>
|
||||
<li><code>Widget/Vec3dController.cs</code> — the REST surface behind <code>/api/Vec3d</code>: New, NewWithValue, Get, Update,
|
||||
UpdateAt, ParseAndUpdate and Normalize. The SPA reaches Update.</li>
|
||||
<li><code>wwwroot-src/src/components/topo/StaticRotationEditor.vue</code> — the full binding contract in one
|
||||
place: <code>v-model</code>, <code>@update:model-value</code>, the normalize opt-in and its <code>@normalize</code> handler</li>
|
||||
<li><code>wwwroot-src/src/components/topo/StaticTranslationEditor.vue</code> — one instance, no normalize button</li>
|
||||
<li><code>wwwroot-src/src/components/topo/DynamicRotationEditor.vue</code> — axis with normalize, plus pivot</li>
|
||||
<li><code>wwwroot-src/src/components/topo/DynamicTranslationEditor.vue</code> — axis with normalize</li>
|
||||
<li><code>wwwroot-src/src/components/geom/Box3dEditor.vue</code> — four instances (min, max, dimension, center)
|
||||
whose <code>readonly</code> state follows the current edit mode, persisted through the Box3d endpoint</li>
|
||||
<li><code>wwwroot-src/src/components/geom/StlFileEditor.vue</code> — four display-only instances for the STL
|
||||
bounding-box pad</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/widgets.ts</code> — the tooltip strings under <code>widgets.vecInput</code></li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="mat4d-control.html">Mat4dControl Component</a> — the sibling numeric-geometry editor, and the one a reader usually wants next</li>
|
||||
<li><a class="xref" href="../conventions/numeric-io-utilities.html">Numeric Input/Output</a> — why NaN and the infinities are text on the wire, and how the three inputs differ</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>
|
||||
Reference in New Issue
Block a user