beckhoff-xts-viewer-3d 5.2.3 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -12,7 +12,7 @@ import { GLTF } from 'three-stdlib';
12
12
  * Run `npm run sync-version` (or any script that depends on it) to refresh
13
13
  * after bumping the package version.
14
14
  */
15
- declare const VERSION: "5.2.3";
15
+ declare const VERSION: "5.3.0";
16
16
 
17
17
  /**
18
18
  * Default CDN URL for the GLB asset bundle (`beckhoff-xts-viewer-3d-assets`).
@@ -32,7 +32,7 @@ declare const VERSION: "5.2.3";
32
32
  * build time so every install streams from a known-compatible GLB
33
33
  * release.
34
34
  */
35
- declare const JSDELIVR_ASSETS_BASE_URL: "https://cdn.jsdelivr.net/npm/beckhoff-xts-viewer-3d-assets@2.0.1/models";
35
+ declare const JSDELIVR_ASSETS_BASE_URL: "https://cdn.jsdelivr.net/npm/beckhoff-xts-viewer-3d-assets@2.1.0/models";
36
36
 
37
37
  /**
38
38
  * Catalogue taxonomy — the string unions naming every module, mover, tool and
@@ -44,7 +44,7 @@ declare const JSDELIVR_ASSETS_BASE_URL: "https://cdn.jsdelivr.net/npm/beckhoff-x
44
44
  * public type surface. Nothing here imports anything.
45
45
  */
46
46
  /** Beckhoff XTS module types supported by the 3D viewer. */
47
- type ModuleType3D = 'AT2000_0250' | 'AT2001_0250' | 'AT2002_0250' | 'AT2002_0249' | 'AT2002_0249_ZX2002_0001' | 'AT2000_0233' | 'AT2000_0249' | 'AT2100_0250' | 'AT2102_0250' | 'AT2020_0250' | 'AT2021_0250' | 'AT2025_0250' | 'AT2026_0250' | 'AT2040_0250' | 'AT2041_0250' | 'AT2042_0250' | 'AT2140_0250' | 'AT2050_0500' | 'AT2050_0501' | 'AT2050_0500_180' | 'AT2200_0500' | 'AT2202_0500' | 'ATH2000_0250' | 'ATH2001_0250' | 'ATH2002_0250' | 'ATH2020_0250' | 'ATH2040_0250' | 'ATH2041_0250' | 'ATH2042_0250' | 'ATH2050_0500' | 'ATH2050_0501' | 'ATH2050_0500_180';
47
+ type ModuleType3D = 'AT2000_0250' | 'AT2001_0250' | 'AT2002_0250' | 'AT2002_0249' | 'AT2000_0233' | 'AT2000_0249' | 'AT2100_0250' | 'AT2102_0250' | 'AT2020_0250' | 'AT2021_0250' | 'AT2025_0250' | 'AT2026_0250' | 'AT2040_0250' | 'AT2041_0250' | 'AT2050_0500' | 'AT2050_0501' | 'AT2050_0500_180' | 'AT2200_0500' | 'AT2202_0500' | 'ATH2000_0250' | 'ATH2001_0250' | 'ATH2002_0250' | 'ATH2020_0250' | 'ATH2040_0250' | 'ATH2041_0250' | 'ATH2042_0250' | 'ATH2050_0500' | 'ATH2050_0501' | 'ATH2050_0500_180';
48
48
  /**
49
49
  * Rail system beneath the module. Only affects the visual mesh
50
50
  * (Beckhoff aluminium rail vs. Hepco GFX), not the path math.
@@ -65,7 +65,20 @@ type RailSystem = 'Beckhoff' | 'HepcoGfx';
65
65
  * The `_S25` carrier ships without a magnet plate; the `_C25/_C34/_M34/_M40`
66
66
  * variants include the Beckhoff magnet plate set (`7P`/`10P` = 7/10-pole).
67
67
  */
68
- type MoverType3D = 'AT9011_0050' | 'AT9011_0070' | 'AT9012_0050' | 'AT9014_0055' | 'AT9014_0070' | 'ATH9011_0075' | 'ATH9013_0075' | 'Hepco_GFX2_1TC_S25' | 'Hepco_GFX2_1TC_C25' | 'Hepco_GFX2_1TC_C34' | 'Hepco_GFX2_1TC_M34' | 'Hepco_GFX2_1TC_M34_7P' | 'Hepco_GFX2_FCC_C25' | 'Hepco_GFX2_FCC_M34' | 'Hepco_GFX2_FCC_M34_7P' | 'Hepco_GFX2_FCC_M34_10P' | 'Hepco_GFX2_FCC_M40_10P' | 'Custom';
68
+ type MoverType3D = 'AT9014_0055' | 'AT9014_0070' | 'ATH9011_0075' | 'Hepco_GFX2_1TC_S25' | 'Hepco_GFX2_1TC_C25' | 'Hepco_GFX2_1TC_C34' | 'Hepco_GFX2_1TC_M34' | 'Hepco_GFX2_1TC_M34_7P' | 'Hepco_GFX2_FCC_C25' | 'Hepco_GFX2_FCC_M34' | 'Hepco_GFX2_FCC_M34_7P' | 'Hepco_GFX2_FCC_M34_10P' | 'Hepco_GFX2_FCC_M40_10P' | 'Custom';
69
+ /**
70
+ * Beckhoff magnet plate sets — the part carrying the mover's magnets and,
71
+ * on the AT9001 family, the encoder flag ("Geberfahne").
72
+ *
73
+ * The digit after the dash is the pole count in base 16 (`0450` → 4,
74
+ * `0550` → 5, `0775` → 7, `0AA0` → 10), verified by counting magnets in each
75
+ * converted mesh rather than read off the part number. `ATH9001` is the
76
+ * hygienic family, whose rows are encapsulated in sealed stainless.
77
+ *
78
+ * Every set is symmetric about its own travel axis, so its GLB origin already
79
+ * sits on the magnet-plate centre that the mover frame is defined around.
80
+ */
81
+ type MagnetPlateType3D = 'AT9001_0450' | 'AT9001_0550' | 'AT9001_0775' | 'AT9001_0AA0' | 'ATH9001_0550' | 'ATH9001_0550_0001' | 'ATH9001_0AA0';
69
82
  /** Predefined mover-tool / carrier-plate types. */
70
83
  type MoverToolType3D = 'AT8200_1000_0100' | 'AT8200_2000_0100' | 'Custom';
