@2112-lab/central-plant 0.3.49 → 0.3.50

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.
@@ -226,204 +226,344 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
226
226
  }
227
227
 
228
228
  /**
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)
229
+ * Enrich sceneData with worldBoundingBox for segments and components
230
+ * Connectors remain with just position information.
231
+ *
232
+ * Uses _bboxCache to avoid recomputing filtered / io-device bboxes
233
+ * when the underlying object's world matrix has not changed.
234
+ *
235
+ * @param {Object} sceneData - Original scene data
236
+ * @returns {Object} Enriched scene data (shallow copy with modifications)
232
237
  * @private
233
238
  */
234
239
  }, {
235
240
  key: "_enrichSceneDataWithBoundingBoxes",
236
241
  value: function _enrichSceneDataWithBoundingBoxes(sceneData) {
237
242
  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);
243
+ // Create a shallow copy of sceneData structure
244
+ var enriched = _rollupPluginBabelHelpers.objectSpread2({}, sceneData);
245
+ if (!sceneData.children || !Array.isArray(sceneData.children)) {
246
+ console.warn('⚠️ sceneData has no children array');
247
+ return enriched;
248
+ }
244
249
 
250
+ // Process children to add worldBoundingBox to segments and components
251
+ enriched.children = sceneData.children.map(function (child) {
245
252
  // Skip computed objects ( elbows etc )
246
- if (node.userData && (node.userData.isComputed === true || node.userData.objectType === 'elbow')) {
247
- return node;
253
+ if (child.userData && (child.userData.isComputed === true || child.userData.objectType === 'elbow')) {
254
+ return child;
248
255
  }
249
256
 
250
- // ── Enrich Segments ──
251
- if (node.userData && node.userData.objectType === 'segment') {
252
- var segmentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
257
+ // Enrich segments (check if objectType is 'segment' in userData)
258
+ if (child.userData && child.userData.objectType === 'segment') {
259
+ // Find the actual segment object in the scene
260
+ var segmentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
253
261
  if (segmentObject) {
262
+ // ── Cache check ──
254
263
  var hash = _this4._matrixHash(segmentObject);
255
- var cached = _this4._bboxCache.get(node.uuid);
264
+ var cached = _this4._bboxCache.get(child.uuid);
256
265
  if (cached && cached.matrixHash === hash && cached.segmentBBox) {
257
- enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
258
- worldBoundingBox: cached.segmentBBox
259
- });
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), {}, {
271
- worldBoundingBox: bboxData
266
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
267
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
268
+ worldBoundingBox: cached.segmentBBox
269
+ })
272
270
  });
273
271
  }
272
+
273
+ // Compute world bounding box
274
+ var worldBBox = new THREE__namespace.Box3().setFromObject(segmentObject);
275
+ var bboxData = {
276
+ min: [worldBBox.min.x, worldBBox.min.y, worldBBox.min.z],
277
+ max: [worldBBox.max.x, worldBBox.max.y, worldBBox.max.z]
278
+ };
279
+
280
+ // Store in cache
281
+ _this4._bboxCache.set(child.uuid, {
282
+ matrixHash: hash,
283
+ segmentBBox: bboxData
284
+ });
285
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
286
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
287
+ worldBoundingBox: bboxData
288
+ })
289
+ });
290
+ } else {
291
+ console.warn("\u26A0\uFE0F Could not find segment object in scene: ".concat(child.uuid));
274
292
  }
275
293
  }
276
294
 
