@2112-lab/central-plant 0.3.4 → 0.3.6

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.
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
35
35
  * Initialize the CentralPlant manager
36
36
  *
37
37
  * @constructor
38
- * @version 0.3.4
38
+ * @version 0.3.6
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -6,6 +6,7 @@ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHel
6
6
  var THREE = require('three');
7
7
  var ioDeviceUtils = require('../../utils/ioDeviceUtils.js');
8
8
  var modelPreloader = require('../../rendering/modelPreloader.js');
9
+ var boundingBoxUtils = require('../../utils/boundingBoxUtils.js');
9
10
 
10
11
  function _interopNamespace(e) {
11
12
  if (e && e.__esModule) return e;
@@ -579,15 +580,28 @@ var ModelManager = /*#__PURE__*/function () {
579
580
  _context5.n = 2;
580
581
  return Promise.all(glbLoadingPromises);
581
582
  case 2:
582
- // Update world bounding boxes for loaded models
583
+ // Update world bounding boxes for loaded models and propagate to the live Three.js objects
583
584
  libraryObjectsToReplace.forEach(function (_ref2) {
584
- var _jsonData$userData2;
585
585
  var jsonData = _ref2.jsonData,
586
586
  glbModel = _ref2.glbModel;
587
- if (glbModel && (_jsonData$userData2 = jsonData.userData) !== null && _jsonData$userData2 !== void 0 && _jsonData$userData2.worldBoundingBox) {
588
- var worldBoundingBox = _this3._calculateWorldBoundingBox(glbModel);
589
- jsonData.userData.worldBoundingBox = worldBoundingBox;
590
- }
587
+ if (!glbModel) return;
588
+ // Use filtered bbox (excludes connectors + io-devices) so it matches
589
+ // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
590
+ var filteredBox = boundingBoxUtils.computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
591
+ var worldBoundingBox = filteredBox.isEmpty() ? _this3._calculateWorldBoundingBox(glbModel) : {
592
+ min: [filteredBox.min.x, filteredBox.min.y, filteredBox.min.z],
593
+ max: [filteredBox.max.x, filteredBox.max.y, filteredBox.max.z]
594
+ };
595
+ // Update both the JSON data object AND the live scene object
596
+ jsonData.userData.worldBoundingBox = worldBoundingBox;
597
+ glbModel.userData.worldBoundingBox = worldBoundingBox;
598
+ // Snapshot the object's local position so viewport2DManager can compute
599
+ // world-bbox updates via a fast O(1) position delta instead of re-traversing geometry
600
+ glbModel.userData._wbbBasePosition = {
601
+ x: glbModel.position.x,
602
+ y: glbModel.position.y,
603
+ z: glbModel.position.z
604
+ };
591
605
  });
592
606
 
593
607
  // Dispatch completion event
@@ -7,7 +7,6 @@ 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 boundingBoxUtils = require('../../utils/boundingBoxUtils.js');
11
10
 
12
11
  function _interopNamespace(e) {
13
12
  if (e && e.__esModule) return e;
@@ -647,123 +646,52 @@ var SceneOperationsManager = /*#__PURE__*/function () {
647
646
  }
648
647
 
649
648
  /**
650
- * Helper function to compute world bounding boxes
651
- * For components: uses filtered bbox (excludes io-device and connector subtrees)
652
- * For io-devices: computes separate bounding boxes and injects them as children
649
+ * Sync world-space positions and isDeclared flags for gateways and connectors
650
+ * into the scene JSON data so the pathfinder can read them.
651
+ *
652
+ * Bounding boxes for components and segments are intentionally NOT computed here.
653
+ * They are computed (with matrix-hash caching) by
654
+ * PathfindingManager._enrichSceneDataWithBoundingBoxes(), which runs after GLB
655
+ * models are fully loaded and therefore produces correct values.
653
656
  */
654
657
  }, {
655
- key: "computeWorldBoundingBoxes",
656
- value: function computeWorldBoundingBoxes(data) {
657
- var component = this.sceneViewer;
658
- component.scene.traverse(function (object) {
659
- if (object.isMesh) {
660
- // Find the corresponding JSON object
661
- var jsonObject = null;
662
- var _findJsonObject = function findJsonObject(children) {
663
- var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(children),
664
- _step;
665
- try {
666
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
667
- var _object$userData, _child$userData4;
668
- var child = _step.value;
669
- // Enhanced matching logic with hardcoded UUID priority
670
-
671
- // Strategy 1: Direct hardcoded UUID match (HIGHEST PRIORITY)
672
- if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.originalUuid)) {
673
- return child;
674
- }
675
-
676
- // Recursively search children
677
- if (child.children) {
678
- var found = _findJsonObject(child.children);
679
- if (found) return found;
680
- }
681
- }
682
- } catch (err) {
683
- _iterator.e(err);
684
- } finally {
685
- _iterator.f();
686
- }
687
- return null;
688
- };
689
- jsonObject = _findJsonObject(data.scene.children);
690
- if (jsonObject) {
691
- // Store in JSON userData for pathfinder (skip for gateways - they're just routing points)
692
- if (!jsonObject.userData) jsonObject.userData = {};
693
- if (jsonObject.userData.objectType === 'component') {
694
- // For components: compute filtered bounding box (excludes io-device and connector subtrees)
695
- var filteredBBox = boundingBoxUtils.computeFilteredBoundingBox(object, ['io-device', 'connector']);
696
- jsonObject.userData.worldBoundingBox = {
697
- min: filteredBBox.min.toArray(),
698
- max: filteredBBox.max.toArray()
699
- };
700
- console.log("Added filtered world bounding box for component:", jsonObject.userData.worldBoundingBox);
701
-
702
- // Compute and inject separate io-device bounding boxes as children
703
- var ioDeviceBBoxes = boundingBoxUtils.computeIODeviceBoundingBoxes(object);
704
- if (ioDeviceBBoxes.length > 0) {
705
- if (!jsonObject.children) jsonObject.children = [];
706
- ioDeviceBBoxes.forEach(function (deviceBBox) {
707
- var existingIndex = jsonObject.children.findIndex(function (c) {
708
- return c.uuid === deviceBBox.uuid;
709
- });
710
- if (existingIndex >= 0) {
711
- // Update existing entry
712
- if (!jsonObject.children[existingIndex].userData) jsonObject.children[existingIndex].userData = {};
713
- jsonObject.children[existingIndex].userData.objectType = 'io-device';
714
- jsonObject.children[existingIndex].userData.worldBoundingBox = deviceBBox.worldBoundingBox;
715
- } else {
716
- // Create new entry
717
- jsonObject.children.push({
718
- uuid: deviceBBox.uuid,
719
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
720
- worldBoundingBox: deviceBBox.worldBoundingBox
721
- }),
722
- children: []
723
- });
724
- }
725
- });
726
- console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bbox(es) for component ").concat(jsonObject.uuid));
727
- }
728
- } else if (jsonObject.userData.objectType !== 'gateway') {
729
- // For non-component, non-gateway objects: standard bounding box
730
- var boundingBox = new THREE__namespace.Box3().setFromObject(object);
731
- jsonObject.userData.worldBoundingBox = {
732
- min: boundingBox.min.toArray(),
733
- max: boundingBox.max.toArray()
734
- };
735
- console.log("Added world bounding box:", jsonObject.userData.worldBoundingBox);
736
- }
737
-
738
- // For gateways and connectors, ensure userData.position exists in scene data
739
- // This is REQUIRED for pathfinder compatibility
740
- if (jsonObject.userData.objectType === 'gateway' || jsonObject.userData.objectType === 'connector') {
741
- // Use the object's world position (from Three.js mesh)
742
- var worldPos = new THREE__namespace.Vector3();
743
- object.getWorldPosition(worldPos);
744
-
745
- // ALWAYS update userData.position with world position
746
- // This is critical for manual segment connectors which start with local positions
747
- jsonObject.userData.position = [worldPos.x, worldPos.y, worldPos.z];
748
- console.log("\u2705 Set userData.position for ".concat(jsonObject.userData.objectType, " ").concat(jsonObject.uuid, ": [").concat(worldPos.x.toFixed(2), ", ").concat(worldPos.y.toFixed(2), ", ").concat(worldPos.z.toFixed(2), "]"));
749
-
750
- // For gateways, ensure isDeclared flag is in scene data
751
- if (jsonObject.userData.objectType === 'gateway') {
752
- if (jsonObject.userData.isDeclared === undefined) {
753
- jsonObject.userData.isDeclared = true;
754
- }
755
- }
756
-
757
- // For manual segment connectors, ensure isDeclared is set in scene data
758
- if (jsonObject.userData.objectType === 'segment-connector' && jsonObject.userData.isDeclared === undefined) {
759
- jsonObject.userData.isDeclared = true;
760
- console.log("\u2705 Set isDeclared=true for manual segment connector in scene data: ".concat(jsonObject.uuid));
761
- }
762
-
763
- // Also sync the mesh's userData.position (belt and suspenders approach)
764
- object.userData.position = [worldPos.x, worldPos.y, worldPos.z];
765
- }
658
+ key: "_syncPositionsForPathfinding",
659
+ value: function _syncPositionsForPathfinding(data) {
660
+ var scene = this.sceneViewer.scene;
661
+ var worldPos = new THREE__namespace.Vector3();
662
+ var syncPosition = function syncPosition(jsonObject) {
663
+ var _jsonObject$userData;
664
+ var object = scene.getObjectByProperty('uuid', jsonObject.uuid) || scene.getObjectByProperty('uuid', (_jsonObject$userData = jsonObject.userData) === null || _jsonObject$userData === void 0 ? void 0 : _jsonObject$userData.originalUuid);
665
+ if (!object) return;
666
+ object.getWorldPosition(worldPos);
667
+ var pos = [worldPos.x, worldPos.y, worldPos.z];
668
+ jsonObject.userData.position = pos;
669
+ object.userData.position = pos;
670
+ };
671
+ data.scene.children.forEach(function (jsonObject) {
672
+ var _jsonObject$userData2;
673
+ var type = (_jsonObject$userData2 = jsonObject.userData) === null || _jsonObject$userData2 === void 0 ? void 0 : _jsonObject$userData2.objectType;
674
+ if (type === 'gateway') {
675
+ syncPosition(jsonObject);
676
+ if (jsonObject.userData.isDeclared === undefined) {
677
+ jsonObject.userData.isDeclared = true;
766
678
  }
679
+ } else if (type === 'connector') {
680
+ syncPosition(jsonObject);
681
+ } else if (type === 'segment-connector') {
682
+ syncPosition(jsonObject);
683
+ if (jsonObject.userData.isDeclared === undefined) {
684
+ jsonObject.userData.isDeclared = true;
685
+ }
686
+ } else if (type === 'component' && Array.isArray(jsonObject.children)) {
687
+ // Connectors are injected as JSON children by _injectConnectorChildrenFromDictionary
688
+ // and their Three.js objects exist in the scene, created recursively by createSceneObject
689
+ jsonObject.children.forEach(function (childJson) {
690
+ var _childJson$userData;
691
+ if (((_childJson$userData = childJson.userData) === null || _childJson$userData === void 0 ? void 0 : _childJson$userData.objectType) === 'connector') {
692
+ syncPosition(childJson);
693
+ }
694
+ });
767
695
  }
768
696
  });
769
697
  }
@@ -928,10 +856,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
928
856
  var componentsProcessed = 0;
929
857
  var connectorsInjected = 0;
930
858
  data.scene.children.forEach(function (child) {
931
- var _child$userData5, _child$userData6, _child$userData7;
932
- var childType = ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) || ((_child$userData6 = child.userData) === null || _child$userData6 === void 0 ? void 0 : _child$userData6.objectType);
859
+ var _child$userData4, _child$userData5, _child$userData6;
860
+ var childType = ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType);
933
861
  // Only process components with libraryId
934
- if (childType === 'component' && (_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId) {
862
+ if (childType === 'component' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.libraryId) {
935
863
  var libraryId = child.userData.libraryId;
936
864
  var dictEntry = componentDictionary[libraryId];
937
865
 
@@ -1088,23 +1016,25 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1088
1016
  geometries = this.createSceneGeometries(data, componentDictionary); // Create basic objects and track GLB replacements
1089
1017
  libraryObjectsToReplace = [];
1090
1018
  data.scene.children.forEach(function (child, index) {
1091
- var _child$userData8, _child$userData9;
1019
+ var _child$userData7, _child$userData8;
1092
1020
  var createdObject = _this4.createSceneObject(child, geometries, materials, componentDictionary);
1093
1021
  _this4.sceneViewer.scene.add(createdObject);
1094
1022
 
1095
1023
  // Track objects that need GLB model replacement
1096
- if ((_child$userData8 = child.userData) !== null && _child$userData8 !== void 0 && _child$userData8.libraryId && componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]) {
1097
- var _child$userData0;
1024
+ if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId && componentDictionary[(_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.libraryId]) {
1025
+ var _child$userData9;
1098
1026
  libraryObjectsToReplace.push({
1099
1027
  basicObject: createdObject,
1100
1028
  jsonData: child,
1101
- componentData: componentDictionary[(_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId]
1029
+ componentData: componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]
1102
1030
  });
1103
1031
  }
1104
1032
  });
1105
1033
 
1106
- // Compute bounding boxes for pathfinding
1107
- this.computeWorldBoundingBoxes(data);
1034
+ // Sync gateway/connector world positions into JSON before pathfinding.
1035
+ // Bounding boxes are computed later by PathfindingManager._enrichSceneDataWithBoundingBoxes
1036
+ // (after GLB models are loaded), so no bbox work is done here.
1037
+ this._syncPositionsForPathfinding(data);
1108
1038
  this._saveOriginalWorldMatrices(this.sceneViewer.scene);
1109
1039
  return _context6.a(2, {
1110
1040
  componentDictionary: componentDictionary,
@@ -1250,8 +1180,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1250
1180
  var instanceBehaviors = [];
1251
1181
  if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
1252
1182
  data.scene.children.forEach(function (child) {
1253
- var _child$userData1, _compData$defaultBeha;
1254
- var libraryId = (_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.libraryId;
1183
+ var _child$userData0, _compData$defaultBeha;
1184
+ var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
1255
1185
  if (!libraryId) return;
1256
1186
  var instanceUuid = child.uuid;
1257
1187
  // Skip instances whose defaults were already resolved by Step A
@@ -1447,8 +1377,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1447
1377
  key: "_saveOriginalWorldMatrices",
1448
1378
  value: function _saveOriginalWorldMatrices(scene) {
1449
1379
  scene.traverse(function (object) {
1450
- var _object$userData2;
1451
- if ((_object$userData2 = object.userData) !== null && _object$userData2 !== void 0 && _object$userData2.direction) {
1380
+ var _object$userData;
1381
+ if ((_object$userData = object.userData) !== null && _object$userData !== void 0 && _object$userData.direction) {
1452
1382
  var originalMatrix = new THREE__namespace.Matrix4();
1453
1383
  originalMatrix.copy(object.matrixWorld);
1454
1384
  }
@@ -1652,8 +1582,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1652
1582
  // Process children (connectors, etc.) if they exist
1653
1583
  if (componentModel.children && componentModel.children.length > 0) {
1654
1584
  componentModel.children.forEach(function (child) {
1655
- var _child$userData10, _child$userData11;
1656
- var childType = ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType) || ((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType);
1585
+ var _child$userData1, _child$userData10;
1586
+ var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
1657
1587
  if (childType === 'connector') {
1658
1588
  var _child$geometry;
1659
1589
  var childBoundingBox = new THREE__namespace.Box3().setFromObject(child);
@@ -1738,8 +1668,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1738
1668
  if (segment.children && segment.children.length > 0) {
1739
1669
  var childrenToRemove = _rollupPluginBabelHelpers.toConsumableArray(segment.children);
1740
1670
  childrenToRemove.forEach(function (child) {
1741
- var _child$userData12;
1742
- if ((_child$userData12 = child.userData) !== null && _child$userData12 !== void 0 && _child$userData12.isPipeElbow) {
1671
+ var _child$userData11;
1672
+ if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
1743
1673
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
1744
1674
  segment.remove(child);
1745
1675
  if (child.geometry) child.geometry.dispose();