71
84
  /**
@@ -73,17 +86,23 @@ type MoverToolType3D = 'AT8200_1000_0100' | 'AT8200_2000_0100' | 'Custom';
73
86
  * when `RailSystem === 'Beckhoff'` to visualise the outer guide
74
87
  * profile that the mover wheels engage.
75
88
  *
76
- * Curve sign drives the selection (see `MODULE_GUIDING_RAIL_MAP`):
89
+ * Curve sign drives the selection, and on curves so does the mover — see
90
+ * `resolveGuidingRailType`:
77
91
  * - Straights: AT9000_0249 / AT9000_0250 / AT9000_0500
78
92
  * - +22.5° curve: AT9020_1250
79
93
  * - −22.5° curve: AT9025_1466
80
94
  * - 45° curve: AT9040_0750
81
95
  * - 180° clothoid (entire AT2050 500 mm path): AT9050_0500
82
96
  *
97
+ * Beckhoff builds the curve rails per mover: the unsuffixed types above are
98
+ * the `-0055` order variants, made for the AT9014-0055, and the `_0170` ones
99
+ * are the `-0170` variants for the wider AT9014-0070. The straights carry no
100
+ * such variant — one AT9000 profile serves every mover.
101
+ *
83
102
  * Hygienic ATH modules ship with their guide profile baked into the module
84
103
  * GLB and therefore do NOT receive a separate guiding-rail mesh.
85
104
  */
86
- type RailType3D = 'AT9000_0249' | 'AT9000_0250' | 'AT9000_0500' | 'AT9020_1250' | 'AT9025_1466' | 'AT9040_0750' | 'AT9050_0500';
105
+ type RailType3D = 'AT9000_0249' | 'AT9000_0250' | 'AT9000_0500' | 'AT9020_1250' | 'AT9020_1250_0170' | 'AT9025_1466' | 'AT9025_1466_0170' | 'AT9040_0750' | 'AT9050_0500' | 'AT9050_0500_0170';
87
106
 
88
107
  /**
89
108
  * Scalar tuples shared by every other type module.
@@ -330,8 +349,13 @@ interface MoverIdLabelOptions {
330
349
  */
331
350
  offsetMm?: Vec3;
332
351
  /**
333
- * URL of a TTF / OTF / WOFF font, passed straight to troika-three-text.
334
- * Default: `DEFAULT_LABEL_FONT_URL` (Roboto Condensed via jsDelivr).
352
+ * URL of a TTF / OTF / WOFF font for the label.
353
+ *
354
+ * Default: the viewer's built-in Roboto Condensed, compiled into the
355
+ * bundle and served from a blob URL — no request, and nothing that can
356
+ * fail. A custom URL is fetched once and shared by every label; until
357
+ * it answers, and permanently if it never does, labels render in the
358
+ * built-in font and the failure is reported once via `console.warn`.
335
359
  */
336
360
  fontUrl?: string;
337
361
  /** Outline thickness in mm. 0 disables the outline. Default: 0. */
