@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
@@ -161,10 +161,11 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
161
161
  }, {
162
162
  key: "_matrixHash",
163
163
  value: function _matrixHash(obj) {
164
+ // Force matrix update to ensure we're looking at the latest data
165
+ obj.updateMatrixWorld(true);
164
166
  var e = obj.matrixWorld.elements;
165
- // Use position (12-14) + first diagonal entries (0,5,10) — covers
166
- // translation AND rotation/scale changes with minimal work.
167
- 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));
167
+ // Just hash the position/rotation/scale part of the matrix (12 elements)
168
+ 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));
168
169
  }
169
170
 
170
171
  /**
@@ -182,11 +183,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
182
183
  * Compute a lightweight fingerprint of the pathfinding inputs so that
183
184
  * consecutive identical runs can be skipped entirely.
184
185
  *
185
- * The fingerprint captures:
186
- * • The connections list (from / to pairs)
187
- * • The world transform of every top-level child in sceneData
188
- *
189
- * @param {Object} sceneData - scene data object with .children
186
+ * @param {Object} sceneData - scene object or manifest
190
187
  * @param {Array} connections - array of {from, to}
191
188
  * @returns {string}
192
189
  * @private
@@ -194,223 +191,241 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
194
191
  }, {
195
192
  key: "_computePathfindingFingerprint",
196
193
  value: function _computePathfindingFingerprint(sceneData, connections) {
194
+ var _this3 = this;
197
195
  var parts = [];
198
196
 
199
197
  // 1. Connections (sorted so order doesn't matter)
200
- parts.push(connections.map(function (c) {
198
+ parts.push((connections || []).map(function (c) {
201
199
  return "".concat(c.from, "->").concat(c.to);
202
200
  }).sort().join(','));
203
201
 
204
- // 2. World transforms of every referenced scene object
205
- if (sceneData.children) {
206
- var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(sceneData.children),
207
- _step;
208
- try {
209
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
210
- var child = _step.value;
211
- var obj = this.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
212
- if (obj) {
213
- parts.push("".concat(child.uuid, ":").concat(this._matrixHash(obj)));
214
- } else {
215
- var _p$x, _p$y, _p$z;
216
- // Object only in data, not in scene — include its serialised position
217
- var p = child.position || {};
218
- 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)));
219
- }
202
+ // 2. World transforms of every referenced scene object (recursive)
203
+ var _addTransformsRecursive = function addTransformsRecursive(node) {
204
+ if (node.uuid) {
205
+ var obj = _this3.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
206
+ if (obj) {
207
+ parts.push("".concat(node.uuid, ":").concat(_this3._matrixHash(obj)));
208
+ } else {
209
+ var _p$x, _p$y, _p$z;
210
+ // Object only in data, not in scene — include its serialised position
211
+ var p = node.position || {};
212
+ 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)));
220
213
  }
221
- } catch (err) {
222
- _iterator.e(err);
223
- } finally {
224
- _iterator.f();
225
214
  }
215
+
216
+ // Handle both flattened array and nested Three.js JSON structures
217
+ var children = node.children || node.object && node.object.children;
218
+ if (children && Array.isArray(children)) {
219
+ children.forEach(_addTransformsRecursive);
220
+ }
221
+ };
222
+ if (sceneData) {
223
+ _addTransformsRecursive(sceneData);
226
224
  }
227
225
  return parts.join('|');
228
226
  }
229
227
 
230
228
  /**
231
- * Enrich sceneData with worldBoundingBox for segments and components
232
- * Connectors remain with just position information.
233
- *
234
- * Uses _bboxCache to avoid recomputing filtered / io-device bboxes
235
- * when the underlying object's world matrix has not changed.
236
- *
237
- * @param {Object} sceneData - Original scene data
238
- * @returns {Object} Enriched scene data (shallow copy with modifications)
229
+ * Enrich scene data with world-space bounding boxes and positions for collision avoidance
230
+ * @param {Object|Array} sceneData - Original scene data or array of children
231
+ * @returns {Object|Array} Enriched scene data (shallow copy with modifications)
239
232
  * @private
240
233
  */
