@2112-lab/central-plant 0.1.28 → 0.1.30

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