@@ -675,6 +699,20 @@ interface ProcessingUnitConfig {
675
699
  */
676
700
  objectId: number;
677
701
  moverType: MoverType3D;
702
+ /**
703
+ * Magnet plate set attached to the carriage.
704
+ *
705
+ * Only meaningful for a mover whose GLB ships bare — today just
706
+ * `Hepco_GFX2_1TC_S25`. Every other mover models its plate already
707
+ * (see `MOVER_INTEGRATED_PLATE`) and ignores this, so setting it there
708
+ * cannot double-plate the mover. Defaults to `AT9001_0550`, the 5-pole set
709
+ * the equivalent `_C25` carriage ships with.
710
+ *
711
+ * Deliberately per processing unit rather than per mover: `moverType` is
712
+ * too, and a plate that varied between movers would force every one of them
713
+ * to claim its own scene node, disabling the instanced render path.
714
+ */
715
+ magnetPlateType?: MagnetPlateType3D;
678
716
  customMoverLayout?: CustomMoverLayout;
679
717
  /** Default: 'Beckhoff'. */
680
718
  railSystem?: RailSystem;
@@ -1090,6 +1128,7 @@ interface AssetManifest {
1090
1128
  modules?: Partial<Record<ModuleType3D, ModuleAssetEntry>>;
1091
1129
  movers?: Partial<Record<MoverType3D, MoverAssetEntry>>;
1092
1130
  tools?: Partial<Record<MoverToolType3D, MoverToolAssetEntry>>;
1131
+ magnetPlates?: Partial<Record<MagnetPlateType3D, MagnetPlateAssetEntry>>;
1093
1132
  guidingRails?: Partial<Record<RailType3D, RailAssetEntry>>;
1094
1133
  }
1095
1134
  interface RailAssetEntry {
@@ -1112,6 +1151,10 @@ interface MoverToolAssetEntry {
1112
1151
  glbUrl: string;
1113
1152
  sidecarUrl?: string;
1114
1153
  }
1154
+ interface MagnetPlateAssetEntry {
1155
+ glbUrl: string;
1156
+ sidecarUrl?: string;
1157
+ }
1115
1158
 
1116
1159
  /**
1117
1160
  * Public types for the measurement + annotation feature.
@@ -1459,6 +1502,18 @@ interface MoverSidecar {
1459
1502
  magnetPlateCenterMm: Vec3;
1460
1503
  pathLengthMm: number;
1461
1504
  }
1505
+ /**
1506
+ * Calibration sidecar for a standalone magnet plate set. The plate is drawn
1507
+ * in the mover-local frame (+X travel, +Z up) with this correction applied,
1508
+ * so `originCorrection` is what seats it on the carriage.
1509
+ */
1510
+ interface MagnetPlateSidecar {
1511
+ plateType: MagnetPlateType3D;
1512
+ glb: string;
1513
+ originCorrection: OriginCorrection;
1514
+ poleCount: number;
1515
+ lengthMm: number;
1516
+ }
1462
1517
  interface MoverToolSidecar {
1463
1518
  toolType: MoverToolType3D;
1464
1519
  glbUrl: string;
@@ -1542,8 +1597,15 @@ declare function pickGlbForRail(entry: GlbByRailSystem | undefined, rail: RailSy
1542
1597
  * here and imported by both.
1543
1598
  */
1544
1599
 
1545
- /** Where the view cube sits in the canvas. */
1600
+ /** Where the orientation gizmo sits in the canvas. */
1546
1601
  type ViewCubeAlignment = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center' | 'center-left' | 'center-right' | 'center-center';
1602
+ /**
1603
+ * The corner navigation gizmo: three labelled X / Y / Z axis handles that
1604
+ * track the camera and snap it onto an axis when clicked.
1605
+ *
1606
+ * Named `ViewCube*` for backwards compatibility — earlier versions drew a
1607
+ * navigation cube in the same spot.
1608
+ */
1547
1609
  interface ViewCubeOptions {
1548
1610
  enabled?: boolean;
1549
1611
  /** Default `'bottom-right'`. */
@@ -1551,6 +1613,28 @@ interface ViewCubeOptions {
1551
1613
  /** Distance from the aligned corner, in CSS px. Default `[80, 80]`. */
1552
1614
  marginPx?: [number, number];
1553
1615
  }
1616
+ /**
1617
+ * Keyboard navigation — moving the camera *through* the scene rather than
1618
+ * orbiting it around a fixed point.
1619
+ *
1620
+ * The arrow keys (and the WASD block) walk the camera and the orbit pivot
1621
+ * together along the ground plane, PageUp / PageDown climb, Shift goes
1622
+ * faster and Alt finer. Keys are read from the canvas once it has focus,
1623
+ * which it takes on pointer-down, so an embedded viewer never swallows the
1624
+ * host page's arrow keys.
1625
+ *
1626
+ * Off automatically while `lock.pan` is set — moving through the scene is
1627
+ * panning by another input device.
1628
+ */
1629
+ interface KeyboardNavigationOptions {
1630
+ /** Default `true`. */
1631
+ enabled?: boolean;
1632
+ /**
1633
+ * Multiplier on the default travel rate, which is ~0.6 × the current
1634
+ * orbit distance per second. Default `1`.
1635
+ */
1636
+ speed?: number;
1637
+ }
1554
1638
  /** Interaction locks. Each `true` disables that interaction. */
1555
1639
  interface InteractionLocks {
1556
1640
  rotate?: boolean;
@@ -1612,6 +1696,7 @@ interface SidecarOverrides {
1612
1696
  modules?: Partial<Record<ModuleType3D, OriginCorrection>>;
1613
1697
  movers?: Partial<Record<MoverType3D, OriginCorrection>>;
1614
1698
  tools?: Partial<Record<MoverToolType3D, OriginCorrection>>;
1699
+ magnetPlates?: Partial<Record<MagnetPlateType3D, OriginCorrection>>;
1615
1700
  rails?: Partial<Record<RailType3D, OriginCorrection>>;
1616
1701
  }
1617
1702
 
@@ -1970,12 +2055,17 @@ declare function checkAllMoverCollisions(config: NormalizedXtsConfig, livePositi
1970
2055
  * are excluded; everything else (cross-part, cross-XPU, same-part
1971
2056
  * non-adjacent) is tested.
1972
2057
  *
1973
- * The box cross-section (`halfWidthMm` 50, `heightMm` 100) is a coarse but
1974
- * deterministic, asset-independent envelope. Real per-module CAD bounds live
1975
- * in the GLB sidecars (`approximateBoundsMm`) and could refine it later.
1976
- * Likewise the `warningGapMm` near-miss is an axis-projected SAT separation,
1977
- * not a true Euclidean (GJK) distance — slightly conservative for
1978
- * corner-to-corner cases.
2058
+ * The box height comes from the module's own GLB sidecar
2059
+ * (`moduleHeightSpan`, moduleCrossSection.ts): a motor module measures about
2060
+ * 40 mm and straddles the path plane. It used to be one coarse box per type,
2061
+ * `0 … 100` mm and entirely above the plane, which reported an overlap between
2062
+ * any two tracks stacked closer than 100 mm — the arrangement a doublesided
2063
+ * guide rail is built as. The half-width stays the coarse `halfWidthMm` 50,
2064
+ * because a curved module's sidecar box spans its whole arc in X and Y and is
2065
+ * a cross-section only in Z; moduleCrossSection.ts sets that out.
2066
+ *
2067
+ * The `warningGapMm` near-miss is an axis-projected SAT separation, not a true
2068
+ * Euclidean (GJK) distance — slightly conservative for corner-to-corner cases.
1979
2069
  */
1980
2070
 
1981
2071
  /** Default box half-width (Y) — matches `approximateModuleBounds`. */
@@ -2022,7 +2112,16 @@ interface CheckModuleCollisionsOptions {
2022
2112
  includeSamePart?: boolean;
2023
2113
  /** Box half-width in Y (mm). Default 50. */
2024
2114
  halfWidthMm?: number;
2025
- /** Box height in Z (mm). Default 100. */
2115
+ /**
2116
+ * Force one box height in Z (mm), spanning `0 … heightMm` above the path
2117
+ * plane for every module type.
2118
+ *
2119
+ * Left unset — the default — each module is given the vertical extent its
2120
+ * GLB sidecar reports (`moduleHeightSpan`), which both is the real height
2121
+ * and sits on the real side of the path plane. Setting this restores the one
2122
+ * coarse box the check used before that, for a caller who wants a
2123
+ * deliberately conservative envelope.
2124
+ */
2026
2125
  heightMm?: number;
2027
2126
  }
2028
2127
  /** Oriented bounding box in world space (unit axes + scaled half-extents). */
@@ -2928,6 +3027,14 @@ interface XtsViewer3DProps {
2928
3027
  topDown?: boolean;
2929
3028
  /** Lock interactions. */
2930
3029
  lock?: InteractionLocks;
3030
+ /**
3031
+ * Move the camera through the scene with the keyboard instead of only
3032
+ * orbiting it: arrow keys / WASD walk along the ground plane, PageUp /
3033
+ * PageDown climb, Shift is faster and Alt finer. On by default; the canvas
3034
+ * takes focus on pointer-down, so the keys arm themselves on the first
3035
+ * click into the viewer.
3036
+ */
3037
+ keyboardNavigation?: KeyboardNavigationOptions;
2931
3038
  /** Selection-Mode. Default: 'Off'. */
2932
3039
  selectionMode?: SelectionMode;
2933
3040
  /** Controlled selection. */
@@ -3111,9 +3218,61 @@ interface MoverCatalogEntry {
3111
3218
  magnetPlateCenterMm: Vec3;
3112
3219
  }
3113
3220
  declare const MOVER_CATALOG: Readonly<Record<MoverType3D, MoverCatalogEntry>>;
3221
+ /**
3222
+ * The magnet plate set already modelled into each mover's own GLB, or `null`
3223
+ * for a carriage that ships bare and needs one attached.
3224
+ *
3225
+ * Nearly every mover asset is a combined carriage+plate assembly: the Beckhoff
3226
+ * movers say so in the filename (`AT9014-0055-0550` carries an AT9001-0550),
3227
+ * and each Hepco carriage records the plate it was ordered with in its
3228
+ * sidecar's `_calibration.hepcoPartNumber` (`…_AT9001-0775` and friends).
3229
+ * Attaching a second plate to one of those would draw a plate through a plate,
3230
+ * so `<XtsMoverPlate>` renders only where this table says `null`.
3231
+ *
3232
+ * `Custom` is `null` because its geometry comes from `customMoverLayout`,
3233
+ * which may or may not include a plate — the host decides.
3234
+ *
3235
+ * Exhaustive over `MoverType3D` on purpose (`Record`, not `Partial<Record>`):
3236
+ * a new mover type has to state which it is, so it cannot silently end up
3237
+ * double-plated or plate-less.
3238
+ */
3239
+ declare const MOVER_INTEGRATED_PLATE: Readonly<Record<MoverType3D, MagnetPlateType3D | null>>;
3114
3240
  declare function getMoverEntry(moverType: MoverType3D): MoverCatalogEntry | undefined;
3115
3241
  declare function isKnownMoverType(value: string): value is MoverType3D;
3116
3242
 
3243
+ /**
3244
+ * Magnet plate set catalog (3D-canonical).
3245
+ *
3246
+ * A plate set is the part that carries the mover's magnets. Most mover GLBs
3247
+ * ship with one already modelled in (see `MOVER_INTEGRATED_PLATE`); these
3248
+ * entries describe the sets that exist as assets of their own, so a bare
3249
+ * carriage can be given any pole count without a new combined CAD assembly.
3250
+ *
3251
+ * `lengthMm` is the plate's extent along the travel axis, measured from the
3252
+ * converted GLB rather than inferred from the part number. Pole count was
3253
+ * likewise counted in the mesh: each magnet is 7.7 mm wide and sits in a row
3254
+ * just proud of its backing plate, so the magnets separate cleanly along the
3255
+ * travel axis. That count is what establishes the part-number rule (the digit
3256
+ * after the dash is the pole count in base 16) — it is not assumed from it.
3257
+ */
3258
+
3259
+ interface MagnetPlateCatalogEntry {
3260
+ plateType: MagnetPlateType3D;
3261
+ /** Magnets in one row, i.e. the pole count the part number encodes. */
3262
+ poleCount: number;
3263
+ /** Plate extent along the travel axis, mm (GLB-measured). */
3264
+ lengthMm: number;
3265
+ /**
3266
+ * Whether the set models the encoder flag ("Geberfahne"). The AT9001 sets
3267
+ * all carry it as a thin sheet reaching back along −Z; the hygienic ATH9001
3268
+ * sets encapsulate their rows in sealed stainless and model none.
3269
+ */
3270
+ hasEncoderFlag: boolean;
3271
+ }
3272
+ declare const MAGNET_PLATE_CATALOG: Readonly<Record<MagnetPlateType3D, MagnetPlateCatalogEntry>>;
3273
+ declare function getMagnetPlateEntry(plateType: MagnetPlateType3D): MagnetPlateCatalogEntry | undefined;
3274
+ declare function isKnownMagnetPlateType(value: unknown): value is MagnetPlateType3D;
3275
+
3117
3276
  /**
3118
3277
  * Module-local path math.
3119
3278
  *
@@ -3224,6 +3383,29 @@ declare function buildPartPath(modules: ReadonlyArray<ModuleEntry>): PartPath;
3224
3383
  */
3225
3384
  declare function poseToMatrix4(pose: PlanarPose, out?: Matrix4): Matrix4;
3226
3385
 
3386
+ /**
3387
+ * Pure helpers for composing per-XPU `trackTransform` and root
3388
+ * `XtsConfig.orientation` into Three.js Matrix4 instances. Lives in geometry/
3389
+ * so the component layer (XtsViewer3D, XtsScene) and the test suite share a
3390
+ * single source of truth — no duplicated math.
3391
+ *
3392
+ * Composition order:
3393
+ *
3394
+ * world = orientation ⊗ trackTransform ⊗ partTransformation ⊗ chain
3395
+ *
3396
+ * `partTransformation` is applied inside the per-XPU group by XtsScene, so
3397
+ * the helpers here only cover the outermost two layers.
3398
+ */
3399
+
3400
+ /**
3401
+ * Default Z lift for a track that does not specify `positionMm`. The XTS
3402
+ * track sits on a base plate / table top in real installations, so 0 mm
3403
+ * (= floor) is rarely what the user wants. 200 mm matches the standard
3404
+ * Beckhoff table height used throughout the demo configs and renders
3405
+ * correctly under the default shadow-casting ground plane.
3406
+ */
3407
+ declare const DEFAULT_TRACK_Z_MM = 200;
3408
+
3227
3409
  /**
3228
3410
  * Point-cloud registry for Free-path modules.
3229
3411
  *
@@ -3328,6 +3510,67 @@ declare function interpolateHeatmapValue(samples: ReadonlyArray<HeatmapSample>,
3328
3510
  */
3329
3511
  declare function normaliseToHeatmapRange(value: number, min: number, max: number): number;
3330
3512
 
3513
+ /**
3514
+ * The vertical extent of a track module, in the path frame.
3515
+ *
3516
+ * `moduleCollision.ts` needs to know how tall a module is and where it sits
3517
+ * relative to the mover path. It used to assume one coarse box for every type —
3518
+ * `0 … DEFAULT_MODULE_HEIGHT_MM`, entirely *above* the path plane — which is
3519
+ * neither the right height nor the right side: a motor module measures about
3520
+ * 40 mm and straddles the plane, roughly half above and half below. Two tracks
3521
+ * stacked closer than 100 mm therefore reported an overlap that does not exist,
3522
+ * and the deepest ones reported it on every module pair.
3523
+ *
3524
+ * ## Why only Z, and not the width too
3525
+ *
3526
+ * The GLB sidecar carries `approximateBoundsMm`, the axis-aligned box of the
3527
+ * CAD body *before* `originCorrection` is applied. Composing the two gives the
3528
+ * body in the path frame — but only its **Z** range is a cross-section.
3529
+ *
3530
+ * Module paths are planar in XY and curves bend about Z, so a curved module's
3531
+ * GLB spans its whole arc in X and Y: the 180° AT2050_0500 measures 307 mm
3532
+ * across in Y, which is the sweep of the U-turn and not how wide the beam is.
3533
+ * Its Z range, on the other hand, is the same 40.1 mm as the straight it is
3534
+ * built from, because the sweep never leaves the plane.
3535
+ *
3536
+ * So the height is read from the asset and the half-width stays the coarse
3537
+ * constant. Taking the width from the same box would inflate every curve
3538
+ * segment to the size of its arc and report a collision for every oval.
3539
+ *
3540
+ * Overrides are not consulted: `SidecarOverrides` can replace a module's
3541
+ * `originCorrection` but carries no bounds, so there is nothing to override.
3542
+ */
3543
+
3544
+ /** Where a module's body sits along Z, relative to the mover path plane. */
3545
+ interface ModuleHeightSpan {
3546
+ /** Lowest point of the body [mm]; negative means below the path plane. */
3547
+ minZMm: number;
3548
+ /** Highest point of the body [mm]. */
3549
+ maxZMm: number;
3550
+ }
3551
+ /**
3552
+ * The span a module without usable bounds falls back to: the historic coarse
3553
+ * box, so a type the asset package does not describe behaves as it always did.
3554
+ */
3555
+ declare function coarseHeightSpan(heightMm: number): ModuleHeightSpan;
3556
+ /**
3557
+ * Compose `approximateBoundsMm` through `originCorrection` and keep the Z range.
3558
+ *
3559
+ * All eight corners are transformed rather than only the two extremes: every
3560
+ * shipped correction happens to be a multiple of 90°, which would keep the box
3561
+ * axis-aligned, but nothing in the sidecar format promises that, and under a
3562
+ * 45° correction two corners would describe a box far too small.
3563
+ */
3564
+ declare function heightSpanOf(sidecar: ModuleSidecar): ModuleHeightSpan | null;
3565
+ /**
3566
+ * How tall `moduleType` is and where it sits, or the coarse fallback.
3567
+ *
3568
+ * Resolved per module type rather than once per run: an Eco straight, a
3569
+ * hygienic module with its tower and an AT2002 with its infeed box are three
3570
+ * different heights, and a stack has to be judged against the one it holds.
3571
+ */
3572
+ declare function moduleHeightSpan(moduleType: ModuleType3D, fallbackHeightMm: number): ModuleHeightSpan;
3573
+
3331
3574
  /**
3332
3575
  * Shared closed-loop detection for built chains.
3333
3576
  *
@@ -3530,24 +3773,61 @@ interface Props {
3530
3773
  declare const ModuleCornerMarkers: React__default.FC<Props>;
3531
3774
 
3532
3775
  /**
3533
- * <DimensionLabel> — camera-facing text used by the Dimensions overlay
3534
- * (and re-usable elsewhere). Wraps drei's `<Billboard>` + `<Text>` so the
3535
- * label always reads horizontally regardless of camera orbit, and is
3536
- * placed at a small +Z offset above the dimension tick so the text and
3537
- * the marker don't z-fight.
3776
+ * labelFont — resolution of the font URL every 3D text label is drawn
3777
+ * with, and the reason none of them can stall the scene any more.
3778
+ *
3779
+ * troika-three-text loads its font over XHR and, when that request
3780
+ * fails, only logs `Failure loading font <url>`: the internal callback
3781
+ * queue for that URL is never drained. drei's `<Text>` wraps exactly
3782
+ * that callback in `suspend(...)`, so a dangling load becomes a promise
3783
+ * that never settles. With one `<Suspense fallback={null}>` around the
3784
+ * scene, a single unreachable font blanked the whole viewer — geometry,
3785
+ * grid, ViewCube and all — with nothing in the console beyond troika's
3786
+ * own line. Every sample that carried a station or an area label went
3787
+ * dark that way.
3788
+ *
3789
+ * Two things prevent that here:
3790
+ *
3791
+ * 1. The default font is compiled in (`labelFontData.ts`) and handed to
3792
+ * troika as a `blob:` URL, so the common path never touches the
3793
+ * network and cannot fail.
3794
+ * 2. A caller-supplied font URL is HEAD/GET-probed before it is handed
3795
+ * over. Until it answers, labels render in the built-in font; if it
3796
+ * never answers, they keep rendering in the built-in font and the
3797
+ * failure is reported once, loudly. troika is never given a URL that
3798
+ * has not already been fetched successfully.
3799
+ *
3800
+ * `<DimensionLabel>` / `<MoverIdLabel>` additionally wrap their `<Text>`
3801
+ * in a local Suspense boundary, so even a font that parses badly costs
3802
+ * one label rather than the scene.
3538
3803
  */
3539
-
3540
3804
  /**
3541
- * Default 3D-text font: Roboto Condensed (Latin, regular weight) served
3542
- * from jsDelivr's @fontsource mirror. Pinned to a fixed major version so
3543
- * the stack stays reproducible; the URL responds with a long
3544
- * Cache-Control TTL so repeated reads come from the browser cache.
3805
+ * Default 3D-text font: Roboto Condensed (Latin, regular weight).
3806
+ *
3807
+ * The value is the jsDelivr `@fontsource` mirror it was originally
3808
+ * fetched from, kept stable because it is part of the public API. It is
3809
+ * no longer fetched: the viewer ships the same WOFF compiled in, and
3810
+ * treats this URL as "use the built-in copy", so passing it explicitly
3811
+ * is equivalent to passing nothing and costs no request.
3545
3812
  *
3546
- * `font` on troika-three-text expects an OTF / TTF / WOFF URL — NOT a CSS
3547
- * font-family name. Consumers can override via `<DimensionLabel
3548
- * fontFamily="https://…/whatever.woff" />` to swap in a brand font.
3813
+ * `font` on troika-three-text expects an OTF / TTF / WOFF URL — NOT a
3814
+ * CSS font-family name. Any other URL is used as given, once a probe
3815
+ * has confirmed it actually loads.
3549
3816
  */
3550
3817
  declare const DEFAULT_LABEL_FONT_URL: "https://cdn.jsdelivr.net/npm/@fontsource/roboto-condensed@5.2.7/files/roboto-condensed-latin-400-normal.woff";
3818
+ /**
3819
+ * URL of the compiled-in Roboto Condensed WOFF, as a `blob:` object URL
3820
+ * created on first use and cached for the lifetime of the document.
3821
+ *
3822
+ * A blob URL rather than a `data:` URL on purpose: troika loads fonts
3823
+ * through `XMLHttpRequest` and discards any response whose `status` is
3824
+ * 0, which is what several engines report for non-HTTP schemes. Blob
3825
+ * URLs answer with a regular 200.
3826
+ *
3827
+ * Falls back to a `data:` URL where `URL.createObjectURL` is missing
3828
+ * (jsdom, some SSR shims) so the value is always a usable string.
3829
+ */
3830
+ declare function bundledLabelFontUrl(): string;
3551
3831
 
3552
3832
  /**
3553
3833
  * <MoverIdLabel> — Camera-facing 3D text label that shows a mover's
@@ -3558,7 +3838,7 @@ declare const DEFAULT_LABEL_FONT_URL: "https://cdn.jsdelivr.net/npm/@fontsource/
3558
3838
  * the hood) so the label stays readable as the camera orbits. All
3559
3839
  * styling is driven by `MoverIdLabelOptions`; the component supplies
3560
3840
  * the documented defaults (15 mm black text, 55 mm above the mover's
3561
- * magnet-plate origin, Roboto Condensed via jsDelivr).
3841
+ * magnet-plate origin, the viewer's built-in Roboto Condensed).
3562
3842
  */
3563
3843
 
3564
3844
  interface MoverIdLabelProps {
@@ -3581,7 +3861,8 @@ declare const MoverIdLabel: React__default.FC<MoverIdLabelProps>;
3581
3861
  * Module count: 27
3582
3862
  * Mover count: 13
3583
3863
  * Tool count: 2
3584
- * Rail count: 8
3864
+ * Plate count: 7
3865
+ * Rail count: 11
3585
3866
  */
3586
3867
 
3587
3868
  /**
@@ -3635,8 +3916,8 @@ declare function useSidecarSource(): SidecarSourceConfig;
3635
3916
  * only set `Beckhoff`; the loader silently falls back to that variant when
3636
3917
  * `HepcoGfx` is requested but unavailable.
3637
3918
  *
3638
- * `null` for `Beckhoff` marks "prepared but not delivered"
3639
- * (AT2042 / AT2140) — the loader renders a placeholder AABB mesh.
3919
+ * A `null` `Beckhoff` entry marks a module type that is catalogued but has
3920
+ * no GLB — the loader renders a placeholder AABB mesh instead.
3640
3921
  */
3641
3922
 
3642
3923
  /** Filename mapping per ModuleType3D (relative to assetsBaseUrl). */
@@ -3662,7 +3943,7 @@ declare const DEFAULT_RAIL_MANIFEST: Readonly<Record<RailType3D, {
3662
3943
  * - Straights pick the matching-length AT9000 rail (0249 / 0250 / 0500).
3663
3944
  * - +22.5° curves (AT2020 / AT2021) use AT9020.
3664
3945
  * - −22.5° curves (AT2025 / AT2026) use AT9025.
3665
- * - 45° curves (AT2040 / AT2041 / AT2042 / AT2140) use AT9040.
3946
+ * - 45° curves (AT2040 / AT2041) use AT9040.
3666
3947
  * - The 180° clothoid pair (AT2050 0500/0501 + merged 0500_180) uses
3667
3948
  * AT9050, whose GLB already covers the full 500 mm path.
3668
3949
  * - Hygienic ATH modules already include the rail in their module GLB
@@ -3674,10 +3955,12 @@ declare function resolveModuleGlbUrl(moduleType: ModuleType3D, rail: RailSystem,
3674
3955
  declare function resolveMoverGlbUrl(moverType: MoverType3D, override?: AssetManifest['movers']): string;
3675
3956
  declare function resolveToolGlbUrl(toolType: MoverToolType3D, override?: AssetManifest['tools']): string;
3676
3957
  /**
3677
- * Pick the guiding-rail RailType for a module, or `null` if no separate
3678
- * rail mesh should render (ATH variants, undeclared module types).
3958
+ * Which guiding rail a module gets, given the mover running on it.
3959
+ *
3960
+ * `moverType` is optional so existing callers keep the -0055 default; pass it
3961
+ * to get the variant Beckhoff actually pairs with that mover.
3679
3962
  */
3680
- declare function resolveGuidingRailType(moduleType: ModuleType3D): RailType3D | null;
3963
+ declare function resolveGuidingRailType(moduleType: ModuleType3D, moverType?: MoverType3D): RailType3D | null;
3681
3964
  /**
3682
3965
  * Resolve the GLB filename for a guiding-rail type, honoring user-supplied
3683
3966
  * manifest overrides. Returns empty string when the rail is unknown.
@@ -3774,9 +4057,9 @@ declare function getSharedAssetLoader(): AssetLoader;
3774
4057
  declare function composeAssetUrl(baseUrl: string, filename: string): string;
3775
4058
 
3776
4059
  /**
3777
- * The four sidecar kinds as data.
4060
+ * The five sidecar kinds as data.
3778
4061
  *
3779
- * Modules, movers, tools and rails all resolve their calibration the same
4062
+ * Modules, movers, tools, magnet plates and rails all resolve the same
3780
4063
  * way — compiled-in table, else `<assetsBaseUrl>/<filename>`, else identity.
3781
4064
  * Only two things differ per kind: which built-in table to read and how the
3782
4065
  * filename is spelled. Keeping those two as a table (rather than four copies
@@ -3803,6 +4086,10 @@ interface SidecarKinds {
3803
4086
  id: MoverToolType3D;
3804
4087
  sidecar: MoverToolSidecar;
3805
4088
  };
4089
+ plate: {
4090
+ id: MagnetPlateType3D;
4091
+ sidecar: MagnetPlateSidecar;
4092
+ };
3806
4093
  rail: {
3807
4094
  id: RailType3D;
3808
4095
  sidecar: RailSidecar;
@@ -3922,4 +4209,4 @@ declare function snapToGrid(positionMm: Vec3, gridMm: number): Vec3;
3922
4209
  /** Convenience: `resolveSceneContext` + `toEntityRef`. */
3923
4210
  declare function resolveEntityFromObject(object: Object3D | null, instanceId?: number): MeasuredEntityRef | null;
3924
4211
 
3925
- export { type AngleMeasurement, type Annotation, type AnnotationAnchor, type AnnotationInput, type AreaConfig, type AreaOptions, AssetLoader, type AssetManifest, BUILTIN_MODULE_SIDECARS, BUILTIN_MOVER_SIDECARS, BUILTIN_RAIL_SIDECARS, BUILTIN_TOOL_SIDECARS, type BuiltChain, type BuiltModule, type CameraProjection, type CameraState, type CaptureDimensions, type CaptureMode, type CaptureRequest, type CheckModuleCollisionsOptions, type CheckMoverCollisionsOptions, type ClickModifiers, type CoordinateSystem, type CustomAssetBinding, type CustomAssetConfig, type CustomMoverLayout, DEFAULT_LABEL_FONT_URL, DEFAULT_MEASUREMENT_STYLE, DEFAULT_MODULE_HALF_WIDTH_MM, DEFAULT_MODULE_HEIGHT_MM, DEFAULT_MODULE_MANIFEST, DEFAULT_MOVER_MANIFEST, DEFAULT_ORIGIN_CORRECTION, DEFAULT_RAIL_MANIFEST, DEFAULT_SNAP_KINDS, DEFAULT_SNAP_RADIUS_PX, DEFAULT_TOOL_MANIFEST, type DimensionOptions, type DisplayOptions, type DistanceMeasurement, EMPTY_SELECTION, type EndDelta, type FeedSegment, type FeedSegmentHighlight, type FeedSegmentSelector, type FeedSegmentSpan, type FocusOptions, type FocusTarget, type FrameCaptureOptions, type FrameCaptureSession, HEPCO_GFX_PROFILE, type HeatmapSample, type HepcoGfxRailProfileDims, IDENTITY_POSE, INFEED_MODULE_TYPES, type InfoBarConfig, type InfoBarMarkerConfig, type InfoBarOptions, type InteractionLocks, JSDELIVR_ASSETS_BASE_URL, MODULE_CATALOG, MODULE_GUIDING_RAIL_MAP, MOVER_CATALOG, type MarkerOptions, type MarkerShape, type MeasuredEntityRef, type Measurement, type MeasurementApi, type MeasurementAxis, type MeasurementDocument, type MeasurementDraft, type MeasurementFormatOptions, type MeasurementId, type MeasurementInput, type MeasurementKind, type MeasurementOptions, type MeasurementPoint, type MeasurementResult, type MeasurementSnapshot, type MeasurementStyle, type MeasurementTool, type ModuleAssetEntry, type ModuleCatalogEntry, type ModuleCollision, type ModuleCollisionPartInput, type ModuleCollisionXpuInput, ModuleCornerMarkers, type ModuleEntry, type ModuleHighlight, type ModuleProbe, type ModuleRef, type ModuleSidecar, type ModuleStatusEntry, type ModuleType3D, type MoverAssetEntry, type MoverCatalogEntry, type MoverCollision, type MoverConfig, MoverIdLabel, type MoverIdLabelOptions, type MoverPositionEntry, type MoverProbe, type MoverRef, type MoverSidecar, type MoverToolAssetEntry, type MoverToolConfig, type MoverToolSidecar, type MoverToolType3D, type MoverType3D, type NormalizationWarning, type NormalizedXtsConfig, type Orientation, type OriginCorrection, type PartConfig, type PartPath, type PartTransformation, type PathMeasurement, type PathSample, type PathType, type PerformanceOptions, type PlanarPose, type PointCloud, type PointProjector, type PositionFrame, type ProcessingUnitConfig, type RailAssetEntry, type RailSidecar, type RailSystem, type RailType3D, type ResolvedMeasurementStyle, SIDECAR_KINDS, type ScreenshotFormat, type ScreenshotMode, type ScreenshotOptions, type ScreenshotResult, type SelectionMode, type SelectionState, type SidecarIdOf, type SidecarKind, type SidecarKindSpec, type SidecarKinds, SidecarLoader, type SidecarOf, type SidecarOverrides, type SidecarSourceConfig, SidecarSourceContext, type SnapCandidate, type SnapKind, type SnapOptions, type SplitFeedSegmentsOptions, type StationConfig, type StationOptions, type StatorHeatmap, type StopPositionMoverOptions, type TextOptions, type TrackAnchor, type TrackDistanceMeasurement, type TrackIndex, type TrackIndexEntry, type TrackMetrics, type TrackMetricsResolver, type TrackProjection, type TrackTransform, VERSION, type Vec2, type Vec3, type ViewCubeAlignment, type ViewCubeOptions, XtsArcCurve3, type XtsConfig, XtsConfigNormalizer, type XtsModelDocument, XtsPointCloudCurve3, type XtsRuntimeState, XtsViewer3D, type XtsViewer3DProps, type XtsViewer3DRef, type XtsViewerError, type XtsViewerErrorCode, XtsViewerErrorException, angleDeg, applyBeckhoffXtsConvention, applyEmptyClick, applyModuleClick, applyMoverClick, arcPoints, axisDistanceMm, buildChain, buildModuleProbes, buildPartPath, buildTrackIndex, chainToUserPositionMm, checkAllModuleCollisions, checkAllMoverCollisions, checkModuleCollisions, checkSamePathCollisions, chooseSnapCandidate, collectFeedSegments, collectFeedSegmentsForXpu, collectTriangleSnapCandidates, composeAssetUrl, computeMeasurementResult, createHepcoGfxBaseplateShape, createHepcoGfxRailShape, deselectAll, distanceMm, findModuleAt, formatAngleDeg, formatLengthMm, getModuleEntry, getMoverEntry, getPointCloud, getSharedAssetLoader, interpolateHeatmapValue, isChainClosed, isKnownModuleType, isKnownMoverType, isModuleSelected, isMoverSelected, labelAnchorFor, moduleAnchorWorld, moduleEndDelta, moduleSidecarFilename, moverAnchorWorld, moverSidecarFilename, moverWorldAt, nearestTrackPoint, normaliseToHeatmapRange, normalizeXtsConfig, pathLengthsMm, pickGlbForRail, poseToMatrix4, railSidecarFilename, registerPointCloud, resolveEntityFromObject, resolveGuidingRailGlbUrl, resolveGuidingRailType, resolveHepcoGfxProfile, resolveMeasurementStyle, resolveModuleGlbUrl, resolveMoverGlbUrl, resolveOriginCorrection, resolveStopPositionMm, resolveToolGlbUrl, sampleChainPoint, sampleChainRange, sampleModulePath, snapToGrid, sortHeatmapSamples, splitIntoFeedSegments, toModuleCollisionInputs, toolSidecarFilename, trackAnchorWorld, trackDistanceMm, trackLengthOf, trackPolylineWorld, unregisterPointCloud, useSidecarSource, userToChainPositionMm, worldPointOnPart };
4212
+ export { type AngleMeasurement, type Annotation, type AnnotationAnchor, type AnnotationInput, type AreaConfig, type AreaOptions, AssetLoader, type AssetManifest, BUILTIN_MODULE_SIDECARS, BUILTIN_MOVER_SIDECARS, BUILTIN_RAIL_SIDECARS, BUILTIN_TOOL_SIDECARS, type BuiltChain, type BuiltModule, type CameraProjection, type CameraState, type CaptureDimensions, type CaptureMode, type CaptureRequest, type CheckModuleCollisionsOptions, type CheckMoverCollisionsOptions, type ClickModifiers, type CoordinateSystem, type CustomAssetBinding, type CustomAssetConfig, type CustomMoverLayout, DEFAULT_LABEL_FONT_URL, DEFAULT_MEASUREMENT_STYLE, DEFAULT_MODULE_HALF_WIDTH_MM, DEFAULT_MODULE_HEIGHT_MM, DEFAULT_MODULE_MANIFEST, DEFAULT_MOVER_MANIFEST, DEFAULT_ORIGIN_CORRECTION, DEFAULT_RAIL_MANIFEST, DEFAULT_SNAP_KINDS, DEFAULT_SNAP_RADIUS_PX, DEFAULT_TOOL_MANIFEST, DEFAULT_TRACK_Z_MM, type DimensionOptions, type DisplayOptions, type DistanceMeasurement, EMPTY_SELECTION, type EndDelta, type FeedSegment, type FeedSegmentHighlight, type FeedSegmentSelector, type FeedSegmentSpan, type FocusOptions, type FocusTarget, type FrameCaptureOptions, type FrameCaptureSession, HEPCO_GFX_PROFILE, type HeatmapSample, type HepcoGfxRailProfileDims, IDENTITY_POSE, INFEED_MODULE_TYPES, type InfoBarConfig, type InfoBarMarkerConfig, type InfoBarOptions, type InteractionLocks, JSDELIVR_ASSETS_BASE_URL, type KeyboardNavigationOptions, MAGNET_PLATE_CATALOG, MODULE_CATALOG, MODULE_GUIDING_RAIL_MAP, MOVER_CATALOG, MOVER_INTEGRATED_PLATE, type MagnetPlateAssetEntry, type MagnetPlateCatalogEntry, type MagnetPlateType3D, type MarkerOptions, type MarkerShape, type MeasuredEntityRef, type Measurement, type MeasurementApi, type MeasurementAxis, type MeasurementDocument, type MeasurementDraft, type MeasurementFormatOptions, type MeasurementId, type MeasurementInput, type MeasurementKind, type MeasurementOptions, type MeasurementPoint, type MeasurementResult, type MeasurementSnapshot, type MeasurementStyle, type MeasurementTool, type ModuleAssetEntry, type ModuleCatalogEntry, type ModuleCollision, type ModuleCollisionPartInput, type ModuleCollisionXpuInput, ModuleCornerMarkers, type ModuleEntry, type ModuleHeightSpan, type ModuleHighlight, type ModuleProbe, type ModuleRef, type ModuleSidecar, type ModuleStatusEntry, type ModuleType3D, type MoverAssetEntry, type MoverCatalogEntry, type MoverCollision, type MoverConfig, MoverIdLabel, type MoverIdLabelOptions, type MoverPositionEntry, type MoverProbe, type MoverRef, type MoverSidecar, type MoverToolAssetEntry, type MoverToolConfig, type MoverToolSidecar, type MoverToolType3D, type MoverType3D, type NormalizationWarning, type NormalizedXtsConfig, type Orientation, type OriginCorrection, type PartConfig, type PartPath, type PartTransformation, type PathMeasurement, type PathSample, type PathType, type PerformanceOptions, type PlanarPose, type PointCloud, type PointProjector, type PositionFrame, type ProcessingUnitConfig, type RailAssetEntry, type RailSidecar, type RailSystem, type RailType3D, type ResolvedMeasurementStyle, SIDECAR_KINDS, type ScreenshotFormat, type ScreenshotMode, type ScreenshotOptions, type ScreenshotResult, type SelectionMode, type SelectionState, type SidecarIdOf, type SidecarKind, type SidecarKindSpec, type SidecarKinds, SidecarLoader, type SidecarOf, type SidecarOverrides, type SidecarSourceConfig, SidecarSourceContext, type SnapCandidate, type SnapKind, type SnapOptions, type SplitFeedSegmentsOptions, type StationConfig, type StationOptions, type StatorHeatmap, type StopPositionMoverOptions, type TextOptions, type TrackAnchor, type TrackDistanceMeasurement, type TrackIndex, type TrackIndexEntry, type TrackMetrics, type TrackMetricsResolver, type TrackProjection, type TrackTransform, VERSION, type Vec2, type Vec3, type ViewCubeAlignment, type ViewCubeOptions, XtsArcCurve3, type XtsConfig, XtsConfigNormalizer, type XtsModelDocument, XtsPointCloudCurve3, type XtsRuntimeState, XtsViewer3D, type XtsViewer3DProps, type XtsViewer3DRef, type XtsViewerError, type XtsViewerErrorCode, XtsViewerErrorException, angleDeg, applyBeckhoffXtsConvention, applyEmptyClick, applyModuleClick, applyMoverClick, arcPoints, axisDistanceMm, buildChain, buildModuleProbes, buildPartPath, buildTrackIndex, bundledLabelFontUrl, chainToUserPositionMm, checkAllModuleCollisions, checkAllMoverCollisions, checkModuleCollisions, checkSamePathCollisions, chooseSnapCandidate, coarseHeightSpan, collectFeedSegments, collectFeedSegmentsForXpu, collectTriangleSnapCandidates, composeAssetUrl, computeMeasurementResult, createHepcoGfxBaseplateShape, createHepcoGfxRailShape, deselectAll, distanceMm, findModuleAt, formatAngleDeg, formatLengthMm, getMagnetPlateEntry, getModuleEntry, getMoverEntry, getPointCloud, getSharedAssetLoader, heightSpanOf, interpolateHeatmapValue, isChainClosed, isKnownMagnetPlateType, isKnownModuleType, isKnownMoverType, isModuleSelected, isMoverSelected, labelAnchorFor, moduleAnchorWorld, moduleEndDelta, moduleHeightSpan, moduleSidecarFilename, moverAnchorWorld, moverSidecarFilename, moverWorldAt, nearestTrackPoint, normaliseToHeatmapRange, normalizeXtsConfig, pathLengthsMm, pickGlbForRail, poseToMatrix4, railSidecarFilename, registerPointCloud, resolveEntityFromObject, resolveGuidingRailGlbUrl, resolveGuidingRailType, resolveHepcoGfxProfile, resolveMeasurementStyle, resolveModuleGlbUrl, resolveMoverGlbUrl, resolveOriginCorrection, resolveStopPositionMm, resolveToolGlbUrl, sampleChainPoint, sampleChainRange, sampleModulePath, snapToGrid, sortHeatmapSamples, splitIntoFeedSegments, toModuleCollisionInputs, toolSidecarFilename, trackAnchorWorld, trackDistanceMm, trackLengthOf, trackPolylineWorld, unregisterPointCloud, useSidecarSource, userToChainPositionMm, worldPointOnPart };