@2112-lab/central-plant 0.1.29 → 0.1.31

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.
@@ -6,7 +6,6 @@ console.log("Loading Central Plant Utils Bundle...");
6
6
  Object.defineProperty(exports, '__esModule', { value: true });
7
7
 
8
8
  var THREE = require('three');
9
- var pathfinder = require('@2112-lab/pathfinder');
10
9
 
11
10
  function _interopNamespace(e) {
12
11
  if (e && e.__esModule) return e;
@@ -17362,6 +17361,1845 @@ var KeyboardControlsManager = /*#__PURE__*/function () {
17362
17361
  }]);
17363
17362
  }();
17364
17363
 
17364
+ /**
17365
+ * A simple 3D vector class for internal use
17366
+ * @class Vector3
17367
+ */
17368
+ var Vector3 = /*#__PURE__*/function () {
17369
+ /**
17370
+ * Create a new Vector3
17371
+ * @param {number} [x=0] - The x component
17372
+ * @param {number} [y=0] - The y component
17373
+ * @param {number} [z=0] - The z component
17374
+ */
17375
+ function Vector3() {
17376
+ var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
17377
+ var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
17378
+ var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
17379
+ _classCallCheck(this, Vector3);
17380
+ this.x = x;
17381
+ this.y = y;
17382
+ this.z = z;
17383
+ }
17384
+
17385
+ /**
17386
+ * Create a copy of this vector
17387
+ * @returns {Vector3} A new vector with the same components
17388
+ */
17389
+ return _createClass(Vector3, [{
17390
+ key: "clone",
17391
+ value: function clone() {
17392
+ return new Vector3(this.x, this.y, this.z);
17393
+ }
17394
+
17395
+ /**
17396
+ * Convert the vector to an array
17397
+ * @returns {Array<number>} Array representation [x, y, z]
17398
+ */
17399
+ }, {
17400
+ key: "toArray",
17401
+ value: function toArray() {
17402
+ return [this.x, this.y, this.z];
17403
+ }
17404
+
17405
+ /**
17406
+ * Normalize this vector (make it unit length)
17407
+ * @returns {Vector3} This vector (for chaining)
17408
+ */
17409
+ }, {
17410
+ key: "normalize",
17411
+ value: function normalize() {
17412
+ var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
17413
+ if (length > 0) {
17414
+ this.x /= length;
17415
+ this.y /= length;
17416
+ this.z /= length;
17417
+ }
17418
+ return this;
17419
+ }
17420
+
17421
+ /**
17422
+ * Subtract another vector from this one
17423
+ * @param {Vector3} v - Vector to subtract
17424
+ * @returns {Vector3} This vector (for chaining)
17425
+ */
17426
+ }, {
17427
+ key: "sub",
17428
+ value: function sub(v) {
17429
+ this.x -= v.x;
17430
+ this.y -= v.y;
17431
+ this.z -= v.z;
17432
+ return this;
17433
+ }
17434
+
17435
+ /**
17436
+ * Multiply this vector by a scalar value
17437
+ * @param {number} scalar - The scalar value to multiply by
17438
+ * @returns {Vector3} This vector (for chaining)
17439
+ */
17440
+ }, {
17441
+ key: "multiplyScalar",
17442
+ value: function multiplyScalar(scalar) {
17443
+ this.x *= scalar;
17444
+ this.y *= scalar;
17445
+ this.z *= scalar;
17446
+ return this;
17447
+ }
17448
+
17449
+ /**
17450
+ * Add another vector to this one
17451
+ * @param {Vector3} v - Vector to add
17452
+ * @returns {Vector3} This vector (for chaining)
17453
+ */
17454
+ }, {
17455
+ key: "add",
17456
+ value: function add(v) {
17457
+ this.x += v.x;
17458
+ this.y += v.y;
17459
+ this.z += v.z;
17460
+ return this;
17461
+ }
17462
+ }]);
17463
+ }();
17464
+
17465
+ /**
17466
+ * Manages scene operations including object finding, position calculations, and hierarchy traversal
17467
+ */
17468
+ var SceneManager = /*#__PURE__*/function () {
17469
+ /**
17470
+ * Create a new SceneManager instance
17471
+ * @param {Object} scene - The scene configuration object
17472
+ */
17473
+ function SceneManager(scene) {
17474
+ _classCallCheck(this, SceneManager);
17475
+ this.scene = scene;
17476
+ }
17477
+
17478
+ /**
17479
+ * Get the root scene object
17480
+ * @returns {Object} The root scene object
17481
+ */
17482
+ return _createClass(SceneManager, [{
17483
+ key: "getRoot",
17484
+ value: function getRoot() {
17485
+ return this.scene.object;
17486
+ }
17487
+
17488
+ /**
17489
+ * Get world position from worldBoundingBox center
17490
+ * @param {Object} object - Scene object
17491
+ * @returns {Vector3} World position
17492
+ */
17493
+ }, {
17494
+ key: "getWorldPosition",
17495
+ value: function getWorldPosition(object) {
17496
+ var _object$userData;
17497
+ if (!((_object$userData = object.userData) !== null && _object$userData !== void 0 && _object$userData.worldBoundingBox)) {
17498
+ console.log("[DEBUG] Object ".concat(object.uuid, " has no worldBoundingBox in userData"));
17499
+ return new Vector3();
17500
+ }
17501
+ var _object$userData$worl = object.userData.worldBoundingBox,
17502
+ min = _object$userData$worl.min,
17503
+ max = _object$userData$worl.max;
17504
+ return new Vector3((min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2);
17505
+ }
17506
+
17507
+ /**
17508
+ * Get bounding box from worldBoundingBox in userData
17509
+ * @param {Object} object - Scene object
17510
+ * @returns {{min: Vector3, max: Vector3}|null} Bounding box or null if no worldBoundingBox
17511
+ */
17512
+ }, {
17513
+ key: "getBoundingBox",
17514
+ value: function getBoundingBox(object) {
17515
+ var _object$userData2;
17516
+ if (!((_object$userData2 = object.userData) !== null && _object$userData2 !== void 0 && _object$userData2.worldBoundingBox)) {
17517
+ console.log("[DEBUG] Object ".concat(object.uuid, " has no worldBoundingBox in userData"));
17518
+ return null;
17519
+ }
17520
+ var _object$userData$worl2 = object.userData.worldBoundingBox,
17521
+ min = _object$userData$worl2.min,
17522
+ max = _object$userData$worl2.max;
17523
+ return {
17524
+ min: new Vector3(min[0], min[1], min[2]),
17525
+ max: new Vector3(max[0], max[1], max[2])
17526
+ };
17527
+ }
17528
+
17529
+ /**
17530
+ * Find an object by UUID in the scene hierarchy
17531
+ * @param {string} uuid - UUID to search for
17532
+ * @returns {Object|null} Found object or null
17533
+ */
17534
+ }, {
17535
+ key: "findObjectByUUID",
17536
+ value: function findObjectByUUID(uuid) {
17537
+ return this._findObjectByUUIDRecursive(this.getRoot(), uuid);
17538
+ }
17539
+
17540
+ /**
17541
+ * Recursive helper for findObjectByUUID
17542
+ * @private
17543
+ * @param {Object} node - Current node to search
17544
+ * @param {string} uuid - UUID to search for
17545
+ * @returns {Object|null} Found object or null
17546
+ */
17547
+ }, {
17548
+ key: "_findObjectByUUIDRecursive",
17549
+ value: function _findObjectByUUIDRecursive(node, uuid) {
17550
+ if (node.uuid === uuid) {
17551
+ return node;
17552
+ }
17553
+ if (node.children) {
17554
+ var _iterator = _createForOfIteratorHelper(node.children),
17555
+ _step;
17556
+ try {
17557
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
17558
+ var child = _step.value;
17559
+ var found = this._findObjectByUUIDRecursive(child, uuid);
17560
+ if (found) return found;
17561
+ }
17562
+ } catch (err) {
17563
+ _iterator.e(err);
17564
+ } finally {
17565
+ _iterator.f();
17566
+ }
17567
+ }
17568
+ return null;
17569
+ }
17570
+
17571
+ /**
17572
+ * Find the parent object of a given object in the scene hierarchy
17573
+ * @param {Object} target - Target object to find parent for
17574
+ * @returns {Object|null} Parent object or null if not found or if parent is the scene
17575
+ */
17576
+ }, {
17577
+ key: "findParentObject",
17578
+ value: function findParentObject(target) {
17579
+ if (target.userData && Array.isArray(target.userData.direction)) {
17580
+ return {
17581
+ isDirectionParent: true
17582
+ };
17583
+ }
17584
+ return this._findParentObjectRecursive(this.getRoot(), target);
17585
+ }
17586
+
17587
+ /**
17588
+ * Recursive helper for findParentObject
17589
+ * @private
17590
+ * @param {Object} root - Root object to search from
17591
+ * @param {Object} target - Target object to find parent for
17592
+ * @returns {Object|null} Parent object or null
17593
+ */
17594
+ }, {
17595
+ key: "_findParentObjectRecursive",
17596
+ value: function _findParentObjectRecursive(root, target) {
17597
+ if (root.children) {
17598
+ var _iterator2 = _createForOfIteratorHelper(root.children),
17599
+ _step2;
17600
+ try {
17601
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
17602
+ var child = _step2.value;
17603
+ if (child.uuid === target.uuid) {
17604
+ if (root.type === 'Scene') {
17605
+ return null;
17606
+ }
17607
+ return root;
17608
+ }
17609
+ }
17610
+ } catch (err) {
17611
+ _iterator2.e(err);
17612
+ } finally {
17613
+ _iterator2.f();
17614
+ }
17615
+ var _iterator3 = _createForOfIteratorHelper(root.children),
17616
+ _step3;
17617
+ try {
17618
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
17619
+ var _child = _step3.value;
17620
+ var parent = this._findParentObjectRecursive(_child, target);
17621
+ if (parent) {
17622
+ return parent;
17623
+ }
17624
+ }
17625
+ } catch (err) {
17626
+ _iterator3.e(err);
17627
+ } finally {
17628
+ _iterator3.f();
17629
+ }
17630
+ }
17631
+ return null;
17632
+ }
17633
+
17634
+ /**
17635
+ * Check if a voxel is occupied by any mesh object
17636
+ * @param {string} voxelKey - Voxel key to check
17637
+ * @param {number} gridSize - Size of each grid cell
17638
+ * @returns {boolean} True if the voxel is occupied by a mesh object
17639
+ */
17640
+ }, {
17641
+ key: "isVoxelOccupiedByMesh",
17642
+ value: function isVoxelOccupiedByMesh(voxelKey, gridSize) {
17643
+ var _voxelKey$split$map = voxelKey.split(',').map(Number),
17644
+ _voxelKey$split$map2 = _slicedToArray(_voxelKey$split$map, 3),
17645
+ x = _voxelKey$split$map2[0],
17646
+ y = _voxelKey$split$map2[1],
17647
+ z = _voxelKey$split$map2[2];
17648
+ var worldPos = new Vector3(x * gridSize, y * gridSize, z * gridSize);
17649
+ return this._checkObjectOccupancy(this.getRoot(), worldPos);
17650
+ }
17651
+
17652
+ /**
17653
+ * Recursive helper for isVoxelOccupiedByMesh
17654
+ * @private
17655
+ * @param {Object} object - Object to check
17656
+ * @param {Vector3} worldPos - World position to check
17657
+ * @returns {boolean} True if the position is occupied
17658
+ */
17659
+ }, {
17660
+ key: "_checkObjectOccupancy",
17661
+ value: function _checkObjectOccupancy(object, worldPos) {
17662
+ if (object.type === 'Mesh') {
17663
+ var bbox = this.getBoundingBox(object);
17664
+ if (bbox) {
17665
+ if (worldPos.x >= bbox.min.x && worldPos.x <= bbox.max.x && worldPos.y >= bbox.min.y && worldPos.y <= bbox.max.y && worldPos.z >= bbox.min.z && worldPos.z <= bbox.max.z) {
17666
+ return true;
17667
+ }
17668
+ }
17669
+ }
17670
+ if (object.children) {
17671
+ var _iterator4 = _createForOfIteratorHelper(object.children),
17672
+ _step4;
17673
+ try {
17674
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
17675
+ var child = _step4.value;
17676
+ if (this._checkObjectOccupancy(child, worldPos)) return true;
17677
+ }
17678
+ } catch (err) {
17679
+ _iterator4.e(err);
17680
+ } finally {
17681
+ _iterator4.f();
17682
+ }
17683
+ }
17684
+ return false;
17685
+ }
17686
+ }]);
17687
+ }();
17688
+
17689
+ /**
17690
+ * Manages grid operations including voxel conversions, neighbor calculations, and distance metrics
17691
+ */
17692
+ var GridSystem = /*#__PURE__*/function () {
17693
+ /**
17694
+ * Create a new GridSystem instance
17695
+ * @param {number} gridSize - Size of each grid cell in world units
17696
+ * @param {number} safetyMargin - Safety margin around obstacles in world units
17697
+ */
17698
+ function GridSystem() {
17699
+ var gridSize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.5;
17700
+ var safetyMargin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
17701
+ _classCallCheck(this, GridSystem);
17702
+ this.gridSize = gridSize;
17703
+ this.safetyMargin = safetyMargin;
17704
+ }
17705
+
17706
+ /**
17707
+ * Convert a position to a voxel key
17708
+ * @param {Vector3|Object} pos - Position to convert
17709
+ * @param {number} pos.x - X coordinate
17710
+ * @param {number} pos.y - Y coordinate
17711
+ * @param {number} pos.z - Z coordinate
17712
+ * @returns {string} Voxel key in format "x,y,z"
17713
+ */
17714
+ return _createClass(GridSystem, [{
17715
+ key: "voxelKey",
17716
+ value: function voxelKey(pos) {
17717
+ return [Math.round(pos.x / this.gridSize), Math.round(pos.y / this.gridSize), Math.round(pos.z / this.gridSize)].join(',');
17718
+ }
17719
+
17720
+ /**
17721
+ * Convert a voxel key to a Vector3
17722
+ * @param {string} key - Voxel key
17723
+ * @returns {Vector3} Vector3 position
17724
+ */
17725
+ }, {
17726
+ key: "voxelToVec3",
17727
+ value: function voxelToVec3(key) {
17728
+ var _key$split$map = key.split(',').map(Number),
17729
+ _key$split$map2 = _slicedToArray(_key$split$map, 3),
17730
+ x = _key$split$map2[0],
17731
+ y = _key$split$map2[1],
17732
+ z = _key$split$map2[2];
17733
+ return new Vector3(x * this.gridSize, y * this.gridSize, z * this.gridSize);
17734
+ }
17735
+
17736
+ /**
17737
+ * Get neighboring voxels (axis-aligned)
17738
+ * @param {string} vox - Voxel key
17739
+ * @returns {string[]} Array of neighboring voxel keys
17740
+ */
17741
+ }, {
17742
+ key: "getNeighbors",
17743
+ value: function getNeighbors(vox) {
17744
+ var _vox$split$map = vox.split(',').map(Number),
17745
+ _vox$split$map2 = _slicedToArray(_vox$split$map, 3),
17746
+ x = _vox$split$map2[0],
17747
+ y = _vox$split$map2[1],
17748
+ z = _vox$split$map2[2];
17749
+ return [[x + 1, y, z], [x - 1, y, z], [x, y + 1, z], [x, y - 1, z], [x, y, z + 1], [x, y, z - 1]].map(function (arr) {
17750
+ return arr.join(',');
17751
+ });
17752
+ }
17753
+
17754
+ /**
17755
+ * Calculate Manhattan distance between two voxels
17756
+ * @param {string} a - First voxel key
17757
+ * @param {string} b - Second voxel key
17758
+ * @returns {number} Manhattan distance
17759
+ */
17760
+ }, {
17761
+ key: "manhattan",
17762
+ value: function manhattan(a, b) {
17763
+ var _a$split$map = a.split(',').map(Number),
17764
+ _a$split$map2 = _slicedToArray(_a$split$map, 3),
17765
+ ax = _a$split$map2[0],
17766
+ ay = _a$split$map2[1],
17767
+ az = _a$split$map2[2];
17768
+ var _b$split$map = b.split(',').map(Number),
17769
+ _b$split$map2 = _slicedToArray(_b$split$map, 3),
17770
+ bx = _b$split$map2[0],
17771
+ by = _b$split$map2[1],
17772
+ bz = _b$split$map2[2];
17773
+ return Math.abs(ax - bx) + Math.abs(ay - by) + Math.abs(az - bz);
17774
+ }
17775
+
17776
+ /**
17777
+ * Calculate the number of voxels needed for a given world distance
17778
+ * @param {number} worldDistance - Distance in world units
17779
+ * @returns {number} Number of voxels
17780
+ */
17781
+ }, {
17782
+ key: "worldToVoxelDistance",
17783
+ value: function worldToVoxelDistance(worldDistance) {
17784
+ return Math.ceil(worldDistance / this.gridSize);
17785
+ }
17786
+
17787
+ /**
17788
+ * Calculate the world distance for a given number of voxels
17789
+ * @param {number} voxelCount - Number of voxels
17790
+ * @returns {number} Distance in world units
17791
+ */
17792
+ }, {
17793
+ key: "voxelToWorldDistance",
17794
+ value: function voxelToWorldDistance(voxelCount) {
17795
+ return voxelCount * this.gridSize;
17796
+ }
17797
+
17798
+ /**
17799
+ * Get the safety margin in voxel units
17800
+ * @returns {number} Safety margin in voxels
17801
+ */
17802
+ }, {
17803
+ key: "getSafetyMarginVoxels",
17804
+ value: function getSafetyMarginVoxels() {
17805
+ return this.worldToVoxelDistance(this.safetyMargin);
17806
+ }
17807
+
17808
+ /**
17809
+ * Check if a voxel is within the safety margin of a bounding box
17810
+ * @param {string} voxelKey - Voxel key to check
17811
+ * @param {Object} bbox - Bounding box
17812
+ * @param {Vector3} bbox.min - Minimum coordinates
17813
+ * @param {Vector3} bbox.max - Maximum coordinates
17814
+ * @returns {boolean} True if the voxel is within the safety margin
17815
+ */
17816
+ }, {
17817
+ key: "isVoxelInSafetyMargin",
17818
+ value: function isVoxelInSafetyMargin(voxelKey, bbox) {
17819
+ var voxelPos = this.voxelToVec3(voxelKey);
17820
+ var margin = this.safetyMargin;
17821
+ return voxelPos.x >= bbox.min.x - margin && voxelPos.x <= bbox.max.x + margin && voxelPos.y >= bbox.min.y - margin && voxelPos.y <= bbox.max.y + margin && voxelPos.z >= bbox.min.z - margin && voxelPos.z <= bbox.max.z + margin;
17822
+ }
17823
+
17824
+ /**
17825
+ * Get all voxel keys within a bounding box plus safety margin
17826
+ * @param {Object} bbox - Bounding box
17827
+ * @param {Vector3} bbox.min - Minimum coordinates
17828
+ * @param {Vector3} bbox.max - Maximum coordinates
17829
+ * @returns {string[]} Array of voxel keys
17830
+ */
17831
+ }, {
17832
+ key: "getVoxelsInBoundingBox",
17833
+ value: function getVoxelsInBoundingBox(bbox) {
17834
+ var margin = this.getSafetyMarginVoxels();
17835
+ var min = bbox.min;
17836
+ var max = bbox.max;
17837
+ var voxels = [];
17838
+ for (var x = Math.round(min.x / this.gridSize) - margin; x <= Math.round(max.x / this.gridSize) + margin; x++) {
17839
+ for (var y = Math.round(min.y / this.gridSize) - margin; y <= Math.round(max.y / this.gridSize) + margin; y++) {
17840
+ for (var z = Math.round(min.z / this.gridSize) - margin; z <= Math.round(max.z / this.gridSize) + margin; z++) {
17841
+ voxels.push("".concat(x, ",").concat(y, ",").concat(z));
17842
+ }
17843
+ }
17844
+ }
17845
+ return voxels;
17846
+ }
17847
+ }]);
17848
+ }();
17849
+
17850
+ /**
17851
+ * Manages connector-related operations for the pathfinding system
17852
+ * @class ConnectorManager
17853
+ */
17854
+ var ConnectorManager = /*#__PURE__*/function () {
17855
+ /**
17856
+ * Create a new ConnectorManager instance
17857
+ * @param {Object} config - The configuration object
17858
+ * @param {Array<Object>} config.connections - Array of connections between objects
17859
+ * @param {string} config.connections[].from - UUID of the source object
17860
+ * @param {string} config.connections[].to - UUID of the target object
17861
+ * @param {number} minSegmentLength - Minimum length for straight pipe segments in world units
17862
+ * @param {SceneManager} sceneManager - Instance of SceneManager for scene operations
17863
+ * @param {GridSystem} gridSystem - Instance of GridSystem for grid operations
17864
+ */
17865
+ function ConnectorManager(config, minSegmentLength, sceneManager, gridSystem) {
17866
+ _classCallCheck(this, ConnectorManager);
17867
+ this.config = config;
17868
+ this.MIN_SEGMENT_LENGTH = minSegmentLength;
17869
+ this.sceneManager = sceneManager;
17870
+ this.gridSystem = gridSystem;
17871
+ }
17872
+
17873
+ /**
17874
+ * Check if an object is a connector by checking if it's mentioned in connections
17875
+ * @param {Object} object - Scene object
17876
+ * @returns {boolean} True if the object is a connector
17877
+ */
17878
+ return _createClass(ConnectorManager, [{
17879
+ key: "isConnector",
17880
+ value: function isConnector(object) {
17881
+ if (!this.config.connections) return false;
17882
+
17883
+ // Check if the object's UUID appears in any connection
17884
+ return this.config.connections.some(function (conn) {
17885
+ return conn.from === object.uuid || conn.to === object.uuid;
17886
+ });
17887
+ }
17888
+
17889
+ /**
17890
+ * Calculate direction vector for a connector
17891
+ * @param {Object} connector - Connector object
17892
+ * @returns {Vector3|null} Direction vector or null if no parent or direction
17893
+ */
17894
+ }, {
17895
+ key: "getConnectorDirection",
17896
+ value: function getConnectorDirection(connector) {
17897
+ // First check if direction is directly specified in userData
17898
+ if (connector.userData && Array.isArray(connector.userData.direction)) {
17899
+ var _connector$userData$d = _slicedToArray(connector.userData.direction, 3),
17900
+ x = _connector$userData$d[0],
17901
+ y = _connector$userData$d[1],
17902
+ z = _connector$userData$d[2];
17903
+ return new Vector3(x, y, z);
17904
+ }
17905
+
17906
+ // If no direction in userData and no parent, return null
17907
+ if (!connector.parent) {
17908
+ return null;
17909
+ }
17910
+
17911
+ // For backward compatibility, calculate direction from parent if no userData.direction
17912
+ console.log("[WARNING] No direction in userData for ".concat(connector.name || connector.uuid, " - using parent-based calculation (legacy mode)"));
17913
+
17914
+ // Get world position of the connector
17915
+ var connectorPos = this.sceneManager.getWorldPosition(connector);
17916
+
17917
+ // For the parent, use the center of its bounding box if available
17918
+ var parentPos;
17919
+ var parentBBox = this.sceneManager.getBoundingBox(connector.parent);
17920
+ if (parentBBox) {
17921
+ // Calculate the center of the parent's bounding box
17922
+ parentPos = new Vector3((parentBBox.min.x + parentBBox.max.x) / 2, (parentBBox.min.y + parentBBox.max.y) / 2, (parentBBox.min.z + parentBBox.max.z) / 2);
17923
+ } else {
17924
+ // Fall back to parent's position if bounding box is not available
17925
+ parentPos = this.sceneManager.getWorldPosition(connector.parent);
17926
+ }
17927
+
17928
+ // Calculate the direction vector from parent to connector
17929
+ var direction = new Vector3(connectorPos.x - parentPos.x, connectorPos.y - parentPos.y, connectorPos.z - parentPos.z).normalize();
17930
+
17931
+ // Enforce orthogonality by aligning with the dominant axis
17932
+ var absX = Math.abs(direction.x);
17933
+ var absY = Math.abs(direction.y);
17934
+ var absZ = Math.abs(direction.z);
17935
+ if (absX >= absY && absX >= absZ) {
17936
+ // X is dominant
17937
+ direction.y = 0;
17938
+ direction.z = 0;
17939
+ direction.x = Math.sign(direction.x);
17940
+ } else if (absY >= absX && absY >= absZ) {
17941
+ // Y is dominant
17942
+ direction.x = 0;
17943
+ direction.z = 0;
17944
+ direction.y = Math.sign(direction.y);
17945
+ } else {
17946
+ // Z is dominant
17947
+ direction.x = 0;
17948
+ direction.y = 0;
17949
+ direction.z = Math.sign(direction.z);
17950
+ }
17951
+ return direction;
17952
+ }
17953
+
17954
+ /**
17955
+ * Create a virtual segment for a connector
17956
+ * @param {Object} connector - Connector object
17957
+ * @param {Vector3} connectorPos - World position of the connector
17958
+ * @returns {Object|null} Virtual segment info or null if no parent
17959
+ */
17960
+ }, {
17961
+ key: "createVirtualSegment",
17962
+ value: function createVirtualSegment(connector, connectorPos) {
17963
+ var direction = this.getConnectorDirection(connector);
17964
+ if (!direction) {
17965
+ return null;
17966
+ }
17967
+
17968
+ // Create a virtual segment starting from the connector position
17969
+ // and extending in the direction vector for MIN_SEGMENT_LENGTH
17970
+ var endPos = new Vector3(connectorPos.x + direction.x * this.MIN_SEGMENT_LENGTH, connectorPos.y + direction.y * this.MIN_SEGMENT_LENGTH, connectorPos.z + direction.z * this.MIN_SEGMENT_LENGTH);
17971
+
17972
+ // Convert to voxel keys
17973
+ var startKey = this.gridSystem.voxelKey(connectorPos);
17974
+ var endKey = this.gridSystem.voxelKey(endPos);
17975
+ return {
17976
+ startPos: connectorPos,
17977
+ endPos: endPos,
17978
+ direction: direction,
17979
+ startKey: startKey,
17980
+ endKey: endKey
17981
+ };
17982
+ }
17983
+
17984
+ /**
17985
+ * Process all connectors in the scene and mark their virtual segments as occupied
17986
+ * @param {Object} sceneRoot - Root scene object
17987
+ * @param {Set<string>} pathOccupied - Set of occupied voxel keys
17988
+ * @param {string} excludeUUID1 - UUID of first object to exclude
17989
+ * @param {string} excludeUUID2 - UUID of second object to exclude
17990
+ */
17991
+ }, {
17992
+ key: "processConnectors",
17993
+ value: function processConnectors(sceneRoot, pathOccupied, excludeUUID1, excludeUUID2) {
17994
+ this._processConnectorsRecursive(sceneRoot, pathOccupied, excludeUUID1, excludeUUID2);
17995
+ }
17996
+
17997
+ /**
17998
+ * Recursive helper for processConnectors
17999
+ * @private
18000
+ * @param {Object} object - Current object to process
18001
+ * @param {Set<string>} pathOccupied - Set of occupied voxel keys
18002
+ * @param {string} excludeUUID1 - UUID of first object to exclude
18003
+ * @param {string} excludeUUID2 - UUID of second object to exclude
18004
+ */
18005
+ }, {
18006
+ key: "_processConnectorsRecursive",
18007
+ value: function _processConnectorsRecursive(object, pathOccupied, excludeUUID1, excludeUUID2) {
18008
+ // Process current object if it's a mesh
18009
+ if (object.type === 'Mesh') {
18010
+ // Skip the excluded objects
18011
+ if (object.uuid === excludeUUID1 || object.uuid === excludeUUID2) {
18012
+ return;
18013
+ }
18014
+ var connectorPos = this.sceneManager.getWorldPosition(object);
18015
+ var segment = this.createVirtualSegment(object, connectorPos);
18016
+ if (segment) {
18017
+ this._markVirtualSegmentAsOccupied(segment, connectorPos, pathOccupied);
18018
+ }
18019
+ }
18020
+
18021
+ // Process children recursively
18022
+ if (object.children) {
18023
+ var _iterator = _createForOfIteratorHelper(object.children),
18024
+ _step;
18025
+ try {
18026
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
18027
+ var child = _step.value;
18028
+ this._processConnectorsRecursive(child, pathOccupied, excludeUUID1, excludeUUID2);
18029
+ }
18030
+ } catch (err) {
18031
+ _iterator.e(err);
18032
+ } finally {
18033
+ _iterator.f();
18034
+ }
18035
+ }
18036
+ }
18037
+
18038
+ /**
18039
+ * Mark voxels in a virtual segment as occupied
18040
+ * @private
18041
+ * @param {Object} segment - Virtual segment info
18042
+ * @param {Vector3} connectorPos - Position of the connector
18043
+ * @param {Set<string>} pathOccupied - Set of occupied voxel keys
18044
+ */
18045
+ }, {
18046
+ key: "_markVirtualSegmentAsOccupied",
18047
+ value: function _markVirtualSegmentAsOccupied(segment, connectorPos, pathOccupied) {
18048
+ var direction = segment.direction;
18049
+ var distance = this.gridSystem.worldToVoxelDistance(this.MIN_SEGMENT_LENGTH + this.gridSystem.safetyMargin * 2);
18050
+ for (var i = 0; i <= distance; i++) {
18051
+ var x = Math.round(connectorPos.x / this.gridSystem.gridSize + direction.x * i);
18052
+ var y = Math.round(connectorPos.y / this.gridSystem.gridSize + direction.y * i);
18053
+ var z = Math.round(connectorPos.z / this.gridSystem.gridSize + direction.z * i);
18054
+ var key = "".concat(x, ",").concat(y, ",").concat(z);
18055
+ pathOccupied.add(key);
18056
+ }
18057
+ }
18058
+
18059
+ /**
18060
+ * Cluster connections by shared objects
18061
+ * @param {Array<Object>} connections - Array of connections between objects
18062
+ * @returns {Array<Object>} Array of clusters
18063
+ */
18064
+ }, {
18065
+ key: "clusterConnections",
18066
+ value: function clusterConnections(connections) {
18067
+ var _this = this;
18068
+ var clusters = new Map(); // Map of object UUID to its cluster
18069
+ var clusterMap = new Map(); // Map of cluster ID to set of object UUIDs
18070
+ var nextClusterId = 0;
18071
+
18072
+ // First pass: create initial clusters for each object
18073
+ connections.forEach(function (conn) {
18074
+ var from = conn.from,
18075
+ to = conn.to;
18076
+
18077
+ // If neither object is in a cluster, create new cluster
18078
+ if (!clusters.has(from) && !clusters.has(to)) {
18079
+ var clusterId = nextClusterId++;
18080
+ clusters.set(from, clusterId);
18081
+ clusters.set(to, clusterId);
18082
+ clusterMap.set(clusterId, new Set([from, to]));
18083
+ }
18084
+ // If only 'from' is in a cluster, add 'to' to that cluster
18085
+ else if (clusters.has(from) && !clusters.has(to)) {
18086
+ var _clusterId = clusters.get(from);
18087
+ clusters.set(to, _clusterId);
18088
+ clusterMap.get(_clusterId).add(to);
18089
+ }
18090
+ // If only 'to' is in a cluster, add 'from' to that cluster
18091
+ else if (!clusters.has(from) && clusters.has(to)) {
18092
+ var _clusterId2 = clusters.get(to);
18093
+ clusters.set(from, _clusterId2);
18094
+ clusterMap.get(_clusterId2).add(from);
18095
+ }
18096
+ // If both are in different clusters, merge the clusters
18097
+ else if (clusters.has(from) && clusters.has(to)) {
18098
+ var fromCluster = clusters.get(from);
18099
+ var toCluster = clusters.get(to);
18100
+ if (fromCluster !== toCluster) {
18101
+ // Merge toCluster into fromCluster
18102
+ var toClusterObjects = clusterMap.get(toCluster);
18103
+ toClusterObjects.forEach(function (objId) {
18104
+ clusters.set(objId, fromCluster);
18105
+ clusterMap.get(fromCluster).add(objId);
18106
+ });
18107
+ clusterMap.delete(toCluster);
18108
+ }
18109
+ }
18110
+ });
18111
+
18112
+ // Convert clusters to array format
18113
+ var clustersArray = Array.from(clusterMap.entries()).map(function (_ref) {
18114
+ var _ref2 = _slicedToArray(_ref, 2),
18115
+ clusterId = _ref2[0],
18116
+ objects = _ref2[1];
18117
+ return {
18118
+ clusterId: clusterId,
18119
+ objects: Array.from(objects)
18120
+ };
18121
+ });
18122
+
18123
+ // Filter out clusters containing objects with "GATEWAY" in their UUID
18124
+ var filteredClusters = clustersArray.filter(function (cluster) {
18125
+ return !cluster.objects.some(function (uuid) {
18126
+ return uuid.includes('GATEWAY');
18127
+ });
18128
+ });
18129
+
18130
+ // Enrich clusters with point and direction information
18131
+ filteredClusters.forEach(function (cluster) {
18132
+ cluster.objectPoints = cluster.objects.map(function (uuid) {
18133
+ var object = _this.sceneManager.findObjectByUUID(uuid);
18134
+ if (object && object.userData.worldBoundingBox) {
18135
+ var center = _this.sceneManager.getWorldPosition(object);
18136
+ return {
18137
+ uuid: uuid,
18138
+ point: center,
18139
+ direction: object.userData.direction ? _construct(Vector3, _toConsumableArray(object.userData.direction)) : null
18140
+ };
18141
+ }
18142
+ return null;
18143
+ }).filter(function (point) {
18144
+ return point !== null;
18145
+ });
18146
+
18147
+ // Add displaced points to each cluster
18148
+ cluster.displacedPoints = cluster.objectPoints.map(function (objPoint) {
18149
+ var displacedPoint = objPoint.direction ? objPoint.point.clone().add(objPoint.direction.multiplyScalar(0.5)) : objPoint.point.clone();
18150
+ return {
18151
+ uuid: objPoint.uuid,
18152
+ originalPoint: objPoint.point,
18153
+ displacedPoint: displacedPoint,
18154
+ direction: objPoint.direction
18155
+ };
18156
+ });
18157
+ });
18158
+ return filteredClusters;
18159
+ }
18160
+ }]);
18161
+ }();
18162
+
18163
+ /**
18164
+ * Manages path-related operations for the pathfinding system
18165
+ * @class PathManager
18166
+ */
18167
+ var PathManager = /*#__PURE__*/function () {
18168
+ /**
18169
+ * Create a new PathManager instance
18170
+ * @param {SceneManager} sceneManager - Instance of SceneManager for scene operations
18171
+ * @param {GridSystem} gridSystem - Instance of GridSystem for grid operations
18172
+ * @param {ConnectorManager} connectorManager - Instance of ConnectorManager for connector operations
18173
+ * @param {number} minSegmentLength - Minimum length for straight pipe segments in world units
18174
+ * @param {number} astarTimeout - Timeout for A* pathfinding in milliseconds
18175
+ */
18176
+ function PathManager(sceneManager, gridSystem, connectorManager, minSegmentLength, astarTimeout) {
18177
+ _classCallCheck(this, PathManager);
18178
+ this.sceneManager = sceneManager;
18179
+ this.gridSystem = gridSystem;
18180
+ this.connectorManager = connectorManager;
18181
+ this.MIN_SEGMENT_LENGTH = minSegmentLength;
18182
+ this.ASTAR_TIMEOUT = astarTimeout;
18183
+ }
18184
+
18185
+ /**
18186
+ * A* pathfinding algorithm
18187
+ * @private
18188
+ * @param {string} start - Start voxel key
18189
+ * @param {string} goal - Goal voxel key
18190
+ * @param {Set<string>} occupied - Set of occupied voxel keys
18191
+ * @returns {string[]|null} Path as array of voxel keys or null if no path found
18192
+ */
18193
+ return _createClass(PathManager, [{
18194
+ key: "astar",
18195
+ value: function astar(start, goal, occupied) {
18196
+ var open = new Set([start]);
18197
+ var cameFrom = {};
18198
+ var gScore = _defineProperty({}, start, 0);
18199
+ var fScore = _defineProperty({}, start, this.gridSystem.manhattan(start, goal));
18200
+
18201
+ // Add timeout to prevent infinite loops
18202
+ var startTime = Date.now();
18203
+ while (open.size > 0) {
18204
+ // Check for timeout
18205
+ if (Date.now() - startTime > this.ASTAR_TIMEOUT) {
18206
+ console.log('[Warning] Pathfinding timed out');
18207
+ return null;
18208
+ }
18209
+
18210
+ // Get node in open with lowest fScore
18211
+ var current = null;
18212
+ var minF = Infinity;
18213
+ var _iterator = _createForOfIteratorHelper(open),
18214
+ _step;
18215
+ try {
18216
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
18217
+ var node = _step.value;
18218
+ if (fScore[node] < minF) {
18219
+ minF = fScore[node];
18220
+ current = node;
18221
+ }
18222
+ }
18223
+ } catch (err) {
18224
+ _iterator.e(err);
18225
+ } finally {
18226
+ _iterator.f();
18227
+ }
18228
+ if (current === goal) {
18229
+ // Reconstruct path
18230
+ var path = [current];
18231
+ while (cameFrom[current]) {
18232
+ current = cameFrom[current];
18233
+ path.unshift(current);
18234
+ }
18235
+ return path;
18236
+ }
18237
+ open.delete(current);
18238
+ var _iterator2 = _createForOfIteratorHelper(this.gridSystem.getNeighbors(current)),
18239
+ _step2;
18240
+ try {
18241
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
18242
+ var _gScore$neighbor;
18243
+ var neighbor = _step2.value;
18244
+ if (occupied.has(neighbor)) continue;
18245
+ var tentativeG = gScore[current] + 1;
18246
+ if (tentativeG < ((_gScore$neighbor = gScore[neighbor]) !== null && _gScore$neighbor !== void 0 ? _gScore$neighbor : Infinity)) {
18247
+ cameFrom[neighbor] = current;
18248
+ gScore[neighbor] = tentativeG;
18249
+ fScore[neighbor] = tentativeG + this.gridSystem.manhattan(neighbor, goal);
18250
+ open.add(neighbor);
18251
+ }
18252
+ }
18253
+ } catch (err) {
18254
+ _iterator2.e(err);
18255
+ } finally {
18256
+ _iterator2.f();
18257
+ }
18258
+ }
18259
+ return null;
18260
+ }
18261
+
18262
+ /**
18263
+ * Find a path respecting virtual segments
18264
+ * @private
18265
+ * @param {string} startKey - Start voxel key
18266
+ * @param {string} endKey - End voxel key
18267
+ * @param {Set<string>} occupied - Set of occupied voxel keys
18268
+ * @param {Object} startSegment - Virtual segment for start connector (optional)
18269
+ * @param {Object} endSegment - Virtual segment for end connector (optional)
18270
+ * @returns {string[]|null} Path as array of voxel keys or null if no path found
18271
+ */
18272
+ }, {
18273
+ key: "findPathWithVirtualSegments",
18274
+ value: function findPathWithVirtualSegments(startKey, endKey, occupied, startSegment, endSegment) {
18275
+ // If we have virtual segments, we need to ensure the path starts and ends with them
18276
+ var actualStartKey = startKey;
18277
+ var actualEndKey = endKey;
18278
+
18279
+ // Create a copy of occupied set to avoid modifying the original
18280
+ var pathOccupied = new Set(occupied);
18281
+
18282
+ // If we have a start segment, use its end as the actual start for pathfinding
18283
+ // and mark the segment as occupied
18284
+ if (startSegment) {
18285
+ actualStartKey = startSegment.endKey;
18286
+ // Mark all voxels in the start segment as occupied
18287
+ var startPos = startSegment.startPos;
18288
+ var direction = startSegment.direction;
18289
+ var distance = this.gridSystem.worldToVoxelDistance(this.MIN_SEGMENT_LENGTH);
18290
+ // Note: we go up to distance-1 to exclude the connecting end point
18291
+ for (var i = 0; i < distance; i++) {
18292
+ var x = Math.round(startPos.x / this.gridSystem.gridSize + direction.x * i);
18293
+ var y = Math.round(startPos.y / this.gridSystem.gridSize + direction.y * i);
18294
+ var z = Math.round(startPos.z / this.gridSystem.gridSize + direction.z * i);
18295
+ var key = "".concat(x, ",").concat(y, ",").concat(z);
18296
+ pathOccupied.add(key);
18297
+ }
18298
+ }
18299
+
18300
+ // If we have an end segment, use its end as the actual end for pathfinding
18301
+ // and mark the segment as occupied
18302
+ if (endSegment) {
18303
+ actualEndKey = endSegment.endKey;
18304
+ // Mark all voxels in the end segment as occupied
18305
+ var endPos = endSegment.startPos;
18306
+ var _direction = endSegment.direction;
18307
+ var _distance = this.gridSystem.worldToVoxelDistance(this.MIN_SEGMENT_LENGTH);
18308
+ // Note: we go up to distance-1 to exclude the connecting end point
18309
+ for (var _i = 0; _i < _distance; _i++) {
18310
+ var _x = Math.round(endPos.x / this.gridSystem.gridSize + _direction.x * _i);
18311
+ var _y = Math.round(endPos.y / this.gridSystem.gridSize + _direction.y * _i);
18312
+ var _z = Math.round(endPos.z / this.gridSystem.gridSize + _direction.z * _i);
18313
+ var _key = "".concat(_x, ",").concat(_y, ",").concat(_z);
18314
+ pathOccupied.add(_key);
18315
+ }
18316
+ }
18317
+
18318
+ // Find a path between the actual start and end
18319
+ var middlePath = this.astar(actualStartKey, actualEndKey, pathOccupied);
18320
+ if (!middlePath) {
18321
+ return null;
18322
+ }
18323
+
18324
+ // Construct the full path
18325
+ var fullPath = [];
18326
+
18327
+ // Add the start segment if we have one
18328
+ if (startSegment) {
18329
+ fullPath.push(startSegment.startKey);
18330
+ }
18331
+
18332
+ // Add the middle path
18333
+ fullPath = fullPath.concat(middlePath);
18334
+
18335
+ // Add the end segment if we have one
18336
+ if (endSegment) {
18337
+ fullPath.push(endSegment.startKey);
18338
+ }
18339
+ return fullPath;
18340
+ }
18341
+
18342
+ /**
18343
+ * Mark all mesh objects' voxels as occupied in the scene
18344
+ * @private
18345
+ * @param {Object} sceneRoot - Root scene object
18346
+ * @returns {Object} Object containing occupied sets and maps
18347
+ */
18348
+ }, {
18349
+ key: "markAllMeshesAsOccupiedVoxels",
18350
+ value: function markAllMeshesAsOccupiedVoxels(sceneRoot) {
18351
+ var _this = this;
18352
+ var occupied = new Set();
18353
+ var occupiedByUUID = new Map();
18354
+ var _processObject = function processObject(object) {
18355
+ // Skip non-mesh objects
18356
+ if (object.type !== 'Mesh') return;
18357
+
18358
+ // Get object's bounding box
18359
+ var bbox = _this.sceneManager.getBoundingBox(object);
18360
+ if (!bbox) return;
18361
+
18362
+ // Get all voxels in the bounding box plus safety margin
18363
+ var voxels = _this.gridSystem.getVoxelsInBoundingBox(bbox);
18364
+
18365
+ // Create a set for this object's occupied voxels
18366
+ var objectOccupied = new Set(voxels);
18367
+
18368
+ // Add all voxels to the global occupied set
18369
+ var _iterator3 = _createForOfIteratorHelper(voxels),
18370
+ _step3;
18371
+ try {
18372
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
18373
+ var key = _step3.value;
18374
+ occupied.add(key);
18375
+ }
18376
+
18377
+ // Store the object's occupied voxels in the map
18378
+ } catch (err) {
18379
+ _iterator3.e(err);
18380
+ } finally {
18381
+ _iterator3.f();
18382
+ }
18383
+ occupiedByUUID.set(object.uuid, objectOccupied);
18384
+
18385
+ // Process children recursively
18386
+ if (object.children) {
18387
+ var _iterator4 = _createForOfIteratorHelper(object.children),
18388
+ _step4;
18389
+ try {
18390
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
18391
+ var child = _step4.value;
18392
+ _processObject(child);
18393
+ }
18394
+ } catch (err) {
18395
+ _iterator4.e(err);
18396
+ } finally {
18397
+ _iterator4.f();
18398
+ }
18399
+ }
18400
+ };
18401
+
18402
+ // Process all objects in the scene's children array
18403
+ if (sceneRoot.children) {
18404
+ var _iterator5 = _createForOfIteratorHelper(sceneRoot.children),
18405
+ _step5;
18406
+ try {
18407
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
18408
+ var object = _step5.value;
18409
+ _processObject(object);
18410
+ }
18411
+ } catch (err) {
18412
+ _iterator5.e(err);
18413
+ } finally {
18414
+ _iterator5.f();
18415
+ }
18416
+ }
18417
+ return {
18418
+ occupied: occupied,
18419
+ occupiedByUUID: occupiedByUUID
18420
+ };
18421
+ }
18422
+
18423
+ /**
18424
+ * Process a single connection and find its path
18425
+ * @private
18426
+ * @param {Object} connection - Connection object
18427
+ * @param {Set<string>} occupied - Set of occupied voxel keys
18428
+ * @param {Map<string, Set<string>>} occupiedByUUID - Map of occupied voxels by UUID
18429
+ * @returns {Object|null} Path result object or null if connection is invalid
18430
+ */
18431
+ }, {
18432
+ key: "processConnection",
18433
+ value: function processConnection(connection, occupied, occupiedByUUID) {
18434
+ var _this2 = this;
18435
+ var fromObject = this.sceneManager.findObjectByUUID(connection.from);
18436
+ var toObject = this.sceneManager.findObjectByUUID(connection.to);
18437
+ if (!fromObject || !toObject) {
18438
+ console.log("[ERROR] Could not find objects for connection ".concat(connection.from, " - ").concat(connection.to));
18439
+ return null;
18440
+ }
18441
+
18442
+ // Verify that both objects are meshes
18443
+ if (fromObject.type !== 'Mesh' || toObject.type !== 'Mesh') {
18444
+ console.log("[ERROR] Connection endpoints must be meshes: ".concat(fromObject.type, " - ").concat(toObject.type));
18445
+ return null;
18446
+ }
18447
+
18448
+ // Store the parent for each object to access later
18449
+ fromObject.parent = this.sceneManager.findParentObject(fromObject);
18450
+ toObject.parent = this.sceneManager.findParentObject(toObject);
18451
+
18452
+ // Get world positions for both objects
18453
+ var worldPos1 = this.sceneManager.getWorldPosition(fromObject);
18454
+ var worldPos2 = this.sceneManager.getWorldPosition(toObject);
18455
+
18456
+ // Create virtual segments for both connectors
18457
+ var startSegment = this.connectorManager.createVirtualSegment(fromObject, worldPos1);
18458
+ var endSegment = this.connectorManager.createVirtualSegment(toObject, worldPos2);
18459
+
18460
+ // Create a copy of occupied set for this path
18461
+ var pathOccupied = new Set(occupied);
18462
+
18463
+ // Debug copy 1: Initial state after copying from global occupied
18464
+ var debugOccupiedInitial = Array.from(pathOccupied).map(function (key) {
18465
+ return _this2.gridSystem.voxelToVec3(key);
18466
+ });
18467
+
18468
+ // Get bounding boxes for the two objects
18469
+ var fromBbox = this.sceneManager.getBoundingBox(fromObject);
18470
+ var toBbox = this.sceneManager.getBoundingBox(toObject);
18471
+ if (!fromBbox || !toBbox) return null;
18472
+
18473
+ // Clear voxels in bounding boxes only for objects that do NOT have virtual segments
18474
+ if (!startSegment) {
18475
+ var voxels = this.gridSystem.getVoxelsInBoundingBox(fromBbox);
18476
+ var _iterator6 = _createForOfIteratorHelper(voxels),
18477
+ _step6;
18478
+ try {
18479
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
18480
+ var key = _step6.value;
18481
+ pathOccupied.delete(key);
18482
+ }
18483
+ } catch (err) {
18484
+ _iterator6.e(err);
18485
+ } finally {
18486
+ _iterator6.f();
18487
+ }
18488
+ }
18489
+ if (!endSegment) {
18490
+ var _voxels = this.gridSystem.getVoxelsInBoundingBox(toBbox);
18491
+ var _iterator7 = _createForOfIteratorHelper(_voxels),
18492
+ _step7;
18493
+ try {
18494
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
18495
+ var _key2 = _step7.value;
18496
+ pathOccupied.delete(_key2);
18497
+ }
18498
+ } catch (err) {
18499
+ _iterator7.e(err);
18500
+ } finally {
18501
+ _iterator7.f();
18502
+ }
18503
+ }
18504
+
18505
+ // Also clear the voxels for the virtual segments if they exist
18506
+ if (startSegment) {
18507
+ var direction = startSegment.direction;
18508
+ var distance = this.gridSystem.worldToVoxelDistance(this.MIN_SEGMENT_LENGTH + this.gridSystem.safetyMargin * 2);
18509
+ for (var i = 0; i <= distance; i++) {
18510
+ var x = Math.round(worldPos1.x / this.gridSystem.gridSize + direction.x * i);
18511
+ var y = Math.round(worldPos1.y / this.gridSystem.gridSize + direction.y * i);
18512
+ var z = Math.round(worldPos1.z / this.gridSystem.gridSize + direction.z * i);
18513
+ pathOccupied.delete("".concat(x, ",").concat(y, ",").concat(z));
18514
+ }
18515
+ }
18516
+ if (endSegment) {
18517
+ var _direction2 = endSegment.direction;
18518
+ var _distance2 = this.gridSystem.worldToVoxelDistance(this.MIN_SEGMENT_LENGTH + this.gridSystem.safetyMargin * 2);
18519
+ for (var _i2 = 0; _i2 <= _distance2; _i2++) {
18520
+ var _x2 = Math.round(worldPos2.x / this.gridSystem.gridSize + _direction2.x * _i2);
18521
+ var _y2 = Math.round(worldPos2.y / this.gridSystem.gridSize + _direction2.y * _i2);
18522
+ var _z2 = Math.round(worldPos2.z / this.gridSystem.gridSize + _direction2.z * _i2);
18523
+ pathOccupied.delete("".concat(_x2, ",").concat(_y2, ",").concat(_z2));
18524
+ }
18525
+ }
18526
+
18527
+ // Process all other connectors in the scene
18528
+ this.connectorManager.processConnectors(this.sceneManager.getRoot(), pathOccupied, fromObject.uuid, toObject.uuid);
18529
+
18530
+ // Debug copy 3: After clearing virtual segments and adding back previous paths
18531
+ var debugOccupiedAfterSegments = Array.from(pathOccupied).map(function (key) {
18532
+ return _this2.gridSystem.voxelToVec3(key);
18533
+ });
18534
+
18535
+ // Get start and end voxel keys
18536
+ var startKey = this.gridSystem.voxelKey(worldPos1);
18537
+ var endKey = this.gridSystem.voxelKey(worldPos2);
18538
+
18539
+ // Find a path with virtual segments
18540
+ var pathKeys = this.findPathWithVirtualSegments(startKey, endKey, pathOccupied, startSegment, endSegment);
18541
+ var occupiedVoxels = Array.from(pathOccupied).map(function (key) {
18542
+ return _this2.gridSystem.voxelToVec3(key);
18543
+ });
18544
+ if (pathKeys) {
18545
+ var path = pathKeys.map(function (key) {
18546
+ return _this2.gridSystem.voxelToVec3(key);
18547
+ });
18548
+
18549
+ // Create a set for this path's occupied voxels
18550
+ var _pathOccupied = new Set();
18551
+
18552
+ // Mark path points as occupied for next routes
18553
+ var _iterator8 = _createForOfIteratorHelper(path),
18554
+ _step8;
18555
+ try {
18556
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
18557
+ var p = _step8.value;
18558
+ var _key3 = this.gridSystem.voxelKey(p);
18559
+ occupied.add(_key3);
18560
+ _pathOccupied.add(_key3);
18561
+ }
18562
+
18563
+ // Store the path's occupied voxels in the map with a special key
18564
+ } catch (err) {
18565
+ _iterator8.e(err);
18566
+ } finally {
18567
+ _iterator8.f();
18568
+ }
18569
+ var pathKey = "path_".concat(connection.from, "_").concat(connection.to);
18570
+ occupiedByUUID.set(pathKey, _pathOccupied);
18571
+ return {
18572
+ from: connection.from,
18573
+ to: connection.to,
18574
+ path: path,
18575
+ occupied: occupiedVoxels,
18576
+ debug: {
18577
+ initial: debugOccupiedInitial,
18578
+ afterSegments: debugOccupiedAfterSegments
18579
+ },
18580
+ occupiedByUUID: occupiedByUUID // Include the map in the result
18581
+ };
18582
+ } else {
18583
+ console.log("[ERROR] No orthogonal path found for ".concat(connection.from, "-").concat(connection.to));
18584
+ return {
18585
+ from: connection.from,
18586
+ to: connection.to,
18587
+ path: null,
18588
+ start: worldPos1,
18589
+ end: worldPos2,
18590
+ occupied: occupiedVoxels,
18591
+ debug: {
18592
+ initial: debugOccupiedInitial,
18593
+ afterSegments: debugOccupiedAfterSegments
18594
+ },
18595
+ occupiedByUUID: occupiedByUUID // Include the map in the result
18596
+ };
18597
+ }
18598
+ }
18599
+
18600
+ /**
18601
+ * Find paths for all connections
18602
+ * @param {Array<Object>} connections - Array of connections to process
18603
+ * @returns {Array<Object>} Array of path results
18604
+ */
18605
+ }, {
18606
+ key: "findPaths",
18607
+ value: function findPaths(connections) {
18608
+ console.log('[DEBUG] Starting findPaths()');
18609
+
18610
+ // Validate scene structure
18611
+ if (!this.sceneManager.getRoot()) {
18612
+ console.log('[ERROR] Invalid scene structure: missing scene root');
18613
+ return [];
18614
+ }
18615
+
18616
+ // Process all objects in the scene
18617
+ var _this$markAllMeshesAs = this.markAllMeshesAsOccupiedVoxels(this.sceneManager.getRoot()),
18618
+ occupied = _this$markAllMeshesAs.occupied,
18619
+ occupiedByUUID = _this$markAllMeshesAs.occupiedByUUID;
18620
+ var paths = [];
18621
+
18622
+ // For each connection in the config, find a path
18623
+ var _iterator9 = _createForOfIteratorHelper(connections),
18624
+ _step9;
18625
+ try {
18626
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
18627
+ var connection = _step9.value;
18628
+ var result = this.processConnection(connection, occupied, occupiedByUUID);
18629
+ if (result) {
18630
+ paths.push(result);
18631
+ }
18632
+ }
18633
+ } catch (err) {
18634
+ _iterator9.e(err);
18635
+ } finally {
18636
+ _iterator9.f();
18637
+ }
18638
+ return paths;
18639
+ }
18640
+ }]);
18641
+ }();
18642
+
18643
+ /**
18644
+ * Manages tree path calculations and Minimum Spanning Tree (MST) operations
18645
+ * @class TreePathManager
18646
+ */
18647
+ var TreePathManager = /*#__PURE__*/function () {
18648
+ function TreePathManager() {
18649
+ var gridSystem = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
18650
+ var sceneManager = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
18651
+ _classCallCheck(this, TreePathManager);
18652
+ this.gridSystem = gridSystem;
18653
+ this.sceneManager = sceneManager;
18654
+ }
18655
+
18656
+ /**
18657
+ * Calculate distance between two points
18658
+ * @param {Vector3} p1 - First point
18659
+ * @param {Vector3} p2 - Second point
18660
+ * @returns {number} Distance between points
18661
+ */
18662
+ return _createClass(TreePathManager, [{
18663
+ key: "distance",
18664
+ value: function distance(p1, p2) {
18665
+ var dx = p2.x - p1.x;
18666
+ var dy = p2.y - p1.y;
18667
+ var dz = p2.z - p1.z;
18668
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
18669
+ }
18670
+
18671
+ /**
18672
+ * Find Minimum Spanning Tree using Prim's algorithm
18673
+ * @param {Array<Vector3>} points - Array of points to connect
18674
+ * @returns {Array<Array<number>>} Array of edges in the MST
18675
+ */
18676
+ }, {
18677
+ key: "findMST",
18678
+ value: function findMST(points) {
18679
+ var n = points.length;
18680
+ var mst = [];
18681
+ var visited = new Set();
18682
+
18683
+ // Start with the first point
18684
+ visited.add(0);
18685
+ while (visited.size < n) {
18686
+ var minDist = Infinity;
18687
+ var minEdge = null;
18688
+
18689
+ // Find the minimum edge from visited to unvisited
18690
+ var _iterator = _createForOfIteratorHelper(visited),
18691
+ _step;
18692
+ try {
18693
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
18694
+ var i = _step.value;
18695
+ for (var j = 0; j < n; j++) {
18696
+ if (!visited.has(j)) {
18697
+ var dist = this.distance(points[i], points[j]);
18698
+ if (dist < minDist) {
18699
+ minDist = dist;
18700
+ minEdge = [i, j];
18701
+ }
18702
+ }
18703
+ }
18704
+ }
18705
+ } catch (err) {
18706
+ _iterator.e(err);
18707
+ } finally {
18708
+ _iterator.f();
18709
+ }
18710
+ if (minEdge) {
18711
+ mst.push(minEdge);
18712
+ visited.add(minEdge[1]);
18713
+ } else {
18714
+ break; // Break if we can't find any more edges
18715
+ }
18716
+ }
18717
+ return mst;
18718
+ }
18719
+
18720
+ /**
18721
+ * Find the nearest free voxel to a given position
18722
+ * @param {Vector3} position - Position to find free voxel near
18723
+ * @param {number} maxSearchRadius - Maximum search radius in voxel units
18724
+ * @returns {Vector3|null} Nearest free position or null if none found
18725
+ */
18726
+ }, {
18727
+ key: "findNearestFreeVoxel",
18728
+ value: function findNearestFreeVoxel(position) {
18729
+ var maxSearchRadius = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
18730
+ if (!this.gridSystem || !this.sceneManager) {
18731
+ return position; // Return original position if systems not available
18732
+ }
18733
+ var originalVoxelKey = this.gridSystem.voxelKey(position);
18734
+
18735
+ // Check if original position is free
18736
+ if (!this.sceneManager.isVoxelOccupiedByMesh(originalVoxelKey, this.gridSystem.gridSize)) {
18737
+ return position;
18738
+ }
18739
+
18740
+ // Search in expanding rings around the position
18741
+ for (var radius = 1; radius <= maxSearchRadius; radius++) {
18742
+ var _originalVoxelKey$spl = originalVoxelKey.split(',').map(Number),
18743
+ _originalVoxelKey$spl2 = _slicedToArray(_originalVoxelKey$spl, 3),
18744
+ x = _originalVoxelKey$spl2[0],
18745
+ y = _originalVoxelKey$spl2[1],
18746
+ z = _originalVoxelKey$spl2[2];
18747
+
18748
+ // Generate all voxels at this radius
18749
+ var candidates = [];
18750
+
18751
+ // Generate all combinations of offsets at this radius
18752
+ for (var dx = -radius; dx <= radius; dx++) {
18753
+ for (var dy = -radius; dy <= radius; dy++) {
18754
+ for (var dz = -radius; dz <= radius; dz++) {
18755
+ // Only consider voxels exactly at this radius
18756
+ if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) === radius) {
18757
+ candidates.push([x + dx, y + dy, z + dz]);
18758
+ }
18759
+ }
18760
+ }
18761
+ }
18762
+
18763
+ // Check each candidate
18764
+ for (var _i = 0, _candidates = candidates; _i < _candidates.length; _i++) {
18765
+ var _candidates$_i = _slicedToArray(_candidates[_i], 3),
18766
+ cx = _candidates$_i[0],
18767
+ cy = _candidates$_i[1],
18768
+ cz = _candidates$_i[2];
18769
+ var candidateKey = "".concat(cx, ",").concat(cy, ",").concat(cz);
18770
+ if (!this.sceneManager.isVoxelOccupiedByMesh(candidateKey, this.gridSystem.gridSize)) {
18771
+ return this.gridSystem.voxelToVec3(candidateKey);
18772
+ }
18773
+ }
18774
+ }
18775
+ return null;
18776
+ }
18777
+
18778
+ /**
18779
+ * Find joint point
18780
+ * @param {Array<Vector3>} points - Array of points to connect
18781
+ * @returns {Vector3|null} Joint point or null if not possible
18782
+ */
18783
+ }, {
18784
+ key: "findJointPoint",
18785
+ value: function findJointPoint(points) {
18786
+ if (points.length <= 2) {
18787
+ return null;
18788
+ }
18789
+
18790
+ // Find MST first
18791
+ var mst = this.findMST(points);
18792
+ if (mst.length === 0) {
18793
+ return null;
18794
+ }
18795
+
18796
+ // Find the best edge in MST
18797
+ var bestEdge = null;
18798
+ var maxScore = -1;
18799
+ mst.forEach(function (_ref, edgeIndex) {
18800
+ var _ref2 = _slicedToArray(_ref, 2),
18801
+ i = _ref2[0],
18802
+ j = _ref2[1];
18803
+ var pointA = points[i];
18804
+ var pointB = points[j];
18805
+
18806
+ // Calculate edge components
18807
+ var dx = Math.abs(pointB.x - pointA.x);
18808
+ var dy = Math.abs(pointB.y - pointA.y);
18809
+ var dz = Math.abs(pointB.z - pointA.z);
18810
+
18811
+ // Calculate total length
18812
+ var totalLength = dx + dy + dz;
18813
+ if (totalLength > 0) {
18814
+ // Calculate score
18815
+ // Higher score = more orthogonal (closer to being axis-aligned) relative to the total length of the edge
18816
+ var maxComponent = Math.max(dx, dy, dz);
18817
+ var score = maxComponent / totalLength / totalLength;
18818
+
18819
+ // Check if this edge has a better orthogonality score
18820
+ if (score > maxScore) {
18821
+ maxScore = score;
18822
+ bestEdge = [pointA, pointB];
18823
+ }
18824
+ }
18825
+ });
18826
+ if (!bestEdge) {
18827
+ return null;
18828
+ }
18829
+
18830
+ // Place joint point at the midpoint of the most orthogonal edge
18831
+ var jointPoint = new Vector3((bestEdge[0].x + bestEdge[1].x) * 0.5, (bestEdge[0].y + bestEdge[1].y) * 0.5, (bestEdge[0].z + bestEdge[1].z) * 0.5);
18832
+
18833
+ // Snap to 0.5 grid
18834
+ var snapToGrid = function snapToGrid(value) {
18835
+ return Math.round(value * 2) / 2;
18836
+ };
18837
+ jointPoint.x = snapToGrid(jointPoint.x);
18838
+ jointPoint.y = snapToGrid(jointPoint.y);
18839
+ jointPoint.z = snapToGrid(jointPoint.z);
18840
+
18841
+ // Check if the joint point is in an occupied voxel and move it if necessary
18842
+ var freeJointPoint = this.findNearestFreeVoxel(jointPoint);
18843
+ if (freeJointPoint) {
18844
+ return freeJointPoint;
18845
+ } else {
18846
+ return jointPoint; // Return original if no free position found
18847
+ }
18848
+ }
18849
+ }]);
18850
+ }();
18851
+
18852
+ /**
18853
+ * A 3D pathfinding system that finds orthogonal paths between objects
18854
+ * @class Pathfinder
18855
+ */
18856
+ var Pathfinder = /*#__PURE__*/function () {
18857
+ /**
18858
+ * Create a new Pathfinding instance
18859
+ * @param {Object} [config] - Optional configuration object
18860
+ * @param {Object} [config.grid] - Grid system configuration
18861
+ * @param {number} [config.grid.size=0.5] - Size of each grid cell in world units
18862
+ * @param {number} [config.grid.safetyMargin=0] - Safety margin around obstacles in world units
18863
+ * @param {number} [config.grid.minSegmentLength=0.5] - Minimum length for straight pipe segments in world units
18864
+ * @param {number} [config.grid.timeout=1000] - Timeout for A* pathfinding in milliseconds
18865
+ */
18866
+ function Pathfinder() {
18867
+ var _gridConfig$size, _gridConfig$safetyMar, _gridConfig$minSegmen, _gridConfig$timeout;
18868
+ var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
18869
+ _classCallCheck(this, Pathfinder);
18870
+ this.config = config;
18871
+
18872
+ // Grid system settings with defaults
18873
+ var gridConfig = config.grid || {};
18874
+ this.gridSystem = new GridSystem((_gridConfig$size = gridConfig.size) !== null && _gridConfig$size !== void 0 ? _gridConfig$size : 0.5, (_gridConfig$safetyMar = gridConfig.safetyMargin) !== null && _gridConfig$safetyMar !== void 0 ? _gridConfig$safetyMar : 0);
18875
+ this.MIN_SEGMENT_LENGTH = (_gridConfig$minSegmen = gridConfig.minSegmentLength) !== null && _gridConfig$minSegmen !== void 0 ? _gridConfig$minSegmen : 0.5;
18876
+ this.ASTAR_TIMEOUT = (_gridConfig$timeout = gridConfig.timeout) !== null && _gridConfig$timeout !== void 0 ? _gridConfig$timeout : 1000;
18877
+
18878
+ // Initialize connector manager
18879
+ this.connectorManager = new ConnectorManager({
18880
+ grid: gridConfig
18881
+ },
18882
+ // Only pass grid config
18883
+ this.MIN_SEGMENT_LENGTH, null,
18884
+ // sceneManager will be set in findPaths
18885
+ this.gridSystem);
18886
+
18887
+ // Initialize path manager
18888
+ this.pathManager = new PathManager(null,
18889
+ // sceneManager will be set in findPaths
18890
+ this.gridSystem, this.connectorManager, this.MIN_SEGMENT_LENGTH, this.ASTAR_TIMEOUT);
18891
+
18892
+ // Initialize tree path manager
18893
+ this.treePathManager = new TreePathManager(this.gridSystem, null);
18894
+ }
18895
+
18896
+ /**
18897
+ * Find paths for all connections
18898
+ * @param {Object} scene - The scene configuration
18899
+ * @param {Object} scene.object - The scene object containing children
18900
+ * @param {string} scene.object.uuid - Unique identifier for the scene object
18901
+ * @param {string} scene.object.type - Type of the scene object (typically "Scene")
18902
+ * @param {Array<Object>} scene.object.children - Array of scene objects
18903
+ * @param {string} scene.object.children[].uuid - Unique identifier for the child object
18904
+ * @param {string} scene.object.children[].type - Type of the child object (must be "Mesh")
18905
+ * @param {Object} scene.object.children[].userData - User data for the child object
18906
+ * @param {Object} scene.object.children[].userData.worldBoundingBox - Bounding box data
18907
+ * @param {Array<number>} scene.object.children[].userData.worldBoundingBox.min - Minimum coordinates [x,y,z]
18908
+ * @param {Array<number>} scene.object.children[].userData.worldBoundingBox.max - Maximum coordinates [x,y,z]
18909
+ * @param {Array<number>} [scene.object.children[].userData.direction] - Optional direction vector [x,y,z]
18910
+ * @param {Array<Object>} connections - Array of connections between objects
18911
+ * @param {string} connections[].from - UUID of the source object
18912
+ * @param {string} connections[].to - UUID of the target object
18913
+ * @returns {Object} Result object containing paths, rewired connections, and gateways
18914
+ * @property {Array<Object>} paths - Array of path results
18915
+ * @property {string} paths[].from - Source object UUID
18916
+ * @property {string} paths[].to - Target object UUID
18917
+ * @property {Array<Vector3>|null} paths[].path - Array of path points or null if no path found
18918
+ * @property {Array<Vector3>} [paths[].occupied] - Array of occupied points
18919
+ * @property {Vector3} [paths[].start] - Start position (if no path found)
18920
+ * @property {Vector3} [paths[].end] - End position (if no path found)
18921
+ * @property {Array<Object>} rewiredConnections - Array of connections after gateway optimization
18922
+ * @property {string} rewiredConnections[].from - Source object UUID
18923
+ * @property {string} rewiredConnections[].to - Target object UUID
18924
+ * @property {Array<Object>} gateways - Array of gateway information
18925
+ * @property {string} gateways[].clusterId - ID of the cluster this gateway belongs to
18926
+ * @property {number} gateways[].id - Unique identifier for the gateway
18927
+ * @property {Vector3} gateways[].position - Position of the gateway in 3D space
18928
+ * @property {Object} gateways[].connections - Connection mapping information
18929
+ * @property {Array<Object>} gateways[].connections.removed - Array of original connections that were removed
18930
+ * @property {string} gateways[].connections.removed[].from - Source object UUID
18931
+ * @property {string} gateways[].connections.removed[].to - Target object UUID
18932
+ * @property {Array<Object>} gateways[].connections.added - Array of new connections that were added
18933
+ * @property {string} gateways[].connections.added[].from - Source object UUID
18934
+ * @property {string} gateways[].connections.added[].to - Target object UUID
18935
+ */
18936
+ return _createClass(Pathfinder, [{
18937
+ key: "findPaths",
18938
+ value: function findPaths(scene, connections) {
18939
+ var _this = this;
18940
+ // Create scene manager with the provided scene
18941
+ var sceneManager = new SceneManager(scene);
18942
+
18943
+ // Update scene manager references
18944
+ this.connectorManager.sceneManager = sceneManager;
18945
+ this.pathManager.sceneManager = sceneManager;
18946
+ this.treePathManager.sceneManager = sceneManager;
18947
+
18948
+ // Cluster the connections
18949
+ var clusters = this.connectorManager.clusterConnections(connections);
18950
+
18951
+ // Filter clusters to only include those with more than 2 objects
18952
+ var filteredClusters = clusters.filter(function (cluster) {
18953
+ return cluster.objects.length > 2;
18954
+ });
18955
+
18956
+ // Calculate joint points for each cluster
18957
+ filteredClusters.forEach(function (cluster, index) {
18958
+ var points = cluster.displacedPoints.map(function (p) {
18959
+ return p.displacedPoint;
18960
+ });
18961
+ cluster.jointPoint = _this.treePathManager.findJointPoint(points);
18962
+ });
18963
+ var gatewayMap = new Map();
18964
+ filteredClusters.forEach(function (cluster) {
18965
+ if (cluster.jointPoint) {
18966
+ // Generate a shorter unique ID (8 characters)
18967
+ var uuid = crypto.randomUUID().replace(/-/g, '').substring(0, 8);
18968
+ var gatewayId = "Gateway-".concat(uuid);
18969
+ gatewayMap.set(cluster.clusterId, gatewayId);
18970
+ }
18971
+ });
18972
+
18973
+ // Create virtual gateway objects in the scene
18974
+ filteredClusters.forEach(function (cluster) {
18975
+ if (cluster.jointPoint) {
18976
+ var gatewayId = gatewayMap.get(cluster.clusterId);
18977
+ var gatewayObject = {
18978
+ uuid: gatewayId,
18979
+ type: 'Mesh',
18980
+ userData: {
18981
+ worldBoundingBox: {
18982
+ min: [cluster.jointPoint.x - 0.25, cluster.jointPoint.y - 0.25, cluster.jointPoint.z - 0.25],
18983
+ max: [cluster.jointPoint.x + 0.25, cluster.jointPoint.y + 0.25, cluster.jointPoint.z + 0.25]
18984
+ }
18985
+ }
18986
+ };
18987
+ scene.object.children.push(gatewayObject);
18988
+ }
18989
+ });
18990
+
18991
+ // Rewire connections through gateways
18992
+ var rewiredConnections = [];
18993
+ var connectionSet = new Set(); // Track unique connections
18994
+ var gatewayConnectionMappings = new Map(); // Track which original connections are replaced by which gateway connections
18995
+
18996
+ // Find direct connections (not part of clusters with >2 objects)
18997
+ var directConnections = connections.filter(function (conn) {
18998
+ var fromCluster = clusters.find(function (cluster) {
18999
+ return cluster.objects.includes(conn.from);
19000
+ });
19001
+ var toCluster = clusters.find(function (cluster) {
19002
+ return cluster.objects.includes(conn.to);
19003
+ });
19004
+ return !fromCluster || !toCluster || fromCluster.objects.length <= 2 || toCluster.objects.length <= 2;
19005
+ });
19006
+
19007
+ // First add direct connections
19008
+ directConnections.forEach(function (conn) {
19009
+ if (!connectionSet.has(JSON.stringify(conn))) {
19010
+ rewiredConnections.push(conn);
19011
+ connectionSet.add(JSON.stringify(conn));
19012
+ }
19013
+ });
19014
+
19015
+ // Then add gateway connections
19016
+ var gatewayConnections = [];
19017
+ connections.forEach(function (conn) {
19018
+ var _clusters$find, _clusters$find2;
19019
+ var fromCluster = (_clusters$find = clusters.find(function (cluster) {
19020
+ return cluster.objects.includes(conn.from);
19021
+ })) === null || _clusters$find === void 0 ? void 0 : _clusters$find.clusterId;
19022
+ var toCluster = (_clusters$find2 = clusters.find(function (cluster) {
19023
+ return cluster.objects.includes(conn.to);
19024
+ })) === null || _clusters$find2 === void 0 ? void 0 : _clusters$find2.clusterId;
19025
+ if (fromCluster !== undefined && toCluster !== undefined) {
19026
+ var fromGateway = gatewayMap.get(fromCluster);
19027
+ var toGateway = gatewayMap.get(toCluster);
19028
+ if (fromGateway !== undefined && toGateway !== undefined) {
19029
+ var fromGatewayId = fromGateway;
19030
+ var toGatewayId = toGateway;
19031
+
19032
+ // Track the original connection that will be replaced
19033
+ var originalConnection = {
19034
+ from: conn.from,
19035
+ to: conn.to
19036
+ };
19037
+ var addedConnections = [];
19038
+
19039
+ // If both objects are in the same cluster, connect through their gateway
19040
+ if (fromCluster === toCluster) {
19041
+ var conn1 = {
19042
+ from: conn.from,
19043
+ to: fromGatewayId
19044
+ };
19045
+ var conn2 = {
19046
+ from: fromGatewayId,
19047
+ to: conn.to
19048
+ };
19049
+
19050
+ // Only add if not already present
19051
+ if (!connectionSet.has(JSON.stringify(conn1))) {
19052
+ gatewayConnections.push(conn1);
19053
+ connectionSet.add(JSON.stringify(conn1));
19054
+ addedConnections.push(conn1);
19055
+ }
19056
+ if (!connectionSet.has(JSON.stringify(conn2))) {
19057
+ gatewayConnections.push(conn2);
19058
+ connectionSet.add(JSON.stringify(conn2));
19059
+ addedConnections.push(conn2);
19060
+ }
19061
+ } else {
19062
+ // If objects are in different clusters, connect through both gateways
19063
+ var _conn = {
19064
+ from: conn.from,
19065
+ to: fromGatewayId
19066
+ };
19067
+ var _conn2 = {
19068
+ from: fromGatewayId,
19069
+ to: toGatewayId
19070
+ };
19071
+ var conn3 = {
19072
+ from: toGatewayId,
19073
+ to: conn.to
19074
+ };
19075
+
19076
+ // Only add if not already present
19077
+ if (!connectionSet.has(JSON.stringify(_conn))) {
19078
+ gatewayConnections.push(_conn);
19079
+ connectionSet.add(JSON.stringify(_conn));
19080
+ addedConnections.push(_conn);
19081
+ }
19082
+ if (!connectionSet.has(JSON.stringify(_conn2))) {
19083
+ gatewayConnections.push(_conn2);
19084
+ connectionSet.add(JSON.stringify(_conn2));
19085
+ addedConnections.push(_conn2);
19086
+ }
19087
+ if (!connectionSet.has(JSON.stringify(conn3))) {
19088
+ gatewayConnections.push(conn3);
19089
+ connectionSet.add(JSON.stringify(conn3));
19090
+ addedConnections.push(conn3);
19091
+ }
19092
+ }
19093
+
19094
+ // Store the mapping for this connection
19095
+ if (addedConnections.length > 0) {
19096
+ gatewayConnectionMappings.set(JSON.stringify(originalConnection), {
19097
+ removed: [originalConnection],
19098
+ added: addedConnections
19099
+ });
19100
+ }
19101
+ }
19102
+ }
19103
+ });
19104
+
19105
+ // Sort gateway connections by edge length
19106
+ gatewayConnections.sort(function (a, b) {
19107
+ var objA1 = sceneManager.findObjectByUUID(a.from);
19108
+ var objA2 = sceneManager.findObjectByUUID(a.to);
19109
+ var objB1 = sceneManager.findObjectByUUID(b.from);
19110
+ var objB2 = sceneManager.findObjectByUUID(b.to);
19111
+ if (!objA1 || !objA2 || !objB1 || !objB2) return 0;
19112
+ var distA = _this.treePathManager.distance(sceneManager.getWorldPosition(objA1), sceneManager.getWorldPosition(objA2));
19113
+ var distB = _this.treePathManager.distance(sceneManager.getWorldPosition(objB1), sceneManager.getWorldPosition(objB2));
19114
+ return distA - distB;
19115
+ });
19116
+
19117
+ // Add sorted gateway connections to rewiredConnections
19118
+ rewiredConnections.push.apply(rewiredConnections, gatewayConnections);
19119
+ var paths = this.pathManager.findPaths(rewiredConnections || []);
19120
+
19121
+ // Aggregate connection mappings by gateway
19122
+ var gatewayConnectionsMap = new Map();
19123
+
19124
+ // Initialize connection mappings for each gateway
19125
+ Array.from(gatewayMap.entries()).forEach(function (_ref) {
19126
+ var _ref2 = _slicedToArray(_ref, 2);
19127
+ _ref2[0];
19128
+ var gatewayId = _ref2[1];
19129
+ gatewayConnectionsMap.set(gatewayId, {
19130
+ removed: [],
19131
+ added: []
19132
+ });
19133
+ });
19134
+
19135
+ // Populate connection mappings
19136
+ gatewayConnectionMappings.forEach(function (mapping, connectionKey) {
19137
+ var _clusters$find3, _clusters$find4;
19138
+ var originalConnection = JSON.parse(connectionKey);
19139
+ var fromCluster = (_clusters$find3 = clusters.find(function (cluster) {
19140
+ return cluster.objects.includes(originalConnection.from);
19141
+ })) === null || _clusters$find3 === void 0 ? void 0 : _clusters$find3.clusterId;
19142
+ var toCluster = (_clusters$find4 = clusters.find(function (cluster) {
19143
+ return cluster.objects.includes(originalConnection.to);
19144
+ })) === null || _clusters$find4 === void 0 ? void 0 : _clusters$find4.clusterId;
19145
+ if (fromCluster !== undefined && toCluster !== undefined) {
19146
+ var fromGateway = gatewayMap.get(fromCluster);
19147
+ var toGateway = gatewayMap.get(toCluster);
19148
+ if (fromGateway !== undefined && toGateway !== undefined) {
19149
+ // Add to both gateways if they're different
19150
+ if (fromGateway === toGateway) {
19151
+ var _gatewayConnections$a;
19152
+ // Same gateway
19153
+ var _gatewayConnections = gatewayConnectionsMap.get(fromGateway);
19154
+ _gatewayConnections.removed.push(originalConnection);
19155
+ (_gatewayConnections$a = _gatewayConnections.added).push.apply(_gatewayConnections$a, _toConsumableArray(mapping.added));
19156
+ } else {
19157
+ // Different gateways - split the connections
19158
+ var fromGatewayConnections = gatewayConnectionsMap.get(fromGateway);
19159
+ var toGatewayConnections = gatewayConnectionsMap.get(toGateway);
19160
+
19161
+ // Add the original connection to both gateways' removed list
19162
+ fromGatewayConnections.removed.push(originalConnection);
19163
+ toGatewayConnections.removed.push(originalConnection);
19164
+
19165
+ // Split added connections by gateway
19166
+ mapping.added.forEach(function (conn) {
19167
+ if (conn.from === originalConnection.from || conn.to === fromGateway) {
19168
+ fromGatewayConnections.added.push(conn);
19169
+ }
19170
+ if (conn.from === toGateway || conn.to === originalConnection.to) {
19171
+ toGatewayConnections.added.push(conn);
19172
+ }
19173
+ });
19174
+ }
19175
+ }
19176
+ }
19177
+ });
19178
+ return {
19179
+ paths: paths,
19180
+ rewiredConnections: rewiredConnections,
19181
+ gateways: Array.from(gatewayMap.entries()).map(function (_ref3) {
19182
+ var _clusters$find5;
19183
+ var _ref4 = _slicedToArray(_ref3, 2),
19184
+ clusterId = _ref4[0],
19185
+ gatewayId = _ref4[1];
19186
+ return {
19187
+ clusterId: clusterId,
19188
+ id: gatewayId,
19189
+ position: (_clusters$find5 = clusters.find(function (c) {
19190
+ return c.clusterId === clusterId;
19191
+ })) === null || _clusters$find5 === void 0 ? void 0 : _clusters$find5.jointPoint,
19192
+ connections: gatewayConnectionsMap.get(gatewayId) || {
19193
+ removed: [],
19194
+ added: []
19195
+ }
19196
+ };
19197
+ })
19198
+ };
19199
+ }
19200
+ }]);
19201
+ }();
19202
+
17365
19203
  var PathfindingManager = /*#__PURE__*/function () {
17366
19204
  function PathfindingManager(sceneViewer) {
17367
19205
  _classCallCheck(this, PathfindingManager);
@@ -17492,7 +19330,7 @@ var PathfindingManager = /*#__PURE__*/function () {
17492
19330
  return this.logPathfinderVersion(context);
17493
19331
  case 1:
17494
19332
  // Create pathfinder instance with configuration only
17495
- this.pathfinder = new pathfinder.Pathfinder(this.pathfinderConfig);
19333
+ this.pathfinder = new Pathfinder(this.pathfinderConfig);
17496
19334
  this.sceneViewer.pathfinder = this.pathfinder;
17497
19335
 
17498
19336
  // Add debugging for pathfinder input
@@ -17518,6 +19356,9 @@ var PathfindingManager = /*#__PURE__*/function () {
17518
19356
  console.log('Generated gateways:', JSON.parse(JSON.stringify(pathfindingResult.gateways)));
17519
19357
  console.log('Rewired connections:', JSON.parse(JSON.stringify(pathfindingResult.rewiredConnections)));
17520
19358
 
19359
+ // Check for duplicate direction vectors going into the same gateways
19360
+ this.checkForDuplicateGatewayDirections(pathfindingResult, sceneData, connections);
19361
+
17521
19362
  // Create gateways in the scene if requested
17522
19363
  if (createGateways && pathfindingResult.gateways) {
17523
19364
  pathfindingResult.gateways.forEach(function (gateway) {
@@ -18769,6 +20610,130 @@ var PathfindingManager = /*#__PURE__*/function () {
18769
20610
  });
18770
20611
  return optimizedPaths;
18771
20612
  }
20613
+
20614
+ /**
20615
+ * Check for duplicate direction vectors going into the same gateways
20616
+ * This helps diagnose path overlapping issues caused by incorrect connector directions after rotation
20617
+ * @param {Object} pathfindingResult - Result from pathfinder.findPaths()
20618
+ * @param {Object} sceneData - Scene data used for pathfinding
20619
+ * @param {Array} connections - Original connections array
20620
+ */
20621
+ }, {
20622
+ key: "checkForDuplicateGatewayDirections",
20623
+ value: function checkForDuplicateGatewayDirections(pathfindingResult, sceneData, connections) {
20624
+ var _this4 = this;
20625
+ if (!pathfindingResult.gateways || !pathfindingResult.rewiredConnections) {
20626
+ return; // No gateways or rewired connections to check
20627
+ }
20628
+ console.log('🔍 Checking for duplicate direction vectors going into gateways...');
20629
+
20630
+ // Group connections by gateway
20631
+ var gatewayConnections = new Map();
20632
+ pathfindingResult.rewiredConnections.forEach(function (conn) {
20633
+ // Check if this connection goes TO a gateway
20634
+ var toGateway = pathfindingResult.gateways.find(function (gw) {
20635
+ return gw.id === conn.to;
20636
+ });
20637
+ if (toGateway) {
20638
+ if (!gatewayConnections.has(conn.to)) {
20639
+ gatewayConnections.set(conn.to, []);
20640
+ }
20641
+ gatewayConnections.get(conn.to).push({
20642
+ connection: conn,
20643
+ gateway: toGateway
20644
+ });
20645
+ }
20646
+ });
20647
+
20648
+ // Check each gateway for duplicate direction vectors
20649
+ var hasErrors = false;
20650
+ gatewayConnections.forEach(function (connectionsToGateway, gatewayId) {
20651
+ if (connectionsToGateway.length < 2) {
20652
+ return; // Need at least 2 connections to have duplicates
20653
+ }
20654
+ console.log("\uD83D\uDD0D Checking gateway ".concat(gatewayId, " with ").concat(connectionsToGateway.length, " incoming connections"));
20655
+
20656
+ // Get direction vectors for each connection going into this gateway
20657
+ var directionVectors = [];
20658
+ connectionsToGateway.forEach(function (_ref) {
20659
+ var connection = _ref.connection;
20660
+ // Find the connector object in the scene data
20661
+ var connectorObj = _this4.findObjectInSceneData(sceneData, connection.from);
20662
+ if (connectorObj && connectorObj.userData && connectorObj.userData.direction) {
20663
+ var direction = connectorObj.userData.direction;
20664
+ directionVectors.push({
20665
+ connectionFrom: connection.from,
20666
+ direction: direction,
20667
+ directionString: "[".concat(direction[0], ", ").concat(direction[1], ", ").concat(direction[2], "]")
20668
+ });
20669
+ console.log(" \uD83D\uDCCD Connector ".concat(connection.from, " direction: [").concat(direction[0], ", ").concat(direction[1], ", ").concat(direction[2], "]"));
20670
+ } else {
20671
+ console.warn(" \u26A0\uFE0F Could not find direction for connector ".concat(connection.from));
20672
+ }
20673
+ });
20674
+
20675
+ // Check for duplicate directions
20676
+ var directionMap = new Map();
20677
+ directionVectors.forEach(function (_ref2) {
20678
+ var connectionFrom = _ref2.connectionFrom;
20679
+ _ref2.direction;
20680
+ var directionString = _ref2.directionString;
20681
+ if (!directionMap.has(directionString)) {
20682
+ directionMap.set(directionString, []);
20683
+ }
20684
+ directionMap.get(directionString).push(connectionFrom);
20685
+ });
20686
+
20687
+ // Find duplicates
20688
+ directionMap.forEach(function (connectors, directionString) {
20689
+ if (connectors.length > 1) {
20690
+ hasErrors = true;
20691
+ console.error("\u274C DUPLICATE DIRECTION VECTORS DETECTED!\n" + " Gateway: ".concat(gatewayId, "\n") + " Direction: ".concat(directionString, "\n") + " Connectors with same direction: ".concat(connectors.join(', '), "\n") + " This will cause path overlapping! The connectors going into the same gateway should have different direction vectors after component rotation.");
20692
+ }
20693
+ });
20694
+ });
20695
+ if (!hasErrors) {
20696
+ console.log('✅ No duplicate direction vectors detected in gateway connections');
20697
+ } else {
20698
+ console.error("\u274C PATHFINDING DIAGNOSTIC: Found duplicate direction vectors going into gateways.\n" + " This is likely caused by connector direction vectors not being properly updated after component rotation.\n" + " The rotation code should transform the connector userData.direction arrays to reflect the new orientation.");
20699
+ }
20700
+ }
20701
+
20702
+ /**
20703
+ * Helper method to find an object in scene data by UUID
20704
+ * @param {Object} sceneData - Scene data to search
20705
+ * @param {string} uuid - UUID to find
20706
+ * @returns {Object|null} Found object or null
20707
+ */
20708
+ }, {
20709
+ key: "findObjectInSceneData",
20710
+ value: function findObjectInSceneData(sceneData, uuid) {
20711
+ var _searchChildren = function searchChildren(children) {
20712
+ var _iterator2 = _createForOfIteratorHelper(children),
20713
+ _step2;
20714
+ try {
20715
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
20716
+ var child = _step2.value;
20717
+ if (child.uuid === uuid) {
20718
+ return child;
20719
+ }
20720
+ if (child.children) {
20721
+ var found = _searchChildren(child.children);
20722
+ if (found) return found;
20723
+ }
20724
+ }
20725
+ } catch (err) {
20726
+ _iterator2.e(err);
20727
+ } finally {
20728
+ _iterator2.f();
20729
+ }
20730
+ return null;
20731
+ };
20732
+ if (sceneData.object && sceneData.object.children) {
20733
+ return _searchChildren(sceneData.object.children);
20734
+ }
20735
+ return null;
20736
+ }
18772
20737
  }]);
18773
20738
  }();
