@2112-lab/central-plant 0.3.45 → 0.3.47

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.
Files changed (31) hide show
  1. package/dist/bundle/index.js +635 -337
  2. package/dist/cjs/src/core/centralPlant.js +342 -218
  3. package/dist/cjs/src/core/centralPlantInternals.js +2 -0
  4. package/dist/cjs/src/core/sceneViewer.js +0 -1
  5. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +1 -2
  6. package/dist/cjs/src/managers/components/componentDataManager.js +0 -1
  7. package/dist/cjs/src/managers/controls/componentDragManager.js +4 -8
  8. package/dist/cjs/src/managers/pathfinding/pathfindingManager.js +55 -1
  9. package/dist/cjs/src/managers/scene/collisionManager.js +142 -0
  10. package/dist/cjs/src/managers/scene/componentTooltipManager.js +2 -3
  11. package/dist/cjs/src/managers/scene/sceneExportManager.js +32 -11
  12. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +17 -33
  13. package/dist/cjs/src/utils/behaviorDispatch.js +11 -42
  14. package/dist/cjs/src/utils/boundingBoxUtils.js +54 -8
  15. package/dist/cjs/src/utils/ioDeviceUtils.js +3 -9
  16. package/dist/esm/src/core/centralPlant.js +342 -218
  17. package/dist/esm/src/core/centralPlantInternals.js +2 -0
  18. package/dist/esm/src/core/sceneViewer.js +0 -1
  19. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +1 -2
  20. package/dist/esm/src/managers/components/componentDataManager.js +0 -1
  21. package/dist/esm/src/managers/controls/componentDragManager.js +4 -8
  22. package/dist/esm/src/managers/pathfinding/pathfindingManager.js +56 -2
  23. package/dist/esm/src/managers/scene/collisionManager.js +118 -0
  24. package/dist/esm/src/managers/scene/componentTooltipManager.js +2 -3
  25. package/dist/esm/src/managers/scene/sceneExportManager.js +33 -12
  26. package/dist/esm/src/managers/scene/sceneOperationsManager.js +17 -33
  27. package/dist/esm/src/utils/behaviorDispatch.js +11 -42
  28. package/dist/esm/src/utils/boundingBoxUtils.js +55 -10
  29. package/dist/esm/src/utils/ioDeviceUtils.js +3 -9
  30. package/dist/index.d.ts +0 -6
  31. package/package.json +1 -1
@@ -17,6 +17,7 @@ import { KeyboardControlsManager } from '../managers/controls/keyboardControlsMa
17
17
  import { PathfindingManager } from '../managers/pathfinding/pathfindingManager.js';
18
18
  import { PathFlowManager } from '../managers/pathfinding/PathFlowManager.js';
19
19
  import { SceneOperationsManager } from '../managers/scene/sceneOperationsManager.js';
20
+ import { CollisionManager } from '../managers/scene/collisionManager.js';
20
21
  import { AnimationManager } from '../managers/scene/animationManager.js';
21
22
  import { CameraControlsManager } from '../managers/controls/cameraControlsManager.js';
22
23
  import { ComponentDragManager } from '../managers/controls/componentDragManager.js';
@@ -145,6 +146,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
145
146
  this.centralPlant.managers.keyboardControlsManager = new KeyboardControlsManager(this.centralPlant.sceneViewer);
146
147
  this.centralPlant.managers.pathfindingManager = new PathfindingManager(this.centralPlant.sceneViewer);
147
148
  this.centralPlant.managers.pathFlowManager = new PathFlowManager(this.centralPlant.sceneViewer);
149
+ this.centralPlant.managers.collisionManager = new CollisionManager(this.centralPlant.sceneViewer);
148
150
  this.centralPlant.managers.sceneOperationsManager = new SceneOperationsManager(this.centralPlant.sceneViewer);
149
151
  this.centralPlant.managers.animationManager = new AnimationManager(this.centralPlant.sceneViewer);