241
234
  }, {
242
235
  key: "_enrichSceneDataWithBoundingBoxes",
243
236
  value: function _enrichSceneDataWithBoundingBoxes(sceneData) {
244
- var _this3 = this;
245
- // Create a shallow copy of sceneData structure
246
- var enriched = _rollupPluginBabelHelpers.objectSpread2({}, sceneData);
247
- if (!sceneData.children || !Array.isArray(sceneData.children)) {
248
- console.warn('⚠️ sceneData has no children array');
249
- return enriched;
250
- }
237
+ var _this4 = this;
238
+ if (!sceneData) return sceneData;
239
+
240
+ // Recursive helper to enrich a node and its children
241
+ var _enrichNode = function enrichNode(node) {
242
+ if (!node) return node;
243
+ var enrichedNode = _rollupPluginBabelHelpers.objectSpread2({}, node);
244
+
245
+ // Skip computed objects ( elbows etc )
246
+ if (node.userData && (node.userData.isComputed === true || node.userData.objectType === 'elbow')) {
247
+ return node;
248
+ }
251
249
 
252
- // Process children to add worldBoundingBox to segments and components
253
- enriched.children = sceneData.children.map(function (child) {
254
- // Enrich segments (check if objectType is 'segment' in userData)
255
- if (child.userData && child.userData.objectType === 'segment') {
256
- // Find the actual segment object in the scene
257
- var segmentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
250
+ // ── Enrich Segments ──
251
+ if (node.userData && node.userData.objectType === 'segment') {
252
+ var segmentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
258
253
  if (segmentObject) {
259
- // ── Cache check ──
260
- var hash = _this3._matrixHash(segmentObject);
261
- var cached = _this3._bboxCache.get(child.uuid);
254
+ var hash = _this4._matrixHash(segmentObject);
255
+ var cached = _this4._bboxCache.get(node.uuid);
262
256
  if (cached && cached.matrixHash === hash && cached.segmentBBox) {
263
- return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
264
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
265
- worldBoundingBox: cached.segmentBBox
266
- })
257
+ enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
258
+ worldBoundingBox: cached.segmentBBox
267
259
  });
268
- }
269
-
270
- // Compute world bounding box
271
- var worldBBox = new THREE__namespace.Box3().setFromObject(segmentObject);
272
- var bboxData = {
273
- min: [worldBBox.min.x, worldBBox.min.y, worldBBox.min.z],
274
- max: [worldBBox.max.x, worldBBox.max.y, worldBBox.max.z]
275
- };
276
-
277
- // Store in cache
278
- _this3._bboxCache.set(child.uuid, {
279
- matrixHash: hash,
280
- segmentBBox: bboxData
281
- });
282
- return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
283
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
260
+ } else {
261
+ var worldBBox = new THREE__namespace.Box3().setFromObject(segmentObject);
262
+ var bboxData = {
263
+ min: [worldBBox.min.x, worldBBox.min.y, worldBBox.min.z],
264
+ max: [worldBBox.max.x, worldBBox.max.y, worldBBox.max.z]
265
+ };
266
+ _this4._bboxCache.set(node.uuid, {
267
+ matrixHash: hash,
268
+ segmentBBox: bboxData
269
+ });
270
+ enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
284
271
  worldBoundingBox: bboxData
285
- })
286
- });
287
- } else {
288
- console.warn("\u26A0\uFE0F Could not find segment object in scene: ".concat(child.uuid));
272
+ });
273
+ }
289
274
  }
290
275
  }
291
276
 
