beckhoff-xts-viewer-3d 0.3.1 → 2.0.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
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { ColorRepresentation, Curve, Vector3, CurvePath, Matrix4 } from 'three';
3
+ import { ColorRepresentation, Shape, Curve, Vector3, CurvePath, Matrix4 } from 'three';
4
4
  import { GLTF } from 'three-stdlib';
5
5
 
6
6
  /**
@@ -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: "0.3.1";
15
+ declare const VERSION: "2.0.0";
16
16
 
17
17
  /**
18
18
  * Default CDN URL for the GLB asset bundle (`beckhoff-xts-viewer-3d-assets`).
@@ -24,9 +24,13 @@ declare const VERSION: "0.3.1";
24
24
  * `Cannot access 'JSDELIVR_ASSETS_BASE_URL' before initialization`
25
25
  * because the const sits in the TDZ when XtsViewer3D's module body runs.
26
26
  *
27
- * Pinned to this package's version so a `viewer@x.y.z` install always
28
- * resolves to `assets@x.y.z` — no version drift between the calibration
29
- * data baked into the JS bundle and the GLB geometry served by the CDN.
27
+ * Pinned to the assets package version this viewer build targets (see
28
+ * ./assetsVersion.ts). The assets release line is decoupled from the
29
+ * viewer release line — assets only publish a new version when GLB
30
+ * geometry actually changes — so `viewer@x.y.z` does NOT generally
31
+ * resolve to `assets@x.y.z`. The exact version is captured at viewer
32
+ * build time so every install streams from a known-compatible GLB
33
+ * release.
30
34
  */
31
35
  declare const JSDELIVR_ASSETS_BASE_URL: "https://cdn.jsdelivr.net/npm/beckhoff-xts-viewer-3d-assets@0.3.1/models";
32
36
 
@@ -42,15 +46,20 @@ type ModuleType3D = 'AT2000_0250' | 'AT2001_0250' | 'AT2002_0250' | 'AT2002_0249
42
46
  /**
43
47
  * Schienensystem unter dem Modul. Beeinflusst nur das visuelle Mesh
44
48
  * (Beckhoff Aluminium-Schiene vs. Hepco GFX), nicht die Pfad-Math.
49
+ *
50
+ * `Beckhoff` ist die Beckhoff-Aluminium-Führungsschiene (alle
51
+ * AT-Standard- + Eco-Familien); `HepcoGfx` ist das alternative
52
+ * Hepco-GFX-Profil. ATH-Module bringen ihre Schiene im Modul-GLB
53
+ * mit und sind von dieser Wahl unabhängig.
45
54
  */
46
- type RailSystem = 'BeckhoffGuidingRail' | 'HepcoGfx';
55
+ type RailSystem = 'Beckhoff' | 'HepcoGfx';
47
56
  /** Beckhoff XTS + Hepco mover types. `Custom` requires `customMoverLayout`. */
48
57
  type MoverType3D = 'AT9011_0050' | 'AT9011_0070' | 'AT9012_0050' | 'AT9014_0055' | 'AT9014_0070' | 'ATH9011_0075' | 'ATH9013_0075' | 'Hepco_GFX2_1TC_S25' | 'Custom';
49
58
  /** Predefined mover-tool / carrier-plate types (spec §4.8). */
50
59
  type MoverToolType3D = 'AT8200_1000_0100' | 'AT8200_2000_0100' | 'Custom';
51
60
  /**
52
61
  * Beckhoff guiding-rail GLB variants. Rendered alongside the motor modules
53
- * when `RailSystem === 'BeckhoffGuidingRail'` to visualise the outer guide
62
+ * when `RailSystem === 'Beckhoff'` to visualise the outer guide
54
63
  * profile that the mover wheels engage.
55
64
  *
56
65
  * Curve sign drives the selection (see `MODULE_GUIDING_RAIL_MAP`):
@@ -139,10 +148,15 @@ interface XtsConfig {
139
148
  customAssets?: CustomAssetConfig[];
140
149
  }
141
150
  interface ProcessingUnitConfig {
142
- index: number;
151
+ /**
152
+ * Stable host-supplied 32-bit unique identifier for this XPU. Required
153
+ * at runtime — must be a non-negative integer that fits in a uint32
154
+ * and is unique across all XPUs in the config.
155
+ */
156
+ objectId: number;
143
157
  moverType: MoverType3D;
144
158
  customMoverLayout?: CustomMoverLayout;
145
- /** Default: 'BeckhoffGuidingRail'. */
159
+ /** Default: 'Beckhoff'. */
146
160
  railSystem?: RailSystem;
147
161
  parts: PartConfig[];
148
162
  movers: MoverConfig[];
@@ -192,20 +206,46 @@ interface TrackTransform {
192
206
  visible?: boolean;
193
207
  }
194
208
  interface PartConfig {
209
+ /**
210
+ * Stable host-supplied 32-bit unique identifier for this part. Distinct
211
+ * from `globalNumber` (server-side ObjectId): callers reference parts
212
+ * by `objectId` at runtime — e.g. in the array form of
213
+ * `XtsViewer3DRef.setMoverPositions` (`{partObjectId, partPosition}`).
214
+ * Required at runtime — must be a non-negative integer that fits in a
215
+ * uint32 and is unique across all parts in the system.
216
+ * `normalizeXtsConfig` emits a `missing-part-object-id` /
217
+ * `invalid-part-object-id` / `duplicate-part-object-id` warning when
218
+ * the contract is violated.
219
+ */
220
+ objectId: number;
195
221
  /** = ServerTypes.PartModel.ObjectId — eindeutig systemweit. */
196
222
  globalNumber: number;
197
- /** 0-basiert in der XPU. */
198
- index: number;
199
223
  modules: ModuleEntry[];
200
224
  partTransformation?: PartTransformation;
201
225
  }
