@2112-lab/central-plant 0.3.46 → 0.3.48

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 (39) hide show
  1. package/dist/bundle/index.js +624 -425
  2. package/dist/cjs/src/core/centralPlant.js +46 -46
  3. package/dist/cjs/src/core/centralPlantInternals.js +4 -2
  4. package/dist/cjs/src/core/sceneViewer.js +1 -2
  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/components/componentManager.js +11 -3
  8. package/dist/cjs/src/managers/components/transformOperationsManager.js +9 -11
  9. package/dist/cjs/src/managers/controls/componentDragManager.js +2 -7
  10. package/dist/cjs/src/managers/controls/transformControlsManager.js +83 -44
  11. package/dist/cjs/src/managers/pathfinding/pathfindingManager.js +274 -192
  12. package/dist/cjs/src/managers/scene/collisionManager.js +1 -2
  13. package/dist/cjs/src/managers/scene/componentTooltipManager.js +2 -3
  14. package/dist/cjs/src/managers/scene/modelManager.js +33 -10
  15. package/dist/cjs/src/managers/scene/sceneExportManager.js +42 -12
  16. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +26 -1
  17. package/dist/cjs/src/utils/behaviorDispatch.js +11 -42
  18. package/dist/cjs/src/utils/boundingBoxUtils.js +79 -36
  19. package/dist/cjs/src/utils/ioDeviceUtils.js +3 -9
  20. package/dist/esm/src/core/centralPlant.js +46 -46
  21. package/dist/esm/src/core/centralPlantInternals.js +4 -2
  22. package/dist/esm/src/core/sceneViewer.js +1 -2
  23. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +1 -2
  24. package/dist/esm/src/managers/components/componentDataManager.js +0 -1
  25. package/dist/esm/src/managers/components/componentManager.js +11 -3
  26. package/dist/esm/src/managers/components/transformOperationsManager.js +9 -11
  27. package/dist/esm/src/managers/controls/componentDragManager.js +2 -7
  28. package/dist/esm/src/managers/controls/transformControlsManager.js +83 -44
  29. package/dist/esm/src/managers/pathfinding/pathfindingManager.js +276 -194
  30. package/dist/esm/src/managers/scene/collisionManager.js +1 -2
  31. package/dist/esm/src/managers/scene/componentTooltipManager.js +2 -3
  32. package/dist/esm/src/managers/scene/modelManager.js +33 -10
  33. package/dist/esm/src/managers/scene/sceneExportManager.js +41 -13
  34. package/dist/esm/src/managers/scene/sceneOperationsManager.js +26 -1
  35. package/dist/esm/src/utils/behaviorDispatch.js +11 -42
  36. package/dist/esm/src/utils/boundingBoxUtils.js +80 -38
  37. package/dist/esm/src/utils/ioDeviceUtils.js +3 -9
  38. package/dist/index.d.ts +0 -6
  39. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, objectSpread2 as _objectSpread2, slicedToArray as _slicedToArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, typeof as _typeof } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, objectSpread2 as _objectSpread2, slicedToArray as _slicedToArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, typeof as _typeof, createForOfIteratorHelper as _createForOfIteratorHelper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  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, computeConnectorBoundingBoxes, computeIODeviceBoundingBoxes } 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';