150
152
  this.centralPlant.managers.cameraControlsManager = new CameraControlsManager(this.centralPlant.sceneViewer);
@@ -643,7 +643,6 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
643
643
  }, {
644
644
  key: "updatePaths",
645
645
  value: function updatePaths() {
646
- console.log("updatePaths started");
647
646
  // Delegate pathfinding update to PathfindingManager
648
647
  if (this.pathfindingManager) {
649
648
  this.pathfindingManager.updatePathfindingAfterTransform(this.currentSceneData);
@@ -513,8 +513,7 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
513
513
  /**
514
514
  * Return tooltip-compatible data point definitions derived from the loaded
515
515
  * animation entries for a given attachment. Used by componentTooltipManager
516
- * to replace the static ioConfig.states[] snapshot with the richer animation
517
- * state definitions created in the Animate window.
516
+ * to provide the richer animation state definitions created in the Animate window.
518
517
  *
519
518
  * One dp object is emitted per unique stateVariable; multiple mesh entries
520
519
  * that share the same stateVariable are collapsed into one.
@@ -864,7 +864,6 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
864
864
  };
865
865
  return _objectSpread2(_objectSpread2({}, baseData), {}, {
866
866
  metadata: component.metadata || {},
867
- ioConfig: component.ioConfig || null,
868
867
  boundingBox: component.boundingBox || null,
869
868
  adaptedBoundingBox: component.adaptedBoundingBox || null,
870
869
  children: component.children || [],
@@ -321,7 +321,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
321
321
  key: "_attachIODeviceModelsToPreview",
322
322
  value: (function () {
323
323
  var _attachIODeviceModelsToPreview2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
324
- var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
324
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
325
325
  return _regenerator().w(function (_context3) {
326
326
  while (1) switch (_context3.n) {
327
327
  case 0:
@@ -384,12 +384,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
384
384
  deviceId: attachment.deviceId,
385
385
  attachmentId: attachmentId,
386
386
  attachmentLabel: attachment.attachmentLabel,
387
- deviceName: deviceData.name || '',
388
- // Snapshot of data point definitions so the tooltip can render state
389
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
390
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
391
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
392
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output'
387
+ deviceName: deviceData.name || ''
393
388
  };
394
389
  if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
395
390
  pos = attachment.attachmentPoint.position;
@@ -643,6 +638,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
643
638
  // Find intersection with ground plane
644
639
  var intersection = this.raycaster.ray.intersectPlane(this.dropPlane, this.dropIntersection);
645
640
  if (intersection) {
641
+ var _this$sceneViewer$col;
646
642
  // Apply 0.5 unit transform snapping to intersection point
647
643
  var snappedPosition = this._applyTransformSnap(intersection);
648
644
  this.dragData.previewObject.position.copy(snappedPosition);
@@ -650,7 +646,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
650
646
 
651
647
  // Check for overlap and update color accordingly
652
648
  var wasOverlapping = this.dragData.isOverlapping;
653
- this.dragData.isOverlapping = this._checkBoundingBoxOverlap();
649
+ this.dragData.isOverlapping = !!((_this$sceneViewer$col = this.sceneViewer.collisionManager) !== null && _this$sceneViewer$col !== void 0 && _this$sceneViewer$col.checkCollision(this.dragData.previewObject));
654
650
 
655
651
  // Update color if overlap state changed
656
652
  if (wasOverlapping !== this.dragData.isOverlapping) {
@@ -3,7 +3,7 @@ import * as THREE from 'three';
3
3
  import { Pathfinder } from '@2112-lab/pathfinder';
4
4
  import { BaseDisposable } from '../../core/baseDisposable.js';
5
5
  import { PathData } from '../../core/pathfindingData.js';
6
- import { computeFilteredBoundingBox, computeIODeviceBoundingBoxes } from '../../utils/boundingBoxUtils.js';
6
+ import { computeFilteredBoundingBox, computeIODeviceBoundingBoxes, computeConnectorBoundingBoxes } from '../../utils/boundingBoxUtils.js';
7
7
  import { SceneDataManager } from './sceneDataManager.js';
8
8
  import { PathRenderingManager } from './PathRenderingManager.js';
9
9
  import { ConnectorManager } from './ConnectorManager.js';
@@ -304,6 +304,31 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
304
304
  }
305
305
  });
306
306
  }
307
+
308
+ // Also reinject connectors from cache
309
+ if (_cached.connectorBBoxes && _cached.connectorBBoxes.length > 0) {
310
+ if (!_enrichedChild.children) _enrichedChild.children = [];
311
+ _cached.connectorBBoxes.forEach(function (connBBox) {
312
+ var existingIndex = _enrichedChild.children.findIndex(function (c) {
313
+ return c.uuid === connBBox.uuid;
314
+ });
315
+ if (existingIndex >= 0) {
316
+ _enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
317
+ userData: _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
318
+ worldBoundingBox: connBBox.worldBoundingBox
319
+ })
320
+ });
321
+ } else {
322
+ _enrichedChild.children.push({
323
+ uuid: connBBox.uuid,
324
+ userData: _objectSpread2(_objectSpread2({}, connBBox.userData), {}, {
325
+ worldBoundingBox: connBBox.worldBoundingBox
326
+ }),
327
+ children: []
328
+ });
329
+ }
330
+ });
331
+ }
307
332
  return _enrichedChild;
308
333
  }
309
334
 
@@ -361,11 +386,40 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
361
386
  console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
362
387
  }
363
388
 
389
+ // PHASE 2: Enrich Connectors (CRITICAL for pathfinding API)
390
+ // Also compute world bounding boxes for connectors and inject into children.
391
+ // This ensures endpoints have world coordinates in sceneDataCopy.
392
+ var connectorBBoxes = computeConnectorBoundingBoxes(componentObject);
393
+ if (connectorBBoxes.length > 0) {
394
+ if (!enrichedChild.children) enrichedChild.children = [];
395
+ connectorBBoxes.forEach(function (connBBox) {
396
+ var existingIndex = enrichedChild.children.findIndex(function (c) {
397
+ return c.uuid === connBBox.uuid;
398
+ });
399
+ if (existingIndex >= 0) {
400
+ enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
401
+ userData: _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
402
+ worldBoundingBox: connBBox.worldBoundingBox
403
+ })
404
+ });
405
+ } else {
406
+ enrichedChild.children.push({
407
+ uuid: connBBox.uuid,
408
+ userData: _objectSpread2(_objectSpread2({}, connBBox.userData), {}, {
409
+ worldBoundingBox: connBBox.worldBoundingBox
410
+ }),
411
+ children: []
412
+ });
413
+ }
414
+ });
415
+ }
416
+
364
417
  // Store in cache
365
418
  _this3._bboxCache.set(child.uuid, {
366
419
  matrixHash: _hash,
367
420
  filteredBBox: _bboxData,
368
- ioDeviceBBoxes: ioDeviceBBoxes
421
+ ioDeviceBBoxes: ioDeviceBBoxes,
422
+ connectorBBoxes: connectorBBoxes
369
423
  });
370
424
  return enrichedChild;
371
425
  } else {
@@ -0,0 +1,118 @@
1
+ import { createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, classCallCheck as _classCallCheck } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import * as THREE from 'three';
3
+
4
+ var CollisionManager = /*#__PURE__*/function () {
5
+ /**
6
+ * @param {Object} sceneViewer - The scene viewer instance
7
+ */
8
+ function CollisionManager(sceneViewer) {
9
+ _classCallCheck(this, CollisionManager);
10
+ this.sceneViewer = sceneViewer;
11
+ }
12
+
13
+ /**
14
+ * Check if a given object overlaps with any other relevant objects in the scene.
15
+ * @param {THREE.Object3D} object - The object to check for collisions
16
+ * @param {Array<string>} excludeTypes - Object types to exclude from checking (e.g. ['ground'])
17
+ * @returns {Object|null} Collision info {object, objectType} if collision detected, null otherwise
18
+ */
19
+ return _createClass(CollisionManager, [{
20
+ key: "checkCollision",
21
+ value: function checkCollision(object) {
22
+ var _this$sceneViewer,
23
+ _this = this;
24
+ var excludeTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['isBaseGround', 'isBaseGrid', 'isBrickWall'];
25
+ if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.scene) || !object) return null;
26
+
27
+ // Compute high-quality bounding box for the object being checked
28
+ // We use setFromObject here because the target object just moved
29
+ var objectBBox = new THREE.Box3().setFromObject(object);
30
+
31
+ // Narrow down potential colliders
32
+ var collisionDetected = null;
33
+
34
+ // Optimization: Only check the root-level CP objects to avoid O(N^2) complexity with meshes
35
+ this.sceneViewer.scene.traverse(function (child) {
36
+ var _child$userData, _child$userData2, _child$userData3, _child$userData4, _child$userData5;
37
+ if (collisionDetected) return; // Short circuit
38
+
39
+ // Skip the object itself and its descendants
40
+ if (child === object || _this._isDescendantOf(object, child)) return;
41
+
42
+ // Filter by CP object types at the root level (skip internal meshes during traverse)
43
+ var isCPRootObject = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'component' || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment' || ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'gateway' || ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) === 'connector' || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) === 'io-device';
44
+ if (isCPRootObject && !_this._shouldExclude(child, excludeTypes)) {
45
+ // Use cached worldBoundingBox if available, otherwise compute it (and cache it)
46
+ var childBBox;
47
+ if (child.userData.worldBoundingBox && !child.userData.isMoving) {
48
+ var _min$x, _min$y, _min$z, _max$x, _max$y, _max$z;
49
+ // Use stored worldBoundingBox (handles array or Box3 format)
50
+ var min = child.userData.worldBoundingBox.min;
51
+ var max = child.userData.worldBoundingBox.max;
52
+ childBBox = new THREE.Box3(new THREE.Vector3((_min$x = min.x) !== null && _min$x !== void 0 ? _min$x : min[0], (_min$y = min.y) !== null && _min$y !== void 0 ? _min$y : min[1], (_min$z = min.z) !== null && _min$z !== void 0 ? _min$z : min[2]), new THREE.Vector3((_max$x = max.x) !== null && _max$x !== void 0 ? _max$x : max[0], (_max$y = max.y) !== null && _max$y !== void 0 ? _max$y : max[1], (_max$z = max.z) !== null && _max$z !== void 0 ? _max$z : max[2]));
53
+ } else {
54
+ // Fallback to high-quality computation for moving objects or first-time checks
55
+ childBBox = new THREE.Box3().setFromObject(child);
56
+
57
+ // Cache the result for next time (non-moving objects)
58
+ if (!child.userData.isMoving) {
59
+ child.userData.worldBoundingBox = {
60
+ min: [childBBox.min.x, childBBox.min.y, childBBox.min.z],
61
+ max: [childBBox.max.x, childBBox.max.y, childBBox.max.z]
62
+ };
63
+ }
64
+ }
65
+ if (objectBBox.intersectsBox(childBBox)) {
66
+ collisionDetected = {
67
+ object: child,
68
+ objectType: child.userData.objectType,
69
+ uuid: child.uuid,
70
+ name: child.name || child.userData.libraryId || child.uuid
71
+ };
72
+ }
73
+ }
74
+ });
75
+ return collisionDetected;
76
+ }
77
+
78
+ /**
79
+ * Helper to check if a node is a descendant of a specific parent
80
+ * @private
81
+ */
82
+ }, {
83
+ key: "_isDescendantOf",
84
+ value: function _isDescendantOf(parent, child) {
85
+ var node = child.parent;
86
+ while (node !== null) {
87
+ if (node === parent) return true;
88
+ node = node.parent;
89
+ }
90
+ return false;
91
+ }
92
+
93
+ /**
94
+ * Helper to determine if an object should be excluded from collision checking
95
+ * @private
96
+ */
97
+ }, {
98
+ key: "_shouldExclude",
99
+ value: function _shouldExclude(object, excludeTypes) {
100
+ if (object.userData.isPreview) return true;
101
+ var _iterator = _createForOfIteratorHelper(excludeTypes),
102
+ _step;
103
+ try {
104
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
105
+ var type = _step.value;
106
+ if (object.userData[type]) return true;
107
+ }
108
+ } catch (err) {
109
+ _iterator.e(err);
110
+ } finally {
111
+ _iterator.f();
112
+ }
113
+ return false;
114
+ }
115
+ }]);
116
+ }();
117
+
118
+ export { CollisionManager };
@@ -458,8 +458,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
458
458
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
459
459
  var attachmentId = child.userData.attachmentId || '';