277
- // ── Enrich Components ──
278
- else if (node.userData && node.userData.objectType === 'component') {
279
- var componentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', node.uuid);
295
+ // Enrich components (check if objectType is 'component' in userData)
296
+ if (child.userData && child.userData.objectType === 'component') {
297
+ // Find the actual component object in the scene
298
+ var componentObject = _this4.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
280
299
  if (componentObject) {
300
+ // Explicitly update matrices for this component subtree before computing bounding boxes
281
301
  componentObject.updateMatrixWorld(true);
302
+
303
+ // ── Cache check ──
282
304
  var _hash = _this4._matrixHash(componentObject);
283
- var _cached = _this4._bboxCache.get(node.uuid);
305
+ var _cached = _this4._bboxCache.get(child.uuid);
284
306
  if (_cached && _cached.matrixHash === _hash && _cached.filteredBBox) {
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]
307
+ // Rebuild enriched child from cached data
308
+ var _enrichedChild = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
309
+ // Sync live transform even on cache hit just in case the manifest (child) is stale
310
+ position: {
311
+ x: componentObject.position.x,
312
+ y: componentObject.position.y,
313
+ z: componentObject.position.z
314
+ },
315
+ rotation: {
316
+ x: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.x),
317
+ y: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.y),
318
+ z: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.z)
319
+ },
320
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
321
+ worldBoundingBox: _cached.filteredBBox,
322
+ position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
323
+ })
298
324
  });
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);
325
+ if (_cached.ioDeviceBBoxes && _cached.ioDeviceBBoxes.length > 0) {
326
+ if (!_enrichedChild.children) _enrichedChild.children = [];
327
+ _cached.ioDeviceBBoxes.forEach(function (deviceBBox) {
328
+ var existingIndex = _enrichedChild.children.findIndex(function (c) {
329
+ return c.uuid === deviceBBox.uuid;
330
+ });
331
+ if (existingIndex >= 0) {
332
+ _enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
333
+ position: {
334
+ x: deviceBBox.userData.position[0],
335
+ y: deviceBBox.userData.position[1],
336
+ z: deviceBBox.userData.position[2]
337
+ },
338
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
339
+ objectType: 'io-device',
340
+ worldBoundingBox: deviceBBox.worldBoundingBox,
341
+ position: deviceBBox.userData.position
342
+ })
343
+ });
344
+ } else {
345
+ _enrichedChild.children.push({
346
+ uuid: deviceBBox.uuid,
347
+ position: {
348
+ x: deviceBBox.userData.position[0],
349
+ y: deviceBBox.userData.position[1],
350
+ z: deviceBBox.userData.position[2]
351
+ },
352
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
353
+ worldBoundingBox: deviceBBox.worldBoundingBox,
354
+ position: deviceBBox.userData.position
355
+ }),
356
+ children: []
357
+ });
358
+ }
305
359
  });
306
360
  }
307
- if (_cached.ioDeviceBBoxes) {
308
- _cached.ioDeviceBBoxes.forEach(function (dev) {
309
- return _this4._mergeEnrichedChild(enrichedNode.children, dev);
361
+
362
+ // Also reinject connectors from cache
363
+ if (_cached.connectorBBoxes && _cached.connectorBBoxes.length > 0) {
364
+ if (!_enrichedChild.children) _enrichedChild.children = [];
365
+ _cached.connectorBBoxes.forEach(function (connBBox) {
366
+ var existingIndex = _enrichedChild.children.findIndex(function (c) {
367
+ return c.uuid === connBBox.uuid;
368
+ });
369
+ if (existingIndex >= 0) {
370
+ _enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
371
+ position: {
372
+ x: connBBox.userData.position[0],
373
+ y: connBBox.userData.position[1],
374
+ z: connBBox.userData.position[2]
375
+ },
376
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
377
+ worldBoundingBox: connBBox.worldBoundingBox,
378
+ position: connBBox.userData.position
379
+ })
380
+ });
381
+ } else {
382
+ _enrichedChild.children.push({
383
+ uuid: connBBox.uuid,
384
+ position: {
385
+ x: connBBox.userData.position[0],
386
+ y: connBBox.userData.position[1],
387
+ z: connBBox.userData.position[2]
388
+ },
389
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, connBBox.userData), {}, {
390
+ worldBoundingBox: connBBox.worldBoundingBox,
391
+ position: connBBox.userData.position
392
+ }),
393
+ children: []
394
+ });
395
+ }
310
396
  });
311
397
  }
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 = {
398
+ return _enrichedChild;
399
+ }
400
+
401
+ // Compute FILTERED bounding box — excludes io-device and connector subtrees
402
+ // so the component body bbox is tight-fitting and doesn't envelop attached devices
403
+ var filteredBBox = boundingBoxUtils.computeFilteredBoundingBox(componentObject, ['io-device', 'connector']);
404
+ 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), "]"));
405
+ var _bboxData = {
406
+ min: [filteredBBox.min.x, filteredBBox.min.y, filteredBBox.min.z],
407
+ max: [filteredBBox.max.x, filteredBBox.max.y, filteredBBox.max.z]
408
+ };
409
+
410
+ // Build the enriched component entry
411
+ var enrichedChild = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
412
+ // Sync live transform to ensure pathfinder has latest coordinates
413
+ position: {
319
414
  x: componentObject.position.x,
320
415
  y: componentObject.position.y,
321
416
  z: componentObject.position.z
322
- };
323
- enrichedNode.rotation = {
417
+ },
418
+ rotation: {
324
419
  x: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.x),
325
420
  y: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.y),
326
421
  z: THREE__namespace.MathUtils.radToDeg(componentObject.rotation.z)
327
- };
328
- enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
422
+ },
423
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
329
424
  worldBoundingBox: _bboxData,
330
425
  position: [componentObject.position.x, componentObject.position.y, componentObject.position.z]
