Table of Contents

Freeform Holder Panel

The key model is FreeformHolder: 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.

The holder occupies four child nodes of the Holder node in the picked tool's Control Tree — Geometry, Geom To Spindle, Geom To Cutter and Resolution, the nodes whose role paths end in .../holder/geometry, .../holder/geom-to-spindle, .../holder/geom-to-cutter and .../holder/resolution, each reached at /tool-house/:toolId/holder/<node>, with every node the three shape nodes grow further down the same path. Its Name, readonly Abstract Note and Note are edited one level up, on the Holder Panel itself, under the Metadata-on-container convention.

The three shape nodes are not holder-specific editors. They are the generic Geometry and Transformer slots of the Control Tree — the workpiece's raw and target geometry on the Equipment page are Geometry slots of the same kind: a kind picker on the slot node, and the chosen kind's own editor on the child node below it. So an STL holder is picked as StlFile and its source file set through the shared StlFile editor, a holder geometry under a transform reads as Transformed StlFile with the transformer's node beneath it (the Transform picker heading the kind node puts it there), a GeomCombination holder grows one node per item, and each placement grows the chosen transformer kind's editor — Static Translation for the usual (0, 0, h) offset.

Layout

Web Layout

  • Geometry Section
    • Geometry Type Selector — None (unset) / Box3d / Cylindroid / StlFile / GeomCombination
    • The chosen kind's editor, on the child node the kind grows, headed by the Transform picker — No Transform / Static Translation / Static Rotation / Static Freeform / General Transform / Dynamic Translation / Dynamic Rotation — with the transformer's editor on a further child
  • Geom To Spindle Section — where the holder sits relative to the spindle
    • Transformer Type Selector — Static Translation / Static Rotation / Static Freeform / Dynamic Translation / Dynamic Rotation / General Transform / No Transform
    • The chosen kind's editor, on the child node the kind grows
  • Geom To Cutter Section — where the cutter sits relative to the holder
    • Transformer Type Selector and the chosen kind's editor, as above
  • Resolution Section
    • Resolution Hint Caption — names the STL tessellation resolution used for display and collision meshing
    • Linear Resolution (mm) Input Field
    • Angle Resolution (deg) Input Field

Feature

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 FreeformHolderController‘s Get with its session key; the server publishes the holder's Geom, GeomToSpindleTransformer and GeomToCutterTransformer in the index service under three per-session keys — {sessionKey}-HolderGeom, {sessionKey}-HolderGeomToSpindle and {sessionKey}-HolderGeomToCutter — and hands the keys back, and the three shape nodes are bound to them. The keys name the very objects the holder holds, so an edit through a kind editor lands on the holder directly. They belong to the session, not to the tool: every Get with the session key re-points them at the members of the holder it reads — the Holder node's panel and the Resolution node's panel call it too — which is why the Tool House tree holds only the picked tool, since two tools’ holder branches built together would leave both sets of nodes addressing the tool built last.

The keys are returned even when a member is empty — nothing is published under the key then and any earlier entry is dropped, the node's picker reads an empty slot, and picking a kind fills that same key — which is how a holder whose geometry was set to None gets a shape again. A freshly switched-to Freeform holder's geometry is a TransformationGeom holding no inner geometry and no transformer; the Geometry node reads it as an empty slot — no kind picked, no child — and picking a kind fills that wrapper in place. Both placements start as a zero Static Translation.

Picking a kind is a container-aware create, not a bare New. The Geometry picker calls CreateGeometry, which builds the geometry, sets it as the holder's Geom and publishes it under the section's key (None clears the geometry). The Transform picker calls SetGeometryTransform, which wraps the holder's geometry in place, swaps its transformer or unwraps it, and re-points Geom. A placement picker first creates the transformer through the kind's own New endpoint and then attaches it with UpdateGeomToSpindleTransformer or UpdateGeomToCutterTransformer. Without the attach, only the index-service entry would change and the holder would keep pointing at its old transformer.

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 UpdateGeometryContent, a placement edit re-attaches the transformer under its key. Both run UpdateByGeom() to regenerate the solid, ClearCache(), and AlignAnchorByExposedCutterHeight() 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.