18774
20739
 
@@ -24217,6 +26182,9 @@ var CentralPlant = /*#__PURE__*/function () {
24217
26182
  // Update the Three.js object position
24218
26183
  component.position[axis] += value;
24219
26184
 
26185
+ // Update world bounding boxes for the component and its children
26186
+ this.updateWorldBoundingBoxes(component);
26187
+
24220
26188
  // Update the associated JSON object in currentSceneData if it exists
24221
26189
  if (this.sceneViewer.currentSceneData && this.sceneViewer.currentSceneData.scene) {
24222
26190
  if (component.userData.associatedJsonObject) {
@@ -24330,6 +26298,14 @@ var CentralPlant = /*#__PURE__*/function () {
24330
26298
  // Apply the rotation
24331
26299
  component.rotation[axis] += radians;
24332
26300
 
26301
+ // Update direction vectors for connectors if this is a 90-degree rotation
26302
+ if (value % 90 === 0) {
26303
+ this.updateDirectionAfterRotation(component, axis, value);
26304
+ }
26305
+
26306
+ // Update world bounding boxes for the component and its children
26307
+ this.updateWorldBoundingBoxes(component);
26308
+
24333
26309
  // Update the associated JSON object in currentSceneData if it exists
24334
26310
  if (this.sceneViewer.currentSceneData && this.sceneViewer.currentSceneData.scene) {
24335
26311
  if (component.userData.associatedJsonObject) {
@@ -24377,6 +26353,173 @@ var CentralPlant = /*#__PURE__*/function () {
24377
26353
  return true;
24378
26354
  }
24379
26355
 
26356
+ /**
26357
+ * Update userData.direction for components after 90-degree rotation
26358
+ * This method handles the direction vector transformation for connectors when their parent component is rotated
26359
+ *
26360
+ * @param {THREE.Object3D} component - The component that was rotated
26361
+ * @param {string} axis - The axis of rotation ('x', 'y', or 'z')
26362
+ * @param {number} degrees - The rotation angle in degrees (should be multiple of 90)
26363
+ * @private
26364
+ */
26365
+ }, {
26366
+ key: "updateDirectionAfterRotation",
26367
+ value: function updateDirectionAfterRotation(component, axis, degrees) {
26368
+ var _this = this;
26369
+ if (!component) {
26370
+ console.warn('⚠️ updateDirectionAfterRotation: No component provided');
26371
+ return;
26372
+ }
26373
+
26374
+ // Only handle 90-degree increments
26375
+ if (degrees % 90 !== 0) {
26376
+ console.warn('⚠️ updateDirectionAfterRotation: Only 90-degree increments are supported');
26377
+ return;
26378
+ }
26379
+
26380
+ // Normalize degrees to 0-360 range
26381
+ var normalizedDegrees = (degrees % 360 + 360) % 360;
26382
+ console.log("\uD83D\uDD04 Updating direction vectors for ".concat(component.name || component.uuid, " after ").concat(degrees, "\xB0 rotation around ").concat(axis, " axis"));
26383
+
26384
+ // Traverse all children (connectors) and update their direction vectors
26385
+ component.traverse(function (child) {
26386
+ if (child.userData && Array.isArray(child.userData.direction) && child.userData.componentType === 'connector') {
26387
+ var originalDirection = _toConsumableArray(child.userData.direction);
26388
+ var newDirection = _this.rotateDirectionVector(originalDirection, axis, normalizedDegrees);
26389
+
26390
+ // Update the direction
26391
+ child.userData.direction = newDirection;
26392
+ console.log("\uD83D\uDCCD Updated connector ".concat(child.name || child.uuid, " direction:"), {
26393
+ original: originalDirection,
26394
+ new: newDirection,
26395
+ rotationAxis: axis,
26396
+ rotationDegrees: degrees
26397
+ });
26398
+ }
26399
+ });
26400
+ }
26401
+
26402
+ /**
26403
+ * Rotate a direction vector by 90-degree increments around a specific axis
26404
+ *
26405
+ * @param {Array<number>} direction - The original direction vector [x, y, z]
26406
+ * @param {string} axis - The axis of rotation ('x', 'y', or 'z')
26407
+ * @param {number} degrees - The rotation angle in degrees (must be multiple of 90)
26408
+ * @returns {Array<number>} The new direction vector [x, y, z]
26409
+ * @private
26410
+ */
26411
+ }, {
26412
+ key: "rotateDirectionVector",
26413
+ value: function rotateDirectionVector(direction, axis, degrees) {
26414
+ if (!Array.isArray(direction) || direction.length !== 3) {
26415
+ console.warn('⚠️ rotateDirectionVector: Invalid direction vector');
26416
+ return direction;
26417
+ }
26418
+ var _direction = _slicedToArray(direction, 3),
26419
+ x = _direction[0],
26420
+ y = _direction[1],
26421
+ z = _direction[2];
26422
+ var steps = degrees / 90; // Number of 90-degree steps
26423
+
26424
+ for (var i = 0; i < steps; i++) {
26425
+ var newX = x,
26426
+ newY = y,
26427
+ newZ = z;
26428
+ switch (axis) {
26429
+ case 'x':
26430
+ // Rotation around X-axis: Y becomes -Z, Z becomes Y
26431
+ newY = -z;
26432
+ newZ = y;
26433
+ break;
26434
+ case 'y':
26435
+ // Rotation around Y-axis: X becomes Z, Z becomes -X
26436
+ newX = z;
26437
+ newZ = -x;
26438
+ break;
26439
+ case 'z':
26440
+ // Rotation around Z-axis: X becomes -Y, Y becomes X
26441
+ newX = -y;
26442
+ newY = x;
26443
+ break;
26444
+ default:
26445
+ console.warn("\u26A0\uFE0F rotateDirectionVector: Invalid axis '".concat(axis, "'"));
26446
+ return direction;
26447
+ }
26448
+ x = newX;
26449
+ y = newY;
26450
+ z = newZ;
26451
+ }
26452
+ return [x, y, z];
26453
+ }
26454
+
26455
+ /**
26456
+ * Update world bounding boxes for a component and its children after transformation
26457
+ * This method recalculates bounding boxes based on the new position/rotation
26458
+ *
26459
+ * @param {THREE.Object3D} component - The component that was transformed
26460
+ * @private
26461
+ */
26462
+ }, {
26463
+ key: "updateWorldBoundingBoxes",
26464
+ value: function updateWorldBoundingBoxes(component) {
26465
+ var _this$sceneViewer, _this$sceneViewer2;
26466
+ if (!component) {
26467
+ console.warn('⚠️ updateWorldBoundingBoxes: No component provided');
26468
+ return;
26469
+ }
26470
+ console.log("\uD83D\uDCE6 Updating world bounding boxes for ".concat(component.name || component.uuid));
26471
+
26472
+ // Update bounding box for the main component and all its children
26473
+ component.traverse(function (child) {
26474
+ if (child.isMesh || child.userData.componentType) {
26475
+ try {
26476
+ // Force matrix updates to ensure accurate bounding box calculation
26477
+ child.updateMatrix();
26478
+ child.updateMatrixWorld(true);
26479
+
26480
+ // Calculate new world bounding box
26481
+ var boundingBox = new THREE__namespace.Box3().setFromObject(child);
26482
+
26483
+ // Update userData.worldBoundingBox if it exists
26484
+ if (child.userData) {
26485
+ child.userData.worldBoundingBox = {
26486
+ min: boundingBox.min.toArray(),
26487
+ max: boundingBox.max.toArray()
26488
+ };
26489
+ console.log("\uD83D\uDCE6 Updated bounding box for ".concat(child.name || child.uuid, ":"), {
26490
+ min: boundingBox.min.toArray(),
26491
+ max: boundingBox.max.toArray()
26492
+ });
26493
+ }
26494
+
26495
+ // Update associated JSON object if it exists
26496
+ if (child.userData.associatedJsonObject) {
26497
+ if (!child.userData.associatedJsonObject.userData) {
26498
+ child.userData.associatedJsonObject.userData = {};
26499
+ }
26500
+ child.userData.associatedJsonObject.userData.worldBoundingBox = {
26501
+ min: boundingBox.min.toArray(),
26502
+ max: boundingBox.max.toArray()
26503
+ };
26504
+ console.log("\uD83D\uDCE6 Updated JSON bounding box for ".concat(child.name || child.uuid));
26505
+ }
26506
+ } catch (error) {
26507
+ console.warn("\u26A0\uFE0F Error updating bounding box for ".concat(child.name || child.uuid, ":"), error);
26508
+ }
26509
+ }
26510
+ });
26511
+
26512
+ // Also trigger the pathfinding manager's recompute method for consistency
26513
+ if ((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.pathfindingManager && (_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.currentSceneData && typeof this.sceneViewer.pathfindingManager.recomputeWorldBoundingBoxes === 'function') {
26514
+ try {
26515
+ this.sceneViewer.pathfindingManager.recomputeWorldBoundingBoxes(this.sceneViewer.currentSceneData);
26516
+ console.log('📦 Triggered pathfinding manager bounding box recompute');
26517
+ } catch (error) {
26518
+ console.warn('⚠️ Error triggering pathfinding bounding box recompute:', error);
26519
+ }
26520
+ }
26521
+ }
26522
+
24380
26523
  /**
24381
26524
  * Update paths in the scene
24382
26525
  * @returns {boolean} True if paths were updated successfully, false otherwise
@@ -24430,7 +26573,7 @@ var CentralPlant = /*#__PURE__*/function () {
24430
26573
  }, {
24431
26574
  key: "syncSceneData",
24432
26575
  value: function syncSceneData() {
24433
- var _this = this;
26576
+ var _this2 = this;
24434
26577
  // Check if scene viewer and currentSceneData are available
24435
26578
  if (!this.sceneViewer || !this.sceneViewer.currentSceneData) {
24436
26579
  console.warn('⚠️ syncSceneData(): Scene viewer or current scene data not available');
@@ -24455,7 +26598,7 @@ var CentralPlant = /*#__PURE__*/function () {
24455
26598
  currentSceneData.scene.object.children.forEach(function (sceneDataChild, index) {
24456
26599
  // Find matching Three.js object
24457
26600
  var matchingThreeObject = null;
24458
- _this.sceneViewer.scene.traverse(function (threeObject) {
26601
+ _this2.sceneViewer.scene.traverse(function (threeObject) {
24459
26602
  var _sceneDataChild$userD, _threeObject$userData, _threeObject$userData2;
24460
26603
  // Check various UUID matching strategies
24461
26604
  if (threeObject.uuid === sceneDataChild.uuid || threeObject.uuid === ((_sceneDataChild$userD = sceneDataChild.userData) === null || _sceneDataChild$userD === void 0 ? void 0 : _sceneDataChild$userD.originalUuid) || ((_threeObject$userData = threeObject.userData) === null || _threeObject$userData === void 0 ? void 0 : _threeObject$userData.originalUuid) === sceneDataChild.uuid || ((_threeObject$userData2 = threeObject.userData) === null || _threeObject$userData2 === void 0 ? void 0 : _threeObject$userData2.hardcodedUuid) === sceneDataChild.uuid) {
@@ -24562,7 +26705,7 @@ var CentralPlant = /*#__PURE__*/function () {
24562
26705
  }, {
24563
26706
  key: "forceSyncSceneData",
24564
26707
  value: function forceSyncSceneData() {
24565
- var _this2 = this;
26708
+ var _this3 = this;
24566
26709
  // Check if scene viewer and sceneOperationsManager are available
24567
26710
  if (!this.sceneViewer || !this.sceneViewer.sceneOperationsManager) {
24568
26711
  console.warn('⚠️ forceSyncSceneData(): Scene viewer or scene operations manager not available');
@@ -24588,7 +26731,7 @@ var CentralPlant = /*#__PURE__*/function () {
24588
26731
  console.log("\uD83D\uDD04 Syncing object: ".concat(threeObject.uuid, " (").concat(threeObject.userData.componentType || 'unknown', ")"));
24589
26732
 
24590
26733
  // Use the existing updateSceneDataAfterTransform method
24591
- var success = _this2.sceneViewer.sceneOperationsManager.updateSceneDataAfterTransform(threeObject, currentSceneData);
26734
+ var success = _this3.sceneViewer.sceneOperationsManager.updateSceneDataAfterTransform(threeObject, currentSceneData);
24592
26735
  if (success) {
24593
26736
  syncedCount++;
24594
26737
  } else {