426
+ })
427
+ });
428
+
429
+ // Compute separate bounding boxes for each attached io-device
430
+ var ioDeviceBBoxes = boundingBoxUtils.computeIODeviceBoundingBoxes(componentObject);
431
+ if (ioDeviceBBoxes.length > 0) {
432
+ // Ensure children array exists (may already contain connectors)
433
+ if (!enrichedChild.children) {
434
+ enrichedChild.children = [];
435
+ }
436
+
437
+ // Inject io-device entries with their own worldBoundingBox
438
+ ioDeviceBBoxes.forEach(function (deviceBBox) {
439
+ var existingIndex = enrichedChild.children.findIndex(function (c) {
440
+ return c.uuid === deviceBBox.uuid;
441
+ });
442
+ if (existingIndex >= 0) {
443
+ enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
444
+ position: {
445
+ x: deviceBBox.userData.position[0],
446
+ y: deviceBBox.userData.position[1],
447
+ z: deviceBBox.userData.position[2]
448
+ },
449
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
450
+ objectType: 'io-device',
451
+ worldBoundingBox: deviceBBox.worldBoundingBox,
452
+ position: deviceBBox.userData.position
453
+ })
454
+ });
455
+ } else {
456
+ enrichedChild.children.push({
457
+ uuid: deviceBBox.uuid,
458
+ position: {
459
+ x: deviceBBox.userData.position[0],
460
+ y: deviceBBox.userData.position[1],
461
+ z: deviceBBox.userData.position[2]
462
+ },
463
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
464
+ worldBoundingBox: deviceBBox.worldBoundingBox,
465
+ position: deviceBBox.userData.position
466
+ }),
467
+ children: []
468
+ });
469
+ }
470
+ console.log("\uD83D\uDCE6 Injected io-device bbox for ".concat(deviceBBox.uuid, ": min=[").concat(deviceBBox.worldBoundingBox.min.map(function (v) {
471
+ return v.toFixed(2);
472
+ }).join(', '), "], max=[").concat(deviceBBox.worldBoundingBox.max.map(function (v) {
473
+ return v.toFixed(2);
474
+ }).join(', '), "]"));
331
475
  });
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
476
+ console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
477
+ }
478
+
479
+ // PHASE 2: Enrich Connectors (CRITICAL for pathfinding API)
480
+ // Also compute world bounding boxes for connectors and inject into children.
481
+ // This ensures endpoints have world coordinates in sceneDataCopy.
482
+ var connectorBBoxes = boundingBoxUtils.computeConnectorBoundingBoxes(componentObject);
483
+ if (connectorBBoxes.length > 0) {
484
+ if (!enrichedChild.children) enrichedChild.children = [];
485
+ connectorBBoxes.forEach(function (connBBox) {
486
+ var existingIndex = enrichedChild.children.findIndex(function (c) {
487
+ return c.uuid === connBBox.uuid;
488
+ });
489
+ if (existingIndex >= 0) {
490
+ enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
491
+ position: {
492
+ x: connBBox.userData.position[0],
493
+ y: connBBox.userData.position[1],
494
+ z: connBBox.userData.position[2]
495
+ },
496
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
497
+ worldBoundingBox: connBBox.worldBoundingBox,
498
+ position: connBBox.userData.position
499
+ })
500
+ });
501
+ } else {
502
+ enrichedChild.children.push({
503
+ uuid: connBBox.uuid,
504
+ position: {
505
+ x: connBBox.userData.position[0],
506
+ y: connBBox.userData.position[1],
507
+ z: connBBox.userData.position[2]
508
+ },
509
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, connBBox.userData), {}, {
510
+ worldBoundingBox: connBBox.worldBoundingBox,
511
+ position: connBBox.userData.position
512
+ }),
513
+ children: []
514
+ });
515
+ }
346
516
  });
347
517
  }
518
+
519
+ // Store in cache
520
+ _this4._bboxCache.set(child.uuid, {
521
+ matrixHash: _hash,
522
+ filteredBBox: _bboxData,
523
+ ioDeviceBBoxes: ioDeviceBBoxes,
524
+ connectorBBoxes: connectorBBoxes
525
+ });
526
+ return enrichedChild;
527
+ } else {
528
+ console.warn("\u26A0\uFE0F Could not find component object in scene: ".concat(child.uuid));
348
529
  }
349
530
  }