@@ -137,10 +137,11 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
137
137
  }, {
138
138
  key: "_matrixHash",
139
139
  value: function _matrixHash(obj) {
140
+ // Force matrix update to ensure we're looking at the latest data
141
+ obj.updateMatrixWorld(true);
140
142
  var e = obj.matrixWorld.elements;
141
- // Use position (12-14) + first diagonal entries (0,5,10) — covers
142
- // translation AND rotation/scale changes with minimal work.
143
- return "".concat(e[0].toFixed(5), ",").concat(e[5].toFixed(5), ",").concat(e[10].toFixed(5), ",").concat(e[12].toFixed(5), ",").concat(e[13].toFixed(5), ",").concat(e[14].toFixed(5));
143
+ // Just hash the position/rotation/scale part of the matrix (12 elements)
144
+ return "".concat(e[0].toFixed(3), ",").concat(e[1].toFixed(3), ",").concat(e[2].toFixed(3), ",").concat(e[4].toFixed(3), ",").concat(e[5].toFixed(3), ",").concat(e[6].toFixed(3), ",").concat(e[8].toFixed(3), ",").concat(e[9].toFixed(3), ",").concat(e[10].toFixed(3), ",").concat(e[12].toFixed(3), ",").concat(e[13].toFixed(3), ",").concat(e[14].toFixed(3));
144
145
  }
145
146
 
146
147
  /**
@@ -158,11 +159,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
158
159
  * Compute a lightweight fingerprint of the pathfinding inputs so that
159
160
  * consecutive identical runs can be skipped entirely.
160
161
  *
161
- * The fingerprint captures:
162
- * • The connections list (from / to pairs)
163
- * • The world transform of every top-level child in sceneData
164
- *
165
- * @param {Object} sceneData - scene data object with .children
162
+ * @param {Object} sceneData - scene object or manifest
166
163
  * @param {Array} connections - array of {from, to}
167
164
  * @returns {string}
168
165
  * @private
@@ -170,223 +167,241 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
170
167
  }, {
171
168
  key: "_computePathfindingFingerprint",
172
169
  value: function _computePathfindingFingerprint(sceneData, connections) {
170
+ var _this3 = this;
173
171
  var parts = [];
174
172
 
175
173
  // 1. Connections (sorted so order doesn't matter)
176
- parts.push(connections.map(function (c) {
174
+ parts.push((connections || []).map(function (c) {
177
175
  return "".concat(c.from, "->").concat(c.to);
178
176
  }).sort().join(','));
179
177
 
180
- // 2. World transforms of every referenced scene object
181
- if (sceneData.children) {
182
- var _iterator = _createForOfIteratorHelper(sceneData.children),
183
- _step;
184
- try {
185
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
186
- var child = _step.value;
187
- var obj = this.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
188
- if (obj) {
189
- parts.push("".concat(child.uuid, ":").concat(this._matrixHash(obj)));
190
- } else {
191
- var _p$x, _p$y, _p$z;
192
- // Object only in data, not in scene — include its serialised position
193
- var p = child.position || {};
194
- parts.push("".concat(child.uuid, ":").concat(((_p$x = p.x) !== null && _p$x !== void 0 ? _p$x : 0).toFixed(5), ",").concat(((_p$y = p.y) !== null && _p$y !== void 0 ? _p$y : 0).toFixed(5), ",").concat(((_p$z = p.z) !== null && _p$z !== void 0 ? _p$z : 0).toFixed(5)));
195
- }
178
+ // 2. World transforms of every referenced scene object (recursive)
179
+ var _addTransformsRecursive = function addTransformsRecursive(node) {
180
+ if (node.uuid) {
181
+ var obj = _this3.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
182
+ if (obj) {
183
+ parts.push("".concat(node.uuid, ":").concat(_this3._matrixHash(obj)));
184
+ } else {
185
+ var _p$x, _p$y, _p$z;
186
+ // Object only in data, not in scene — include its serialised position
187
+ var p = node.position || {};
188
+ parts.push("".concat(node.uuid, ":").concat(((_p$x = p.x) !== null && _p$x !== void 0 ? _p$x : 0).toFixed(5), ",").concat(((_p$y = p.y) !== null && _p$y !== void 0 ? _p$y : 0).toFixed(5), ",").concat(((_p$z = p.z) !== null && _p$z !== void 0 ? _p$z : 0).toFixed(5)));
196
189
  }
197
- } catch (err) {
198
- _iterator.e(err);
199
- } finally {
200
- _iterator.f();
201
190
  }
191
+
192
+ // Handle both flattened array and nested Three.js JSON structures
193
+ var children = node.children || node.object && node.object.children;
194
+ if (children && Array.isArray(children)) {
195
+ children.forEach(_addTransformsRecursive);
196
+ }
197
+ };
198
+ if (sceneData) {
199
+ _addTransformsRecursive(sceneData);
202
200
  }
203
201
  return parts.join('|');
204
202
  }
205
203
 
206
204
  /**
207
- * Enrich sceneData with worldBoundingBox for segments and components
208
- * Connectors remain with just position information.
209
- *
210
- * Uses _bboxCache to avoid recomputing filtered / io-device bboxes
211
- * when the underlying object's world matrix has not changed.
212
- *
213
- * @param {Object} sceneData - Original scene data
214
- * @returns {Object} Enriched scene data (shallow copy with modifications)
205
+ * Enrich scene data with world-space bounding boxes and positions for collision avoidance
206
+ * @param {Object|Array} sceneData - Original scene data or array of children
207
+ * @returns {Object|Array} Enriched scene data (shallow copy with modifications)
215
208
  * @private
216
209
  */
217
210
  }, {
218
211
  key: "_enrichSceneDataWithBoundingBoxes",
219
212
  value: function _enrichSceneDataWithBoundingBoxes(sceneData) {
220
- var _this3 = this;
221
- // Create a shallow copy of sceneData structure
222
- var enriched = _objectSpread2({}, sceneData);
223
- if (!sceneData.children || !Array.isArray(sceneData.children)) {
224
- console.warn('⚠️ sceneData has no children array');
225
- return enriched;
226
- }
213
+ var _this4 = this;
214
+ if (!sceneData) return sceneData;
215
+
216
+ // Recursive helper to enrich a node and its children
217
+ var _enrichNode = function enrichNode(node) {
218
+ if (!node) return node;
219
+ var enrichedNode = _objectSpread2({}, node);
220
+
221
+ // Skip computed objects ( elbows etc )
222
+ if (node.userData && (node.userData.isComputed === true || node.userData.objectType === 'elbow')) {
223
+ return node;
224
+ }
227
225
 
228
- // Process children to add worldBoundingBox to segments and components
229
- enriched.children = sceneData.children.map(function (child) {
230
- // Enrich segments (check if objectType is 'segment' in userData)
231
- if (child.userData && child.userData.objectType === 'segment') {
232
- // Find the actual segment object in the scene
233
- var segmentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
226
+ // ── Enrich Segments ──
227
+ if (node.userData && node.userData.objectType === 'segment') {
228
+ var segmentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
234
229
  if (segmentObject) {
235
- // ── Cache check ──
236
- var hash = _this3._matrixHash(segmentObject);
237
- var cached = _this3._bboxCache.get(child.uuid);
230
+ var hash = _this4._matrixHash(segmentObject);
231
+ var cached = _this4._bboxCache.get(node.uuid);
238
232
  if (cached && cached.matrixHash === hash && cached.segmentBBox) {
239
- return _objectSpread2(_objectSpread2({}, child), {}, {
240
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
241
- worldBoundingBox: cached.segmentBBox
242
- })
233
+ enrichedNode.userData = _objectSpread2(_objectSpread2({}, node.userData), {}, {
234
+ worldBoundingBox: cached.segmentBBox
243
235
  });
244
- }
245
-
246
- // Compute world bounding box
247
- var worldBBox = new THREE.Box3().setFromObject(segmentObject);
248
- var bboxData = {
249
- min: [worldBBox.min.x, worldBBox.min.y, worldBBox.min.z],
250
- max: [worldBBox.max.x, worldBBox.max.y, worldBBox.max.z]
251
- };
252
-
253
- // Store in cache
254
- _this3._bboxCache.set(child.uuid, {
255
- matrixHash: hash,
256
- segmentBBox: bboxData
257
- });
258
- return _objectSpread2(_objectSpread2({}, child), {}, {
259
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
236
+ } else {
237
+ var worldBBox = new THREE.Box3().setFromObject(segmentObject);
238
+ var bboxData = {
239
+ min: [worldBBox.min.x, worldBBox.min.y, worldBBox.min.z],
240
+ max: [worldBBox.max.x, worldBBox.max.y, worldBBox.max.z]
241
+ };
242
+ _this4._bboxCache.set(node.uuid, {
243
+ matrixHash: hash,
244
+ segmentBBox: bboxData
245
+ });
246
+ enrichedNode.userData = _objectSpread2(_objectSpread2({}, node.userData), {}, {
260
247
  worldBoundingBox: bboxData
261
- })
262
- });
263
- } else {
264
- console.warn("\u26A0\uFE0F Could not find segment object in scene: ".concat(child.uuid));
248
+ });
249
+ }
265
250
  }
266
251
  }
267
252
 
268
- // Enrich components (check if objectType is 'component' in userData)
269
- if (child.userData && child.userData.objectType === 'component') {
270
- // Find the actual component object in the scene
271
- var componentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
253
+ // ── Enrich Components ──
254
+ else if (node.userData && node.userData.objectType === 'component') {
255
+ var componentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
272
256
  if (componentObject) {
273
- // ── Cache check ──
274
- var _hash = _this3._matrixHash(componentObject);
275
- var _cached = _this3._bboxCache.get(child.uuid);
257
+ componentObject.updateMatrixWorld(true);
258
+ var _hash = _this4._matrixHash(componentObject);
259
+ var _cached = _this4._bboxCache.get(node.uuid);
276
260
  if (_cached && _cached.matrixHash === _hash && _cached.filteredBBox) {
277
- // Rebuild enriched child from cached data
278
- var _enrichedChild = _objectSpread2(_objectSpread2({}, child), {}, {
279
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
280
- worldBoundingBox: _cached.filteredBBox
281
- })
261
+ enrichedNode.position = {
262
+ x: componentObject.position.x,
263
+ y: componentObject.position.y,
264
+ z: componentObject.position.z
265
+ };
266
+ enrichedNode.rotation = {
267
+ x: THREE.MathUtils.radToDeg(componentObject.rotation.x),
268
+ y: THREE.MathUtils.radToDeg(componentObject.rotation.y),
269
+ z: THREE.MathUtils.radToDeg(componentObject.rotation.z)
270
+ };
271
+ enrichedNode.userData = _objectSpread2(_objectSpread2({}, node.userData), {}, {
272
+ worldBoundingBox: _cached.filteredBBox,
273
+ position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
282
274
  });
283
- if (_cached.ioDeviceBBoxes && _cached.ioDeviceBBoxes.length > 0) {
284
- if (!_enrichedChild.children) _enrichedChild.children = [];
285
- _cached.ioDeviceBBoxes.forEach(function (deviceBBox) {
286
- var existingIndex = _enrichedChild.children.findIndex(function (c) {
287
- return c.uuid === deviceBBox.uuid;
288
- });
289
- if (existingIndex >= 0) {
290
- _enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
291
- userData: _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
292
- objectType: 'io-device',
293
- worldBoundingBox: deviceBBox.worldBoundingBox
294
- })
295
- });
296
- } else {
297
- _enrichedChild.children.push({
298
- uuid: deviceBBox.uuid,
299
- userData: _objectSpread2(_objectSpread2({}, deviceBBox.userData), {}, {
300
- worldBoundingBox: deviceBBox.worldBoundingBox
301
- }),
302
- children: []
303
- });
304
- }
275
+
276
+ // Re-merge cached connectors and devices if they exist
277
+ if (!enrichedNode.children) enrichedNode.children = [];
278
+ if (_cached.connectorBBoxes) {
279
+ _cached.connectorBBoxes.forEach(function (conn) {
280
+ return _this4._mergeEnrichedChild(enrichedNode.children, conn);
281
+ });
282
+ }
283
+ if (_cached.ioDeviceBBoxes) {
284
+ _cached.ioDeviceBBoxes.forEach(function (dev) {
285
+ return _this4._mergeEnrichedChild(enrichedNode.children, dev);
305
286
  });
306
287
  }
307
- return _enrichedChild;
288
+ } else {
289
+ var filteredBBox = computeFilteredBoundingBox(componentObject, ['io-device', 'connector']);
290
+ var _bboxData = {
291
+ min: [filteredBBox.min.x, filteredBBox.min.y, filteredBBox.min.z],
292
+ max: [filteredBBox.max.x, filteredBBox.max.y, filteredBBox.max.z]
293
+ };
294
+ enrichedNode.position = {
295
+ x: componentObject.position.x,
296
+ y: componentObject.position.y,
297
+ z: componentObject.position.z
298
+ };
299
+ enrichedNode.rotation = {
300
+ x: THREE.MathUtils.radToDeg(componentObject.rotation.x),
301
+ y: THREE.MathUtils.radToDeg(componentObject.rotation.y),
302
+ z: THREE.MathUtils.radToDeg(componentObject.rotation.z)
303
+ };
304
+ enrichedNode.userData = _objectSpread2(_objectSpread2({}, node.userData), {}, {
305
+ worldBoundingBox: _bboxData,
306
+ position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
307
+ });
308
+ var connectorBBoxes = computeConnectorBoundingBoxes(componentObject);
309
+ var ioDeviceBBoxes = computeIODeviceBoundingBoxes(componentObject);
310
+ if (!enrichedNode.children) enrichedNode.children = [];
311
+ connectorBBoxes.forEach(function (conn) {
312
+ return _this4._mergeEnrichedChild(enrichedNode.children, conn);
313
+ });
314
+ ioDeviceBBoxes.forEach(function (dev) {
315
+ return _this4._mergeEnrichedChild(enrichedNode.children, dev);
316
+ });
317
+ _this4._bboxCache.set(node.uuid, {
318
+ matrixHash: _hash,
319
+ filteredBBox: _bboxData,
320
+ ioDeviceBBoxes: ioDeviceBBoxes,
321
+ connectorBBoxes: connectorBBoxes
322
+ });
308
323
  }
324
+ }
325
+ }
309
326
 
310
- // Compute FILTERED bounding box excludes io-device and connector subtrees
311
- // so the component body bbox is tight-fitting and doesn't envelop attached devices
312
- var filteredBBox = computeFilteredBoundingBox(componentObject, ['io-device', 'connector']);
313
- console.log("\uD83D\uDD04 Updated worldBoundingBox for component ".concat(child.uuid, " (filtered): min=[").concat(filteredBBox.min.x.toFixed(2), ", ").concat(filteredBBox.min.y.toFixed(2), ", ").concat(filteredBBox.min.z.toFixed(2), "], max=[").concat(filteredBBox.max.x.toFixed(2), ", ").concat(filteredBBox.max.y.toFixed(2), ", ").concat(filteredBBox.max.z.toFixed(2), "]"));
314
- var _bboxData = {
315
- min: [filteredBBox.min.x, filteredBBox.min.y, filteredBBox.min.z],
316
- max: [filteredBBox.max.x, filteredBBox.max.y, filteredBBox.max.z]
327
+ // ── Enrich Standalone Objects (Gateways/Connectors) ──
328
+ else if (node.userData && (node.userData.objectType === 'gateway' || node.userData.objectType === 'connector')) {
329
+ var _node$userData;
330
+ var object = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid) || _this4.sceneViewer.scene.getObjectByProperty('uuid', (_node$userData = node.userData) === null || _node$userData === void 0 ? void 0 : _node$userData.originalUuid);
331
+ if (object) {
332
+ var worldPos = new THREE.Vector3();
333
+ object.getWorldPosition(worldPos);
334
+ enrichedNode.position = {
335
+ x: worldPos.x,
336
+ y: worldPos.y,
337
+ z: worldPos.z
317
338
  };
318
-
319
- // Build the enriched component entry
320
- var enrichedChild = _objectSpread2(_objectSpread2({}, child), {}, {
321
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
322
- worldBoundingBox: _bboxData
323
- })
339
+ enrichedNode.userData = _objectSpread2(_objectSpread2({}, node.userData), {}, {
340
+ position: [worldPos.x, worldPos.y, worldPos.z]
324
341
  });
325
342
 
326
- // Compute separate bounding boxes for each attached io-device
327
- var ioDeviceBBoxes = computeIODeviceBoundingBoxes(componentObject);
328
- if (ioDeviceBBoxes.length > 0) {
329
- // Ensure children array exists (may already contain connectors)
330
- if (!enrichedChild.children) {
331
- enrichedChild.children = [];
332
- }
333
-
334
- // Inject io-device entries with their own worldBoundingBox
335
- ioDeviceBBoxes.forEach(function (deviceBBox) {
336
- var existingIndex = enrichedChild.children.findIndex(function (c) {
337
- return c.uuid === deviceBBox.uuid;
338
- });
339
- if (existingIndex >= 0) {
340
- enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
341
- userData: _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
342
- objectType: 'io-device',
343
- worldBoundingBox: deviceBBox.worldBoundingBox
344
- })
345
- });
346
- } else {
347
- enrichedChild.children.push({
348
- uuid: deviceBBox.uuid,
349
- userData: _objectSpread2(_objectSpread2({}, deviceBBox.userData), {}, {
350
- worldBoundingBox: deviceBBox.worldBoundingBox
351
- }),
352
- children: []
353
- });
354
- }
355
- console.log("\uD83D\uDCE6 Injected io-device bbox for ".concat(deviceBBox.uuid, ": min=[").concat(deviceBBox.worldBoundingBox.min.map(function (v) {
356
- return v.toFixed(2);
357
- }).join(', '), "], max=[").concat(deviceBBox.worldBoundingBox.max.map(function (v) {
358
- return v.toFixed(2);
359
- }).join(', '), "]"));
360
- });
361
- console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
343
+ // If it's a connector, also sync direction
344
+ if (node.userData.objectType === 'connector') {
345
+ var worldDir = new THREE.Vector3(0, 0, 1);
346
+ if (node.userData.direction) worldDir.set(node.userData.direction[0], node.userData.direction[1], node.userData.direction[2]);
347
+ worldDir.applyQuaternion(object.getWorldQuaternion(new THREE.Quaternion())).normalize();
348
+ enrichedNode.userData.direction = [worldDir.x, worldDir.y, worldDir.z];
362
349
  }
363
-
364
- // Store in cache
365
- _this3._bboxCache.set(child.uuid, {
366
- matrixHash: _hash,
367
- filteredBBox: _bboxData,
368
- ioDeviceBBoxes: ioDeviceBBoxes
369
- });
370
- return enrichedChild;
371
- } else {
372
- console.warn("\u26A0\uFE0F Could not find component object in scene: ".concat(child.uuid));
373
350
  }
374
351
  }