202
226
  interface ModuleEntry {
203
227
  moduleType: ModuleType3D;
228
+ /**
229
+ * Stable host-supplied number for this module. Required at runtime —
230
+ * simply counted up per module (1, 2, 3, …) across the whole system
231
+ * and used as the index in `XtsViewer3DRef.setModuleStatuses(array)` —
232
+ * i.e. `statuses[m.globalNumber] === { warning?: …, error?: … }`.
233
+ * `normalizeXtsConfig` emits a `missing-module-global-number` /
234
+ * `invalid-module-global-number` / `duplicate-module-global-number`
235
+ * warning when the contract is violated. Typed optional only so
236
+ * legacy configs and tests keep compiling; new code MUST set it.
237
+ */
238
+ globalNumber?: number;
204
239
  /**
205
240
  * Optional drive-status overlay for this module. Mirrors the
206
241
  * `MoverConfig.status` shape; the same per-mesh emissive blink +
207
242
  * billboard <DriveStatusIcon> surfaces both. Filtered by
208
243
  * `display.showDriveWarnings` / `display.showDriveErrors`.
244
+ *
245
+ * For live updates (e.g. drive warnings/errors streamed from PLC),
246
+ * prefer the imperative
247
+ * `XtsViewer3DRef.setModuleStatuses(array)` channel — index = this
248
+ * module's `globalNumber`. The store wins over this config-time value.
209
249
  */
210
250
  status?: {
211
251
  warning?: boolean;
@@ -213,23 +253,44 @@ interface ModuleEntry {
213
253
  };
214
254
  }
215
255
  /**
216
- * All fields are optional and default to identity (offset = [0,0],
217
- * rotation = 0). `applyLayout.resolvePartTransformation` produces a value
218
- * where `offsetMm` and `rotationDeg` are always populated (`?? 0`), but
219
- * raw host-supplied transforms may omit either side of the split.
256
+ * Per-part 3D pose, applied to EVERYTHING anchored to the part:
257
+ * modules, guiding rails, movers, mover tools, mover-bound custom assets,
258
+ * and the path-derived overlays (stations, areas, dimensions, info bars,
259
+ * stop-position ghost movers, stator heatmap). Composes inside the
260
+ * per-XPU `trackTransform`:
261
+ *
262
+ * world = orientation ⊗ trackTransform ⊗ partTransformation ⊗ chain
263
+ *
264
+ * All fields are optional and default to identity (offset = [0,0,0],
265
+ * rotation = [0,0,0]). Whole-track placement is done via
266
+ * `ProcessingUnitConfig.trackTransform`; this transform is for moving a
267
+ * part WITHIN its XPU — e.g. lifts that translate along Z, parts
268
+ * mounted at an angle, or live-editable kinematics in the playground.
269
+ *
270
+ * Multi-part overlays (stations / areas spanning multiple `partOids`)
271
+ * follow the transform of their FIRST partOid — the chain math
272
+ * upstream is part-local, and points from differently-transformed
273
+ * parts can't be stitched into a single tube. Anchor multi-part
274
+ * overlays to the part you want them to track.
220
275
  */
221
276
  interface PartTransformation {
222
- offsetMm?: Vec2;
223
- rotationDeg?: number;
277
+ /** Translation in mm, in the XPU's track-transform frame. */
278
+ offsetMm?: Vec3;
279
+ /** Intrinsic XYZ Euler angles in degrees. */
280
+ rotationDegEuler?: Vec3;
224
281
  partSide?: 'Default' | 'Driver' | 'Encoder';
282
+ /** Hide everything on this part when false. Default true. */
225
283
  visible?: boolean;
226
- /** Server-Side-Werte für SystemView-Layout (additiv). */
227
- serverOffsetMm?: Vec2;
228
- serverRotationDeg?: number;
229
284
  }
230
285
  interface MoverConfig {
231
- moverIndex: number;
232
- id: string;
286
+ /** Position-on-track index within the XPU. */
287
+ index: number;
288
+ /**
289
+ * Stable host-supplied 32-bit unique identifier for this mover.
290
+ * Required, must be a non-negative integer that fits in a uint32 and
291
+ * is unique across all movers in the config.
292
+ */
293
+ id: number;
233
294
  /** Verweist auf PartConfig.globalNumber. */
234
295
  partOid: number;
235
296
  /** [0, part.trackLengthMm] — clamped wenn out-of-bounds. */
@@ -242,6 +303,28 @@ interface MoverConfig {
242
303
  /** Vordefinierte Werkzeug-Carrier oder Custom-GLBs an diesem Mover. */
243
304
  tools?: MoverToolConfig[];
244
305
  }
306
+ /**
307
+ * Runtime mover-position update entry — pushed through
308
+ * `XtsViewer3DRef.setMoverPositions(array)`. The array is indexed by
309
+ * `MoverConfig.index` (i.e. entry `i` targets the mover with
310
+ * `index === i`); use `null` / `undefined` to skip a slot. The
311
+ * `partObjectId` field references `PartConfig.objectId`.
312
+ */
313
+ interface MoverPositionEntry {
314
+ /** = PartConfig.objectId (the host-side 32-bit unique part ID). */
315
+ partObjectId: number;
316
+ /** Position along the referenced part, in user-frame mm. */
317
+ partPosition: number;
318
+ }
319
+ /**
320
+ * Runtime drive-status entry for a single module — pushed through
321
+ * `XtsViewer3DRef.setModuleStatuses(array)`. The array is indexed by
322
+ * `ModuleEntry.globalNumber`; use `null` / `undefined` to clear a slot.
323
+ */
324
+ interface ModuleStatusEntry {
325
+ warning?: boolean;
326
+ error?: boolean;
327
+ }
245
328
  interface MoverToolConfig {
246
329
  toolType: MoverToolType3D;
247
330
  /** Required when toolType === 'Custom'. */
@@ -276,7 +359,7 @@ type CustomAssetBinding = {
276
359
  } | {
277
360
  type: 'mover';
278
361
  moverRef: {
279
- processingUnitIndex: number;
362
+ processingUnitObjectId: number;
280
363
  moverIndex: number;
281
364
  };
282
365
  offsetMm?: Vec3;
@@ -297,7 +380,6 @@ interface CustomAssetConfig {
297
380
  opacity?: number;
298
381
  visible?: boolean;
299
382
  }
300
- type LayoutMode = 'SystemView' | 'InvertedSystemView' | 'Custom';
301
383
  interface DisplayOptions {
302
384
  displayStations?: boolean;
303
385
  /** Render konfigurierte Areas. Default: true (wenn `areas[]` gesetzt ist). */
@@ -305,9 +387,20 @@ interface DisplayOptions {
305
387
  displayDimensions?: boolean;
306
388
  showDriveErrors?: boolean;
307
389
  showDriveWarnings?: boolean;
390
+ /**
391
+ * Render each mover's `id` as a billboarded 3D text label above the
392
+ * mover. Toggle on/off without re-creating the scene. Visual options
393
+ * (font size, color, offset, font URL, outline) are configurable via
394
+ * `moverIdLabelOptions`. Default: false.
395
+ */
308
396
  showMoverIds?: boolean;
397
+ /**
398
+ * Style + placement overrides for the mover-id labels. Only applied
399
+ * when `showMoverIds === true`. All fields optional; sensible defaults
400
+ * shipped by the renderer.
401
+ */
402
+ moverIdLabelOptions?: MoverIdLabelOptions;
309
403
  invertDimensions?: boolean;
310
- layout?: LayoutMode;
311
404
  stationOptions?: StationOptions;
312
405
  stationMarkerOptions?: MarkerOptions;
313
406
  /**
@@ -396,6 +489,40 @@ interface DisplayOptions {
396
489
  */
397
490
  toneMappingExposure?: number;
398
491
  }
492
+ /**
493
+ * Style + placement overrides for the per-mover-id 3D text labels
494
+ * surfaced when `DisplayOptions.showMoverIds === true`.
495
+ *
496
+ * Defaults (all in scene units / mm; chosen for legibility on the
497
+ * default mover GLB at typical XTS layouts):
498
+ * - `fontSizeMm`: 15
499
+ * - `color`: '#000000'
500
+ * - `offsetMm`: [0, 0, 55] // centred above the mover's magnet plate
501
+ * - `fontUrl`: `DEFAULT_LABEL_FONT_URL` (Roboto Condensed)
502
+ * - `outlineWidthMm`: 0 // no outline
503
+ * - `outlineColor`: '#FFFFFF'
504
+ */
505
+ interface MoverIdLabelOptions {
506
+ /** Text height in mm (world units). Default: 15. */
507
+ fontSizeMm?: number;
508
+ /** CSS color for the text fill. Default: '#000000'. */
509
+ color?: string;
510
+ /**
511
+ * Offset of the label's anchor point relative to the mover's local
512
+ * origin (= magnet-plate center), in mm.
513
+ * `[x, y, z]` — Z is up. Default: `[0, 0, 25]`.
514
+ */
515
+ offsetMm?: Vec3;
516
+ /**
517
+ * URL of a TTF / OTF / WOFF font, passed straight to troika-three-text.
518
+ * Default: `DEFAULT_LABEL_FONT_URL` (Roboto Condensed via jsDelivr).
519
+ */
520
+ fontUrl?: string;
521
+ /** Outline thickness in mm. 0 disables the outline. Default: 0. */
522
+ outlineWidthMm?: number;
523
+ /** Outline color. Only applied if `outlineWidthMm > 0`. Default: '#FFFFFF'. */
524
+ outlineColor?: string;
525
+ }
399
526
  interface StationOptions {
400
527
  thicknessMm?: number;
401
528
  /** Vertical Z lift (mm). Positive = above the table. */
@@ -609,8 +736,8 @@ interface AreaConfig {
609
736
  color: number;
610
737
  }
611
738
  interface InfoBarConfig {
612
- processingUnitIndex: number;
613
- partIndex: number;
739
+ processingUnitObjectId: number;
740
+ partObjectId: number;
614
741
  partStartPositionMm: number;
615
742
  partEndPositionMm: number;
616
743
  thickness: number;
@@ -633,12 +760,12 @@ interface InfoBarMarkerConfig {
633
760
  color?: string;
634
761
  }
635
762
  interface ModuleRef {
636
- processingUnitIndex: number;
637
- partIndex: number;
763
+ processingUnitObjectId: number;
764
+ partObjectId: number;
638
765
  moduleIndex: number;
639
766
  }
640
767
  interface MoverRef {
641
- processingUnitIndex: number;
768
+ processingUnitObjectId: number;
642
769
  moverIndex: number;
643
770
  }
644
771
  interface SelectionState {
@@ -678,7 +805,7 @@ interface RailAssetEntry {
678
805
  interface ModuleAssetEntry {
679
806
  /** Per-RailSystem GLB URL (relative to assetsBaseUrl). */
680
807
  glbByRailSystem: {
681
- BeckhoffGuidingRail: string | null;
808
+ Beckhoff: string | null;
682
809
  HepcoGfx?: string | null;
683
810
  };
684
811
  sidecarUrl?: string;
@@ -707,9 +834,9 @@ interface XtsModelDocument {
707
834
  }
708
835
  interface XtsRuntimeState {
709
836
  movers: Array<{
710
- processingUnitIndex: number;
837
+ processingUnitObjectId: number;
711
838
  moverIndex: number;
712
- id: string;
839
+ id: number;
713
840
  partOid: number;
714
841
  partPositionMm: number;
715
842
  velocity?: {
@@ -807,6 +934,9 @@ interface BuiltModule {
807
934
  startPositionInPartMm: number;
808
935
  /** moduleLength (mm). Used for findModuleContaining. */
809
936
  trackLengthMm: number;
937
+ /** Forwarded from `ModuleEntry.globalNumber` — index into the runtime
938
+ * `ModuleStatusStore` so renderers can subscribe per module. */
939
+ globalNumber?: number;
810
940
  /** Forwarded from `ModuleEntry.status` so the renderer can light up
811
941
  * the per-module drive-status overlay without an extra lookup. */
812
942
  status?: {
@@ -890,8 +1020,8 @@ interface MoverCollision {
890
1020
  * Stable IDs of the two movers (mirrors `MoverConfig.id`). Useful when
891
1021
  * the consumer keys by id rather than by `moverIndex`.
892
1022
  */
893
- idA: string;
894
- idB: string;
1023
+ idA: number;
1024
+ idB: number;
895
1025
  /**
896
1026
  * Penetration along the 1D path:
897
1027
  * • `> 0` — movers overlap by this many mm.
@@ -935,7 +1065,7 @@ interface CheckMoverCollisionsOptions {
935
1065
  */
936
1066
  interface MoverProbe {
937
1067
  ref: MoverRef;
938
- id: string;
1068
+ id: number;
939
1069
  partOid: number;
940
1070
  moverType: MoverType3D;
941
1071
  partPositionMm: number;
@@ -985,7 +1115,7 @@ interface NormalizedXtsConfig extends XtsConfig {
985
1115
  readonly __warnings: NormalizationWarning[];
986
1116
  }
987
1117
  interface NormalizationWarning {
988
- code: 'mover-out-of-range' | 'mover-orphan' | 'mover-duplicate-id' | 'empty-part' | 'prepared-module';
1118
+ code: 'mover-out-of-range' | 'mover-orphan' | 'mover-duplicate-id' | 'mover-missing-id' | 'mover-invalid-id' | 'empty-part' | 'prepared-module' | 'duplicate-xpu-object-id' | 'missing-xpu-object-id' | 'invalid-xpu-object-id' | 'duplicate-part-object-id' | 'missing-part-object-id' | 'invalid-part-object-id' | 'duplicate-module-global-number' | 'missing-module-global-number' | 'invalid-module-global-number';
989
1119
  message: string;
990
1120
  details?: unknown;
991
1121
  }
@@ -1005,10 +1135,10 @@ interface NormalizationWarning {
1005
1135
  */
1006
1136
  declare class XtsConfigNormalizer {
1007
1137
  private _cache;
1008
- normalize(config: XtsConfig, layout?: LayoutMode): NormalizedXtsConfig;
1138
+ normalize(config: XtsConfig): NormalizedXtsConfig;
1009
1139
  clearCache(): void;
1010
1140
  }
1011
- declare function normalizeXtsConfig(config: XtsConfig, layout?: LayoutMode): NormalizedXtsConfig;
1141
+ declare function normalizeXtsConfig(config: XtsConfig): NormalizedXtsConfig;
1012
1142
 
1013
1143
  /**
1014
1144
  * captureScreenshot — render the live r3f scene into an offscreen
@@ -1141,9 +1271,29 @@ interface XtsViewer3DRef {
1141
1271
  }): XtsModelDocument;
1142
1272
  exportScreenshot(opts?: ScreenshotOptions): Promise<ScreenshotResult>;
1143
1273
  reloadAssets(): Promise<void>;
1144
- setMoverPosition(moverId: string, partPositionMm: number): void;
1145
- setMoverPositions(record: Record<string, number>): void;
1146
- getMoverPosition(moverId: string): number | null;
1274
+ setMoverPosition(moverId: number, partPositionMm: number): void;
1275
+ /**
1276
+ * Push mover positions in bulk. Two accepted forms:
1277
+ *
1278
+ * 1. `Record<moverId, partPositionMm>` — backwards-compatible single-
1279
+ * part-per-mover update. The mover stays on its config-time part.
1280
+ * 2. `MoverPositionEntry[]` — array indexed by `MoverConfig.index`
1281
+ * (entry `i` targets the mover with `index === i`); each entry
1282
+ * supplies the `partObjectId` (= `PartConfig.objectId`) the mover is
1283
+ * currently on plus its position. `null` / `undefined` entries are
1284
+ * skipped — leave them untouched in the store.
1285
+ */
1286
+ setMoverPositions(update: Record<number, number> | ReadonlyArray<MoverPositionEntry | null | undefined>): void;
1287
+ getMoverPosition(moverId: number): number | null;
1288
+ /**
1289
+ * Push live drive-status (warning / error) onto motor modules. The
1290
+ * `statuses` array is indexed by `ModuleEntry.globalNumber` — i.e.
1291
+ * `statuses[m.globalNumber] === { warning, error }`. `null` clears a
1292
+ * slot; `undefined` leaves it unchanged.
1293
+ */
1294
+ setModuleStatuses(statuses: ReadonlyArray<ModuleStatusEntry | null | undefined>): void;
1295
+ /** Clear all live module statuses (revert to config-time `ModuleEntry.status`). */
1296
+ clearModuleStatuses(): void;
1147
1297
  checkMoverCollisions(opts?: CheckMoverCollisionsOptions): MoverCollision[];
1148
1298
  }
1149
1299
 
@@ -1166,7 +1316,7 @@ interface OriginCorrection {
1166
1316
  interface ModuleSidecar {
1167
1317
  moduleType: ModuleType3D;
1168
1318
  glbByRailSystem: {
1169
- BeckhoffGuidingRail: string | null;
1319
+ Beckhoff: string | null;
1170
1320
  HepcoGfx?: string | null;
1171
1321
  };
1172
1322
  originCorrection: OriginCorrection;
@@ -1245,6 +1395,103 @@ declare function railSidecarFilename(railType: RailType3D): string;
1245
1395
  */
1246
1396
  declare function pickGlbForRail(sidecar: ModuleSidecar | undefined, rail: RailSystem): string | null;
1247
1397
 
1398
+ /**
1399
+ * HepcoGfxRailProfile — cross-section of the Hepco GFX guiding rail used by
1400
+ * the procedural extrusion in <HepcoGfxRail>.
1401
+ *
1402
+ * The profile is expressed in the Beckhoff path-frame convention:
1403
+ * - path tangent = +X (extrusion direction)
1404
+ * - lateral = ±Y (left/right of the path)
1405
+ * - vertical = ±Z (Z-up, path origin at Z=0)
1406
+ *
1407
+ * THREE.ExtrudeGeometry maps a Shape's local X to the curve's Frenet normal
1408
+ * and Shape's local Y to the binormal. For a planar curve in the world XY
1409
+ * plane the initial Frenet frame computed by three.js is:
1410
+ * normal = (0, 0, -1) -> shape.x maps to world -Z
1411
+ * binormal = lateral in XY -> shape.y maps to world ±Y
1412
+ * To keep this module's public dimensions in natural "Z-up" coordinates,
1413
+ * the Shape factory negates Z when emitting shape.x. Callers should think
1414
+ * in (lateral Y, vertical Z); the negation is an implementation detail.
1415
+ *
1416
+ * The Hepco GFX cross-section is TWO disconnected regions:
1417
+ *
1418
+ * 1. Baseplate — a wide flat plate at the bottom (134.45 mm × 30.1 mm).
1419
+ * One per track; the two plates of an XTS oval sit side-by-side and
1420
+ * together span the full ~268.9 mm system width.
1421
+ *
1422
+ * 2. Rail — a separate spear-profile bar (40 mm × 20 mm) sitting on top
1423
+ * of the baseplate at its +Y edge. Has horizontally pointed apexes on
1424
+ * both lateral sides; the mover's V-rollers engage these apexes.
1425
+ *
1426
+ * Two shape factories are exported so the consumer can extrude each region
1427
+ * as its own mesh; merging them into a single Shape would require a thin
1428
+ * connecting sliver that misrepresents the real geometry.
1429
+ *
1430
+ * The `stackYOffsetMm` / `stackZOffsetMm` fields shift both regions
1431
+ * together — used by the playground calibration UI to fine-tune position
1432
+ * relative to the path origin without changing the inherent geometry.
1433
+ */
1434
+
1435
+ interface HepcoGfxRailProfileDims {
1436
+ /** Baseplate Y-extent per track (mm). Half of the XTS oval's full
1437
+ * cross-section width. */
1438
+ readonly baseplateWidthMm: number;
1439
+ /** Baseplate Z-extent (mm). */
1440
+ readonly baseplateThicknessMm: number;
1441
+ /** Z-coordinate of the baseplate's top face in the path frame (before
1442
+ * stackZOffsetMm is applied). Also the Z of the rail's bottom face. */
1443
+ readonly baseplateTopZMm: number;
1444
+ /** Rail Y-extent, spear-apex to spear-apex (mm). */
1445
+ readonly railWidthMm: number;
1446
+ /** Rail Z-extent, main-body top to main-body bottom (mm). */
1447
+ readonly railHeightMm: number;
1448
+ /** Z-coordinate of the rail's main-body top in the path frame (before
1449
+ * stackZOffsetMm is applied). */
1450
+ readonly railTopZMm: number;
1451
+ /** Lateral protrusion of each spear apex beyond the rail's main body (mm). */
1452
+ readonly spearProtrusionMm: number;
1453
+ /** Half-height of the spear at its inner (main-body-side) edge (mm) —
1454
+ * measured from the apex Z toward both top and bottom shoulders. */
1455
+ readonly spearBaseHalfHeightMm: number;
1456
+ /** Concave-undercut inset on the rail's bottom face just inboard of
1457
+ * each spear (mm) — the rail's bottom edge is shorter than its top
1458
+ * edge by 2× this value. */
1459
+ readonly undercutInsetMm: number;
1460
+ /** Calibration shift in Y applied to baseplate + rail together (mm). */
1461
+ readonly stackYOffsetMm: number;
1462
+ /** Calibration shift in Z applied to baseplate + rail together (mm). */
1463
+ readonly stackZOffsetMm: number;
1464
+ }
1465
+ /**
1466
+ * Scaffolding dimensions (mm). The stack offsets bake in the user-
1467
+ * requested calibration so the rendered geometry is correct out of the
1468
+ * box; the playground / a `*.meta.json` sidecar can override them.
1469
+ */
1470
+ declare const HEPCO_GFX_PROFILE: HepcoGfxRailProfileDims;
1471
+ /**
1472
+ * Merge a partial profile override onto the default profile. Used by the
1473
+ * playground / sidecar loader so callers only need to supply the fields
1474
+ * they want to override.
1475
+ */
1476
+ declare function resolveHepcoGfxProfile(override?: Partial<HepcoGfxRailProfileDims>): HepcoGfxRailProfileDims;
1477
+ /**
1478
+ * Build the baseplate cross-section (mm). Simple rectangle positioned so
1479
+ * the rail's +Y spear apex sits flush with the baseplate's +Y edge; the
1480
+ * baseplate extends in -Y direction from there. Both axes are shifted by
1481
+ * `stackYOffsetMm` / `stackZOffsetMm` before being emitted.
1482
+ */
1483
+ declare function createHepcoGfxBaseplateShape(dims?: HepcoGfxRailProfileDims): Shape;
1484
+ /**
1485
+ * Build the rail cross-section (mm). Symmetric spear-profile bar centered
1486
+ * on Y=0 (before stackYOffsetMm). The mover's V-rollers grip the two
1487
+ * spear apexes (at Y=±railWidth/2, Z=midZ).
1488
+ *
1489
+ * The profile's top edge is flush with the spear shoulder top — there is
1490
+ * NO rectangular section above the spear shoulders. `railHeightMm` therefore
1491
+ * spans only from the main-body bottom up to the spear shoulder top.
1492
+ */
1493
+ declare function createHepcoGfxRailShape(dims?: HepcoGfxRailProfileDims): Shape;
1494
+
1248
1495
  /**
1249
1496
  * <XtsViewer3D> — Top-level React component (spec §6.1).
1250
1497
  *
@@ -1335,6 +1582,12 @@ interface XtsViewer3DProps {
1335
1582
  * Live override of per-XPU `trackTransform`. Keyed by `xpu.index`.
1336
1583
  */
1337
1584
  trackTransformOverrides?: Record<number, TrackTransform>;
1585
+ /**
1586
+ * Live override of the procedural Hepco-GFX rail profile dimensions.
1587
+ * Only the fields you supply are merged onto the defaults — useful for
1588
+ * the playground calibration UI / a sidecar-style `*.meta.json`.
1589
+ */
1590
+ hepcoGfxProfile?: Partial<HepcoGfxRailProfileDims>;
1338
1591
  /** CAD-style ViewCube overlay. */
1339
1592
  viewCube?: {
1340
1593
  enabled?: boolean;
@@ -1604,24 +1857,6 @@ declare function interpolateHeatmapValue(samples: ReadonlyArray<HeatmapSample>,
1604
1857
  */
1605
1858
  declare function normaliseToHeatmapRange(value: number, min: number, max: number): number;
1606
1859
 
1607
- /**
1608
- * applyLayout — resolve PartTransformation per Layout-Mode.
1609
- *
1610
- * Spec §5:
1611
- * - SystemView → effective = serverTransform ⊕ clientTransform
1612
- * - InvertedSystemView → effective = invertX(serverTransform) ⊕ clientTransform
1613
- * - Custom → effective = clientTransform (server ignored)
1614
- *
1615
- * "Invert" only flips the X-component of the offset.
1616
- *
1617
- * The function returns a NEW PartTransformation with no separate server/
1618
- * client split — downstream components only need the resolved value.
1619
- */
1620
-
1621
- declare function resolvePartTransformation(part: PartConfig, layout: LayoutMode): PartTransformation | undefined;
1622
- /** Pre-resolve PartTransformations across the whole config. */
1623
- declare function applyLayoutMode(xpus: ReadonlyArray<ProcessingUnitConfig>, layout: LayoutMode): ProcessingUnitConfig[];
1624
-
1625
1860
  /**
1626
1861
  * Path-sample helpers — convert a chain + a [startMm, endMm] range into a
1627
1862
  * world-frame polyline of Vector3s. Used by Stations, Dimensions and
@@ -1734,6 +1969,25 @@ declare const ModuleCornerMarkers: React__default.FC<Props>;
1734
1969
  */
1735
1970
  declare const DEFAULT_LABEL_FONT_URL: "https://cdn.jsdelivr.net/npm/@fontsource/roboto-condensed@5.2.7/files/roboto-condensed-latin-400-normal.woff";
1736
1971
 
1972
+ /**
1973
+ * <MoverIdLabel> — Camera-facing 3D text label that shows a mover's
1974
+ * `id` above the mover GLB. Mounted by `<XtsMover>` when
1975
+ * `DisplayOptions.showMoverIds === true`.
1976
+ *
1977
+ * Renders via drei's `<Billboard>` + `<Text>` (troika-three-text under
1978
+ * the hood) so the label stays readable as the camera orbits. All
1979
+ * styling is driven by `MoverIdLabelOptions`; the component supplies
1980
+ * the documented defaults (15 mm black text, 55 mm above the mover's
1981
+ * magnet-plate origin, Roboto Condensed via jsDelivr).
1982
+ */
1983
+
1984
+ interface MoverIdLabelProps {
1985
+ /** The mover's `id` — rendered verbatim. */
1986
+ text: string;
1987
+ options?: MoverIdLabelOptions;
1988
+ }
1989
+ declare const MoverIdLabel: React__default.FC<MoverIdLabelProps>;
1990
+
1737
1991
  /**
1738
1992
  * AUTO-GENERATED — do not edit by hand.
1739
1993
  *
@@ -1747,7 +2001,7 @@ declare const DEFAULT_LABEL_FONT_URL: "https://cdn.jsdelivr.net/npm/@fontsource/
1747
2001
  * Module count: 27
1748
2002
  * Mover count: 4
1749
2003
  * Tool count: 2
1750
- * Rail count: 7
2004
+ * Rail count: 8
1751
2005
  */
1752
2006
 
1753
2007
  /**
@@ -1798,10 +2052,10 @@ declare function useSidecarSource(): SidecarSourceConfig;
1798
2052
  * skins, or to plug in user GLBs).
1799
2053
  *
1800
2054
  * Hygienic ATH modules carry per-RailSystem URLs. Standard AT/Eco modules
1801
- * only set `BeckhoffGuidingRail`; the loader silently falls back to that
2055
+ * only set `Beckhoff`; the loader silently falls back to that
1802
2056
  * variant when `HepcoGfx` is requested but unavailable (spec §4.3).
1803
2057
  *
1804
- * `null` for `BeckhoffGuidingRail` marks "prepared but not delivered"
2058
+ * `null` for `Beckhoff` marks "prepared but not delivered"
1805
2059
  * (AT2042 / AT2140) — the loader renders a placeholder AABB mesh
1806
2060
  * (spec §14.4).
1807
2061
  */
@@ -1816,7 +2070,7 @@ declare const DEFAULT_TOOL_MANIFEST: Readonly<Record<MoverToolType3D, {
1816
2070
  }>>;
1817
2071
  /**
1818
2072
  * Beckhoff guiding-rail GLBs (the outer rail profile that the mover wheels
1819
- * engage). Rendered per module when `RailSystem === 'BeckhoffGuidingRail'`;
2073
+ * engage). Rendered per module when `RailSystem === 'Beckhoff'`;
1820
2074
  * Hepco GFX uses a different profile and is handled separately.
1821
2075
  */
1822
2076
  declare const DEFAULT_RAIL_MANIFEST: Readonly<Record<RailType3D, {
@@ -1913,4 +2167,4 @@ declare const SPEC_VERSION: "v5";
1913
2167
  /** Returns the spec version this package targets. */
1914
2168
  declare function specVersion(): string;
1915
2169
 
1916
- export { type AreaConfig, type AreaOptions, AssetLoader, type AssetManifest, BUILTIN_MODULE_SIDECARS, BUILTIN_MOVER_SIDECARS, BUILTIN_RAIL_SIDECARS, BUILTIN_TOOL_SIDECARS, type BuiltChain, type BuiltModule, type CameraState, type CheckMoverCollisionsOptions, type ClickModifiers, type CustomAssetBinding, type CustomAssetConfig, type CustomMoverLayout, DEFAULT_LABEL_FONT_URL, DEFAULT_MODULE_MANIFEST, DEFAULT_MOVER_MANIFEST, DEFAULT_ORIGIN_CORRECTION, DEFAULT_RAIL_MANIFEST, DEFAULT_TOOL_MANIFEST, type DimensionOptions, type DisplayOptions, EMPTY_SELECTION, type EndDelta, type HeatmapSample, IDENTITY_POSE, type InfoBarConfig, type InfoBarMarkerConfig, type InfoBarOptions, JSDELIVR_ASSETS_BASE_URL, type LayoutMode, MODULE_CATALOG, MODULE_GUIDING_RAIL_MAP, MOVER_CATALOG, type MarkerOptions, type MarkerShape, type ModuleAssetEntry, type ModuleCatalogEntry, ModuleCornerMarkers, type ModuleEntry, type ModuleRef, type ModuleSidecar, type ModuleType3D, type MoverAssetEntry, type MoverCatalogEntry, type MoverCollision, type MoverConfig, 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 PathSample, type PathType, type PlanarPose, type PointCloud, type PositionFrame, type ProcessingUnitConfig, type RailAssetEntry, type RailSidecar, type RailSystem, type RailType3D, SPEC_VERSION, type ScreenshotFormat, type ScreenshotMode, type ScreenshotOptions, type ScreenshotResult, type SelectionMode, type SelectionState, SidecarLoader, type SidecarOverrides, type SidecarSourceConfig, SidecarSourceContext, type StationConfig, type StationOptions, type StatorHeatmap, type StopPositionMoverOptions, type TextOptions, type TrackTransform, VERSION, type Vec2, type Vec3, XtsArcCurve3, type XtsConfig, XtsConfigNormalizer, type XtsModelDocument, XtsPointCloudCurve3, type XtsRuntimeState, XtsViewer3D, type XtsViewer3DProps, type XtsViewer3DRef, type XtsViewerError, type XtsViewerErrorCode, XtsViewerErrorException, applyEmptyClick, applyLayoutMode, applyModuleClick, applyMoverClick, buildChain, buildPartPath, chainToUserPositionMm, checkSamePathCollisions, composeAssetUrl, deselectAll, findModuleAt, getModuleEntry, getMoverEntry, getPointCloud, getSharedAssetLoader, interpolateHeatmapValue, isKnownModuleType, isKnownMoverType, isModuleSelected, isMoverSelected, moduleEndDelta, moduleSidecarFilename, moverSidecarFilename, moverWorldAt, normaliseToHeatmapRange, normalizeXtsConfig, pickGlbForRail, poseToMatrix4, railSidecarFilename, registerPointCloud, resolveGuidingRailGlbUrl, resolveGuidingRailType, resolveModuleGlbUrl, resolveMoverGlbUrl, resolveOriginCorrection, resolvePartTransformation, resolveStopPositionMm, resolveToolGlbUrl, sampleChainPoint, sampleChainRange, sampleModulePath, sortHeatmapSamples, specVersion, toolSidecarFilename, trackLengthOf, unregisterPointCloud, useSidecarSource, userToChainPositionMm };
2170
+ export { type AreaConfig, type AreaOptions, AssetLoader, type AssetManifest, BUILTIN_MODULE_SIDECARS, BUILTIN_MOVER_SIDECARS, BUILTIN_RAIL_SIDECARS, BUILTIN_TOOL_SIDECARS, type BuiltChain, type BuiltModule, type CameraState, type CheckMoverCollisionsOptions, type ClickModifiers, type CustomAssetBinding, type CustomAssetConfig, type CustomMoverLayout, DEFAULT_LABEL_FONT_URL, DEFAULT_MODULE_MANIFEST, DEFAULT_MOVER_MANIFEST, DEFAULT_ORIGIN_CORRECTION, DEFAULT_RAIL_MANIFEST, DEFAULT_TOOL_MANIFEST, type DimensionOptions, type DisplayOptions, EMPTY_SELECTION, type EndDelta, HEPCO_GFX_PROFILE, type HeatmapSample, type HepcoGfxRailProfileDims, IDENTITY_POSE, type InfoBarConfig, type InfoBarMarkerConfig, type InfoBarOptions, JSDELIVR_ASSETS_BASE_URL, MODULE_CATALOG, MODULE_GUIDING_RAIL_MAP, MOVER_CATALOG, type MarkerOptions, type MarkerShape, type ModuleAssetEntry, type ModuleCatalogEntry, ModuleCornerMarkers, type ModuleEntry, 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 PathSample, type PathType, type PlanarPose, type PointCloud, type PositionFrame, type ProcessingUnitConfig, type RailAssetEntry, type RailSidecar, type RailSystem, type RailType3D, SPEC_VERSION, type ScreenshotFormat, type ScreenshotMode, type ScreenshotOptions, type ScreenshotResult, type SelectionMode, type SelectionState, SidecarLoader, type SidecarOverrides, type SidecarSourceConfig, SidecarSourceContext, type StationConfig, type StationOptions, type StatorHeatmap, type StopPositionMoverOptions, type TextOptions, type TrackTransform, VERSION, type Vec2, type Vec3, XtsArcCurve3, type XtsConfig, XtsConfigNormalizer, type XtsModelDocument, XtsPointCloudCurve3, type XtsRuntimeState, XtsViewer3D, type XtsViewer3DProps, type XtsViewer3DRef, type XtsViewerError, type XtsViewerErrorCode, XtsViewerErrorException, applyEmptyClick, applyModuleClick, applyMoverClick, buildChain, buildPartPath, chainToUserPositionMm, checkSamePathCollisions, composeAssetUrl, createHepcoGfxBaseplateShape, createHepcoGfxRailShape, deselectAll, findModuleAt, getModuleEntry, getMoverEntry, getPointCloud, getSharedAssetLoader, interpolateHeatmapValue, isKnownModuleType, isKnownMoverType, isModuleSelected, isMoverSelected, moduleEndDelta, moduleSidecarFilename, moverSidecarFilename, moverWorldAt, normaliseToHeatmapRange, normalizeXtsConfig, pickGlbForRail, poseToMatrix4, railSidecarFilename, registerPointCloud, resolveGuidingRailGlbUrl, resolveGuidingRailType, resolveHepcoGfxProfile, resolveModuleGlbUrl, resolveMoverGlbUrl, resolveOriginCorrection, resolveStopPositionMm, resolveToolGlbUrl, sampleChainPoint, sampleChainRange, sampleModulePath, sortHeatmapSamples, specVersion, toolSidecarFilename, trackLengthOf, unregisterPointCloud, useSidecarSource, userToChainPositionMm };