350
531
 
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);
532
+ // ────────────────────────────────────────────────────────────────────────
533
+ // PHASE 3: Handle top-level Gateways and Connectors
534
+ // ────────────────────────────────────────────────────────────────────────
535
+ if (child.userData && (child.userData.objectType === 'gateway' || child.userData.objectType === 'connector')) {
536
+ var _child$userData;
537
+ var object = _this4.sceneViewer.scene.getObjectByProperty('uuid', child.uuid) || _this4.sceneViewer.scene.getObjectByProperty('uuid', (_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.originalUuid);
355
538
  if (object) {
356
539
  var worldPos = new THREE__namespace.Vector3();
357
540
  object.getWorldPosition(worldPos);
358
- enrichedNode.position = {
359
- x: worldPos.x,
360
- y: worldPos.y,
361
- z: worldPos.z
362
- };
363
- enrichedNode.userData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, node.userData), {}, {
364
- position: [worldPos.x, worldPos.y, worldPos.z]
541
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child), {}, {
542
+ // Sync live transform to ensure pathfinder has latest coordinates
543
+ position: {
544
+ x: worldPos.x,
545
+ y: worldPos.y,
546
+ z: worldPos.z
547
+ },
548
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, child.userData), {}, {
549
+ position: [worldPos.x, worldPos.y, worldPos.z]
550
+ })
365
551
  });
366
-
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];
373
- }
374
552
  }
375
553
  }
376
554
 
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
- }
555
+ // For non-segments and non-components (and if object search failed), return as-is
556
+ return child;
557
+ });
558
+ return enriched;
394
559
  }
395
560
 
396
561
  /**
397
- * Helper to merge an enriched child (connector/device) into a children array
398
- * @private
562
+ * Core pathfinding logic shared across initialization and updates
399
563
  */
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
- }
424
564
  }, {
425
565
  key: "_executePathfinding",
426
- value: function () {
566
+ value: (function () {
427
567
  var _executePathfinding2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee(sceneData, connections) {
428
568
  var _this5 = this,
429
569
  _sceneDataCopy$childr,
@@ -598,7 +738,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
598
738
  return _executePathfinding2.apply(this, arguments);
599
739
  }
600
740
  return _executePathfinding;
601
- }()
741
+ }())
602
742
  }, {
603
743
  key: "getSimplifiedSceneData",
604
744
  value: function getSimplifiedSceneData() {
@@ -6,6 +6,7 @@ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHel
6
6
  var THREE = require('three');
7
7
  var OrbitControls = require('../../../node_modules/three/examples/jsm/controls/OrbitControls.js');
8
8
  var GLTFLoader = require('../../../node_modules/three/examples/jsm/loaders/GLTFLoader.js');
9
+ var cameraControlsManager = require('../controls/cameraControlsManager.js');
9
10
 
10
11
  function _interopNamespace(e) {
11
12
  if (e && e.__esModule) return e;
@@ -54,7 +55,7 @@ var SceneInitializationManager = /*#__PURE__*/function () {
54
55
  containerWidth = containerRect.width;
55
56
  containerHeight = containerRect.height; // Create camera (Z-up coordinate system with flipped Y)
56
57
  component.camera = new THREE__namespace.PerspectiveCamera(50, containerWidth / containerHeight, 0.01, 1000);
57
- component.camera.position.set(-8, -9, 2); // Flipped Y direction
58
+ component.camera.position.set(cameraControlsManager.DEFAULT_HOME_VIEW_DIRECTION.x, cameraControlsManager.DEFAULT_HOME_VIEW_DIRECTION.y, cameraControlsManager.HOME_EMPTY_CAMERA_HEIGHT_ABOVE_GROUND);
58
59
  component.camera.up.set(0, 0, 1); // Set Z as up vector
59
60
 
60
61
  // Create renderer
@@ -7,6 +7,7 @@ var THREE = require('three');
7
7
  var textureConfig = require('../environment/textureConfig.js');
8
8
  var modelManager = require('./modelManager.js');
9
9
  var sceneClearingUtility = require('../../utils/sceneClearingUtility.js');
10
+ var cameraControlsManager = require('../controls/cameraControlsManager.js');
10
11
 
11
12
  function _interopNamespace(e) {
12
13
  if (e && e.__esModule) return e;
@@ -1142,6 +1143,7 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1142
1143
  }, {
1143
1144
  key: "_finalizeScene",
1144
1145
  value: function _finalizeScene(data, crosscubeTextureSet, isImported) {
1146
+ var _component$cameraCont;
1145
1147
  var component = this.sceneViewer;
1146
1148
  component.currentSceneData = data;
1147
1149
  component.crosscubeTextureSet = crosscubeTextureSet;
@@ -1153,6 +1155,14 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1153
1155
  }
1154
1156
  this._setTransformControlsState(true, false); // Enable but keep hidden initially
1155
1157
  }
1158
+
1159
+ // Frame the camera so all components fit perfectly in view (low, ground-level angle)
1160
+ if ((_component$cameraCont = component.cameraControlsManager) !== null && _component$cameraCont !== void 0 && _component$cameraCont.fitCameraToScene) {
1161
+ component.cameraControlsManager.fitCameraToScene({
1162
+ direction: cameraControlsManager.DEFAULT_HOME_VIEW_DIRECTION,
1163
+ groundLevel: true
1164
+ });
1165
+ }
1156
1166
  }
1157
1167
 
1158
1168
  /**