292
- // Enrich components (check if objectType is 'component' in userData)
293
- if (child.userData && child.userData.objectType === 'component') {
294
- // Find the actual component object in the scene
295
- var componentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
277
+ // ── Enrich Components ──
278
+ else if (node.userData && node.userData.objectType === 'component') {
279
+ var componentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
296
280
  if (componentObject) {
297
- // ── Cache check ──
298
- var _hash = _this3._matrixHash(componentObject);
299
- var _cached = _this3._bboxCache.get(child.uuid);
281
+ componentObject.updateMatrixWorld(true);
282
+ var _hash = _this4._matrixHash(componentObject);
283
+ var _cached = _this4._bboxCache.get(node.uuid);
300
284
  if (_cached && _cached.matrixHash === _hash && _cached.filteredBBox) {
301
- // Rebuild enriched child from cached data
302
- var _enrichedChild = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
303
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
304
- worldBoundingBox: _cached.filteredBBox
305
- })
285
+ enrichedNode.position = {
286
+ x: componentObject.position.x,
287
+ y: componentObject.position.y,
288
+ z: componentObject.position.z
289
+ };
290
+ enrichedNode.rotation = {
291
+ x: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.x),
292
+ y: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.y),
293
+ z: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.z)
294
+ };
295
+ enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
296
+ worldBoundingBox: _cached.filteredBBox,
297
+ position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
306
298
  });
307
- if (_cached.ioDeviceBBoxes && _cached.ioDeviceBBoxes.length > 0) {
308
- if (!_enrichedChild.children) _enrichedChild.children = [];
309
- _cached.ioDeviceBBoxes.forEach(function (deviceBBox) {
310
- var existingIndex = _enrichedChild.children.findIndex(function (c) {
311
- return c.uuid === deviceBBox.uuid;
312
- });
313
- if (existingIndex >= 0) {
314
- _enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
315
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
316
- objectType: 'io-device',
317
- worldBoundingBox: deviceBBox.worldBoundingBox
318
- })
319
- });
320
- } else {
321
- _enrichedChild.children.push({
322
- uuid: deviceBBox.uuid,
323
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
324
- worldBoundingBox: deviceBBox.worldBoundingBox
325
- }),
326
- children: []
327
- });
328
- }
299
+
300
+ // Re-merge cached connectors and devices if they exist
301
+ if (!enrichedNode.children) enrichedNode.children = [];
302
+ if (_cached.connectorBBoxes) {
303
+ _cached.connectorBBoxes.forEach(function (conn) {
304
+ return _this4._mergeEnrichedChild(enrichedNode.children, conn);
305
+ });
306
+ }
307
+ if (_cached.ioDeviceBBoxes) {
308
+ _cached.ioDeviceBBoxes.forEach(function (dev) {
309
+ return _this4._mergeEnrichedChild(enrichedNode.children, dev);
329
310
  });
330
311
  }
331
- return _enrichedChild;
312
+ } else {
313
+ var filteredBBox = boundingBoxUtils.computeFilteredBoundingBox(componentObject, ['io-device', 'connector']);
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]
317
+ };
318
+ enrichedNode.position = {
319
+ x: componentObject.position.x,
320
+ y: componentObject.position.y,
321
+ z: componentObject.position.z
322
+ };
323
+ enrichedNode.rotation = {
324
+ x: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.x),
325
+ y: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.y),
326
+ z: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.z)
327
+ };
328
+ enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
329
+ worldBoundingBox: _bboxData,
330
+ position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
331
+ });
332
+ var connectorBBoxes = boundingBoxUtils.computeConnectorBoundingBoxes(componentObject);
333
+ var ioDeviceBBoxes = boundingBoxUtils.computeIODeviceBoundingBoxes(componentObject);
334
+ if (!enrichedNode.children) enrichedNode.children = [];
335
+ connectorBBoxes.forEach(function (conn) {
336
+ return _this4._mergeEnrichedChild(enrichedNode.children, conn);
337
+ });
338
+ ioDeviceBBoxes.forEach(function (dev) {
339
+ return _this4._mergeEnrichedChild(enrichedNode.children, dev);
340
+ });
341
+ _this4._bboxCache.set(node.uuid, {
342
+ matrixHash: _hash,
343
+ filteredBBox: _bboxData,
344
+ ioDeviceBBoxes: ioDeviceBBoxes,
345
+ connectorBBoxes: connectorBBoxes
346
+ });
332
347
  }
348
+ }
349
+ }
333
350
 