375
352
 
376
- // For non-segments and non-components (including connectors), return as-is
377
- return child;
378
- });
379
- return enriched;
353
+ // Recurse into children
354
+ if (node.children && Array.isArray(node.children)) {
355
+ enrichedNode.children = node.children.map(_enrichNode);
356
+ }
357
+ return enrichedNode;
358
+ };
359
+
360
+ // Handle root being an array or object
361
+ if (Array.isArray(sceneData)) {
362
+ return sceneData.map(_enrichNode);
363
+ } else if (sceneData.children && Array.isArray(sceneData.children)) {
364
+ var enrichedRoot = _objectSpread2({}, sceneData);
365
+ enrichedRoot.children = sceneData.children.map(_enrichNode);
366
+ return enrichedRoot;
367
+ } else {
368
+ return _enrichNode(sceneData);
369
+ }
380
370
  }
381
371
 
382
372
  /**
383
- * Core pathfinding logic shared across initialization and updates
373
+ * Helper to merge an enriched child (connector/device) into a children array
374
+ * @private
384
375
  */
376
+ }, {
377
+ key: "_mergeEnrichedChild",
378
+ value: function _mergeEnrichedChild(childrenArray, enrichedData) {
379
+ var existingIndex = childrenArray.findIndex(function (c) {
380
+ return c.uuid === enrichedData.uuid;
381
+ });
382
+ var nodeToMerge = {
383
+ uuid: enrichedData.uuid,
384
+ position: {
385
+ x: enrichedData.userData.position[0],
386
+ y: enrichedData.userData.position[1],
387
+ z: enrichedData.userData.position[2]
388
+ },
389
+ userData: _objectSpread2(_objectSpread2({}, enrichedData.userData), {}, {
390
+ worldBoundingBox: enrichedData.worldBoundingBox
391
+ }),
392
+ children: []
393
+ };
394
+ if (existingIndex >= 0) {
395
+ childrenArray[existingIndex] = _objectSpread2(_objectSpread2({}, childrenArray[existingIndex]), nodeToMerge);
396
+ } else {
397
+ childrenArray.push(nodeToMerge);
398
+ }
399
+ }
385
400
  }, {
386
401
  key: "_executePathfinding",
387
- value: (function () {
402
+ value: function () {
388
403
  var _executePathfinding2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(sceneData, connections) {
389
- var _this4 = this,
404
+ var _this5 = this,
390
405
  _sceneDataCopy$childr,
391
406
  _sceneDataCopy$childr2,
392
407
  _pathfindingResult$pa,
@@ -398,6 +413,12 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
398
413
  totalStart,
399
414
  inputGenStart,
400
415
  enrichedSceneData,
416
+ _from$userData,
417
+ _to$userData,
418
+ firstConn,
419
+ findEndpoint,
420
+ from,
421
+ to,
401
422
  connectionsCopy,
402
423
  sceneDataCopy,
403
424
  algoStart,
@@ -414,6 +435,10 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
414
435
  while (1) switch (_context.n) {
415
436
  case 0:
416
437
  options = _args.length > 2 && _args[2] !== undefined ? _args[2] : {};
438
+ // Fallback to sceneData connections if not explicitly provided (e.g. during transform updates)
439
+ if (!connections && sceneData && sceneData.connections) {
440
+ connections = sceneData.connections;
441
+ }
417
442
  options.createGateways, _options$context = options.context, context = _options$context === void 0 ? 'Pathfinding' : _options$context;
418
443
  timers = {};
419
444
  totalStart = performance.now(); // Create pathfinder instance with configuration only
@@ -442,7 +467,50 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
442
467
  console.log("\uD83D\uDD04 Updated matrix world for ".concat(context, " pathfinding execution"));
443
468
 
444
469
  // Enrich sceneData with worldBoundingBox for segments before deep copy
445
- enrichedSceneData = this._enrichSceneDataWithBoundingBoxes(sceneData); // Deep copy connections and sceneData to prevent pathfinder from mutating the original
470
+ enrichedSceneData = this._enrichSceneDataWithBoundingBoxes(sceneData); // DEBUG: Log first connection endpoints in enriched data
471
+ if (connections && connections.length > 0 && enrichedSceneData.children) {
472
+ firstConn = connections[0];
473
+ findEndpoint = function findEndpoint(uuid) {
474
+ var _iterator = _createForOfIteratorHelper(enrichedSceneData.children),
475
+ _step;
476
+ try {
477
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
478
+ var child = _step.value;
479
+ if (child.uuid === uuid) return child;
480
+ if (child.children) {
481
+ var _iterator2 = _createForOfIteratorHelper(child.children),
482
+ _step2;
483
+ try {
484
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
485
+ var nested = _step2.value;
486
+ if (nested.uuid === uuid) return nested;
487
+ }
488
+ } catch (err) {
489
+ _iterator2.e(err);
490
+ } finally {
491
+ _iterator2.f();
492
+ }
493
+ }
494
+ }
495
+ } catch (err) {
496
+ _iterator.e(err);
497
+ } finally {
498
+ _iterator.f();
499
+ }
500
+ return null;
501
+ };
502
+ from = findEndpoint(firstConn.from);
503
+ to = findEndpoint(firstConn.to);
504
+ console.log("\uD83D\uDCE1 [DEBUG] Pathfinding from ".concat(firstConn.from, " to ").concat(firstConn.to));
505
+ if (from) console.log(" From BBox: min=[".concat((_from$userData = from.userData) === null || _from$userData === void 0 || (_from$userData = _from$userData.worldBoundingBox) === null || _from$userData === void 0 || (_from$userData = _from$userData.min) === null || _from$userData === void 0 ? void 0 : _from$userData.map(function (v) {
506
+ return v.toFixed(2);
507
+ }), "]"));
508
+ if (to) console.log(" To BBox: min=[".concat((_to$userData = to.userData) === null || _to$userData === void 0 || (_to$userData = _to$userData.worldBoundingBox) === null || _to$userData === void 0 || (_to$userData = _to$userData.min) === null || _to$userData === void 0 ? void 0 : _to$userData.map(function (v) {
509
+ return v.toFixed(2);
510
+ }), "]"));
511
+ }
512
+
513
+ // Deep copy connections and sceneData to prevent pathfinder from mutating the original
446
514
  connectionsCopy = structuredClone(connections);
447
515
  sceneDataCopy = structuredClone(enrichedSceneData);
448
516
  timers.inputGeneration = performance.now() - inputGenStart;
@@ -473,7 +541,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
473
541
  if (pathfindingResult.paths) {
474
542
  pathfindingResult.paths.forEach(function (path) {
475
543
  var pathId = "".concat(path.from, "-->").concat(path.to);
476
- _this4._getOrCreatePathData(pathId, path.from, path.to);
544
+ _this5._getOrCreatePathData(pathId, path.from, path.to);
477
545
  });
478
546
  }
479
547
  timers.pathRendering = performance.now() - renderStart;
@@ -506,7 +574,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
506
574
  return _executePathfinding2.apply(this, arguments);
507
575
  }
508
576
  return _executePathfinding;
509
- }())
577
+ }()
510
578
  }, {
511
579
  key: "getSimplifiedSceneData",
512
580
  value: function getSimplifiedSceneData() {
@@ -545,7 +613,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
545
613
  }, {
546
614
  key: "_propagateFlowAttributesAndApplyVisualizations",
547
615
  value: function _propagateFlowAttributesAndApplyVisualizations(connections, pathfindingResult) {
548
- var _this5 = this,
616
+ var _this6 = this,
549
617
  _this$sceneViewer;
550
618
  if (!Array.isArray(connections) || !pathfindingResult) return;
551
619
 
@@ -573,7 +641,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
573
641
  var subPathIds = new Set(originalToSubPaths.get(origId) || []);
574
642
  subPathIds.add(origId);
575
643
  subPathIds.forEach(function (subPathId) {
576
- var pd = _this5.pathDataStore.get(subPathId);
644
+ var pd = _this6.pathDataStore.get(subPathId);
577
645
  if (pd && Object.keys(pd.flowAttributes).length === 0) {
578
646
  Object.entries(conn.flowAttributes).forEach(function (_ref2) {
579
647
  var _ref3 = _slicedToArray(_ref2, 2),
@@ -637,7 +705,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
637
705
  key: "initializePathfinder",
638
706
  value: (function () {
639
707
  var _initializePathfinder = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(data, crosscubeTextureSet) {
640
- var _this6 = this;
708
+ var _this7 = this;
641
709
  var pathfindingResult;
642
710
  return _regenerator().w(function (_context2) {
643
711
  while (1) switch (_context2.n) {
@@ -651,7 +719,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
651
719
  data.connections.forEach(function (conn) {
652
720
  if (conn.flowAttributes && _typeof(conn.flowAttributes) === 'object') {
653
721
  var pathId = "".concat(conn.from, "-->").concat(conn.to);
654
- var pd = _this6._getOrCreatePathData(pathId, conn.from, conn.to);
722
+ var pd = _this7._getOrCreatePathData(pathId, conn.from, conn.to);
655
723
  Object.entries(conn.flowAttributes).forEach(function (_ref6) {
656
724
  var _ref7 = _slicedToArray(_ref6, 2),
657
725
  key = _ref7[0],
@@ -769,33 +837,47 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
769
837
  key: "updatePathfindingAfterTransform",
770
838
  value: (function () {
771
839
  var _updatePathfindingAfterTransform = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(currentSceneData) {
772
- var fingerprint, result;
840
+ var _currentSceneData$sce;
841
+ var fingerprint, dataRoot, connections, result;
773
842
  return _regenerator().w(function (_context3) {
774
843
  while (1) switch (_context3.n) {
775
844
  case 0:
845
+ if (!(!currentSceneData || !currentSceneData.scene)) {
846
+ _context3.n = 1;
847
+ break;
848
+ }
849
+ console.warn('⚠️ updatePathfindingAfterTransform: Invalid scene data');
850
+ return _context3.a(2, null);
851
+ case 1:
776
852
  // ── Skip-unchanged-paths check ──────────────────────────────────
777
853
  // Ensure matrices are fresh so the fingerprint is accurate.
778
854
  this.sceneViewer.scene.updateMatrixWorld(true);
779
855
  fingerprint = this._computePathfindingFingerprint(currentSceneData.scene, currentSceneData.connections);
856
+ console.log("\uD83D\uDD0D Pathfinding update check. Fingerprint: ".concat(fingerprint.substring(0, 50), "..."));
780
857
  if (!(fingerprint === this._lastPathfindingFingerprint && this._lastPathfindingResult)) {
781
- _context3.n = 1;
858
+ _context3.n = 2;
782
859
  break;
783
860
  }
784
861
  console.log('⏭️ Pathfinding skipped — inputs unchanged since last run');
785
862
  return _context3.a(2, this._lastPathfindingResult);
786
- case 1:
863
+ case 2:
864
+ // Clear the cache for the specific fingerprint mismatch to ensure fresh enrichment
865
+ this.invalidateBBoxCache();
866
+
787
867
  // Remove all existing paths from the scene
788
868
  this.removeComputedObjects();
789
869
  console.log('🔄 Starting pathfinding with updated scene data...');
790
870
 
791
- // Note: Matrix updates and bounding box recomputation now handled in _executePathfinding
871
+ // Robust root discovery for different manifest structures
872
+ dataRoot = ((_currentSceneData$sce = currentSceneData.scene) === null || _currentSceneData$sce === void 0 ? void 0 : _currentSceneData$sce.object) || currentSceneData.scene || currentSceneData;
873
+ connections = currentSceneData.connections || []; // Note: Matrix updates and bounding box recomputation now handled in _executePathfinding
792
874
  // Use shared pathfinding logic (no gateways needed for transform updates)
793
- _context3.n = 2;
794
- return this._executePathfinding(currentSceneData.scene, currentSceneData.connections, {
875
+ _context3.n = 3;
876
+ return this._executePathfinding(dataRoot, connections, {
795
877
  createGateways: true,
796
878
  context: 'Transform Update'
797
879
  });
798
- case 2:
880
+ case 3:
799
881
  result = _context3.v;
800
882
  // Re-apply flow attribute colors (new materials are created on each execution)
801
883
  this._propagateFlowAttributesAndApplyVisualizations(currentSceneData.connections, result);