The Resolution section writes the polar resolution straight onto the holder: SetPolarResolution assigns a fresh PolarResolution2d and the holder swaps its solid for one born with the new value, keeping the geometry. Get also returns the current pair, which is how the two fields are seeded.

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 FreeformHolder; SetPolarResolution 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.

Switching a tool away from Freeform discards its holder — None clears it and Cylindroid assigns a new one — but re-picking Freeform does not: SetHolderType assigns a new FreeformHolder 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.

Source Code Path

See HiNC App Anatomy for git repository links.

HiNC-2025-webservice (Quasar CLI SPA):

  • wwwroot-src/src/components/controlTree/toolHouseItemTypes.ts — grows the four child nodes for a FreeformHolder: reads Get to publish the three members, binds the Geometry node to the generic Geometry item type and the two placements to the generic Transformer item type (with the create hooks and the resync chain), and routes Resolution to the HolderSection item type.
  • wwwroot-src/src/components/controlTree/itemTypes.ts — the generic Geometry and Transformer slot types: the one kind node a filled slot grows, the transformer node a transformed slot hangs under it, and the per-item nodes a GeomCombination grows below it.
  • wwwroot-src/src/components/controlTree/GeometrySlotPanel.vue / TransformerSlotPanel.vue — the kind pickers on the three shape nodes; the chosen kind's editor lives on the child node (wwwroot-src/src/components/geom/geometryEditors.ts, wwwroot-src/src/components/topo/transformerEditors.ts).
  • wwwroot-src/src/components/controlTree/toolhouse/HolderSectionPanel.vue — the Resolution node, dispatching on the holder type to FreeformHolderController for a Freeform holder.
  • wwwroot-src/src/components/controlTree/toolhouse/ToolHolderPanel.vue — the parent Holder node's panel, which hosts this holder's Name, readonly Abstract Note and Note and commits them through setFreeformHolderName / setFreeformHolderNote.
  • wwwroot-src/src/api/freeformHolder.ts — the typed wrapper over /api/FreeformHolder: getFreeformHolder, createFreeformHolderGeometry, setFreeformHolderGeometryTransform, updateFreeformHolderGeometry, resyncFreeformHolder, updateFreeformHolderGeomToSpindle, updateFreeformHolderGeomToCutter, setFreeformHolderName, setFreeformHolderNote, setFreeformHolderResolution.
  • wwwroot-src/src/i18n/en/toolhouse.ts — the two placement section labels (toolhouse.node.geomToSpindle / geomToCutter) and the Freeform sections hint; the zh-Hans, zh-Hant, vi and ja files carry the same keys.
  • Mech/FreeformHolderController.cs — POST Get (name / note / abstract note, the polar resolution, and — with a session key — the three member keys) plus POST CreateGeometry | SetGeometryTransform | UpdateGeometry | IndexCurrentGeometry | UpdateGeometryContent | UpdateGeomToSpindleTransformer | UpdateGeomToCutterTransformer | SetName | SetNote | SetPolarResolution. All but Get and IndexCurrentGeometry, the two that only index, answer 409 ToolEditRefusedWhileRunning while the pace player is locked — a run in progress or paused — since the holder hangs on the tool the run mounted and the resync swaps its solid and re-places the cutter.

See Also

  • Holder Panel — the Holder node above these four: the type selector, and this holder's Name, Abstract Note and Note
  • Polar Resolution 2D Panel — the linear / angle resolution pair the Resolution node edits
  • Transformer Panel — the kind picker behind the Geom To Spindle and Geom To Cutter sections
  • Fixture Page — the other part whose geometry and placing transforms are edited by kind, there on the Topology column's anchors and branches