334
- // Compute FILTERED bounding box excludes io-device and connector subtrees
335
- // so the component body bbox is tight-fitting and doesn't envelop attached devices
336
- var filteredBBox = boundingBoxUtils.computeFilteredBoundingBox(componentObject, ['io-device', 'connector']);
337
- 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), "]"));
338
- var _bboxData = {
339
- min: [filteredBBox.min.x, filteredBBox.min.y, filteredBBox.min.z],
340
- max: [filteredBBox.max.x, filteredBBox.max.y, filteredBBox.max.z]
351
+ // ── Enrich Standalone Objects (Gateways/Connectors) ──
352
+ else if (node.userData && (node.userData.objectType === 'gateway' || node.userData.objectType === 'connector')) {
353
+ var _node$userData;
354
+ 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);
355
+ if (object) {
356
+ var worldPos = new THREE__namespace.Vector3();
357
+ object.getWorldPosition(worldPos);
358
+ enrichedNode.position = {
359
+ x: worldPos.x,
360
+ y: worldPos.y,
361
+ z: worldPos.z
341
362
  };
342
-
343
- // Build the enriched component entry
344
- var enrichedChild = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
345
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
346
- worldBoundingBox: _bboxData
347
- })
363
+ enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
364
+ position: [worldPos.x, worldPos.y, worldPos.z]
348
365
  });
349
366
 
350
- // Compute separate bounding boxes for each attached io-device
351
- var ioDeviceBBoxes = boundingBoxUtils.computeIODeviceBoundingBoxes(componentObject);
352
- if (ioDeviceBBoxes.length > 0) {
353
- // Ensure children array exists (may already contain connectors)
354
- if (!enrichedChild.children) {
355
- enrichedChild.children = [];
356
- }
357
-
358
- // Inject io-device entries with their own worldBoundingBox
359
- ioDeviceBBoxes.forEach(function (deviceBBox) {
360
- var existingIndex = enrichedChild.children.findIndex(function (c) {
361
- return c.uuid === deviceBBox.uuid;
362
- });
363
- if (existingIndex >= 0) {
364
- enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
365
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
366
- objectType: 'io-device',
367
- worldBoundingBox: deviceBBox.worldBoundingBox
368
- })
369
- });
370
- } else {
371
- enrichedChild.children.push({
372
- uuid: deviceBBox.uuid,
373
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
374
- worldBoundingBox: deviceBBox.worldBoundingBox
375
- }),
376
- children: []
377
- });
378
- }
379
- console.log("\uD83D\uDCE6 Injected io-device bbox for ".concat(deviceBBox.uuid, ": min=[").concat(deviceBBox.worldBoundingBox.min.map(function (v) {
380
- return v.toFixed(2);
381
- }).join(', '), "], max=[").concat(deviceBBox.worldBoundingBox.max.map(function (v) {
382
- return v.toFixed(2);
383
- }).join(', '), "]"));
384
- });
385
- console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
367
+ // If it's a connector, also sync direction
368
+ if (node.userData.objectType === 'connector') {
369
+ var worldDir = new THREE__namespace.Vector3(0, 0, 1);
370
+ if (node.userData.direction) worldDir.set(node.userData.direction[0], node.userData.direction[1], node.userData.direction[2]);
371
+ worldDir.applyQuaternion(object.getWorldQuaternion(new THREE__namespace.Quaternion())).normalize();
372
+ enrichedNode.userData.direction = [worldDir.x, worldDir.y, worldDir.z];
386
373
  }
387
-
388
- // Store in cache
389
- _this3._bboxCache.set(child.uuid, {
390
- matrixHash: _hash,
391
- filteredBBox: _bboxData,
392
- ioDeviceBBoxes: ioDeviceBBoxes
393
- });
394
- return enrichedChild;
395
- } else {
396
- console.warn("\u26A0\uFE0F Could not find component object in scene: ".concat(child.uuid));
397
374
  }
398
375
  }
399
376
 
