This commit is contained in:
2026-09-09 18:06:21 +08:00
parent 161af68fe3
commit 45b9f9ed5d
79 changed files with 1167 additions and 1532 deletions
@@ -89,7 +89,7 @@
<p>The main model is <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html">AptProfile</a> and its property <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html">AptProfile</a>.<a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a>.</p>
<p>See <a class="xref" href="../../../technique/milling-physics/cutter-geometry.html">Cutter Geometry</a>. <a class="xref" href="../../../api/Hi.Milling.Apts.GeneralApt.html">GeneralApt</a> is the generalization of the other <a class="xref" href="../../../api/Hi.Milling.Apts.IAptBased.html">IAptBased</a> types.</p>
<p>On the web client these fields have no panel of their own: they are rendered inline by the Flute Profile section of the cutter, the Control-Tree node whose role path is <code>toolhouse/tool-&lt;id&gt;/cutter/profile</code>, reached at <code>/tool-house/:toolId/cutter/profile</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. That same section carries the Profile Type selector above the fields. In the WPF client the APT fields are a component of their own, hosted by the Milling Cutter panel's Flute-Profile tab.</p>
<p>These fields have no panel of their own: they are rendered inline by the Flute Profile section of the cutter, the Control-Tree node whose role path is <code>toolhouse/tool-&lt;id&gt;/cutter/profile</code>, reached at <code>/tool-house/:toolId/cutter/profile</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. That same section carries the Profile Type selector above the fields.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
<ul>
@@ -155,29 +155,6 @@
</table>
<p>The interface casts live one layer down, on the read side: the cutter DTO builder fills <code>rc_mm</code>, <code>rr_mm</code>, <code>rz_mm</code>, <code>alpha_deg</code> and <code>beta_deg</code> by casting the APT to <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRc.html">IAptRc</a>, <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRr.html">IAptRr</a>, <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRz.html">IAptRz</a>, <a class="xref" href="../../../api/Hi.Milling.Apts.IAptAlpha.html">IAptAlpha</a> and <a class="xref" href="../../../api/Hi.Milling.Apts.IAptBeta.html">IAptBeta</a>, and emits null for a cast that fails.</p>
<p>Taper APT is where the panel's table and the engine's types disagree. <a class="xref" href="../../../api/Hi.Milling.Apts.TaperApt.html">TaperApt</a> is declared over <code>IAptBased</code>, <code>IAptRz</code>, <code>IAptAlpha</code> and <code>IAptBeta</code> and carries no round radius, so the table's Rc entry gives a Taper profile a Round Radius box that the DTO fills with null — the field reads 0 — and the value the panel sends back is not read by the server's Taper branch, which builds the APT from Diameter, Rz, Alpha, Beta and Flute Height alone. Rc on a Taper APT is therefore inert in both directions.</p>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>APT Profile Panel
<ul>
<li>Diameter Input Field (mm)</li>
<li>Round Radius Input Field (mm)
Visible if <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> is <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRc.html">IAptRc</a></li>
<li>Round Ring Radius Input Field (mm)
Visible if <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> is <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRr.html">IAptRr</a></li>
<li>Round Ring Height Input Field (mm)
Visible if <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> is <a class="xref" href="../../../api/Hi.Milling.Apts.IAptRz.html">IAptRz</a></li>
<li>Bottom Cone Angle Input Field (deg)
Visible if <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> is <a class="xref" href="../../../api/Hi.Milling.Apts.IAptAlpha.html">IAptAlpha</a></li>
<li>Top Cone Angle Input Field (deg)
Visible if <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> is <a class="xref" href="../../../api/Hi.Milling.Apts.IAptBeta.html">IAptBeta</a></li>
<li>Length of Cut Input Field (mm)</li>
</ul>
</li>
</ul>
<div class="TIP">
<h5>Tip</h5>
<p>Keep field format <code>G4</code> on this client. The web fields apply no format: they show the value as stored and commit on blur or Enter.</p>
</div>
<h2 id="features">Features</h2>
<p>Every edit re-sends the whole profile. Changing the type, or any one field, builds a fresh shaper-profile request carrying the type, Diameter, Length of Cut and only those of Rc / Rr / Rz / Alpha / Beta that the chosen type's table lists, and the server replaces the cutter's <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html">AptProfile</a> with a newly constructed APT of that type. The panel then clears the tool cache so the canvas redraws.</p>
<p>Clearing a field is ignored. The shared numeric input emits null for an empty box, and the panel returns on null instead of committing, so a blanked box keeps the previous value rather than writing 0.</p>
@@ -89,7 +89,7 @@
<h2 id="overview">Overview</h2>
<p>The key component is <a class="xref" href="../../../api/Hi.Machining.ICutter.html">ICutter</a>. A tool carries at most one cutter, and two concrete types answer to that interface: <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html">MillingCutter</a> and <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html">FreeformRemover</a>. This panel is where the type is chosen, and it holds the cutter's own General fields.</p>
<p>On the web client it is the Cutter tab of the <a class="xref" href="../index.html">Tool House Page</a>, reached at <code>/tool-house/:toolId/cutter</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. It is the panel of the Control-Tree Cutter node under the selected tool — the node whose role path is <code>toolhouse/tool-&lt;id&gt;/cutter</code>. In the WPF client the same surface is the Cutter Management Panel embedded in the tool's editor.</p>
<p>It is the Cutter tab of the <a class="xref" href="../index.html">Tool House Page</a>, reached at <code>/tool-house/:toolId/cutter</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. It is the panel of the Control-Tree Cutter node under the selected tool — the node whose role path is <code>toolhouse/tool-&lt;id&gt;/cutter</code>.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
<ul>
@@ -105,7 +105,7 @@
<li>Sections Hint Caption — points at the section tabs below</li>
</ul>
</li>
<li>Freeform Remover branch — one caption, <code>toolhouse.cutter.freeformRemoverUnavailable</code>, sending the reader to the WPF client or HiNcRcl</li>
<li>Freeform Remover branch — one caption, <code>toolhouse.cutter.freeformRemoverUnavailable</code>, saying the editor is not available here</li>
<li>No-cutter branch — one hint, <code>toolhouse.cutter.noCutterHint</code>, inviting the reader to pick Milling Cutter</li>
</ul>
</li>
@@ -113,39 +113,8 @@
<p>The three numeric fields are the cutter's General fields, and they sit here under the Metadata-on-container convention — a container's own General fields are edited on the container's panel instead of in a General child. They are plain, always-editable inputs: one label each, no label switching and no auto-computed mode, and all three commit together through the general endpoint, which accepts exactly <code>shankMass_g</code>, <code>honeRadius_um</code> and <code>reliefAngle_deg</code>. A blanked box commits nothing, so clearing a field never writes a zero.</p>
<p>The type also decides what grows below the node. A Milling Cutter grows the cutter's section tabs — Material, Flute Profile, Flute Contours, Upper Beam, Optimization — as the nodes whose role paths end in <code>.../cutter/material</code>, <code>.../cutter/profile</code>, <code>.../cutter/contours</code>, <code>.../cutter/upper-beam</code> and <code>.../cutter/opt</code>; Material is the one gated behind the Advanced Physics preference. Selecting one replaces this panel with that section's own panel; see <a class="xref" href="milling-cutter-panel.html">Milling Cutter Panel</a>. A Freeform Remover or no cutter grows nothing, so the Cutter node is a leaf and the caption or hint is the whole surface; see <a class="xref" href="freeform-remover-panel.html">Freeform Remover Panel</a>.</p>
<p>This panel carries no object management of its own. Object management for the tool library sits on the tool-house root node, where the <a class="xref" href="../../widget/object-management-menu-button.html">Object Management Menu Button</a> is bound to the whole <code>.MachiningToolHouse</code> file rather than to a per-cutter file.</p>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Cutter Management Panel
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="../../widget/object-management-menu-button.html">Object Management Menu Button</a>
<ul>
<li>File extension is <code>.Cutter</code>, load type <a class="xref" href="../../../api/Hi.Machining.ICutter.html">ICutter</a></li>
<li>The pointed Editor Panel is the cutter content presenter below</li>
</ul>
</li>
<li>Title Label</li>
<li>Cutter Type Selection Dropdown
<ul>
<li>Options: Milling Cutter, Freeform Remover, Unset</li>
</ul>
</li>
</ul>
</li>
<li>Cutter Content Presenter
Varies by the Cutter Type. It can be:
<ul>
<li><a class="xref" href="milling-cutter-panel.html">Milling Cutter Panel</a></li>
<li><a class="xref" href="freeform-remover-panel.html">Freeform Remover Panel</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The per-cutter <code>.Cutter</code> file is this client's, and so is the third option's Unset wording; the web selector's third option reads None.</p>
<h2 id="features">Features</h2>
<p>Committing a type on the web is one server call plus a cache clear. Milling Cutter calls <code>EnsureCutter</code>, which keeps the tool's existing MillingCutter and only creates one when the tool has none; None calls <code>ClearCutter</code>, which drops the tool's cutter altogether. The panel then clears the tool cache, re-reads the tool and emits a structure change for its own node, which is how the section tabs appear or disappear with the type.</p>
<p>Committing a type is one server call plus a cache clear. Milling Cutter calls <code>EnsureCutter</code>, which keeps the tool's existing MillingCutter and only creates one when the tool has none; None calls <code>ClearCutter</code>, which drops the tool's cutter altogether. The panel then clears the tool cache, re-reads the tool and emits a structure change for its own node, which is how the section tabs appear or disappear with the type.</p>
<p>Cache clearing is not a step the user performs. <a class="xref" href="../../../api/Hi.Machining.ICutter.html">ICutter</a> implements <a class="xref" href="../../../api/Hi.Common.IClearCache.html">IClearCache</a>, and every cutter endpoint that changes a field or a geometry clears the cache server-side before it answers: the field endpoints call <code>ClearCache()</code> on the milling cutter directly, while the ones that change flute or beam geometry go through the controller's resync hook, which clears the cache <em>and</em> re-aligns the tool's holder-to-cutter anchor transformer so the cutter is re-placed under the holder at its new height. On top of that the panels call <code>clearToolCache(toolId)</code>, which clears the tool's cached solids and the holder's, so the tool canvas redraws.</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>
@@ -88,76 +88,18 @@
<h1 id="freeform-remover-panel">Freeform Remover Panel</h1>
<p>The key model is <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html">FreeformRemover</a>: a cutter whose removal volume comes from two referenced geometries — a non-cutting strut and a cutting shaper — anchored to the holder buckle.</p>
<p>The two clients divide this cutter between them. The WPF client owns the editing surface: both geometries, the anchor, the spinning flag and the identity fields. The web client offers the type in the Cutter Type selector and nothing more; there is no Freeform Remover editor on the web, and no freeform-remover endpoint in the backend, so a freeform remover is built and edited on the WPF client or through HiNcRcl.</p>
<p>This cutter is offered in the Cutter Type selector and nowhere else. There is no Freeform Remover editor here and no freeform-remover endpoint in the backend, so nothing on this surface can create or change one — the shipped caption says as much, naming the WPF client and HiNcRcl.</p>
<h2 id="layout">Layout</h2>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Freeform Remover Panel
<ul>
<li>Tabs
<ul>
<li>Strut Geometry Tab
<ul>
<li><a class="xref" href="../../geom/geom-manage-control.html">Geometry Management Panel</a>
<ul>
<li>Manages <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_StrutGeom">StrutGeom</a></li>
</ul>
</li>
</ul>
</li>
<li>Shaper Geometry Tab
<ul>
<li><a class="xref" href="../../geom/geom-manage-control.html">Geometry Management Panel</a>
<ul>
<li>Manages <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_ShaperGeom">ShaperGeom</a></li>
</ul>
</li>
</ul>
</li>
<li>Anchor Tab
<ul>
<li>Label: Geometry Anchor To Holder Buckle</li>
<li><a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_KeepHolderBuckleOnTop">KeepHolderBuckleOnTop</a> Checkbox</li>
<li><a class="xref" href="../../geom/transformer-panel.html">Transformer Manage Panel</a>
<ul>
<li>Model is <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_GeomToHolderTransformer">GeomToHolderTransformer</a></li>
<li>Enabled if <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_KeepHolderBuckleOnTop">KeepHolderBuckleOnTop</a> is true.</li>
</ul>
</li>
</ul>
</li>
<li>Property Tab
<ul>
<li>Is Spinning Cutter Checkbox
<ul>
<li>Controls <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_IsSpinningCutter">IsSpinningCutter</a></li>
</ul>
</li>
</ul>
</li>
<li>Info Tab
<ul>
<li>Name TextField (editable)</li>
<li>AbstractNote TextField (readonly)</li>
<li>Note TextField (editable)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>This panel calls <a class="xref" href="../../../api/Hi.Machining.FreeformRemover.html#Hi_Machining_FreeformRemover_ClearCache">ClearCache()</a> itself after a geometry or anchor change, so the cached solids are dropped before the tool is redrawn.</p>
<h3 id="web-layout">Web Layout</h3>
<ul>
<li>Cutter Node Panel, the Cutter tab of the <a class="xref" href="../index.html">Tool House Page</a> at <code>/tool-house/:toolId/cutter</code> — the Control-Tree node <code>toolhouse/tool-&lt;id&gt;/cutter</code>
<ul>
<li>Cutter Type Selector — its second option, Freeform Remover, is this cutter</li>
<li>Freeform Remover Caption (<code>toolhouse.cutter.freeformRemoverUnavailable</code>) — the panel's whole body while that option is showing, directing the reader to the WPF client or HiNcRcl</li>
<li>Freeform Remover Caption (<code>toolhouse.cutter.freeformRemoverUnavailable</code>) — the panel's whole body while that option is showing</li>
</ul>
</li>
</ul>
<p>Nothing else on the web belongs to this cutter. The Shank Mass / Hone Radius / Relief Angle fields on that panel are gated to the Milling Cutter branch, and the cutter's child builder grows section tabs only for a MillingCutter, so the Cutter node is a leaf here: no Material, Flute Profile, Flute Contours, Upper Beam or Optimization section appears. See <a class="xref" href="cutter-panel.html">Cutter Panel</a> for the panel these live on.</p>
<p>Nothing else on this surface belongs to this cutter. The Shank Mass / Hone Radius / Relief Angle fields on that panel are gated to the Milling Cutter branch, and the cutter's child builder grows section tabs only for a MillingCutter, so the Cutter node is a leaf here: no Material, Flute Profile, Flute Contours, Upper Beam or Optimization section appears. See <a class="xref" href="cutter-panel.html">Cutter Panel</a> for the panel these live on.</p>
<h2 id="geometry-definitions">Geometry Definitions</h2>
<ul>
<li><strong>Strut Geometry</strong> — The non-cutting portion (holder/shank)
@@ -173,10 +115,10 @@
</li>
</ul>
<h2 id="features">Features</h2>
<p>Picking Freeform Remover on the web is a no-op that does not stick. The panel makes no API call for that option — it sets the selector locally, clears the tool cache, then re-reads the tool, and the re-read is what puts the selector back to Milling Cutter or None. The model on the tool is left exactly as it was.</p>
<p>Picking Freeform Remover is a no-op that does not stick. The panel makes no API call for that option — it sets the selector locally, clears the tool cache, then re-reads the tool, and the re-read is what puts the selector back to Milling Cutter or None. The model on the tool is left exactly as it was.</p>
<p>A tool that already carries a freeform remover reads as None. The tool detail casts the tool's cutter to a MillingCutter and reports both <code>hasCutter</code> and the embedded cutter DTO from that one cast, so a non-milling cutter comes back as no cutter at all, and the selector — which derives its value from those two fields — shows None. The Freeform Remover option is therefore reachable by the user's click but never by a load.</p>
<p>That makes one sequence worth knowing before using the web page on such a tool: picking Milling Cutter calls <code>EnsureCutter</code>, which returns the tool's existing cutter only when it is already a MillingCutter and otherwise assigns a new one — so on a tool holding a freeform remover, that choice replaces it. Leaving the selector alone leaves the freeform remover intact, which is what a round trip through the web page does by default.</p>
<p>Cache clearing needs no user action on either client. The WPF panel clears the remover's cache after each geometry or anchor edit; on the web there is nothing to clear for this cutter, since no endpoint mutates it, and the tool-cache clear the selector performs only drops the tool's and the holder's cached solids so the canvas redraws.</p>
<p>Cache clearing needs no user action: there is nothing to clear for this cutter, since no endpoint mutates it, and the tool-cache clear the selector performs only drops the tool's and the holder's cached solids so the canvas redraws.</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>
@@ -87,8 +87,8 @@
<article data-uid="Anatomy-MillingCutterPanel">
<h1 id="milling-cutter-panel">Milling Cutter Panel</h1>
<p>The key model is <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html">MillingCutter</a>, the cutter type both clients edit in full.</p>
<p>On the web client this cutter is not one panel but the section tabs the <a class="xref" href="cutter-panel.html">Cutter Panel</a> grows once a Milling Cutter is assigned: the Control-Tree nodes under <code>toolhouse/tool-&lt;id&gt;/cutter</code>, reached at <code>/tool-house/:toolId/cutter/:subtab</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. The five <code>:subtab</code> values are <code>material</code>, <code>profile</code>, <code>contours</code>, <code>upper-beam</code> and <code>opt</code>. The cutter's General fields — Shank Mass, Hone Radius, Relief Angle — are not among them: they sit on the Cutter node's own panel. In the WPF client the same cutter is one tab control, and it carries several groups the web does not offer.</p>
<p>The key model is <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html">MillingCutter</a>, the one cutter type edited here in full.</p>
<p>This cutter is not one panel but the section tabs the <a class="xref" href="cutter-panel.html">Cutter Panel</a> grows once a Milling Cutter is assigned: the Control-Tree nodes under <code>toolhouse/tool-&lt;id&gt;/cutter</code>, reached at <code>/tool-house/:toolId/cutter/:subtab</code> under the page route <code>/tool-house/:toolId?/:tab?/:subtab?</code>. The five <code>:subtab</code> values are <code>material</code>, <code>profile</code>, <code>contours</code>, <code>upper-beam</code> and <code>opt</code>. The cutter's General fields — Shank Mass, Hone Radius, Relief Angle — are not among them: they sit on the Cutter node's own panel.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
<ul>
@@ -102,8 +102,8 @@
</ul>
</li>
</ul>
<p>Material is the only physics-gated section. Flute Profile, Flute Contours, Upper Beam and Optimization grow for every Milling Cutter. The preference behind the gate is the SPA's Advanced Physics option, held as <code>isShowPhysicsOptions</code> in the app-state store and read from the server's preference endpoint, which reports it as on only while the physics feature is licensed. <code>UserService.EnablePhysics</code> is the WPF client's name for the same preference.</p>
<p>Integral mode is read-only on the web. The cutter DTO reports it, and the Material section reads it to decide whether the Shank Material picker applies, but no endpoint sets it; it is chosen on the WPF client's Property tab.</p>
<p>Material is the only physics-gated section. Flute Profile, Flute Contours, Upper Beam and Optimization grow for every Milling Cutter. The preference behind the gate is the SPA's Advanced Physics option, held as <code>isShowPhysicsOptions</code> in the app-state store and read from the server's preference endpoint, which reports it as on only while the physics feature is licensed.</p>
<p>Integral mode is read-only. The cutter DTO reports it, and the Material section reads it to decide whether the Shank Material picker applies, but no endpoint sets it.</p>
<h4 id="material-section">Material Section</h4>
<ul>
<li>Material Section (<code>.../cutter/material</code>)
@@ -180,7 +180,7 @@ Manages <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html
</ul>
<p>The five types are all wrapped by <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html">AptProfile</a>, with a different <a class="xref" href="../../../api/Hi.Milling.Cutters.AptProfile.html#Hi_Milling_Cutters_AptProfile_Apt">Apt</a> assigned; which fields appear depends on the type. See <a class="xref" href="apt-profile-panel.html">APT Profile Panel</a> for the field list and the per-type table that drives it.</p>
<p>See the <code>DemoBuildMachiningProject</code> sample in the <a class="xref" href="../../../technique/api-foundations/packages-and-samples.html">Hi.Sample repository</a> for creating the APT profile and setting it on the cutter.</p>
<p><code>CustomSpinningProfile</code> is the WPF client's sixth profile type; the web offers the five APT types only.</p>
<p><code>CustomSpinningProfile</code> is not offered here; the five APT types are.</p>
<h4 id="flute-contours-section">Flute Contours Section</h4>
<p>This part manages <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html#Hi_Milling_Cutters_MillingCutter_Fluting">Fluting</a>. It is a stem, not a leaf: the section node carries only the type selector, and the fluting itself is a sub-tree of nodes below it.</p>
<ul>
@@ -277,58 +277,7 @@ Manages <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html
</li>
</ul>
<p>Both checkboxes are plain labelled checkboxes — neither displays a computed value. The server's defaults when a limit set is written without them are a Yielding Safety Factor of 3 (a utilization factor of about 0.33), a Max Feed Per Tooth of 999, and both limit flags on.</p>
<p>The minimum uncut chip thickness itself is not on this surface: no cutter endpoint returns it, and no field displays it. It is the WPF client's Optimization panel that computes it from the cutter and the project workpiece's cutting parameter and shows it beside the checkbox.</p>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Milling Cutter Panel
<ul>
<li>Tabs
<ul>
<li>Flute-Profile Tab — the profile type dropdown plus the APT panel, and the <code>CustomSpinningProfile</code> option with its own geometry-management panel</li>
<li>Upper-Beam Tab
<ul>
<li><a class="xref" href="../../geom/geom-manage-control.html">Geometry Management Control</a>, with the <a class="xref" href="../../../api/Hi.Geom.ExtendedCylinder.html">ExtendedCylinder</a> option enabled</li>
</ul>
</li>
<li>Property Tab
<ul>
<li>Integral Mode Selection Dropdown — Solid End / Insert End</li>
<li>Cutter/Shank Mass Input Field (g), with an Auto Update CheckBox driven by <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html#Hi_Milling_Cutters_MillingCutter_ShankMassAssignmentMode">ShankMassAssignmentMode</a>: when enabled the field turns readonly and shows the mass computed from the inner-beam and upper-beam volume and the density</li>
<li>Hone Radius (um) and Relief Angle (deg) Input Fields</li>
<li>Minimum Available Cutting Thickness (um), readonly, from <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html#Hi_Milling_Cutters_MillingCutter_GetMinimumUncutChipThickness_um_Hi_MillingForces_Fittings_ICuttingPara_">GetMinimumUncutChipThickness_um(ICuttingPara)</a> with the project workpiece's <a class="xref" href="../../../api/Hi.NcMech.Workpieces.Workpiece.html#Hi_NcMech_Workpieces_Workpiece_CuttingPara">CuttingPara</a>, and a note label naming that cutting parameter</li>
</ul>
</li>
<li>Insert-Cutter Tab
<ul>
<li>Insert Number, Insert Mass (g) and Insert Thickness (mm) Input Fields; the thickness feeds heat transfer</li>
</ul>
</li>
<li>Material Tab</li>
<li>Flute-Contours Tab</li>
<li>Flute-Inner-Beam Tab
<ul>
<li>Profile Type Selection Dropdown for <a class="xref" href="../../../api/Hi.Milling.Cutters.MillingCutter.html#Hi_Milling_Cutters_MillingCutter_InnerBeamProfile">InnerBeamProfile</a> — Flute Dependent Ratio (<a class="xref" href="../../../api/Hi.Milling.Cutters.FluteDependentRatioProfile.html">FluteDependentRatioProfile</a>), Const Ratio (<a class="xref" href="../../../api/Hi.Milling.Cutters.ConstRatioProfile.html">ConstRatioProfile</a>), Custom Spinning (<a class="xref" href="../../../api/Hi.Milling.Cutters.CustomSpinningProfile.html">CustomSpinningProfile</a>)</li>
<li>For Flute Dependent Ratio: a readonly Radius Ratio field whose label also names the flute number it depends on</li>
<li>For Const Ratio: an editable Radius Ratio field</li>
<li>For Custom Spinning: a <a class="xref" href="../../geom/geom-manage-control.html">Geometry Management Control</a></li>
</ul>
</li>
<li>Optimization Tab — the same limits as the web section, plus the computed minimum uncut chip thickness beside the minimum-feed checkbox</li>
<li>Info Tab
<ul>
<li>Name TextField (editable)</li>
<li>AbstractNote TextField (readonly)</li>
<li>Note TextField (editable)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><code>UserService.EnablePhysics</code> gates the Property, Material, Flute-Contours, Flute-Inner-Beam and Optimization tabs on this client; the Insert-Cutter tab additionally requires Insert End. Flute-Profile, Upper-Beam and Info are always visible. On the web only the Material section is gated, and the same preference is named <code>isShowPhysicsOptions</code>.</p>
<p>Numeric fields on this client are formatted <code>G4</code>. The web's numeric field applies no format: it shows the value as stored and commits on blur or Enter.</p>
<p>The cutter has no identity fields on the web. Tool ID, Note and the auto-derived Abstract Note belong to the tool, one level up, and are edited on the tool's own General tab; see <a class="xref" href="../index.html">Tool House Page</a>.</p>
<p>The minimum uncut chip thickness itself is not on this surface: no cutter endpoint returns it, and no field displays it.</p>
<h2 id="features">Features</h2>
<p>Every web edit is a whole-object commit followed by a cache clear. The Flute Profile panel re-sends the entire shaper profile on any field change — only the fields the selected APT type carries are included — then clears the tool cache. The contour panels re-send the whole flute contour. The optimization panel re-sends the whole limit set. The material and coating endpoints each answer with the full cutter DTO, which the panels re-render from.</p>
<p>A blanked numeric box commits nothing. The shared numeric field emits null for an empty entry, and every cutter panel returns on null rather than writing a zero, so clearing a field leaves the stored value alone.</p>
@@ -88,7 +88,7 @@
<h1 id="cylindroid-holder-panel">Cylindroid Holder Panel</h1>
<p>The key model is <a class="xref" href="../../../api/Hi.NcMech.Holders.CylindroidHolder.html">CylindroidHolder</a>: a holder revolved from a Z-R profile, carrying its own name and note and its own STL tessellation resolution.</p>
<p>On the web client the holder occupies two child sections of the Holder node under the selected tool — Geometry and Resolution, the nodes whose role paths end in <code>.../holder/geometry</code> and <code>.../holder/resolution</code>, reached at <code>/tool-house/:toolId/holder/geometry</code> and <code>/tool-house/:toolId/holder/resolution</code>. Its Name, readonly Abstract Note and Note are edited one level up, on the <a class="xref" href="holder-panel.html">Holder Panel</a> itself, under the Metadata-on-container convention. Every field ships on both clients; what differs is the surface hosting them — the WPF client gives the same three fields a tab of their own.</p>
<p>The holder occupies two child sections of the Holder node under the selected tool — Geometry and Resolution, the nodes whose role paths end in <code>.../holder/geometry</code> and <code>.../holder/resolution</code>, reached at <code>/tool-house/:toolId/holder/geometry</code> and <code>/tool-house/:toolId/holder/resolution</code>. Its Name, readonly Abstract Note and Note are edited one level up, on the <a class="xref" href="holder-panel.html">Holder Panel</a> itself, under the Metadata-on-container convention.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
<ul>
@@ -106,38 +106,6 @@
</ul>
</li>
</ul>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Cylindroid Holder Panel
<ul>
<li>Head Line
<ul>
<li>Title Label</li>
</ul>
</li>
<li>Tabs
<ul>
<li>Geometry Tab
<ul>
<li><a class="xref" href="../../geom/cylindroid-control.html">Cylindroid Panel</a></li>
</ul>
</li>
<li>Resolution Tab
Model: <a class="xref" href="../../../api/Hi.NcMech.Holders.CylindroidHolder.html#Hi_NcMech_Holders_CylindroidHolder_PolarResolution2d">PolarResolution2d</a>
<a class="xref" href="../../widget/polar-resolution-2d-panel.html">Polar Resolution 2d</a></li>
<li>Info Tab
<ul>
<li>Name TextField (editable)</li>
<li>AbstractNote TextField (readonly)</li>
<li>Note TextField (editable)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Remember to call <a class="xref" href="../../../api/Hi.NcMech.Holders.CylindroidHolder.html#Hi_NcMech_Holders_CylindroidHolder_UpdateByCylindroid">UpdateByCylindroid()</a> after geometry reference or content changed.</p>
<h2 id="feature">Feature</h2>
<p>The two web sections reach the model by different routes, and that difference is why the holder resync belongs to one of them and not the other.</p>
<p><strong>Geometry.</strong> The section mounts the reusable Cylindroid editor on a key. <code>Get</code> publishes the holder's own <a class="xref" href="../../../api/Hi.NcMech.Holders.CylindroidHolder.html#Hi_NcMech_Holders_CylindroidHolder_Cylindroid">Cylindroid</a> in the index service under a per-session <code>-HolderCylindroid</code> key and hands that key back — but only when the caller supplies its session key; with no session key the response carries a null key and the section shows its initializing caption in the editor's place. The key names the same object the holder holds, so an edit lands on the holder's geometry directly. Because that generic editor mutates the bare geometry and holds no reference back to the holder, the section issues the holder resync itself after each geometry edit: <code>UpdateGeometryContent</code> runs <code>UpdateByCylindroid()</code> and then <code>ClearCache()</code>, recomputing the holder topology before regenerating the solid — clearing the cache alone would skip the recompute. The slave column mounts only the selected node's panel, so at most one holder-geometry panel is alive at a time and the shared key never collides.</p>
@@ -88,7 +88,7 @@
<h1 id="freeform-holder-panel">Freeform Holder Panel</h1>
<p>The key model is <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html">FreeformHolder</a>: a holder whose shape comes from a referenced geometry, typically an STL, positioned by two anchor transformers — one to the spindle, one to the cutter.</p>
<p>On the web client the holder occupies four child sections of the Holder node under the selected tool — Geometry, Geom To Spindle, Geom To Cutter and Resolution, the nodes whose role paths end in <code>.../holder/geometry</code>, <code>.../holder/geom-to-spindle</code>, <code>.../holder/geom-to-cutter</code> and <code>.../holder/resolution</code>, reached at <code>/tool-house/:toolId/holder/:subtab</code>. Its Name, readonly Abstract Note and Note are edited one level up, on the <a class="xref" href="holder-panel.html">Holder Panel</a> itself, under the Metadata-on-container convention. Every field ships on both clients; what differs is the surface hosting them — the WPF client gives the same fields a tab strip of its own.</p>
<p>The holder occupies four child sections of the Holder node under the selected tool — Geometry, Geom To Spindle, Geom To Cutter and Resolution, the nodes whose role paths end in <code>.../holder/geometry</code>, <code>.../holder/geom-to-spindle</code>, <code>.../holder/geom-to-cutter</code> and <code>.../holder/resolution</code>, reached at <code>/tool-house/:toolId/holder/:subtab</code>. Its Name, readonly Abstract Note and Note are edited one level up, on the <a class="xref" href="holder-panel.html">Holder Panel</a> itself, under the Metadata-on-container convention.</p>
<p>The three shape sections are not holder-specific editors. They are the same generic Geometry and Transformer slots the <a class="xref" href="../../general-setup/fixture.html">Fixture</a> branch of the General Setup page is built from: a kind picker on the section node, and the chosen kind's own editor on the child node below it. So an STL holder is picked as <code>StlFile</code> and its source file set through the shared StlFile editor, a <code>TransformationGeom</code> holder grows Inner Geometry and Inner Transformer nodes, a <code>GeomCombination</code> holder grows one node per item, and each placement grows the chosen transformer kind's editor — Static Translation for the usual <code>(0, 0, h)</code> offset.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
@@ -118,52 +118,13 @@
</ul>
</li>
</ul>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Freeform Holder Panel
<ul>
<li>Head Line
<ul>
<li>Title Label</li>
</ul>
</li>
<li>Tabs
<ul>
<li>Geometry Tab
<ul>
<li><a class="xref" href="../../geom/geom-manage-control.html">Geometry Management Panel</a></li>
</ul>
</li>
<li>Anchor Tab
(Apply <a class="xref" href="../../geom/transformer-panel.html">Transformer Manage Panel</a> to set the following tabs)
<ul>
<li>Geom To Spindle Tab</li>
<li>Geom To Cutter Tab</li>
</ul>
</li>
<li>Resolution Tab
Model: <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html#Hi_NcMech_Holders_FreeformHolder_PolarResolution2d">PolarResolution2d</a>
<a class="xref" href="../../widget/polar-resolution-2d-panel.html">Polar Resolution 2d</a></li>
<li>Info Tab
<ul>
<li>Name TextField (editable)</li>
<li>AbstractNote TextField (readonly)</li>
<li>Note TextField (editable)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Remember to call <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html#Hi_NcMech_Holders_FreeformHolder_UpdateByGeom">UpdateByGeom()</a> after geometry reference or content changed.</p>
<h2 id="feature">Feature</h2>
<p>The generic editors reach the holder's own members, and that is what makes the split work. When the Holder node's children are built, the page calls <code>FreeformHolderController</code>'s <code>Get</code> with its session key; the server publishes the holder's <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html#Hi_NcMech_Holders_FreeformHolder_Geom">Geom</a>, <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html#Hi_NcMech_Holders_FreeformHolder_GeomToSpindleTransformer">GeomToSpindleTransformer</a> and <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html#Hi_NcMech_Holders_FreeformHolder_GeomToCutterTransformer">GeomToCutterTransformer</a> in the index service under three per-session keys (<code>-HolderGeom</code>, <code>-HolderGeomToSpindle</code>, <code>-HolderGeomToCutter</code>) and hands the keys back. The keys name the very objects the holder holds, so an edit through a kind editor lands on the holder directly. The keys are returned even when a member is empty — nothing is published then, the section's picker reads an empty slot, and picking a kind fills that same key — which is how a freshly switched-to Freeform holder, whose geometry starts empty, gets its first shape.</p>
<p>Picking a kind is a container-aware create, not a bare <code>New</code>. The Geometry picker calls <code>CreateGeometry</code>, which builds the geometry, sets it as the holder's <code>Geom</code> and publishes it under the section's key (None clears the geometry). A placement picker first creates the transformer through the kind's own <code>New</code> endpoint and then attaches it with <code>UpdateGeomToSpindleTransformer</code> or <code>UpdateGeomToCutterTransformer</code>. Without the attach, only the index-service entry would change and the holder would keep pointing at its old transformer.</p>
<p>Because the generic editors hold no reference back to the holder, every edit at or below a section ends in the holder resync the page wires onto the section: a geometry edit calls <code>UpdateGeometryContent</code>, a placement edit re-attaches the transformer under its key. Both run <code>UpdateByGeom()</code> to regenerate the solid, <code>ClearCache()</code>, and <code>AlignAnchorByExposedCutterHeight()</code> to re-place the cutter under the holder — clearing the cache alone would leave the cutter where it was. The page then refreshes the tool list and the canvas.</p>
<p>The Resolution section writes the polar resolution straight onto the holder: <code>SetPolarResolution</code> assigns a fresh <a class="xref" href="../../../api/Hi.Geom.Resolution.PolarResolution2d.html">PolarResolution2d</a> and the holder swaps its solid for one born with the new value, keeping the geometry. <code>Get</code> also returns the current pair, which is how the two fields are seeded.</p>
<p>Guards sit on both sides. Every action on the controller answers Not Found for an unknown tool id and Bad Request when the tool's holder is not a <code>FreeformHolder</code>; <code>SetPolarResolution</code> rejects a linear or angle value that is not greater than zero, and the Resolution section refuses to send one. Fields commit on blur or Enter.</p>
<p>Switching a tool to Freeform is a committing choice. <code>SetHolderType</code> assigns a new <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html">FreeformHolder</a> only when the tool does not already carry one, so a holder authored on the WPF client or through HiNcRcl survives a round trip through the web page with its geometry, anchors and resolution intact.</p>
<p>Switching a tool to Freeform is a committing choice. <code>SetHolderType</code> assigns a new <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html">FreeformHolder</a> only when the tool does not already carry one, so a holder authored elsewhere — through HiNcRcl, say — survives a round trip through this page with its geometry, anchors and resolution intact.</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>
@@ -98,7 +98,7 @@
<li><strong><a class="xref" href="freeform-holder-panel.html">Freeform Holder</a></strong>: Represents holders with more complex, freeform geometry, often defined by STL files. See <a class="xref" href="../../../api/Hi.NcMech.Holders.FreeformHolder.html">FreeformHolder</a>.</li>
</ul>
<p>Each holder type has its own user interface elements for defining its geometry and properties.</p>
<p>On the web client this panel is the Holder tab of the <a class="xref" href="../index.html">Tool House Page</a>, reached at <code>/tool-house/:toolId/holder</code>. It is the panel of the Control-Tree Holder node under the selected tool — the node whose role path ends in <code>.../holder</code>.</p>
<p>This panel is the Holder tab of the <a class="xref" href="../index.html">Tool House Page</a>, reached at <code>/tool-house/:toolId/holder</code>. It is the panel of the Control-Tree Holder node under the selected tool — the node whose role path ends in <code>.../holder</code>.</p>
<h2 id="layout">Layout</h2>
<h3 id="web-layout">Web Layout</h3>
<ul>
@@ -121,33 +121,8 @@
<p>The three identity fields belong to whichever holder the tool carries. They sit on this panel under the Metadata-on-container convention — a container's own General fields are edited on the container's panel instead of in a General child — and they commit through the per-type holder controller's <code>SetName</code> and <code>SetNote</code> (<code>CylindroidHolderController</code> or <code>FreeformHolderController</code>), not through <code>ToolHouseController</code>. No holder leaves the panel without them.</p>
<p>The type also decides what grows below the node. A Cylindroid holder grows two child sections, Geometry and Resolution — the nodes whose role paths end in <code>.../holder/geometry</code> and <code>.../holder/resolution</code>; see <a class="xref" href="cylindroid-holder-panel.html">Cylindroid Holder Panel</a>. A Freeform holder grows four — Geometry, Geom To Spindle, Geom To Cutter and Resolution; see <a class="xref" href="freeform-holder-panel.html">Freeform Holder Panel</a>. Selecting a section replaces this panel in the slave column with that section's own panel. None grows no children, so the Holder node is a leaf and the hint is the whole surface.</p>
<p>This panel carries no object management of its own. Object management for the tool library sits on the tool-house root node, where the <a class="xref" href="../../widget/object-management-menu-button.html">Object Management Menu Button</a> is bound to the whole <code>.MachiningToolHouse</code> file rather than to a per-holder file.</p>
<h3 id="wpf-layout">WPF Layout</h3>
<ul>
<li>Holder Panel
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="../../widget/object-management-menu-button.html">Object Management Menu Button</a>
<ul>
<li>File extension is <code>.Holder</code>, load type <a class="xref" href="../../../api/Hi.NcMech.Holders.IHolder.html">IHolder</a></li>
<li>The pointed Editor Panel is the Holder Sub Management Panel.</li>
</ul>
</li>
<li>Title Label</li>
</ul>
</li>
<li>Holder Management Panel
<ul>
<li>Holder Type Selection Bar — None / Cylindroid / Freeform</li>
<li>Holder Sub Management Panel
A ContentPresenter whose content varies by the Holder Type: the <a class="xref" href="cylindroid-holder-panel.html">Cylindroid Holder Panel</a>, the <a class="xref" href="freeform-holder-panel.html">Freeform Holder Panel</a>, or nothing for None.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="feature">Feature</h2>
<p>Committing a type on the web is one server call plus a cache clear. <code>SetHolderType</code> maps the selector value onto the holder classes: none clears the tool's holder, while cylindroid and freeform each assign a new holder <strong>only when the tool does not already carry one of that class</strong> — so re-picking the type a tool already has keeps the holder it has, and the edits on it, rather than replacing it. When the caller passes its session key the server re-indexes the resulting holder under that session's holder key, and removes that entry for None, so the session key keeps pointing at the tool's current holder. The response carries the holder's class name and its abstract note.</p>
<p>Committing a type is one server call plus a cache clear. <code>SetHolderType</code> maps the selector value onto the holder classes: none clears the tool's holder, while cylindroid and freeform each assign a new holder <strong>only when the tool does not already carry one of that class</strong> — so re-picking the type a tool already has keeps the holder it has, and the edits on it, rather than replacing it. When the caller passes its session key the server re-indexes the resulting holder under that session's holder key, and removes that entry for None, so the session key keeps pointing at the tool's current holder. The response carries the holder's class name and its abstract note.</p>
<p>The panel then calls <code>ClearToolCache</code>, which clears the tool's cached solids and, when the holder implements <a class="xref" href="../../../api/Hi.Common.IClearCache.html">IClearCache</a>, the holder's own cache as well, so the tool canvas redraws against the new holder. Finally it emits a structure change for its own node, which is how the Geometry and Resolution sections appear or disappear with the type.</p>
<p><code>GetHolder</code> reports the same pair — class name and abstract note — and is what the panel reads on mount to seed the selector; an unknown tool id answers Not Found. The panel then reads the per-type controller's <code>Get</code> (<code>CylindroidHolderController</code> or <code>FreeformHolderController</code>) to fill the three identity fields; for a Freeform holder that same call also publishes the holder's geometry and two placements under the session's keys for the sections below.</p>
<h2 id="source-code-path">Source Code Path</h2>
@@ -87,9 +87,9 @@
<article data-uid="Anatomy-ToolHousePage">
<h1 id="tool-house-page">Tool House Page</h1>
<p>The Tool House is a page of its own at <code>/tool-house/:toolId?/:tab?/:subtab?</code>, reached from the menu bar's <code>Page ▾</code> dropdown. In the WPF client it is a sub-window opened from the <a class="xref" href="../shell/main-panel.html">Main Panel</a>.</p>
<p>The Tool House is a page of its own at <code>/tool-house/:toolId?/:tab?/:subtab?</code>, reached from the menu bar's <code>Page ▾</code> dropdown.</p>
<p>The key model is MachiningToolHouse.
The model <code>UserService</code> is per-client. On the web it is the server-side <code>Environments/UserService.cs</code>, which <code>Mech/ToolHouseDisplayController.cs</code> reads <code>EnablePhysics</code> from; the WPF client resolves its own <code>UserService</code> from the application's service provider.</p>
The model <code>UserService</code> is the server-side <code>Environments/UserService.cs</code>, which <code>Mech/ToolHouseDisplayController.cs</code> reads <code>EnablePhysics</code> from.</p>
<h2 id="layout">Layout</h2>
<p>Three columns, left to right, in two nested resizable splitters — the tool house and its tool list, the selected tool's editor tabs, and the tool canvas. Creating a tool opens no window of its own: <strong>New Tool</strong> adds a default tool and the user sets it up in the editor tabs.</p>
<ul>
@@ -164,8 +164,8 @@ The model <code>UserService</code> is per-client. On the web it is the server-si
</li>
</ul>
<p>The three anchor-and-buckle flags of <a class="xref" href="../../api/Hi.NcMech.Holders.HolderEditorDisplayee.html">HolderEditorDisplayee</a> and the Holder Rendering Mode radios are all disabled while Show Holder is off, so a child option is reachable only when its parent is on. The menu is one flat list of labelled groups rather than nested submenus, which is what keeps it one click deep.</p>
<p>The cutter's shape mode is not a control here. The server picks it when the canvas binds: <code>InitializeDisplay</code> sets <a class="xref" href="../../api/Hi.Milling.Cutters.MillingCutterEditorDisplayee.html#Hi_Milling_Cutters_MillingCutterEditorDisplayee_ShapeMode">ShapeMode</a> to Solid Bounding Shape whenever <code>UserService.EnablePhysics</code> is false, and the web Display Options dropdown offers no switch for it. The WPF page is the client whose Editor Displayee Options menu carries the Solid Bounding Shape / Detail Physics Shape radio submenu.</p>
<p>The left column is the other place the two clients differ. The WPF Tool House page lists its tools in a DataGrid with a checkbox column, an editable Tool ID column and a read-only Note column, and puts a Batch Actions menu above it — Select All, De-Select All, then Batch Duplicate and Batch Delete — with Duplicate and Delete also on the grid's context menu. On the web page Tool ID and Note are edited on the selected tool's General tab, where Duplicate and Delete are per-tool icon buttons, so the list itself stays a plain navigation column of router links.</p>
<p>The cutter's shape mode is not a control here. The server picks it when the canvas binds: <code>InitializeDisplay</code> sets <a class="xref" href="../../api/Hi.Milling.Cutters.MillingCutterEditorDisplayee.html#Hi_Milling_Cutters_MillingCutterEditorDisplayee_ShapeMode">ShapeMode</a> to Solid Bounding Shape whenever <code>UserService.EnablePhysics</code> is false, and the Display Options dropdown offers no switch for it.</p>
<p>The left column carries no editing of its own. Tool ID and Note are edited on the selected tool's General tab, where Duplicate and Delete are per-tool icon buttons, so the list stays a plain navigation column of router links. There is no batch selection and no batch action.</p>
<p>The Tool ID can not be repeated. Create Tool takes the largest existing ID plus 1 (or 1 in an empty house), Duplicate Tool inserts the clone at the first free ID past both the source and the largest existing ID, and renaming a tool onto an ID the house already holds is refused as a conflict.</p>
<p>When the canvas first binds, <code>InitializeDisplay</code> attaches the <a class="xref" href="../../api/Hi.Milling.MillingTools.MillingToolEditorDisplayee.html">MillingToolEditorDisplayee</a> and snaps the camera to the isometric view. Each later tool selection re-points that displayee's <code>MillingToolGetter</code> at the chosen tool and calls the renderingCanvas.<a class="xref" href="../../api/Hi.Disp.DispEngine.html">DispEngine</a>.<a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_SetViewToHomeView">SetViewToHomeView()</a>.</p>
<h3 id="duplication-button">Duplication Button</h3>
@@ -211,8 +211,8 @@ The model <code>UserService</code> is per-client. On the web it is the server-si
<h5>Note</h5>
<p>The fluting type is named loosely on the wire, and every spelling resolves. <code>PUT /{id}/fluting</code> accepts <code>UniformFluting</code> / <code>uniform</code> and <code>FreeFluting</code> / <code>free</code>, and equally <code>UniformContourTray</code> / <code>FreeContourTray</code>. A Control-Tree id whose cutter branch reads <code>…/contours/tray…</code> resolves to the <code>…/contours/fluting…</code> node.</p>
</div>
<p>One editor lives on the WPF client only, and the app says so where the type is chosen: the <strong>FreeformRemover</strong> cutter. Selecting that type on the web page keeps the existing model intact and shows a note pointing at the WPF client or HiNcRcl.</p>
<p>Three smaller surfaces are likewise the WPF client's: the InsertCutter and FluteInnerBeam physics groups, and the <code>CustomSpinningProfile</code> shaper-profile type — the web profile tab offers the five APT types (General / Ball / Column / Cone / Taper).</p>
<p>One cutter type has no editor here, and the app says so where the type is chosen: the <strong>FreeformRemover</strong>. Selecting it keeps the existing model intact and shows a note — <em>FreeformRemover editor is not yet available in the web frontend. Use the WPF client or HiNcRcl for now.</em></p>
<p>Three smaller surfaces are absent for the same reason: the InsertCutter and FluteInnerBeam physics groups, and the <code>CustomSpinningProfile</code> shaper-profile type — the profile tab offers the five APT types (General / Ball / Column / Cone / Taper).</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a href="cutter/index.html">Cutter Tab</a> — the cutter half of a tool, and the editors each cutter type grows</li>
@@ -91,9 +91,7 @@
<p>On the shipped web client the Stick Tool Panel is the <code>toolhouse/tool-&lt;n&gt;</code> node of the
<a class="xref" href="index.html">Tool House Page</a>: picking a tool in that page's left list mounts it, and
its editor is the tab strip filling the page's middle column.</p>
<p>The key model is <a class="xref" href="../../api/Hi.Milling.MillingTools.MillingTool.html">MillingTool</a>.
Other model: <code>UserService</code> — the WPF client reads its <code>EnablePhysics</code> to decide whether its
Intelligent Holder tab is shown.</p>
<p>The key model is <a class="xref" href="../../api/Hi.Milling.MillingTools.MillingTool.html">MillingTool</a>.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>Tool Node (<code>toolhouse/tool-&lt;n&gt;</code>)
@@ -136,19 +134,14 @@ Intelligent Holder tab is shown.</p>
</ul>
</li>
</ul>
<p>All five tabs ship unconditionally on the web page: the Int. Holder tab is one of the five
<code>TOOL_TABS</code>, and the tool node always grows its <code>…/intelligent</code> child. The WPF Stick Tool Panel is
the client that hides its Intelligent Holder tab when <code>UserService.EnablePhysics</code> is false, from
<code>UpdateIntelligentHolderTabVisibility</code> in <code>Mech/ToolHouse/StickToolPanel.xaml.cs</code>.</p>
<p>Object Management on the web page is house-level rather than per-tool: the
<p>All five tabs ship unconditionally: the Int. Holder tab is one of the five <code>TOOL_TABS</code>, and the tool
node always grows its <code>…/intelligent</code> child. Nothing gates it on <code>UserService.EnablePhysics</code>.</p>
<p>Object Management is house-level rather than per-tool: the
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> sits on the Tool House root
panel with file extension <code>.MachiningToolHouse</code> (load type <code>Hi.Machining.MachiningToolHouse, HiMech</code>)
and covers the whole house, while the tool node offers Duplicate and Delete for the single tool. The
WPF Stick Tool Panel is the client that carries a per-tool Object Management Menu Button on its head
line, with file extension <code>.MillingTool</code> and the Stick Tool Management Panel as its pointed editor
panel. That client also gives the identity fields a tab of their own — <strong>Info</strong>, a read-only Abstract
Note TextBox over an editable Note TextBox; on the web page those two fields are the General tab,
alongside Tool ID and the Duplicate / Delete buttons.</p>
and covers the whole house, while the tool node offers Duplicate and Delete for the single tool.
There is no per-tool object-management button, and no separate tab for the identity fields — the
Abstract Note and Note sit on the General tab, alongside Tool ID and the Duplicate / Delete buttons.</p>
<h2 id="tabs-and-the-url">Tabs and the URL</h2>
<p>The tabs are <code>TOOL_TABS</code> — <strong>General</strong>, <strong>Cutter</strong>, <strong>Holder</strong>, <strong>Clamping</strong>, <strong>Int. Holder</strong> —
declared in <code>wwwroot-src/src/router/treeRoutes.ts</code>, and those same five names are the <code>:tab?</code> segment