460
460
 
461
- // Use only data points from the animate window (behaviorConfig).
462
- // The static ioConfig.states[] snapshot on userData is intentionally ignored.
461
+ // Use data points from the animate window (behaviorConfig).
463
462
  var dataPoints = resolveDataPoints(parentUuid, attachmentId, child.userData, getIoBehaviorManager(_this3.sceneViewer));
464
463
 
465
464
  // When data points come from behaviorConfig they already carry direction:'input'.
@@ -672,7 +671,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
672
671
  * Input / bidirectional → shows an interactive control.
673
672
  *
674
673
  * @param {string} scopedAttachmentId - Scoped attachment ID (parentUuid::attachmentId) for state isolation
675
- * @param {Object} dp - data point definition from ioConfig.dataPoints
674
+ * @param {Object} dp - data point definition
676
675
  * @param {string} [deviceDirection] - device-level direction ('input'|'output'), overrides dp.direction
677
676
  * @param {string} [originalAttachmentId] - Original attachment ID for behavior triggering
678
677
  * @returns {HTMLElement}
@@ -1,4 +1,4 @@
1
- import { createClass as _createClass, classCallCheck as _classCallCheck, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { createClass as _createClass, classCallCheck as _classCallCheck, toConsumableArray as _toConsumableArray, objectSpread2 as _objectSpread2, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import 'three';
3
3
  import { GLTFExporter } from '../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js';
4
4
  import { getHardcodedUuid } from '../../utils/nameUtils.js';
@@ -145,6 +145,8 @@ var SceneExportManager = /*#__PURE__*/function () {
145
145
  // Internal tracking - not needed in export
146
146
  'initialPosition',
147
147
  // Internal tracking - not needed in export
148
+ 'attachedDevices',
149
+ // Stored in smart component dictionary, not scene JSON
148
150
  // Exclude internal segment tracking properties
149
151
  'segmentId',
150
152
  // Internal tracking
@@ -217,11 +219,11 @@ var SceneExportManager = /*#__PURE__*/function () {
217
219
  };
218
220
  }
219
221
 
220
- // For components: no children exported connector positions are defined in the component
221
- // dictionary GLB and will be re-injected at import time via _injectConnectorChildrenFromDictionary.
222
- // Exporting them here would prevent that injection (it skips if children already exist)
223
- // and would leave the pathfinder with connectors in incompatible local-position format.
222
+ // For components: only export child connectors if they were manually added/defined.
223
+ // Most connectors are injected from dictionary at import time, but some (like manually placed ones)
224
+ // need to be persisted to maintain connections in the exported scene.
224
225
  if (threeObject.children && threeObject.children.length > 0) {
226
+ var _threeObject$userData11;
225
227
  var exportableChildren = [];
226
228
  if (isManualSegment) {
227
229
  // For manual segments, export their connector children
@@ -235,6 +237,25 @@ var SceneExportManager = /*#__PURE__*/function () {
235
237
  }
236
238
  }
237
239
  });
240
+ } else if (((_threeObject$userData11 = threeObject.userData) === null || _threeObject$userData11 === void 0 ? void 0 : _threeObject$userData11.objectType) === 'component') {
241
+ // For components, only export connectors that have objectType='connector'
242
+ // Standard dictionary-injected connectors should be exported so connections work on re-import
243
+ threeObject.children.forEach(function (child) {
244
+ var _child$userData2;
245
+ if (((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'connector') {
246
+ exportableChildren.push({
247
+ uuid: child.uuid,
248
+ name: child.name,
249
+ type: 'Mesh',
250
+ position: {
251
+ x: roundIfClose(child.position.x),
252
+ y: roundIfClose(child.position.y),
253
+ z: roundIfClose(child.position.z)
254
+ },
255
+ userData: _objectSpread2({}, child.userData)
256
+ });
257
+ }
258
+ });
238
259
  }
239
260
  if (exportableChildren.length > 0) {
240
261
  jsonObject.children = exportableChildren;
@@ -249,9 +270,9 @@ var SceneExportManager = /*#__PURE__*/function () {
249
270
  // Extract main scene objects (components and standalone connectors)
250
271
  var sceneChildren = [];
251
272
  this.sceneViewer.scene.children.forEach(function (child) {
252
- var _child$userData2;
273
+ var _child$userData3;
253
274
  // Only export components and connectors; skip segments, gateways, polylines, etc.
254
- var objectType = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType;
275
+ var objectType = (_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType;
255
276
  if (objectType !== 'component' && objectType !== 'connector') {
256
277
  return;
257
278
  }
@@ -415,14 +436,14 @@ var SceneExportManager = /*#__PURE__*/function () {
415
436
  BufferGeometryUtils = BufferGeometryUtilsModule.BufferGeometryUtils || BufferGeometryUtilsModule.default || BufferGeometryUtilsModule; // Create a new scene for export instead of cloning
416
437
  exportScene = new _THREE.Scene(); // Helper function to check if an object should be exported
417
438
  shouldExport = function shouldExport(child) {
418
- var _child$name, _child$userData3, _child$userData4, _child$userData5, _child$userData6;
439
+ var _child$name, _child$userData4, _child$userData5, _child$userData6, _child$userData7;
419
440
  if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) return false; // Will handle separately
420
441
  if (child.name === 'fogPlane') return false; // Skip fog plane
421
- if ((_child$userData3 = child.userData) !== null && _child$userData3 !== void 0 && _child$userData3.isBrickWall) return false; // Skip environment
422
- if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBaseGround) return false; // Skip environment
423
- if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGrid) return false; // Skip environment
442
+ if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBrickWall) return false; // Skip environment
443
+ if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGround) return false; // Skip environment
444
+ if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isBaseGrid) return false; // Skip environment
424
445
  if (child.isLight) return false; // Skip lights
425
- if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isTransformControls) return false; // Skip transform controls
446
+ if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.isTransformControls) return false; // Skip transform controls
426
447
  if (child.isTransformControls) return false; // Skip transform controls
427
448
  if (child.type && child.type.includes('TransformControls')) return false;
428
449
  if (child.type && child.type.includes('Helper')) return false; // Skip helpers
@@ -1309,51 +1309,36 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1309
1309
  y: componentModel.scale.y,
1310
1310
  z: componentModel.scale.z
1311
1311
  },