400
- // For non-segments and non-components (including connectors), return as-is
401
- return child;
402
- });
403
- return enriched;
377
+ // Recurse into children
378
+ if (node.children && Array.isArray(node.children)) {
379
+ enrichedNode.children = node.children.map(_enrichNode);
380
+ }
381
+ return enrichedNode;
382
+ };
383
+
384
+ // Handle root being an array or object
385
+ if (Array.isArray(sceneData)) {
386
+ return sceneData.map(_enrichNode);
387
+ } else if (sceneData.children && Array.isArray(sceneData.children)) {
388
+ var enrichedRoot = _rollupPluginBabelHelpers.objectSpread2({}, sceneData);
389
+ enrichedRoot.children = sceneData.children.map(_enrichNode);
390
+ return enrichedRoot;
391
+ } else {
392
+ return _enrichNode(sceneData);
393
+ }
404
394
  }
405
395
 
406
396
  /**
407
- * Core pathfinding logic shared across initialization and updates
397
+ * Helper to merge an enriched child (connector/device) into a children array
398
+ * @private
408
399
  */
400
+ }, {
401
+ key: "_mergeEnrichedChild",
402
+ value: function _mergeEnrichedChild(childrenArray, enrichedData) {
403
+ var existingIndex = childrenArray.findIndex(function (c) {
404
+ return c.uuid === enrichedData.uuid;
405
+ });
406
+ var nodeToMerge = {
407
+ uuid: enrichedData.uuid,
408
+ position: {
409
+ x: enrichedData.userData.position[0],
410
+ y: enrichedData.userData.position[1],
411
+ z: enrichedData.userData.position[2]
412
+ },
413
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedData.userData), {}, {
414
+ worldBoundingBox: enrichedData.worldBoundingBox
415
+ }),
416
+ children: []
417
+ };
418
+ if (existingIndex >= 0) {
419
+ childrenArray[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, childrenArray[existingIndex]), nodeToMerge);
420
+ } else {
421
+ childrenArray.push(nodeToMerge);
422
+ }
423
+ }
409
424
  }, {
410
425
  key: "_executePathfinding",
411
- value: (function () {
426
+ value: function () {
412
427
  var _executePathfinding2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee(sceneData, connections) {
413
- var _this4 = this,
428
+ var _this5 = this,
414
429
  _sceneDataCopy$childr,
415
430
  _sceneDataCopy$childr2,
416
431
  _pathfindingResult$pa,
@@ -422,6 +437,12 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
422
437
  totalStart,
423
438
  inputGenStart,
424
439
  enrichedSceneData,
440
+ _from$userData,
441
+ _to$userData,
442
+ firstConn,
443
+ findEndpoint,
444
+ from,
445
+ to,
425
446
  connectionsCopy,
426
447
  sceneDataCopy,
427
448
  algoStart,
@@ -438,6 +459,10 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
438
459
  while (1) switch (_context.n) {
439
460
  case 0:
440
461
  options = _args.length > 2 && _args[2] !== undefined ? _args[2] : {};
462
+ // Fallback to sceneData connections if not explicitly provided (e.g. during transform updates)
463
+ if (!connections && sceneData && sceneData.connections) {
464
+ connections = sceneData.connections;
465
+ }
441
466
  options.createGateways, _options$context = options.context, context = _options$context === void 0 ? 'Pathfinding' : _options$context;
442
467
  timers = {};
443
468
  totalStart = performance.now(); // Create pathfinder instance with configuration only
@@ -466,7 +491,50 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
466
491
  console.log("\uD83D\uDD04 Updated matrix world for ".concat(context, " pathfinding execution"));
467
492
 
468
493
  // Enrich sceneData with worldBoundingBox for segments before deep copy
469
- enrichedSceneData = this._enrichSceneDataWithBoundingBoxes(sceneData); // Deep copy connections and sceneData to prevent pathfinder from mutating the original
494
+ enrichedSceneData = this._enrichSceneDataWithBoundingBoxes(sceneData); // DEBUG: Log first connection endpoints in enriched data
495
+ if (connections && connections.length > 0 && enrichedSceneData.children) {
496
+ firstConn = connections[0];
497
+ findEndpoint = function findEndpoint(uuid) {
498
+ var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(enrichedSceneData.children),
499
+ _step;
500
+ try {
501
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
502
+ var child = _step.value;
503
+ if (child.uuid === uuid) return child;
504
+ if (child.children) {
505
+ var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(child.children),
506
+ _step2;
507
+ try {
508
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
509
+ var nested = _step2.value;
510
+ if (nested.uuid === uuid) return nested;
511
+ }
512
+ } catch (err) {
513
+ _iterator2.e(err);
514
+ } finally {
515
+ _iterator2.f();
516
+ }
517
+ }
518
+ }
519
+ } catch (err) {
520
+ _iterator.e(err);
521
+ } finally {
522
+ _iterator.f();
523
+ }
524
+ return null;
525
+ };
526
+ from = findEndpoint(firstConn.from);
527
+ to = findEndpoint(firstConn.to);
528
+ console.log("\uD83D\uDCE1 [DEBUG] Pathfinding from ".concat(firstConn.from, " to ").concat(firstConn.to));
529
+ 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) {
530
+ return v.toFixed(2);
531
+ }), "]"));
532
+ 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) {
533
+ return v.toFixed(2);
534
+ }), "]"));
535
+ }
536
+
537
+ // Deep copy connections and sceneData to prevent pathfinder from mutating the original
470
538
  connectionsCopy = structuredClone(connections);
471
539
  sceneDataCopy = structuredClone(enrichedSceneData);
472
540
  timers.inputGeneration = performance.now() - inputGenStart;
@@ -497,7 +565,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
497
565
  if (pathfindingResult.paths) {
498
566
  pathfindingResult.paths.forEach(function (path) {
499
567
  var pathId = "".concat(path.from, "-->").concat(path.to);
500
- _this4._getOrCreatePathData(pathId, path.from, path.to);
568
+ _this5._getOrCreatePathData(pathId, path.from, path.to);
501
569
  });
502
570
  }
503
571
  timers.pathRendering = performance.now() - renderStart;
@@ -530,7 +598,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
530
598
  return _executePathfinding2.apply(this, arguments);
531
599
  }
532
600
  return _executePathfinding;
533
- }())
601
+ }()
534
602
  }, {
535
603
  key: "getSimplifiedSceneData",
536
604
  value: function getSimplifiedSceneData() {
@@ -569,7 +637,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
569
637
  }, {
570
638
  key: "_propagateFlowAttributesAndApplyVisualizations",
571
639
  value: function _propagateFlowAttributesAndApplyVisualizations(connections, pathfindingResult) {
572
- var _this5 = this,
640
+ var _this6 = this,
573
641
  _this$sceneViewer;
574
642
  if (!Array.isArray(connections) || !pathfindingResult) return;
575
643
 
@@ -597,7 +665,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
597
665
  var subPathIds = new Set(originalToSubPaths.get(origId) || []);
598
666
  subPathIds.add(origId);
599
667
  subPathIds.forEach(function (subPathId) {
600
- var pd = _this5.pathDataStore.get(subPathId);
668
+ var pd = _this6.pathDataStore.get(subPathId);
601
669
  if (pd && Object.keys(pd.flowAttributes).length === 0) {
602
670
  Object.entries(conn.flowAttributes).forEach(function (_ref2) {
603
671
  var _ref3 = _rollupPluginBabelHelpers.slicedToArray(_ref2, 2),
@@ -661,7 +729,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
661
729
  key: "initializePathfinder",
662
730
  value: (function () {
663
731
  var _initializePathfinder = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee2(data, crosscubeTextureSet) {
664
- var _this6 = this;
732
+ var _this7 = this;
665
733
  var pathfindingResult;
666
734
  return _rollupPluginBabelHelpers.regenerator().w(function (_context2) {
667
735
  while (1) switch (_context2.n) {
@@ -675,7 +743,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
675
743
  data.connections.forEach(function (conn) {
676
744
  if (conn.flowAttributes && _rollupPluginBabelHelpers["typeof"](conn.flowAttributes) === 'object') {
677
745
  var pathId = "".concat(conn.from, "-->").concat(conn.to);
678
- var pd = _this6._getOrCreatePathData(pathId, conn.from, conn.to);
746
+ var pd = _this7._getOrCreatePathData(pathId, conn.from, conn.to);
679
747
  Object.entries(conn.flowAttributes).forEach(function (_ref6) {
680
748
  var _ref7 = _rollupPluginBabelHelpers.slicedToArray(_ref6, 2),
681
749
  key = _ref7[0],
@@ -793,33 +861,47 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
793
861
  key: "updatePathfindingAfterTransform",
794
862
  value: (function () {
795
863
  var _updatePathfindingAfterTransform = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee3(currentSceneData) {
796
- var fingerprint, result;
864
+ var _currentSceneData$sce;
865
+ var fingerprint, dataRoot, connections, result;
797
866
  return _rollupPluginBabelHelpers.regenerator().w(function (_context3) {
798
867
  while (1) switch (_context3.n) {
799
868
  case 0:
869
+ if (!(!currentSceneData || !currentSceneData.scene)) {
870
+ _context3.n = 1;
871
+ break;
872
+ }
873
+ console.warn('⚠️ updatePathfindingAfterTransform: Invalid scene data');
874
+ return _context3.a(2, null);
875
+ case 1:
800
876
  // ── Skip-unchanged-paths check ──────────────────────────────────
801
877
  // Ensure matrices are fresh so the fingerprint is accurate.
802
878
  this.sceneViewer.scene.updateMatrixWorld(true);
803
879
  fingerprint = this._computePathfindingFingerprint(currentSceneData.scene, currentSceneData.connections);
880
+ console.log("\uD83D\uDD0D Pathfinding update check. Fingerprint: ".concat(fingerprint.substring(0, 50), "..."));
804
881
  if (!(fingerprint === this._lastPathfindingFingerprint && this._lastPathfindingResult)) {
805
- _context3.n = 1;
882
+ _context3.n = 2;
806
883
  break;
807
884
  }
808
885
  console.log('⏭️ Pathfinding skipped — inputs unchanged since last run');
809
886
  return _context3.a(2, this._lastPathfindingResult);
810
- case 1:
887
+ case 2:
888
+ // Clear the cache for the specific fingerprint mismatch to ensure fresh enrichment
889
+ this.invalidateBBoxCache();
890
+
811
891
  // Remove all existing paths from the scene
812
892
  this.removeComputedObjects();
813
893
  console.log('🔄 Starting pathfinding with updated scene data...');
814
894
 
815
- // Note: Matrix updates and bounding box recomputation now handled in _executePathfinding
895
+ // Robust root discovery for different manifest structures
896
+ dataRoot = ((_currentSceneData$sce = currentSceneData.scene) === null || _currentSceneData$sce === void 0 ? void 0 : _currentSceneData$sce.object) || currentSceneData.scene || currentSceneData;
897
+ connections = currentSceneData.connections || []; // Note: Matrix updates and bounding box recomputation now handled in _executePathfinding
816
898
  // Use shared pathfinding logic (no gateways needed for transform updates)
817
- _context3.n = 2;
818
- return this._executePathfinding(currentSceneData.scene, currentSceneData.connections, {
899
+ _context3.n = 3;
900
+ return this._executePathfinding(dataRoot, connections, {
819
901
  createGateways: true,
820
902
  context: 'Transform Update'
821
903
  });
822
- case 2:
904
+ case 3:
823
905
  result = _context3.v;
824
906
  // Re-apply flow attribute colors (new materials are created on each execution)
825
907
  this._propagateFlowAttributesAndApplyVisualizations(currentSceneData.connections, result);
@@ -64,8 +64,7 @@ var CollisionManager = /*#__PURE__*/function () {
64
64
  if (child === object || _this._isDescendantOf(object, child)) return;
65
65
 
66
66
  // Filter by CP object types at the root level (skip internal meshes during traverse)
67
- 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.ioConfig); // IO Device
68
-
67
+ 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';
69
68
  if (isCPRootObject && !_this._shouldExclude(child, excludeTypes)) {
70
69
  // Use cached worldBoundingBox if available, otherwise compute it (and cache it)
71
70
  var childBBox;