1312
- userData: _objectSpread2(_objectSpread2({}, componentModel.userData), {}, {
1312
+ userData: _objectSpread2(_objectSpread2({}, function () {
1313
+ var ud = _objectSpread2({}, componentModel.userData);
1314
+ delete ud.attachedDevices; // Instance data belongs in dictionary, not scene JSON
1315
+ delete ud.isDeclared; // Runtime flag
1316
+ delete ud.originalUuid; // Runtime tracking
1317
+ return ud;
1318
+ }()), {}, {
1313
1319
  worldBoundingBox: {
1314
1320
  min: boundingBox.min.toArray(),
1315
1321
  max: boundingBox.max.toArray()
1316
1322
  }
1317
1323
  }),
1318
- children: []
1324
+ children: [] // Initialize children array
1319
1325
  };
1320
1326
 
1321
- // Process children (connectors, etc.) if they exist
1322
- if (componentModel.children && componentModel.children.length > 0) {
1327
+ // Collect children that are connectors
1328
+ if (componentModel.children) {
1323
1329
  componentModel.children.forEach(function (child) {
1324
- var _child$userData0, _child$userData1;
1325
- var childType = ((_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.objectType) || ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType);
1326
- if (childType === 'connector') {
1327
- var _child$geometry;
1328
- var childBoundingBox = new THREE.Box3().setFromObject(child);
1329
- var childSceneData = {
1330
+ if (child.userData && child.userData.objectType === 'connector') {
1331
+ componentSceneData.children.push({
1330
1332
  uuid: child.uuid,
1331
1333
  name: child.name,
1332
- type: child.type,
1334
+ type: 'Mesh',
1333
1335
  position: {
1334
1336
  x: child.position.x,
1335
1337
  y: child.position.y,
1336
1338
  z: child.position.z
1337
1339
  },
1338
- rotation: {
1339
- x: THREE.MathUtils.radToDeg(child.rotation.x),
1340
- y: THREE.MathUtils.radToDeg(child.rotation.y),
1341
- z: THREE.MathUtils.radToDeg(child.rotation.z)
1342
- },
1343
- scale: {
1344
- x: child.scale.x,
1345
- y: child.scale.y,
1346
- z: child.scale.z
1347
- },
1348
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
1349
- worldBoundingBox: {
1350
- min: childBoundingBox.min.toArray(),
1351
- max: childBoundingBox.max.toArray()
1352
- }
1353
- }),
1354
- geometry: ((_child$geometry = child.geometry) === null || _child$geometry === void 0 ? void 0 : _child$geometry.uuid) || 'CONNECTOR-GEO'
1355
- };
1356
- componentSceneData.children.push(childSceneData);
1340
+ userData: _objectSpread2({}, child.userData)
1341
+ });
1357
1342
  }
1358
1343
  });
1359
1344
  }
@@ -1366,7 +1351,6 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1366
1351
  console.log('✅ addComponentToSceneData: Component added to scene data successfully', {
1367
1352
  componentId: componentModel.uuid,
1368
1353
  libraryId: (_componentModel$userD = componentModel.userData) === null || _componentModel$userD === void 0 ? void 0 : _componentModel$userD.libraryId,
1369
- childrenCount: componentSceneData.children.length,
1370
1354
  totalSceneChildren: currentSceneData.scene.children.length
1371
1355
  });
1372
1356
  return true;
@@ -1407,8 +1391,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1407
1391
  if (segment.children && segment.children.length > 0) {
1408
1392
  var childrenToRemove = _toConsumableArray(segment.children);
1409
1393
  childrenToRemove.forEach(function (child) {
1410
- var _child$userData10;
1411
- if ((_child$userData10 = child.userData) !== null && _child$userData10 !== void 0 && _child$userData10.isPipeElbow) {
1394
+ var _child$userData0;
1395
+ if ((_child$userData0 = child.userData) !== null && _child$userData0 !== void 0 && _child$userData0.isPipeElbow) {
1412
1396
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
1413
1397
  segment.remove(child);
1414
1398
  if (child.geometry) child.geometry.dispose();
@@ -26,11 +26,11 @@ function getIoBehaviorManager(sceneViewer) {
26
26
 
27
27
  /**
28
28
  * Resolve tooltip/drag data points for an I/O device attachment.
29
- * Prefers behaviorConfig-driven animation data points; merges legacy ioConfig snapshot.
29
+ * Prefers behaviorConfig-driven animation data points.
30
30
  *
31
31
  * @param {string} parentUuid
32
32
  * @param {string} attachmentId
33
- * @param {Object} userData - io-device userData (may include dataPoints snapshot)
33
+ * @param {Object} userData - io-device userData
34
34
  * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager|null} ioBehaviorManager
35
35
  * @param {THREE.Object3D|null} [hitMesh]
36
36
  * @returns {Object[]}
@@ -38,21 +38,7 @@ function getIoBehaviorManager(sceneViewer) {
38
38
  function resolveDataPoints(parentUuid, attachmentId, userData, ioBehaviorManager) {
39
39
  var hitMesh = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
40
40
  var fromAnimations = (ioBehaviorManager === null || ioBehaviorManager === void 0 ? void 0 : ioBehaviorManager.getAnimationDataPoints(parentUuid, attachmentId, hitMesh)) || [];
41
- if (fromAnimations.length) return fromAnimations;
42
- var legacy = (userData === null || userData === void 0 ? void 0 : userData.dataPoints) || [];
43
- return legacy.map(function (dp) {
44
- var _dp$defaultValue;
45
- return {
46
- id: dp.id || dp.name,
47
- name: dp.name || dp.id,
48
- stateType: dp.stateType || 'binary',
49
- stateConfig: dp.stateConfig || {},
50
- defaultValue: (_dp$defaultValue = dp.defaultValue) !== null && _dp$defaultValue !== void 0 ? _dp$defaultValue : null,
51
- direction: dp.direction || (userData === null || userData === void 0 ? void 0 : userData.ioDirection) || 'output'
52
- };
53
- }).filter(function (dp) {
54
- return dp.id;
55
- });
41
+ return fromAnimations;
56
42
  }
57
43
 
58
44
  /**
@@ -83,35 +69,18 @@ function applyDefaultIoDeviceStates(centralPlant) {
83
69
  if (seen.has(scopedKey)) return;
84
70
  seen.add(scopedKey);
85
71
  var dps = ioBehavMgr.getAnimationDataPoints(objParentUuid, objAttachmentId) || [];
86
- if (dps.length) {
87
- var _iterator = _createForOfIteratorHelper(dps),
88
- _step;
89
- try {
90
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
91
- var dp = _step.value;
92
- if (!(dp !== null && dp !== void 0 && dp.id) || dp.defaultValue === undefined || dp.defaultValue === null) continue;
93
- centralPlant.setIoDeviceState(objAttachmentId, dp.id, dp.defaultValue, objParentUuid);
94
- }
95
- } catch (err) {
96
- _iterator.e(err);
97
- } finally {
98
- _iterator.f();
99
- }
100
- return;
101
- }
102
- var _iterator2 = _createForOfIteratorHelper(obj.userData.dataPoints || []),
103
- _step2;
72
+ var _iterator = _createForOfIteratorHelper(dps),
73
+ _step;
104
74
  try {
105
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
106
- var _dp = _step2.value;
107
- var dpId = _dp.id || _dp.name;
108
- if (!dpId || _dp.defaultValue === undefined || _dp.defaultValue === null) continue;
109
- centralPlant.setIoDeviceState(objAttachmentId, dpId, _dp.defaultValue, objParentUuid);
75
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
76
+ var dp = _step.value;
77
+ if (!(dp !== null && dp !== void 0 && dp.id) || dp.defaultValue === undefined || dp.defaultValue === null) continue;
78
+ centralPlant.setIoDeviceState(objAttachmentId, dp.id, dp.defaultValue, objParentUuid);
110
79
  }
111
80
  } catch (err) {
112
- _iterator2.e(err);
81
+ _iterator.e(err);
113
82
  } finally {
114
- _iterator2.f();
83
+ _iterator.f();
115
84
  }
116
85
  });
117
86
  }