@2112-lab/central-plant 0.1.55 → 0.1.60

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.
@@ -3993,6 +3993,80 @@ var TransformControlsManager = /*#__PURE__*/function () {
3993
3993
  var hasConnectorName = object.name && object.name.toLowerCase().includes('connector');
3994
3994
  return (hasConnectorGeometry || isSphereConnector) && hasConnectorName;
3995
3995
  }
3996
+
3997
+ /**
3998
+ * Check if a segment can be added to the current selection
3999
+ * Prevents mixing horizontal and vertical segments
4000
+ * @param {THREE.Object3D} segment - The segment to check
4001
+ * @returns {boolean} True if segment can be added, false otherwise
4002
+ * @private
4003
+ */
4004
+ }, {
4005
+ key: "_canAddSegmentToSelection",
4006
+ value: function _canAddSegmentToSelection(segment) {
4007
+ if (!isSegment(segment)) {
4008
+ return true; // Non-segments can always be added
4009
+ }
4010
+
4011
+ // Find any segments already in selection
4012
+ var existingSegments = this.selectedObjects.filter(function (obj) {
4013
+ return isSegment(obj);
4014
+ });
4015
+
4016
+ // If no segments selected yet, this is the first one - allow it
4017
+ if (existingSegments.length === 0) {
4018
+ return true;
4019
+ }
4020
+
4021
+ // Check orientation compatibility
4022
+ var isNewSegmentHorizontal = this._isSegmentHorizontal(segment);
4023
+
4024
+ // Check if all existing segments have the same orientation
4025
+ var _iterator = _createForOfIteratorHelper(existingSegments),
4026
+ _step;
4027
+ try {
4028
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
4029
+ var existingSegment = _step.value;
4030
+ var isExistingHorizontal = this._isSegmentHorizontal(existingSegment);
4031
+
4032
+ // Disallow mixing horizontal and vertical
4033
+ if (isNewSegmentHorizontal !== isExistingHorizontal) {
4034
+ return false;
4035
+ }
4036
+ }
4037
+ } catch (err) {
4038
+ _iterator.e(err);
4039
+ } finally {
4040
+ _iterator.f();
4041
+ }
4042
+ return true;
4043
+ }
4044
+
4045
+ /**
4046
+ * Check if a segment is horizontal (Z-up coordinate system)
4047
+ * @param {THREE.Object3D} segment - The segment to check
4048
+ * @returns {boolean} True if horizontal, false if vertical
4049
+ * @private
4050
+ */
4051
+ }, {
4052
+ key: "_isSegmentHorizontal",
4053
+ value: function _isSegmentHorizontal(segment) {
4054
+ if (!segment || !segment.geometry || !segment.quaternion) {
4055
+ return false;
4056
+ }
4057
+
4058
+ // Get the segment's direction vector (cylinder aligned with Y-axis by default)
4059
+ var direction = new THREE__namespace.Vector3(0, 1, 0);
4060
+ direction.applyQuaternion(segment.quaternion);
4061
+ direction.normalize();
4062
+
4063
+ // In Z-up coordinate system, horizontal segments have Z component near 0
4064
+ var zComponent = Math.abs(direction.z);
4065
+ var tolerance = 0.1; // ~5.7 degrees
4066
+
4067
+ return zComponent < tolerance;
4068
+ }
4069
+
3996
4070
  /**
3997
4071
  * Default object selection filter
3998
4072
  * @param {THREE.Object3D} object - The object to check
@@ -4112,6 +4186,12 @@ var TransformControlsManager = /*#__PURE__*/function () {
4112
4186
  this.selectedObjects.splice(index, 1);
4113
4187
  console.log("\u2796 Removed from selection: ".concat(object.name || object.uuid));
4114
4188
  } else {
4189
+ // Validate segment orientation compatibility before adding
4190
+ if (isSegment(object) && !this._canAddSegmentToSelection(object)) {
4191
+ console.warn('⚠️ Cannot mix horizontal and vertical segments in multi-selection');
4192
+ return;
4193
+ }
4194
+
4115
4195
  // Object is not selected, add it
4116
4196
  this.selectedObjects.push(object);
4117
4197
  console.log("\u2795 Added to selection: ".concat(object.name || object.uuid));
@@ -4768,11 +4848,11 @@ var TransformControlsManager = /*#__PURE__*/function () {
4768
4848
  var objectFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
4769
4849
  var objectsWithBounds = this.getSelectableObjectsWithBounds(objectFilter);
4770
4850
  var intersections = [];
4771
- var _iterator = _createForOfIteratorHelper(objectsWithBounds),
4772
- _step;
4851
+ var _iterator2 = _createForOfIteratorHelper(objectsWithBounds),
4852
+ _step2;
4773
4853
  try {
4774
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
4775
- var item = _step.value;
4854
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4855
+ var item = _step2.value;
4776
4856
  var object = item.object,
4777
4857
  boundingBox = item.boundingBox;
4778
4858
 
@@ -4792,9 +4872,9 @@ var TransformControlsManager = /*#__PURE__*/function () {
4792
4872
 
4793
4873
  // Sort by distance (closest first)
4794
4874
  } catch (err) {
4795
- _iterator.e(err);
4875
+ _iterator2.e(err);
4796
4876
  } finally {
4797
- _iterator.f();
4877
+ _iterator2.f();
4798
4878
  }
4799
4879
  intersections.sort(function (a, b) {
4800
4880
  return a.distance - b.distance;
@@ -4896,35 +4976,20 @@ var TransformControlsManager = /*#__PURE__*/function () {
4896
4976
  key: "applyMultiSelectionTransform",
4897
4977
  value: (function () {
4898
4978
  var _applyMultiSelectionTransform = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
4899
- var _this9 = this;
4900
- var groupPosition, originalCentroid, positionDelta, deltaX, deltaY, deltaZ, threshold, _this$sceneViewer, _this$sceneViewer2, _this$sceneViewer3, _this$sceneViewer4, _this$sceneViewer5, segments, gateways, components, transformOpsManager, THROTTLE_DELAY, SEGMENT_DELAY, _throttleDelay, _loop, i, _THROTTLE_DELAY, _SEGMENT_DELAY, _throttleDelay2, _loop2, _i, GATEWAY_THROTTLE, GATEWAY_DELAY, COMPONENT_THROTTLE, COMPONENT_DELAY, throttleDelay, _i2, _gateway$userData, gateway, gatewayId, _i3, _component$userData, component, componentId;
4901
- return _regenerator().w(function (_context4) {
4902
- while (1) switch (_context4.n) {
4979
+ var _this$_calculateTrans, deltaX, deltaY, deltaZ, threshold, segments, gateways, components;
4980
+ return _regenerator().w(function (_context2) {
4981
+ while (1) switch (_context2.n) {
4903
4982
  case 0:
4904
4983
  if (!(!this.multiSelectionGroup || this.selectedObjects.length === 0)) {
4905
- _context4.n = 1;
4984
+ _context2.n = 1;
4906
4985
  break;
4907
4986
  }
4908
- return _context4.a(2);
4987
+ return _context2.a(2);
4909
4988
  case 1:
4910
4989
  console.log("\uD83D\uDD27 Applying multi-selection transform to ".concat(this.selectedObjects.length, " objects"));
4911
4990
 
4912
- // Calculate the transformation delta from the group
4913
- groupPosition = this.multiSelectionGroup.position.clone(); // Get the original centroid position (from when group was created)
4914
- originalCentroid = new THREE__namespace.Vector3();
4915
- this.selectedObjects.forEach(function (obj) {
4916
- var originalWorldPos = obj.userData._multiSelectOriginalWorldPosition;
4917
- if (originalWorldPos) {
4918
- originalCentroid.add(originalWorldPos);
4919
- }
4920
- });
4921
- originalCentroid.divideScalar(this.selectedObjects.length);
4922
-
4923
- // Calculate position delta - this is the same for ALL objects
4924
- positionDelta = groupPosition.clone().sub(originalCentroid); // Round deltas to avoid floating point precision issues
4925
- deltaX = Math.round(positionDelta.x * 2) / 2;
4926
- deltaY = Math.round(positionDelta.y * 2) / 2;
4927
- deltaZ = Math.round(positionDelta.z * 2) / 2;
4991
+ // Calculate transformation deltas
4992
+ _this$_calculateTrans = this._calculateTransformDeltas(), deltaX = _this$_calculateTrans.deltaX, deltaY = _this$_calculateTrans.deltaY, deltaZ = _this$_calculateTrans.deltaZ, threshold = _this$_calculateTrans.threshold;
4928
4993
  console.log('🔧 Transform delta:', {
4929
4994
  deltaX: deltaX,
4930
4995
  deltaY: deltaY,
@@ -4932,23 +4997,16 @@ var TransformControlsManager = /*#__PURE__*/function () {
4932
4997
  });
4933
4998
 
4934
4999
  // Only process if there's a meaningful translation
4935
- threshold = 0.001;
4936
- if (!(this.currentMode === 'translate' && positionDelta.length() > threshold)) {
4937
- _context4.n = 21;
5000
+ if (!(this.currentMode !== 'translate' || Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold && Math.abs(deltaZ) < threshold)) {
5001
+ _context2.n = 2;
4938
5002
  break;
4939
5003
  }
4940
- // FIRST: Reset all objects to their original positions
4941
- // This undoes the visual transform applied during drag
4942
- this.selectedObjects.forEach(function (obj) {
4943
- var originalPos = obj.userData._multiSelectOriginalPosition;
4944
- if (originalPos) {
4945
- obj.position.copy(originalPos);
4946
- obj.updateMatrixWorld(true);
4947
- }
4948
- });
4949
- console.log('🔄 Reset objects to original positions before API calls');
5004
+ return _context2.a(2);
5005
+ case 2:
5006
+ // Reset all objects to original positions
5007
+ this._resetObjectsToOriginalPositions();
4950
5008
 
4951
- // Separate segments from other objects
5009
+ // Separate objects by type
4952
5010
  segments = this.selectedObjects.filter(function (obj) {
4953
5011
  return isSegment(obj);
4954
5012
  });
@@ -4957,433 +5015,462 @@ var TransformControlsManager = /*#__PURE__*/function () {
4957
5015
  });
4958
5016
  components = this.selectedObjects.filter(function (obj) {
4959
5017
  return !isSegment(obj) && !isGateway(obj);
4960
- }); // Debug: Check availability of managers
4961
- console.log('🔍 Debug - sceneViewer available:', !!this.sceneViewer);
4962
- console.log('🔍 Debug - sceneViewer.managers available:', !!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.managers));
4963
- console.log('🔍 Debug - Available managers:', (_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.managers ? Object.keys(this.sceneViewer.managers) : 'none');
4964
- console.log('🔍 Debug - transformOperationsManager (direct):', !!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformOperationsManager));
4965
- console.log('🔍 Debug - transformOperations (managers.transformOperations):', !!((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && (_this$sceneViewer4 = _this$sceneViewer4.managers) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.transformOperations));
4966
- console.log('🔍 Debug - Segments to translate:', segments.length);
4967
-
4968
- // BATCH SEGMENT TRANSLATIONS: Call translateSegment with skipPathUpdate=true
4969
- // Process segments SEQUENTIALLY with throttling for maximum stability
4970
- // Access transformOperationsManager directly from sceneViewer (it's attached in attachToComponent)
4971
- if (!(segments.length > 0 && (_this$sceneViewer5 = this.sceneViewer) !== null && _this$sceneViewer5 !== void 0 && _this$sceneViewer5.transformOperationsManager)) {
4972
- _context4.n = 5;
5018
+ }); // Translate each object type
5019
+ _context2.n = 3;
5020
+ return this._translateSegments(segments, deltaX, deltaY, deltaZ, threshold);
5021
+ case 3:
5022
+ _context2.n = 4;
5023
+ return this._translateGateways(gateways, deltaX, deltaY, deltaZ, threshold);
5024
+ case 4:
5025
+ _context2.n = 5;
5026
+ return this._translateComponents(components, deltaX, deltaY, deltaZ, threshold);
5027
+ case 5:
5028
+ console.log("\u2705 All ".concat(this.selectedObjects.length, " objects translated"));
5029
+
5030
+ // Cleanup and refresh
5031
+ this._finalizeMultiSelectionTransform();
5032
+ case 6:
5033
+ return _context2.a(2);
5034
+ }
5035
+ }, _callee2, this);
5036
+ }));
5037
+ function applyMultiSelectionTransform() {
5038
+ return _applyMultiSelectionTransform.apply(this, arguments);
5039
+ }
5040
+ return applyMultiSelectionTransform;
5041
+ }()
5042
+ /**
5043
+ * Calculate transformation deltas from multi-selection group
5044
+ * @returns {Object} Delta values and threshold
5045
+ * @private
5046
+ */
5047
+ )
5048
+ }, {
5049
+ key: "_calculateTransformDeltas",
5050
+ value: function _calculateTransformDeltas() {
5051
+ var groupPosition = this.multiSelectionGroup.position.clone();
5052
+
5053
+ // Get original centroid position
5054
+ var originalCentroid = new THREE__namespace.Vector3();
5055
+ this.selectedObjects.forEach(function (obj) {
5056
+ var originalWorldPos = obj.userData._multiSelectOriginalWorldPosition;
5057
+ if (originalWorldPos) {
5058
+ originalCentroid.add(originalWorldPos);
5059
+ }
5060
+ });
5061
+ originalCentroid.divideScalar(this.selectedObjects.length);
5062
+
5063
+ // Calculate and round deltas
5064
+ var positionDelta = groupPosition.clone().sub(originalCentroid);
5065
+ return {
5066
+ deltaX: Math.round(positionDelta.x * 2) / 2,
5067
+ deltaY: Math.round(positionDelta.y * 2) / 2,
5068
+ deltaZ: Math.round(positionDelta.z * 2) / 2,
5069
+ threshold: 0.001
5070
+ };
5071
+ }
5072
+
5073
+ /**
5074
+ * Reset all selected objects to their original positions
5075
+ * @private
5076
+ */
5077
+ }, {
5078
+ key: "_resetObjectsToOriginalPositions",
5079
+ value: function _resetObjectsToOriginalPositions() {
5080
+ this.selectedObjects.forEach(function (obj) {
5081
+ var originalPos = obj.userData._multiSelectOriginalPosition;
5082
+ if (originalPos) {
5083
+ obj.position.copy(originalPos);
5084
+ obj.updateMatrixWorld(true);
5085
+ }
5086
+ });
5087
+ console.log('🔄 Reset objects to original positions before API calls');
5088
+ }
5089
+
5090
+ /**
5091
+ * Translate segments with batched path updates
5092
+ * @private
5093
+ */
5094
+ }, {
5095
+ key: "_translateSegments",
5096
+ value: (function () {
5097
+ var _translateSegments2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(segments, deltaX, deltaY, deltaZ, threshold) {
5098
+ var _this$sceneViewer, _this$sceneViewer2;
5099
+ var transformOpsManager, useOptimizedPath, throttleDelay, AXIS_THROTTLE, OBJECT_DELAY, i, segment, axes, _i, _axes, _axes$_i, axis, delta;
5100
+ return _regenerator().w(function (_context3) {
5101
+ while (1) switch (_context3.n) {
5102
+ case 0:
5103
+ if (!(segments.length === 0)) {
5104
+ _context3.n = 1;
4973
5105
  break;
4974
5106
  }
4975
- console.log("\uD83D\uDD27 Batch translating ".concat(segments.length, " segments (sequential with throttling)..."));
4976
- transformOpsManager = this.sceneViewer.transformOperationsManager; // Throttle delay in milliseconds (adjust as needed for stability)
4977
- THROTTLE_DELAY = 10; // 10ms between axis operations
4978
- SEGMENT_DELAY = 20; // 20ms between segments
4979
- // Helper function for throttled delay
4980
- _throttleDelay = function _throttleDelay(ms) {
5107
+ return _context3.a(2);
5108
+ case 1:
5109
+ transformOpsManager = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.transformOperationsManager;
5110
+ useOptimizedPath = transformOpsManager != null;
5111
+ if (useOptimizedPath) {
5112
+ console.log("\uD83D\uDD27 Batch translating ".concat(segments.length, " segments..."));
5113
+ } else {
5114
+ console.warn('⚠️ TransformOperationsManager not available, using fallback');
5115
+ }
5116
+ throttleDelay = function throttleDelay(ms) {
4981
5117
  return new Promise(function (resolve) {
4982
5118
  return setTimeout(resolve, ms);
4983
5119
  });
4984
- }; // Process each segment sequentially with throttling for stability
4985
- _loop = /*#__PURE__*/_regenerator().m(function _loop(i) {
4986
- var segment, _segment$userData, _segment$userData2, segmentId, success, newSegmentId, _segment$userData3, _segment$userData4, _segmentId, _success, _newSegmentId, _segment$userData5, _segment$userData6, _segmentId2, _success2, _newSegmentId2, selectedIndex;
4987
- return _regenerator().w(function (_context2) {
4988
- while (1) switch (_context2.n) {
4989
- case 0:
4990
- segment = segments[i]; // Call translateSegment API with skipPathUpdate=true to defer path updates
4991
- // IMPORTANT: Refresh segment reference after each axis to handle UUID changes from manualization
4992
- if (!(Math.abs(deltaX) > threshold)) {
4993
- _context2.n = 1;
4994
- break;
4995
- }
4996
- segmentId = segment.uuid || ((_segment$userData = segment.userData) === null || _segment$userData === void 0 ? void 0 : _segment$userData.originalUuid);
4997
- success = transformOpsManager.translateSegment(segmentId, 'x', deltaX, true);
4998
- if (!success) {
4999
- console.warn("\u26A0\uFE0F Failed to translate segment ".concat(segmentId, " on X axis"));
5000
- }
5001
- // Refresh segment reference by searching for the new UUID or by segmentIndex
5002
- // After manualization, UUID changes from SEGMENT-X to Segment-X
5003
- newSegmentId = (_segment$userData2 = segment.userData) !== null && _segment$userData2 !== void 0 && _segment$userData2.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : segmentId;
5004
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', newSegmentId) || segment;
5005
-
5006
- // Update matrix to ensure geometry is current for next operation
5007
- if (segment) {
5008
- segment.updateMatrix();
5009
- segment.updateMatrixWorld(true);
5010
- }
5011
- _context2.n = 1;
5012
- return _throttleDelay(THROTTLE_DELAY);
5013
- case 1:
5014
- if (!(Math.abs(deltaY) > threshold)) {
5015
- _context2.n = 2;
5016
- break;
5017
- }
5018
- _segmentId = segment.uuid || ((_segment$userData3 = segment.userData) === null || _segment$userData3 === void 0 ? void 0 : _segment$userData3.originalUuid);
5019
- _success = transformOpsManager.translateSegment(_segmentId, 'y', deltaY, true);
5020
- if (!_success) {
5021
- console.warn("\u26A0\uFE0F Failed to translate segment ".concat(_segmentId, " on Y axis"));
5022
- }
5023
- // Refresh segment reference by searching for the new UUID
5024
- _newSegmentId = (_segment$userData4 = segment.userData) !== null && _segment$userData4 !== void 0 && _segment$userData4.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : _segmentId;
5025
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', _newSegmentId) || segment;
5026
-
5027
- // Update matrix to ensure geometry is current for next operation
5028
- if (segment) {
5029
- segment.updateMatrix();
5030
- segment.updateMatrixWorld(true);
5031
- }
5032
- _context2.n = 2;
5033
- return _throttleDelay(THROTTLE_DELAY);
5034
- case 2:
5035
- if (!(Math.abs(deltaZ) > threshold)) {
5036
- _context2.n = 3;
5037
- break;
5038
- }
5039
- _segmentId2 = segment.uuid || ((_segment$userData5 = segment.userData) === null || _segment$userData5 === void 0 ? void 0 : _segment$userData5.originalUuid);
5040
- _success2 = transformOpsManager.translateSegment(_segmentId2, 'z', deltaZ, true);
5041
- if (!_success2) {
5042
- console.warn("\u26A0\uFE0F Failed to translate segment ".concat(_segmentId2, " on Z axis"));
5043
- }
5044
- // Refresh segment reference by searching for the new UUID
5045
- _newSegmentId2 = (_segment$userData6 = segment.userData) !== null && _segment$userData6 !== void 0 && _segment$userData6.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : _segmentId2;
5046
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', _newSegmentId2) || segment;
5047
-
5048
- // Update matrix to ensure geometry is current for next operation
5049
- if (segment) {
5050
- segment.updateMatrix();
5051
- segment.updateMatrixWorld(true);
5052
- }
5053
- _context2.n = 3;
5054
- return _throttleDelay(THROTTLE_DELAY);
5055
- case 3:
5056
- // Update the segment in the selectedObjects array with the refreshed reference
5057
- // This ensures transform controls and multi-selection display use the current object
5058
- selectedIndex = _this9.selectedObjects.findIndex(function (obj) {
5059
- var _obj$userData;
5060
- return obj.uuid === segments[i].uuid || ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.originalUuid) === segments[i].uuid;
5061
- });
5062
- if (selectedIndex !== -1 && segment) {
5063
- // Clear bounding box cache for both old and new segment references
5064
- if (_this9.boundingBoxCache.has(segments[i])) {
5065
- _this9.boundingBoxCache.delete(segments[i]);
5066
- }
5067
- if (_this9.boundingBoxCache.has(segment)) {
5068
- _this9.boundingBoxCache.delete(segment);
5069
- }
5070
- _this9.selectedObjects[selectedIndex] = segment;
5071
- console.log("\u2705 Updated selectedObjects[".concat(selectedIndex, "] with refreshed segment reference"));
5072
- }
5073
- if (segment) {
5074
- console.log("\uD83D\uDCE6 Segment ".concat(segment.name, " translated (").concat(i + 1, "/").concat(segments.length, "):"), {
5075
- deltaX: deltaX,
5076
- deltaY: deltaY,
5077
- deltaZ: deltaZ
5078
- });
5079
- } else {
5080
- console.warn("\u26A0\uFE0F Segment reference became null after translation (".concat(i + 1, "/").concat(segments.length, ")"));
5081
- }
5082
-
5083
- // Delay between segments to ensure all updates propagate
5084
- if (!(i < segments.length - 1)) {
5085
- _context2.n = 4;
5086
- break;
5087
- }
5088
- _context2.n = 4;
5089
- return _throttleDelay(SEGMENT_DELAY);
5090
- case 4:
5091
- return _context2.a(2);
5092
- }
5093
- }, _loop);
5094
- });
5120
+ };
5121
+ AXIS_THROTTLE = 10;
5122
+ OBJECT_DELAY = 20;
5095
5123
  i = 0;
5096
5124
  case 2:
5097
5125
  if (!(i < segments.length)) {
5098
- _context4.n = 4;
5126
+ _context3.n = 8;
5099
5127
  break;
5100
5128
  }
5101
- return _context4.d(_regeneratorValues(_loop(i)), 3);
5129
+ segment = segments[i];
5130
+ axes = [{
5131
+ axis: 'x',
5132
+ delta: deltaX
5133
+ }, {
5134
+ axis: 'y',
5135
+ delta: deltaY
5136
+ }, {
5137
+ axis: 'z',
5138
+ delta: deltaZ
5139
+ }]; // Translate on each axis
5140
+ _i = 0, _axes = axes;
5102
5141
  case 3:
5103
- i++;
5104
- _context4.n = 2;
5105
- break;
5106
- case 4:
5107
- // Only call updatePaths ONCE after all segments are translated
5108
- console.log('🔄 Calling updatePaths once for all segments...');
5109
- if (this.sceneViewer && typeof this.sceneViewer.updatePaths === 'function') {
5110
- this.sceneViewer.updatePaths();
5111
- console.log('✅ Paths regenerated successfully for all segments');
5142
+ if (!(_i < _axes.length)) {
5143
+ _context3.n = 6;
5144
+ break;
5112
5145
  }
5113
- _context4.n = 8;
5114
- break;
5115
- case 5:
5116
- if (!(segments.length > 0)) {
5117
- _context4.n = 8;
5146
+ _axes$_i = _axes[_i], axis = _axes$_i.axis, delta = _axes$_i.delta;
5147
+ if (!(Math.abs(delta) > threshold)) {
5148
+ _context3.n = 5;
5118
5149
  break;
5119
5150
  }
5120
- // Fallback to individual API calls if manager not available
5121
- // Note: These calls include updatePaths() so will be slower
5122
- console.warn('⚠️ TransformOperationsManager not available, using individual API calls');
5123
-
5124
- // Use same throttling as above
5125
- _THROTTLE_DELAY = 10;
5126
- _SEGMENT_DELAY = 20;
5127
- _throttleDelay2 = function _throttleDelay2(ms) {
5128
- return new Promise(function (resolve) {
5129
- return setTimeout(resolve, ms);
5130
- });
5131
- };
5132
- _loop2 = /*#__PURE__*/_regenerator().m(function _loop2(_i) {
5133
- var segment, _segment$userData7, _segment$userData8, segmentId, newSegmentId, _segment$userData9, _segment$userData0, _segmentId3, _newSegmentId3, _segment$userData1, _segment$userData10, _segmentId4, _newSegmentId4, selectedIndex;
5134
- return _regenerator().w(function (_context3) {
5135
- while (1) switch (_context3.n) {
5136
- case 0:
5137
- segment = segments[_i];
5138
- if (!(Math.abs(deltaX) > threshold)) {
5139
- _context3.n = 1;
5140
- break;
5141
- }
5142
- segmentId = segment.uuid || ((_segment$userData7 = segment.userData) === null || _segment$userData7 === void 0 ? void 0 : _segment$userData7.originalUuid);
5143
- _this9.centralPlant.translateSegment(segmentId, 'x', deltaX);
5144
- // Refresh segment reference by searching for the new UUID after manualization
5145
- newSegmentId = (_segment$userData8 = segment.userData) !== null && _segment$userData8 !== void 0 && _segment$userData8.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : segmentId;
5146
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', newSegmentId) || segment;
5147
-
5148
- // Update matrix to ensure geometry is current for next operation
5149
- if (segment) {
5150
- segment.updateMatrix();
5151
- segment.updateMatrixWorld(true);
5152
- }
5153
- _context3.n = 1;
5154
- return _throttleDelay2(_THROTTLE_DELAY);
5155
- case 1:
5156
- if (!(Math.abs(deltaY) > threshold)) {
5157
- _context3.n = 2;
5158
- break;
5159
- }
5160
- _segmentId3 = segment.uuid || ((_segment$userData9 = segment.userData) === null || _segment$userData9 === void 0 ? void 0 : _segment$userData9.originalUuid);
5161
- _this9.centralPlant.translateSegment(_segmentId3, 'y', deltaY);
5162
- // Refresh segment reference by searching for the new UUID after manualization
5163
- _newSegmentId3 = (_segment$userData0 = segment.userData) !== null && _segment$userData0 !== void 0 && _segment$userData0.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : _segmentId3;
5164
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', _newSegmentId3) || segment;
5165
-
5166
- // Update matrix to ensure geometry is current for next operation
5167
- if (segment) {
5168
- segment.updateMatrix();
5169
- segment.updateMatrixWorld(true);
5170
- }
5171
- _context3.n = 2;
5172
- return _throttleDelay2(_THROTTLE_DELAY);
5173
- case 2:
5174
- if (!(Math.abs(deltaZ) > threshold)) {
5175
- _context3.n = 3;
5176
- break;
5177
- }
5178
- _segmentId4 = segment.uuid || ((_segment$userData1 = segment.userData) === null || _segment$userData1 === void 0 ? void 0 : _segment$userData1.originalUuid);
5179
- _this9.centralPlant.translateSegment(_segmentId4, 'z', deltaZ);
5180
- // Refresh segment reference by searching for the new UUID after manualization
5181
- _newSegmentId4 = (_segment$userData10 = segment.userData) !== null && _segment$userData10 !== void 0 && _segment$userData10.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : _segmentId4;
5182
- segment = _this9.sceneViewer.scene.getObjectByProperty('uuid', _newSegmentId4) || segment;
5183
-
5184
- // Update matrix to ensure geometry is current for next operation
5185
- if (segment) {
5186
- segment.updateMatrix();
5187
- segment.updateMatrixWorld(true);
5188
- }
5189
- _context3.n = 3;
5190
- return _throttleDelay2(_THROTTLE_DELAY);
5191
- case 3:
5192
- // Update the segment in the selectedObjects array with the refreshed reference
5193
- selectedIndex = _this9.selectedObjects.findIndex(function (obj) {
5194
- var _obj$userData2;
5195
- return obj.uuid === segments[_i].uuid || ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.originalUuid) === segments[_i].uuid;
5196
- });
5197
- if (selectedIndex !== -1 && segment) {
5198
- // Clear bounding box cache for both old and new segment references
5199
- if (_this9.boundingBoxCache.has(segments[_i])) {
5200
- _this9.boundingBoxCache.delete(segments[_i]);
5201
- }
5202
- if (_this9.boundingBoxCache.has(segment)) {
5203
- _this9.boundingBoxCache.delete(segment);
5204
- }
5205
- _this9.selectedObjects[selectedIndex] = segment;
5206
- console.log("\u2705 Updated selectedObjects[".concat(selectedIndex, "] with refreshed segment reference"));
5207
- }
5208
- if (segment) {
5209
- console.log("\uD83D\uDCE6 Segment ".concat(segment.name, " translated (").concat(_i + 1, "/").concat(segments.length, "):"), {
5210
- deltaX: deltaX,
5211
- deltaY: deltaY,
5212
- deltaZ: deltaZ
5213
- });
5214
- } else {
5215
- console.warn("\u26A0\uFE0F Segment reference became null after translation (".concat(_i + 1, "/").concat(segments.length, ")"));
5216
- }
5217
-
5218
- // Delay between segments
5219
- if (!(_i < segments.length - 1)) {
5220
- _context3.n = 4;
5221
- break;
5222
- }
5223
- _context3.n = 4;
5224
- return _throttleDelay2(_SEGMENT_DELAY);
5225
- case 4:
5226
- return _context3.a(2);
5227
- }
5228
- }, _loop2);
5229
- });
5230
- _i = 0;
5151
+ _context3.n = 4;
5152
+ return this._translateSegmentOnAxis(segment, axis, delta, useOptimizedPath, i);
5153
+ case 4:
5154
+ segment = _context3.v;
5155
+ _context3.n = 5;
5156
+ return throttleDelay(AXIS_THROTTLE);
5157
+ case 5:
5158
+ _i++;
5159
+ _context3.n = 3;
5160
+ break;
5231
5161
  case 6:
5232
- if (!(_i < segments.length)) {
5233
- _context4.n = 8;
5162
+ // Update selected objects array with refreshed reference
5163
+ this._updateSegmentReference(segments[i], segment, i);
5164
+ if (segment) {
5165
+ console.log("\uD83D\uDCE6 Segment ".concat(segment.name, " translated (").concat(i + 1, "/").concat(segments.length, ")"));
5166
+ } else {
5167
+ console.warn("\u26A0\uFE0F Segment reference became null (".concat(i + 1, "/").concat(segments.length, ")"));
5168
+ }
5169
+ if (!(i < segments.length - 1)) {
5170
+ _context3.n = 7;
5234
5171
  break;
5235
5172
  }
5236
- return _context4.d(_regeneratorValues(_loop2(_i)), 7);
5173
+ _context3.n = 7;
5174
+ return throttleDelay(OBJECT_DELAY);
5237
5175
  case 7:
5238
- _i++;
5239
- _context4.n = 6;
5176
+ i++;
5177
+ _context3.n = 2;
5240
5178
  break;
5241
5179
  case 8:
5242
- // Throttle delays for gateways and components
5243
- GATEWAY_THROTTLE = 10;
5244
- GATEWAY_DELAY = 15;
5245
- COMPONENT_THROTTLE = 10;
5246
- COMPONENT_DELAY = 15;
5180
+ // Batch update paths for optimized path only
5181
+ if (useOptimizedPath && (_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.updatePaths) {
5182
+ console.log('🔄 Calling updatePaths once for all segments...');
5183
+ this.sceneViewer.updatePaths();
5184
+ console.log('✅ Paths regenerated successfully');
5185
+ }
5186
+ case 9:
5187
+ return _context3.a(2);
5188
+ }
5189
+ }, _callee3, this);
5190
+ }));
5191
+ function _translateSegments(_x, _x2, _x3, _x4, _x5) {
5192
+ return _translateSegments2.apply(this, arguments);
5193
+ }
5194
+ return _translateSegments;
5195
+ }()
5196
+ /**
5197
+ * Refresh segment reference after manualization
5198
+ * Handles the UUID change from original format to SEGMENT-{index} format
5199
+ * @param {THREE.Object3D} segment - Original segment object
5200
+ * @returns {THREE.Object3D|null} Refreshed segment reference or null if not found
5201
+ * @private
5202
+ */
5203
+ )
5204
+ }, {
5205
+ key: "_refreshSegmentReference",
5206
+ value: function _refreshSegmentReference(segment) {
5207
+ var _segment$userData, _segment$userData2;
5208
+ if (!segment) return null;
5209
+ var segmentId = segment.uuid || ((_segment$userData = segment.userData) === null || _segment$userData === void 0 ? void 0 : _segment$userData.originalUuid);
5210
+
5211
+ // Try to find by new UUID format (SEGMENT-{index})
5212
+ var newSegmentId = (_segment$userData2 = segment.userData) !== null && _segment$userData2 !== void 0 && _segment$userData2.segmentIndex ? "SEGMENT-".concat(segment.userData.segmentIndex) : segmentId;
5213
+ var refreshedSegment = this.sceneViewer.scene.getObjectByProperty('uuid', newSegmentId) || segment;
5214
+ if (refreshedSegment) {
5215
+ refreshedSegment.updateMatrix();
5216
+ refreshedSegment.updateMatrixWorld(true);
5217
+ }
5218
+ return refreshedSegment;
5219
+ }
5220
+
5221
+ /**
5222
+ * Translate a segment on a single axis and refresh its reference
5223
+ * @private
5224
+ */
5225
+ }, {
5226
+ key: "_translateSegmentOnAxis",
5227
+ value: (function () {
5228
+ var _translateSegmentOnAxis2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(segment, axis, delta, useOptimizedPath, index) {
5229
+ var _segment$userData3;
5230
+ var segmentId, success;
5231
+ return _regenerator().w(function (_context4) {
5232
+ while (1) switch (_context4.n) {
5233
+ case 0:
5234
+ segmentId = segment.uuid || ((_segment$userData3 = segment.userData) === null || _segment$userData3 === void 0 ? void 0 : _segment$userData3.originalUuid);
5235
+ if (useOptimizedPath) {
5236
+ success = this.sceneViewer.transformOperationsManager.translateSegment(segmentId, axis, delta, true);
5237
+ if (!success) {
5238
+ console.warn("\u26A0\uFE0F Failed to translate segment ".concat(segmentId, " on ").concat(axis, " axis"));
5239
+ }
5240
+ } else {
5241
+ this.centralPlant.translateSegment(segmentId, axis, delta);
5242
+ }
5243
+
5244
+ // Refresh segment reference after manualization
5245
+ return _context4.a(2, this._refreshSegmentReference(segment));
5246
+ }
5247
+ }, _callee4, this);
5248
+ }));
5249
+ function _translateSegmentOnAxis(_x6, _x7, _x8, _x9, _x0) {
5250
+ return _translateSegmentOnAxis2.apply(this, arguments);
5251
+ }
5252
+ return _translateSegmentOnAxis;
5253
+ }()
5254
+ /**
5255
+ * Update segment reference in selectedObjects array and clear cache
5256
+ * @private
5257
+ */
5258
+ )
5259
+ }, {
5260
+ key: "_updateSegmentReference",
5261
+ value: function _updateSegmentReference(oldSegment, newSegment, index) {
5262
+ var selectedIndex = this.selectedObjects.findIndex(function (obj) {
5263
+ var _obj$userData;
5264
+ return obj.uuid === oldSegment.uuid || ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.originalUuid) === oldSegment.uuid;
5265
+ });
5266
+ if (selectedIndex !== -1 && newSegment) {
5267
+ // Clear bounding box cache
5268
+ this.boundingBoxCache.delete(oldSegment);
5269
+ this.boundingBoxCache.delete(newSegment);
5270
+ this.selectedObjects[selectedIndex] = newSegment;
5271
+ console.log("\u2705 Updated selectedObjects[".concat(selectedIndex, "] with refreshed reference"));
5272
+ }
5273
+ }
5274
+
5275
+ /**
5276
+ * Translate gateways on all axes
5277
+ * @private
5278
+ */
5279
+ }, {
5280
+ key: "_translateGateways",
5281
+ value: (function () {
5282
+ var _translateGateways2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(gateways, deltaX, deltaY, deltaZ, threshold) {
5283
+ var _this9 = this;
5284
+ var throttleDelay, AXIS_THROTTLE, OBJECT_DELAY, i, _gateway$userData, gateway, gatewayId;
5285
+ return _regenerator().w(function (_context5) {
5286
+ while (1) switch (_context5.n) {
5287
+ case 0:
5288
+ if (!(gateways.length === 0)) {
5289
+ _context5.n = 1;
5290
+ break;
5291
+ }
5292
+ return _context5.a(2);
5293
+ case 1:
5247
5294
  throttleDelay = function throttleDelay(ms) {
5248
5295
  return new Promise(function (resolve) {
5249
5296
  return setTimeout(resolve, ms);
5250
5297
  });
5251
- }; // Translate gateways sequentially with throttling
5252
- _i2 = 0;
5253
- case 9:
5254
- if (!(_i2 < gateways.length)) {
5255
- _context4.n = 14;
5298
+ };
5299
+ AXIS_THROTTLE = 10;
5300
+ OBJECT_DELAY = 15;
5301
+ i = 0;
5302
+ case 2:
5303
+ if (!(i < gateways.length)) {
5304
+ _context5.n = 5;
5256
5305
  break;
5257
5306
  }
5258
- gateway = gateways[_i2];
5307
+ gateway = gateways[i];
5259
5308
  gatewayId = gateway.uuid || ((_gateway$userData = gateway.userData) === null || _gateway$userData === void 0 ? void 0 : _gateway$userData.originalUuid);
5260
- if (!(Math.abs(deltaX) > threshold)) {
5261
- _context4.n = 10;
5262
- break;
5263
- }
5264
- this.centralPlant.translateGateway(gatewayId, 'x', deltaX);
5265
- _context4.n = 10;
5266
- return throttleDelay(GATEWAY_THROTTLE);
5267
- case 10:
5268
- if (!(Math.abs(deltaY) > threshold)) {
5269
- _context4.n = 11;
5270
- break;
5271
- }
5272
- this.centralPlant.translateGateway(gatewayId, 'y', deltaY);
5273
- _context4.n = 11;
5274
- return throttleDelay(GATEWAY_THROTTLE);
5275
- case 11:
5276
- if (!(Math.abs(deltaZ) > threshold)) {
5277
- _context4.n = 12;
5278
- break;
5279
- }
5280
- this.centralPlant.translateGateway(gatewayId, 'z', deltaZ);
5281
- _context4.n = 12;
5282
- return throttleDelay(GATEWAY_THROTTLE);
5283
- case 12:
5284
- console.log("\uD83D\uDEAA Gateway ".concat(gateway.name, " translated (").concat(_i2 + 1, "/").concat(gateways.length, "):"), {
5285
- deltaX: deltaX,
5286
- deltaY: deltaY,
5287
- deltaZ: deltaZ
5309
+ _context5.n = 3;
5310
+ return this._translateObjectOnAxes(gatewayId, deltaX, deltaY, deltaZ, threshold, throttleDelay, AXIS_THROTTLE, function (id, axis, delta) {
5311
+ return _this9.centralPlant.translateGateway(id, axis, delta);
5288
5312
  });
5289
-
5290
- // Delay between gateways
5291
- if (!(_i2 < gateways.length - 1)) {
5292
- _context4.n = 13;
5313
+ case 3:
5314
+ console.log("\uD83D\uDEAA Gateway ".concat(gateway.name, " translated (").concat(i + 1, "/").concat(gateways.length, ")"));
5315
+ if (!(i < gateways.length - 1)) {
5316
+ _context5.n = 4;
5293
5317
  break;
5294
5318
  }
5295
- _context4.n = 13;
5296
- return throttleDelay(GATEWAY_DELAY);
5297
- case 13:
5298
- _i2++;
5299
- _context4.n = 9;
5319
+ _context5.n = 4;
5320
+ return throttleDelay(OBJECT_DELAY);
5321
+ case 4:
5322
+ i++;
5323
+ _context5.n = 2;
5300
5324
  break;
5301
- case 14:
5302
- _i3 = 0;
5303
- case 15:
5304
- if (!(_i3 < components.length)) {
5305
- _context4.n = 20;
5306
- break;
5307
- }
5308
- component = components[_i3];
5309
- componentId = component.uuid || ((_component$userData = component.userData) === null || _component$userData === void 0 ? void 0 : _component$userData.originalUuid);
5310
- if (!(Math.abs(deltaX) > threshold)) {
5311
- _context4.n = 16;
5312
- break;
5313
- }
5314
- this.centralPlant.translate(componentId, 'x', deltaX);
5315
- _context4.n = 16;
5316
- return throttleDelay(COMPONENT_THROTTLE);
5317
- case 16:
5318
- if (!(Math.abs(deltaY) > threshold)) {
5319
- _context4.n = 17;
5325
+ case 5:
5326
+ return _context5.a(2);
5327
+ }
5328
+ }, _callee5, this);
5329
+ }));
5330
+ function _translateGateways(_x1, _x10, _x11, _x12, _x13) {
5331
+ return _translateGateways2.apply(this, arguments);
5332
+ }
5333
+ return _translateGateways;
5334
+ }()
5335
+ /**
5336
+ * Translate components on all axes
5337
+ * @private
5338
+ */
5339
+ )
5340
+ }, {
5341
+ key: "_translateComponents",
5342
+ value: (function () {
5343
+ var _translateComponents2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(components, deltaX, deltaY, deltaZ, threshold) {
5344
+ var _this0 = this;
5345
+ var throttleDelay, AXIS_THROTTLE, OBJECT_DELAY, i, _component$userData, component, componentId;
5346
+ return _regenerator().w(function (_context6) {
5347
+ while (1) switch (_context6.n) {
5348
+ case 0:
5349
+ if (!(components.length === 0)) {
5350
+ _context6.n = 1;
5320
5351
  break;
5321
5352
  }
5322
- this.centralPlant.translate(componentId, 'y', deltaY);
5323
- _context4.n = 17;
5324
- return throttleDelay(COMPONENT_THROTTLE);
5325
- case 17:
5326
- if (!(Math.abs(deltaZ) > threshold)) {
5327
- _context4.n = 18;
5353
+ return _context6.a(2);
5354
+ case 1:
5355
+ throttleDelay = function throttleDelay(ms) {
5356
+ return new Promise(function (resolve) {
5357
+ return setTimeout(resolve, ms);
5358
+ });
5359
+ };
5360
+ AXIS_THROTTLE = 10;
5361
+ OBJECT_DELAY = 15;
5362
+ i = 0;
5363
+ case 2:
5364
+ if (!(i < components.length)) {
5365
+ _context6.n = 5;
5328
5366
  break;
5329
5367
  }
5330
- this.centralPlant.translate(componentId, 'z', deltaZ);
5331
- _context4.n = 18;
5332
- return throttleDelay(COMPONENT_THROTTLE);
5333
- case 18:
5334
- console.log("\uD83D\uDD27 Component ".concat(component.name, " translated (").concat(_i3 + 1, "/").concat(components.length, "):"), {
5335
- deltaX: deltaX,
5336
- deltaY: deltaY,
5337
- deltaZ: deltaZ
5368
+ component = components[i];
5369
+ componentId = component.uuid || ((_component$userData = component.userData) === null || _component$userData === void 0 ? void 0 : _component$userData.originalUuid);
5370
+ _context6.n = 3;
5371
+ return this._translateObjectOnAxes(componentId, deltaX, deltaY, deltaZ, threshold, throttleDelay, AXIS_THROTTLE, function (id, axis, delta) {
5372
+ return _this0.centralPlant.translate(id, axis, delta);
5338
5373
  });
5339
-
5340
- // Delay between components
5341
- if (!(_i3 < components.length - 1)) {
5342
- _context4.n = 19;
5374
+ case 3:
5375
+ console.log("\uD83D\uDD27 Component ".concat(component.name, " translated (").concat(i + 1, "/").concat(components.length, ")"));
5376
+ if (!(i < components.length - 1)) {
5377
+ _context6.n = 4;
5343
5378
  break;
5344
5379
  }
5345
- _context4.n = 19;
5346
- return throttleDelay(COMPONENT_DELAY);
5347
- case 19:
5348
- _i3++;
5349
- _context4.n = 15;
5380
+ _context6.n = 4;
5381
+ return throttleDelay(OBJECT_DELAY);
5382
+ case 4:
5383
+ i++;
5384
+ _context6.n = 2;
5350
5385
  break;
5351
- case 20:
5352
- console.log("\u2705 All ".concat(this.selectedObjects.length, " objects translated with delta:"), {
5353
- deltaX: deltaX,
5354
- deltaY: deltaY,
5355
- deltaZ: deltaZ
5356
- });
5357
- case 21:
5358
- // Reset the multi-selection group transform (if it still exists)
5359
- // Note: The group might have been cleared during async operations
5360
- if (this.multiSelectionGroup) {
5361
- this.multiSelectionGroup.position.set(0, 0, 0);
5362
- this.multiSelectionGroup.rotation.set(0, 0, 0);
5363
- this.multiSelectionGroup.scale.set(1, 1, 1);
5364
- } else {
5365
- console.warn('⚠️ Multi-selection group was cleared during transformation');
5386
+ case 5:
5387
+ return _context6.a(2);
5388
+ }
5389
+ }, _callee6, this);
5390
+ }));
5391
+ function _translateComponents(_x14, _x15, _x16, _x17, _x18) {
5392
+ return _translateComponents2.apply(this, arguments);
5393
+ }
5394
+ return _translateComponents;
5395
+ }()
5396
+ /**
5397
+ * Generic method to translate object on all axes with throttling
5398
+ * @private
5399
+ */
5400
+ )
5401
+ }, {
5402
+ key: "_translateObjectOnAxes",
5403
+ value: (function () {
5404
+ var _translateObjectOnAxes2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(objectId, deltaX, deltaY, deltaZ, threshold, throttleDelay, axisThrottle, translateFn) {
5405
+ var axes, _i2, _axes2, _axes2$_i, axis, delta;
5406
+ return _regenerator().w(function (_context7) {
5407
+ while (1) switch (_context7.n) {
5408
+ case 0:
5409
+ axes = [{
5410
+ axis: 'x',
5411
+ delta: deltaX
5412
+ }, {
5413
+ axis: 'y',
5414
+ delta: deltaY
5415
+ }, {
5416
+ axis: 'z',
5417
+ delta: deltaZ
5418
+ }];
5419
+ _i2 = 0, _axes2 = axes;
5420
+ case 1:
5421
+ if (!(_i2 < _axes2.length)) {
5422
+ _context7.n = 3;
5423
+ break;
5366
5424
  }
5367
-
5368
- // CRITICAL: Clear bounding box cache after manualization
5369
- // Manualization changes object references, making cached boxes invalid
5370
- this.clearBoundingBoxCache();
5371
-
5372
- // Update the multi-selection display with new positions (only if we still have selected objects)
5373
- if (this.selectedObjects.length > 0) {
5374
- this.updateMultiSelection();
5425
+ _axes2$_i = _axes2[_i2], axis = _axes2$_i.axis, delta = _axes2$_i.delta;
5426
+ if (!(Math.abs(delta) > threshold)) {
5427
+ _context7.n = 2;
5428
+ break;
5375
5429
  }
5376
- console.log("\u2705 Multi-selection transform applied");
5377
- case 22:
5378
- return _context4.a(2);
5430
+ translateFn(objectId, axis, delta);
5431
+ _context7.n = 2;
5432
+ return throttleDelay(axisThrottle);
5433
+ case 2:
5434
+ _i2++;
5435
+ _context7.n = 1;
5436
+ break;
5437
+ case 3:
5438
+ return _context7.a(2);
5379
5439
  }
5380
- }, _callee2, this);
5440
+ }, _callee7);
5381
5441
  }));
5382
- function applyMultiSelectionTransform() {
5383
- return _applyMultiSelectionTransform.apply(this, arguments);
5442
+ function _translateObjectOnAxes(_x19, _x20, _x21, _x22, _x23, _x24, _x25, _x26) {
5443
+ return _translateObjectOnAxes2.apply(this, arguments);
5384
5444
  }
5385
- return applyMultiSelectionTransform;
5445
+ return _translateObjectOnAxes;
5386
5446
  }()
5447
+ /**
5448
+ * Finalize multi-selection transform by resetting group and updating display
5449
+ * @private
5450
+ */
5451
+ )
5452
+ }, {
5453
+ key: "_finalizeMultiSelectionTransform",
5454
+ value: function _finalizeMultiSelectionTransform() {
5455
+ // Reset multi-selection group transform
5456
+ if (this.multiSelectionGroup) {
5457
+ this.multiSelectionGroup.position.set(0, 0, 0);
5458
+ this.multiSelectionGroup.rotation.set(0, 0, 0);
5459
+ this.multiSelectionGroup.scale.set(1, 1, 1);
5460
+ } else {
5461
+ console.warn('⚠️ Multi-selection group was cleared during transformation');
5462
+ }
5463
+
5464
+ // Clear bounding box cache after manualization
5465
+ this.clearBoundingBoxCache();
5466
+
5467
+ // Update multi-selection display
5468
+ if (this.selectedObjects.length > 0) {
5469
+ this.updateMultiSelection();
5470
+ }
5471
+ console.log("\u2705 Multi-selection transform applied");
5472
+ }
5473
+
5387
5474
  /**
5388
5475
  * Handle transformation of a pipe segment using the centralPlant translateSegment API
5389
5476
  *
@@ -5393,11 +5480,10 @@ var TransformControlsManager = /*#__PURE__*/function () {
5393
5480
  *
5394
5481
  * @param {THREE.Object3D} segment - The transformed pipe segment
5395
5482
  */
5396
- )
5397
5483
  }, {
5398
5484
  key: "handlePipeSegmentTransform",
5399
5485
  value: function handlePipeSegmentTransform(segment) {
5400
- var _this$transformState, _this$transformState2, _segment$userData11;
5486
+ var _this$transformState, _this$transformState2, _segment$userData4;
5401
5487
  console.log('🔧 Pipe segment transformed:', segment.name);
5402
5488
 
5403
5489
  // Safety checks
@@ -5434,7 +5520,7 @@ var TransformControlsManager = /*#__PURE__*/function () {
5434
5520
  });
5435
5521
 
5436
5522
  // Get the segment ID (try both uuid and originalUuid)
5437
- var segmentId = segment.uuid || ((_segment$userData11 = segment.userData) === null || _segment$userData11 === void 0 ? void 0 : _segment$userData11.originalUuid);
5523
+ var segmentId = segment.uuid || ((_segment$userData4 = segment.userData) === null || _segment$userData4 === void 0 ? void 0 : _segment$userData4.originalUuid);
5438
5524
  if (!segmentId) {
5439
5525
  console.error('❌ Cannot find segment ID for translateSegment API call');
5440
5526
  return;
@@ -5453,14 +5539,14 @@ var TransformControlsManager = /*#__PURE__*/function () {
5453
5539
  if (Math.abs(deltaY) > threshold) {
5454
5540
  // Round to nearest 0.5 to match grid snapping
5455
5541
  var roundedDeltaY = Math.round(deltaY * 2) / 2;
5456
- var _success3 = this.centralPlant.translateSegment(segmentId, 'y', roundedDeltaY);
5457
- if (_success3) translationApplied = true;
5542
+ var _success = this.centralPlant.translateSegment(segmentId, 'y', roundedDeltaY);
5543
+ if (_success) translationApplied = true;
5458
5544
  }
5459
5545
  if (Math.abs(deltaZ) > threshold) {
5460
5546
  // Round to nearest 0.5 to match grid snapping
5461
5547
  var roundedDeltaZ = Math.round(deltaZ * 2) / 2;
5462
- var _success4 = this.centralPlant.translateSegment(segmentId, 'z', roundedDeltaZ);
5463
- if (_success4) translationApplied = true;
5548
+ var _success2 = this.centralPlant.translateSegment(segmentId, 'z', roundedDeltaZ);
5549
+ if (_success2) translationApplied = true;
5464
5550
  }
5465
5551
  if (translationApplied) {
5466
5552
  console.log("\u2705 Applied segment translation through centralPlant translateSegment API");
@@ -5536,14 +5622,14 @@ var TransformControlsManager = /*#__PURE__*/function () {
5536
5622
  if (Math.abs(deltaY) > threshold) {
5537
5623
  // Round to nearest 0.5 to match grid snapping
5538
5624
  var roundedDeltaY = Math.round(deltaY * 2) / 2;
5539
- var _success5 = this.centralPlant.translateGateway(gatewayId, 'y', roundedDeltaY);
5540
- if (_success5) translationApplied = true;
5625
+ var _success3 = this.centralPlant.translateGateway(gatewayId, 'y', roundedDeltaY);
5626
+ if (_success3) translationApplied = true;
5541
5627
  }
5542
5628
  if (Math.abs(deltaZ) > threshold) {
5543
5629
  // Round to nearest 0.5 to match grid snapping
5544
5630
  var roundedDeltaZ = Math.round(deltaZ * 2) / 2;
5545
- var _success6 = this.centralPlant.translateGateway(gatewayId, 'z', roundedDeltaZ);
5546
- if (_success6) translationApplied = true;
5631
+ var _success4 = this.centralPlant.translateGateway(gatewayId, 'z', roundedDeltaZ);
5632
+ if (_success4) translationApplied = true;
5547
5633
  }
5548
5634
  if (translationApplied) {
5549
5635
  console.log("\u2705 Applied gateway translation through centralPlant translateGateway API");
@@ -19059,10 +19145,15 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19059
19145
  console.error("\u274C translateComponent(): Object with ID '".concat(componentId, "' is not a valid component"));
19060
19146
  return false;
19061
19147
  }
19062
-
19063
- // Apply the translation
19064
19148
  console.log("\uD83D\uDD04 translateComponent(): Translating component ".concat(componentId, " on ").concat(axis, " axis by ").concat(value));
19065
19149
 
19150
+ // Run collision validation checks before applying the translation
19151
+ var collisionValidation = this.validateTranslateComponentCollisions(component, axis, value);
19152
+ if (!collisionValidation) {
19153
+ return false;
19154
+ }
19155
+
19156
+ // All validations passed - apply the translation
19066
19157
  // Update the Three.js object position
19067
19158
  component.position[axis] += value;
19068
19159
 
@@ -19106,6 +19197,100 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19106
19197
  return true;
19107
19198
  }
19108
19199
 
19200
+ /**
19201
+ * Validate collision checks for translateComponent operation
19202
+ * @param {THREE.Object3D} component - The component to translate
19203
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
19204
+ * @param {number} value - The value to translate by
19205
+ * @returns {boolean} True if no collisions detected, false if translation would cause collision
19206
+ * @private
19207
+ */
19208
+ }, {
19209
+ key: "validateTranslateComponentCollisions",
19210
+ value: function validateTranslateComponentCollisions(component, axis, value) {
19211
+ // Store original position for reverting
19212
+ var originalPosition = component.position[axis];
19213
+
19214
+ // Temporarily apply the translation to check for collisions
19215
+ component.position[axis] += value;
19216
+ component.updateMatrix();
19217
+ component.updateMatrixWorld(true);
19218
+ console.log("\uD83D\uDD0D Checking collisions with translated position: ".concat(axis, "=").concat(component.position[axis].toFixed(3)));
19219
+
19220
+ // Check for collision with manual segments
19221
+ var manualSegmentCollision = this.checkComponentManualSegmentCollision(component);
19222
+ if (manualSegmentCollision) {
19223
+ console.warn("\u26A0\uFE0F translateComponent(): Component would collide with manual segment (allowed)");
19224
+ console.warn(" Segment ID: ".concat(manualSegmentCollision.segmentId));
19225
+ console.warn(" Distance to segment: ".concat(manualSegmentCollision.distance.toFixed(3)));
19226
+ console.warn(" Segment endpoints: [".concat(manualSegmentCollision.segmentStart.x.toFixed(3), ", ").concat(manualSegmentCollision.segmentStart.y.toFixed(3), ", ").concat(manualSegmentCollision.segmentStart.z.toFixed(3), "] to [").concat(manualSegmentCollision.segmentEnd.x.toFixed(3), ", ").concat(manualSegmentCollision.segmentEnd.y.toFixed(3), ", ").concat(manualSegmentCollision.segmentEnd.z.toFixed(3), "]"));
19227
+ // Note: We do NOT return false - collision is allowed
19228
+ } else {
19229
+ console.log('✅ No collisions detected');
19230
+ }
19231
+
19232
+ // Revert the temporary translation
19233
+ component.position[axis] = originalPosition;
19234
+ component.updateMatrix();
19235
+ component.updateMatrixWorld(true);
19236
+ return true; // Always allow translation
19237
+ }
19238
+
19239
+ /**
19240
+ * Validate collision checks for translateGateway operation
19241
+ * @param {THREE.Object3D} gateway - The gateway to translate
19242
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
19243
+ * @param {number} value - The value to translate by
19244
+ * @returns {boolean} True if no collisions detected, false if translation would cause collision
19245
+ * @private
19246
+ */
19247
+ }, {
19248
+ key: "validateTranslateGatewayCollisions",
19249
+ value: function validateTranslateGatewayCollisions(gateway, axis, value) {
19250
+ // Store original position for reverting
19251
+ var originalPosition = gateway.position[axis];
19252
+
19253
+ // Temporarily apply the translation to check for collisions
19254
+ gateway.position[axis] += value;
19255
+ gateway.updateMatrix();
19256
+ gateway.updateMatrixWorld(true);
19257
+ console.log("\uD83D\uDD0D Checking collisions with translated gateway position: ".concat(axis, "=").concat(gateway.position[axis].toFixed(3)));
19258
+
19259
+ // Check for collision with components (this should block translation)
19260
+ var componentCollision = this.checkGatewayComponentCollision(gateway);
19261
+ if (componentCollision) {
19262
+ // Revert the translation
19263
+ gateway.position[axis] = originalPosition;
19264
+ gateway.updateMatrix();
19265
+ gateway.updateMatrixWorld(true);
19266
+ console.warn("\u26A0\uFE0F translateGateway(): Translation canceled - gateway would collide with component");
19267
+ console.warn(" Component ID: ".concat(componentCollision.componentId));
19268
+ console.warn(" Component Name: ".concat(componentCollision.componentName));
19269
+ return false;
19270
+ }
19271
+
19272
+ // Check for collision with manual segments (this should also block translation)
19273
+ var manualSegmentCollision = this.checkGatewayManualSegmentCollision(gateway);
19274
+ if (manualSegmentCollision) {
19275
+ // Revert the translation
19276
+ gateway.position[axis] = originalPosition;
19277
+ gateway.updateMatrix();
19278
+ gateway.updateMatrixWorld(true);
19279
+ console.warn("\u26A0\uFE0F translateGateway(): Translation canceled - gateway would collide with manual segment");
19280
+ console.warn(" Segment ID: ".concat(manualSegmentCollision.segmentId));
19281
+ console.warn(" Distance to segment: ".concat(manualSegmentCollision.distance.toFixed(3), " (threshold: 0.25)"));
19282
+ console.warn(" Segment endpoints: [".concat(manualSegmentCollision.segmentStart.x.toFixed(3), ", ").concat(manualSegmentCollision.segmentStart.y.toFixed(3), ", ").concat(manualSegmentCollision.segmentStart.z.toFixed(3), "] to [").concat(manualSegmentCollision.segmentEnd.x.toFixed(3), ", ").concat(manualSegmentCollision.segmentEnd.y.toFixed(3), ", ").concat(manualSegmentCollision.segmentEnd.z.toFixed(3), "]"));
19283
+ return false;
19284
+ }
19285
+ console.log('✅ No collisions detected');
19286
+
19287
+ // Revert the temporary translation
19288
+ gateway.position[axis] = originalPosition;
19289
+ gateway.updateMatrix();
19290
+ gateway.updateMatrixWorld(true);
19291
+ return true; // Allow translation only if no collisions
19292
+ }
19293
+
19109
19294
  /**
19110
19295
  * Translate a pipe segment by segmentId
19111
19296
  * @param {string} segmentId - The UUID of the pipe segment to translate
@@ -19277,12 +19462,10 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19277
19462
  console.warn("\u26A0\uFE0F translateSegment(): Translation canceled - segment would intersect with other segment(s)");
19278
19463
  return false;
19279
19464
  }
19280
- console.log('✅ No intersections detected, proceeding with translation');
19465
+ console.log('✅ No intersections detected, translation pre-applied');
19281
19466
 
19282
- // Revert the temporary translation
19283
- segment.position[axis] = originalPosition;
19284
- segment.updateMatrix();
19285
- segment.updateMatrixWorld(true);
19467
+ // Leave the translation applied - caller expects it to be ready
19468
+ // This eliminates redundant matrix updates
19286
19469
  return true;
19287
19470
  }
19288
19471
  }, {
@@ -19298,16 +19481,14 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19298
19481
  var segment = validationResult.segment;
19299
19482
  console.log("\uD83D\uDD04 translateSegment(): Translating segment ".concat(segmentId, " on ").concat(axis, " axis by ").concat(value));
19300
19483
 
19301
- // Run collision validation checks
19484
+ // Run collision validation checks (applies translation if successful)
19302
19485
  var collisionValidation = this.validateTranslateSegmentCollisions(segment, axis, value);
19303
19486
  if (!collisionValidation) {
19304
19487
  return false;
19305
19488
  }
19306
19489
 
19307
- // All validations passed - apply the translation
19308
- segment.position[axis] += value;
19309
- segment.updateMatrix();
19310
- segment.updateMatrixWorld(true);
19490
+ // All validations passed - translation already applied by validateTranslateSegmentCollisions
19491
+ // No need to redundantly update matrices again
19311
19492
 
19312
19493
  // Validate PathfindingManager and SceneOperationsManager availability
19313
19494
  var pathfindingManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.pathfindingManager;
@@ -19432,7 +19613,13 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19432
19613
  }
19433
19614
  console.log("\uD83D\uDD04 translateGateway(): Translating gateway ".concat(gatewayId, " on ").concat(axis, " axis by ").concat(value));
19434
19615
 
19435
- // Apply the translation to the Three.js object
19616
+ // Run collision validation checks before applying the translation
19617
+ var collisionValidation = this.validateTranslateGatewayCollisions(gateway, axis, value);
19618
+ if (!collisionValidation) {
19619
+ return false;
19620
+ }
19621
+
19622
+ // All validations passed - apply the translation to the Three.js object
19436
19623
  gateway.position[axis] += value;
19437
19624
 
19438
19625
  // Update matrices
@@ -19842,7 +20029,9 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19842
20029
  * Check if a segment intersects with any segments in the scene
19843
20030
  * For grid-based segments (0.5 grid), checks if segments cross or overlap
19844
20031
  * Uses only endpoint comparisons, no radius checks
19845
- * Allows T-junctions (endpoint touching line) if segments are connected (same pathFrom/pathTo)
20032
+ * Allows T-junctions in two cases:
20033
+ * 1. Segments are part of the same connection path (same pathFrom/pathTo)
20034
+ * 2. One segment's endpoint touches another segment's line (not at endpoints)
19846
20035
  * @param {THREE.Object3D} segment - The segment to check for intersections
19847
20036
  * @returns {boolean} True if segment intersects with any segment
19848
20037
  * @private
@@ -19905,19 +20094,26 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19905
20094
 
19906
20095
  // If exactly 1 endpoint is shared, check if they overlap along the same line
19907
20096
  if (sharedEndpointCount === 1) {
19908
- // Check if segments are collinear (on the same line)
19909
- var _distance = _this2.calculateSegmentToSegmentDistance(segmentEndpoints.start, segmentEndpoints.end, otherEndpoints.start, otherEndpoints.end);
19910
- if (_distance < endpointTolerance) {
19911
- // Segments are on the same line and share one endpoint
19912
- // This means they overlap along part of their length
19913
- console.log("\uD83D\uDD34 Overlapping collinear segments detected - share 1 endpoint and are on same line");
20097
+ // Check if segments are collinear (on the same line) by comparing direction vectors
20098
+ // Get direction vectors for both segments
20099
+ var dir1 = new THREE__namespace.Vector3().subVectors(segmentEndpoints.end, segmentEndpoints.start).normalize();
20100
+ var dir2 = new THREE__namespace.Vector3().subVectors(otherEndpoints.end, otherEndpoints.start).normalize();
20101
+
20102
+ // Calculate the absolute dot product (to handle opposite directions)
20103
+ var dotProduct = Math.abs(dir1.dot(dir2));
20104
+
20105
+ // If dot product is close to 1.0, the segments are parallel/collinear
20106
+ // Threshold of 0.9999 allows for ~0.8 degree deviation
20107
+ var collinearThreshold = 0.9999;
20108
+ if (dotProduct > collinearThreshold) {
20109
+ // Segments are collinear and share one endpoint - they overlap
20110
+ console.log("\uD83D\uDD34 Overlapping collinear segments detected - share 1 endpoint and are parallel (dot product: ".concat(dotProduct.toFixed(6), ")"));
19914
20111
  console.log(" Segment 1: [".concat(segmentEndpoints.start.x.toFixed(2), ", ").concat(segmentEndpoints.start.y.toFixed(2), ", ").concat(segmentEndpoints.start.z.toFixed(2), "] to [").concat(segmentEndpoints.end.x.toFixed(2), ", ").concat(segmentEndpoints.end.y.toFixed(2), ", ").concat(segmentEndpoints.end.z.toFixed(2), "]"));
19915
20112
  console.log(" Segment 2: [".concat(otherEndpoints.start.x.toFixed(2), ", ").concat(otherEndpoints.start.y.toFixed(2), ", ").concat(otherEndpoints.start.z.toFixed(2), "] to [").concat(otherEndpoints.end.x.toFixed(2), ", ").concat(otherEndpoints.end.y.toFixed(2), ", ").concat(otherEndpoints.end.z.toFixed(2), "]"));
19916
- console.log(" Distance: ".concat(_distance.toFixed(6)));
19917
20113
  hasIntersection = true;
19918
20114
  } else {
19919
- // Valid end-to-end connection at different angles
19920
- console.log("\u2705 Segments connected at one endpoint with different directions - valid connection");
20115
+ // Valid end-to-end connection at different angles (L-junction, etc.)
20116
+ console.log("\u2705 Segments connected at one endpoint with different directions - valid L-junction (dot product: ".concat(dotProduct.toFixed(6), ")"));
19921
20117
  }
19922
20118
  return;
19923
20119
  }
@@ -19928,12 +20124,23 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19928
20124
  // If distance is near zero, segments cross or overlap
19929
20125
  if (distance < endpointTolerance) {
19930
20126
  // Special case: T-junction (one segment's endpoint touches the other's line)
19931
- // This is allowed if the segments are part of the same connection path
20127
+ // This is allowed if:
20128
+ // 1. Segments are part of the same connection path, OR
20129
+ // 2. The moving segment's endpoint touches the manual segment (T-junction)
19932
20130
  if (sameConnection) {
19933
20131
  var _segment$userData8, _segment$userData9;
19934
20132
  console.log("\u2705 T-junction detected but allowed - segments are part of the same connection (".concat((_segment$userData8 = segment.userData) === null || _segment$userData8 === void 0 ? void 0 : _segment$userData8.pathFrom, " \u2192 ").concat((_segment$userData9 = segment.userData) === null || _segment$userData9 === void 0 ? void 0 : _segment$userData9.pathTo, ")"));
19935
20133
  return;
19936
20134
  }
20135
+
20136
+ // Check if this is a valid T-junction (moving segment endpoint touching manual segment)
20137
+ var isTJunction = _this2.isTJunction(segmentEndpoints.start, segmentEndpoints.end, otherEndpoints.start, otherEndpoints.end, endpointTolerance);
20138
+ if (isTJunction) {
20139
+ console.log("\u2705 T-junction detected but allowed - moving segment endpoint touches manual segment");
20140
+ console.log(" Moving segment: [".concat(segmentEndpoints.start.x.toFixed(2), ", ").concat(segmentEndpoints.start.y.toFixed(2), ", ").concat(segmentEndpoints.start.z.toFixed(2), "] to [").concat(segmentEndpoints.end.x.toFixed(2), ", ").concat(segmentEndpoints.end.y.toFixed(2), ", ").concat(segmentEndpoints.end.z.toFixed(2), "]"));
20141
+ console.log(" Manual segment: [".concat(otherEndpoints.start.x.toFixed(2), ", ").concat(otherEndpoints.start.y.toFixed(2), ", ").concat(otherEndpoints.start.z.toFixed(2), "] to [").concat(otherEndpoints.end.x.toFixed(2), ", ").concat(otherEndpoints.end.y.toFixed(2), ", ").concat(otherEndpoints.end.z.toFixed(2), "]"));
20142
+ return;
20143
+ }
19937
20144
  console.log("\uD83D\uDD34 Intersection detected - segments cross or overlap");
19938
20145
  console.log(" Segment 1: [".concat(segmentEndpoints.start.x.toFixed(2), ", ").concat(segmentEndpoints.start.y.toFixed(2), ", ").concat(segmentEndpoints.start.z.toFixed(2), "] to [").concat(segmentEndpoints.end.x.toFixed(2), ", ").concat(segmentEndpoints.end.y.toFixed(2), ", ").concat(segmentEndpoints.end.z.toFixed(2), "]"));
19939
20146
  console.log(" Segment 2: [".concat(otherEndpoints.start.x.toFixed(2), ", ").concat(otherEndpoints.start.y.toFixed(2), ", ").concat(otherEndpoints.start.z.toFixed(2), "] to [").concat(otherEndpoints.end.x.toFixed(2), ", ").concat(otherEndpoints.end.y.toFixed(2), ", ").concat(otherEndpoints.end.z.toFixed(2), "]"));
@@ -19971,6 +20178,71 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19971
20178
  return this.calculateSegmentEndpoints(segment);
19972
20179
  }
19973
20180
 
20181
+ /**
20182
+ * Check if two segments form a valid T-junction
20183
+ * A T-junction occurs when one segment's endpoint touches the other segment's line (not at an endpoint)
20184
+ * @param {THREE.Vector3} seg1Start - Start point of segment 1
20185
+ * @param {THREE.Vector3} seg1End - End point of segment 1
20186
+ * @param {THREE.Vector3} seg2Start - Start point of segment 2
20187
+ * @param {THREE.Vector3} seg2End - End point of segment 2
20188
+ * @param {number} tolerance - Distance tolerance for "touching"
20189
+ * @returns {boolean} True if segments form a T-junction
20190
+ * @private
20191
+ */
20192
+ }, {
20193
+ key: "isTJunction",
20194
+ value: function isTJunction(seg1Start, seg1End, seg2Start, seg2End, tolerance) {
20195
+ // Helper function to check if a point lies on a line segment (not at endpoints)
20196
+ var isPointOnLineSegment = function isPointOnLineSegment(point, lineStart, lineEnd, tol) {
20197
+ // Direction vector of the line
20198
+ var lineDir = new THREE__namespace.Vector3().subVectors(lineEnd, lineStart);
20199
+ var lineLength = lineDir.length();
20200
+ if (lineLength < 1e-10) {
20201
+ return false; // Degenerate line segment
20202
+ }
20203
+ lineDir.normalize();
20204
+
20205
+ // Vector from line start to point
20206
+ var toPoint = new THREE__namespace.Vector3().subVectors(point, lineStart);
20207
+
20208
+ // Project point onto line to find closest point on line
20209
+ var projectionLength = toPoint.dot(lineDir);
20210
+
20211
+ // Check if projection is within the line segment (not at endpoints)
20212
+ if (projectionLength <= tol || projectionLength >= lineLength - tol) {
20213
+ return false; // Point is at or near an endpoint, not a T-junction
20214
+ }
20215
+
20216
+ // Calculate closest point on line
20217
+ var closestPoint = new THREE__namespace.Vector3().copy(lineStart).add(lineDir.clone().multiplyScalar(projectionLength));
20218
+
20219
+ // Check if point is close enough to the line
20220
+ var distance = point.distanceTo(closestPoint);
20221
+ return distance < tol;
20222
+ };
20223
+
20224
+ // Check if seg1's start point touches seg2's line (not at endpoints)
20225
+ if (isPointOnLineSegment(seg1Start, seg2Start, seg2End, tolerance)) {
20226
+ return true;
20227
+ }
20228
+
20229
+ // Check if seg1's end point touches seg2's line (not at endpoints)
20230
+ if (isPointOnLineSegment(seg1End, seg2Start, seg2End, tolerance)) {
20231
+ return true;
20232
+ }
20233
+
20234
+ // Check if seg2's start point touches seg1's line (not at endpoints)
20235
+ if (isPointOnLineSegment(seg2Start, seg1Start, seg1End, tolerance)) {
20236
+ return true;
20237
+ }
20238
+
20239
+ // Check if seg2's end point touches seg1's line (not at endpoints)
20240
+ if (isPointOnLineSegment(seg2End, seg1Start, seg1End, tolerance)) {
20241
+ return true;
20242
+ }
20243
+ return false;
20244
+ }
20245
+
19974
20246
  /**
19975
20247
  * Check if any point along a segment's path would collide with component connector within 0.5 radius
19976
20248
  * @param {THREE.Object3D} segment - The segment to check
@@ -20020,8 +20292,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20020
20292
 
20021
20293
  // Handle degenerate case (zero-length segment)
20022
20294
  if (segmentLengthSquared < 1e-10) {
20023
- var _distance2 = startPoint.distanceTo(connectorWorldPos);
20024
- if (_distance2 <= collisionRadius) {
20295
+ var _distance = startPoint.distanceTo(connectorWorldPos);
20296
+ if (_distance <= collisionRadius) {
20025
20297
  // Find the parent component
20026
20298
  var component = child.parent;
20027
20299
  while (component && !((_component$userData2 = component.userData) !== null && _component$userData2 !== void 0 && _component$userData2.libraryId)) {
@@ -20041,7 +20313,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20041
20313
  y: startPoint.y,
20042
20314
  z: startPoint.z
20043
20315
  },
20044
- distance: _distance2
20316
+ distance: _distance
20045
20317
  };
20046
20318
  }
20047
20319
  return;
@@ -20185,6 +20457,210 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20185
20457
  return collision;
20186
20458
  }
20187
20459
 
20460
+ /**
20461
+ * Check if a component would collide with any manual (declared) segment
20462
+ * @param {THREE.Object3D} component - The component to check for collisions
20463
+ * @returns {Object|null} Collision info {segmentId, distance, segmentStart, segmentEnd} if collision detected, null otherwise
20464
+ * @private
20465
+ */
20466
+ }, {
20467
+ key: "checkComponentManualSegmentCollision",
20468
+ value: function checkComponentManualSegmentCollision(component) {
20469
+ var _this$sceneViewer8,
20470
+ _this3 = this;
20471
+ if (!((_this$sceneViewer8 = this.sceneViewer) !== null && _this$sceneViewer8 !== void 0 && _this$sceneViewer8.scene) || !component) {
20472
+ return null;
20473
+ }
20474
+
20475
+ // Create a bounding box for the component
20476
+ var componentBBox = new THREE__namespace.Box3().setFromObject(component);
20477
+ var collision = null;
20478
+
20479
+ // Traverse scene to find all manual segments (isDeclared === true)
20480
+ this.sceneViewer.scene.traverse(function (child) {
20481
+ var _child$userData15, _child$userData16;
20482
+ if (collision) return; // Stop if collision already found
20483
+
20484
+ // Only check manual segments (isDeclared === true)
20485
+ if (((_child$userData15 = child.userData) === null || _child$userData15 === void 0 ? void 0 : _child$userData15.objectType) === 'segment' && ((_child$userData16 = child.userData) === null || _child$userData16 === void 0 ? void 0 : _child$userData16.isDeclared) === true) {
20486
+ // Get segment endpoints
20487
+ var segmentEndpoints = _this3.getSegmentEndpoints(child);
20488
+
20489
+ // Check if segment intersects with component's bounding box
20490
+ // We'll use a line-box intersection test
20491
+ var line = new THREE__namespace.Line3(segmentEndpoints.start, segmentEndpoints.end);
20492
+ var closestPoint = new THREE__namespace.Vector3();
20493
+ line.closestPointToPoint(componentBBox.getCenter(new THREE__namespace.Vector3()), true, closestPoint);
20494
+
20495
+ // Check if the closest point on the line is within the bounding box
20496
+ if (componentBBox.containsPoint(closestPoint)) {
20497
+ console.log('⚠️ TransformOperationsManager: Component bounding box collision with manual segment:', child.uuid);
20498
+ collision = {
20499
+ segmentId: child.uuid,
20500
+ distance: 0,
20501
+ // Inside the bounding box
20502
+ segmentStart: segmentEndpoints.start.clone(),
20503
+ segmentEnd: segmentEndpoints.end.clone()
20504
+ };
20505
+ return;
20506
+ }
20507
+
20508
+ // Also check if either endpoint of the segment is inside the component bounding box
20509
+ if (componentBBox.containsPoint(segmentEndpoints.start) || componentBBox.containsPoint(segmentEndpoints.end)) {
20510
+ console.log('⚠️ TransformOperationsManager: Component bounding box contains manual segment endpoint:', child.uuid);
20511
+ collision = {
20512
+ segmentId: child.uuid,
20513
+ distance: 0,
20514
+ // Inside the bounding box
20515
+ segmentStart: segmentEndpoints.start.clone(),
20516
+ segmentEnd: segmentEndpoints.end.clone()
20517
+ };
20518
+ return;
20519
+ }
20520
+
20521
+ // Additionally, check if the segment line intersects any of the bounding box faces
20522
+ // This catches cases where the segment passes through the box without endpoints inside
20523
+ var ray = new THREE__namespace.Ray(segmentEndpoints.start, new THREE__namespace.Vector3().subVectors(segmentEndpoints.end, segmentEndpoints.start).normalize());
20524
+ var segmentLength = segmentEndpoints.start.distanceTo(segmentEndpoints.end);
20525
+ var intersection = ray.intersectBox(componentBBox, new THREE__namespace.Vector3());
20526
+ if (intersection) {
20527
+ var distanceToIntersection = segmentEndpoints.start.distanceTo(intersection);
20528
+ if (distanceToIntersection <= segmentLength) {
20529
+ console.log('⚠️ TransformOperationsManager: Manual segment intersects component bounding box:', child.uuid);
20530
+ collision = {
20531
+ segmentId: child.uuid,
20532
+ distance: 0,
20533
+ // Intersects the bounding box
20534
+ segmentStart: segmentEndpoints.start.clone(),
20535
+ segmentEnd: segmentEndpoints.end.clone()
20536
+ };
20537
+ }
20538
+ }
20539
+ }
20540
+ });
20541
+ return collision;
20542
+ }
20543
+
20544
+ /**
20545
+ * Check if a gateway would collide with any manual (declared) segment
20546
+ * Gateways are treated as point objects, so we check if the gateway point
20547
+ * is too close to any manual segment line. Gateways ARE allowed to be at
20548
+ * segment endpoints, but NOT along the segment between endpoints.
20549
+ * @param {THREE.Object3D} gateway - The gateway to check for collisions
20550
+ * @returns {Object|null} Collision info {segmentId, distance, segmentStart, segmentEnd} if collision detected, null otherwise
20551
+ * @private
20552
+ */
20553
+ }, {
20554
+ key: "checkGatewayManualSegmentCollision",
20555
+ value: function checkGatewayManualSegmentCollision(gateway) {
20556
+ var _this$sceneViewer9,
20557
+ _this4 = this;
20558
+ if (!((_this$sceneViewer9 = this.sceneViewer) !== null && _this$sceneViewer9 !== void 0 && _this$sceneViewer9.scene) || !gateway) {
20559
+ return null;
20560
+ }
20561
+
20562
+ // Get gateway world position
20563
+ var gatewayPos = new THREE__namespace.Vector3();
20564
+ gateway.getWorldPosition(gatewayPos);
20565
+
20566
+ // Define collision threshold - gateways are small point objects
20567
+ var collisionThreshold = 0.25; // Gateway is too close to segment if within this distance
20568
+ var endpointTolerance = 0.01; // Tolerance for considering gateway at an endpoint
20569
+
20570
+ var collision = null;
20571
+
20572
+ // Traverse scene to find all manual segments (isDeclared === true)
20573
+ this.sceneViewer.scene.traverse(function (child) {
20574
+ var _child$userData17, _child$userData18;
20575
+ if (collision) return; // Stop if collision already found
20576
+
20577
+ // Only check manual segments (isDeclared === true)
20578
+ if (((_child$userData17 = child.userData) === null || _child$userData17 === void 0 ? void 0 : _child$userData17.objectType) === 'segment' && ((_child$userData18 = child.userData) === null || _child$userData18 === void 0 ? void 0 : _child$userData18.isDeclared) === true) {
20579
+ // Get segment endpoints
20580
+ var segmentEndpoints = _this4.getSegmentEndpoints(child);
20581
+
20582
+ // Check if gateway is at either endpoint - this is ALLOWED
20583
+ var distToStart = gatewayPos.distanceTo(segmentEndpoints.start);
20584
+ var distToEnd = gatewayPos.distanceTo(segmentEndpoints.end);
20585
+ if (distToStart < endpointTolerance || distToEnd < endpointTolerance) {
20586
+ console.log('✅ TransformOperationsManager: Gateway at segment endpoint (allowed):', child.uuid);
20587
+ return; // Skip collision check - this is allowed
20588
+ }
20589
+
20590
+ // Calculate distance from gateway point to segment line
20591
+ var line = new THREE__namespace.Line3(segmentEndpoints.start, segmentEndpoints.end);
20592
+ var closestPoint = new THREE__namespace.Vector3();
20593
+ line.closestPointToPoint(gatewayPos, true, closestPoint);
20594
+ var distance = gatewayPos.distanceTo(closestPoint);
20595
+
20596
+ // Check if gateway is too close to the segment (but not at endpoints)
20597
+ if (distance < collisionThreshold) {
20598
+ console.log('⚠️ TransformOperationsManager: Gateway too close to manual segment:', child.uuid);
20599
+ console.log(" Distance: ".concat(distance.toFixed(3), " (threshold: ").concat(collisionThreshold, ")"));
20600
+ collision = {
20601
+ segmentId: child.uuid,
20602
+ distance: distance,
20603
+ segmentStart: segmentEndpoints.start.clone(),
20604
+ segmentEnd: segmentEndpoints.end.clone()
20605
+ };
20606
+ return;
20607
+ }
20608
+ }
20609
+ });
20610
+ return collision;
20611
+ }
20612
+
20613
+ /**
20614
+ * Check if a gateway would collide with any component's bounding box
20615
+ * Uses worldBoundingBox from currentSceneData for optimal performance
20616
+ * @param {THREE.Object3D} gateway - The gateway to check for collisions
20617
+ * @returns {Object|null} Collision info {componentId, componentName} if collision detected, null otherwise
20618
+ * @private
20619
+ */
20620
+ }, {
20621
+ key: "checkGatewayComponentCollision",
20622
+ value: function checkGatewayComponentCollision(gateway) {
20623
+ var _this$sceneViewer0;
20624
+ if (!((_this$sceneViewer0 = this.sceneViewer) !== null && _this$sceneViewer0 !== void 0 && _this$sceneViewer0.scene) || !gateway) {
20625
+ return null;
20626
+ }
20627
+
20628
+ // Get gateway world position
20629
+ var gatewayPos = new THREE__namespace.Vector3();
20630
+ gateway.getWorldPosition(gatewayPos);
20631
+ var collision = null;
20632
+
20633
+ // Traverse scene to find all components
20634
+ this.sceneViewer.scene.traverse(function (child) {
20635
+ var _child$userData19, _child$userData20;
20636
+ if (collision) return; // Stop if collision already found
20637
+
20638
+ // Check if this is a component (equipment)
20639
+ if (((_child$userData19 = child.userData) === null || _child$userData19 === void 0 ? void 0 : _child$userData19.objectType) === 'component' && (_child$userData20 = child.userData) !== null && _child$userData20 !== void 0 && _child$userData20.libraryId) {
20640
+ // Try to get worldBoundingBox from userData first (most up-to-date)
20641
+ var bbox = null;
20642
+ if (child.userData.worldBoundingBox) {
20643
+ // Use stored worldBoundingBox (array format: {min: [x,y,z], max: [x,y,z]})
20644
+ bbox = new THREE__namespace.Box3(_construct(THREE__namespace.Vector3, _toConsumableArray(child.userData.worldBoundingBox.min)), _construct(THREE__namespace.Vector3, _toConsumableArray(child.userData.worldBoundingBox.max)));
20645
+ } else {
20646
+ // Fallback: compute from object
20647
+ bbox = new THREE__namespace.Box3().setFromObject(child);
20648
+ }
20649
+
20650
+ // Check if gateway position is inside the component's bounding box
20651
+ if (bbox.containsPoint(gatewayPos)) {
20652
+ console.log('⚠️ TransformOperationsManager: Gateway position inside component bounding box:', child.userData.libraryId || child.name || child.uuid);
20653
+ collision = {
20654
+ componentId: child.uuid,
20655
+ componentName: child.name || child.userData.libraryId || 'Unknown'
20656
+ };
20657
+ return;
20658
+ }
20659
+ }
20660
+ });
20661
+ return collision;
20662
+ }
20663
+
20188
20664
  /**
20189
20665
  * Calculate the minimum distance between two line segments in 3D space
20190
20666
  * @param {THREE.Vector3} p1 - Start point of first segment
@@ -20294,10 +20770,10 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20294
20770
  }, {
20295
20771
  key: "snapSegmentConnectorsToNearbyEndpoints",
20296
20772
  value: function snapSegmentConnectorsToNearbyEndpoints(movedSegment) {
20297
- var _this$sceneViewer8,
20298
- _this$sceneViewer9,
20299
- _this3 = this;
20300
- if (!movedSegment || !((_this$sceneViewer8 = this.sceneViewer) !== null && _this$sceneViewer8 !== void 0 && _this$sceneViewer8.scene)) {
20773
+ var _this$sceneViewer1,
20774
+ _this$sceneViewer10,
20775
+ _this5 = this;
20776
+ if (!movedSegment || !((_this$sceneViewer1 = this.sceneViewer) !== null && _this$sceneViewer1 !== void 0 && _this$sceneViewer1.scene)) {
20301
20777
  return [];
20302
20778
  }
20303
20779
  console.log('🔗 Finding adjacent segments connected to moved segment...');
@@ -20305,8 +20781,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20305
20781
  // Get the moved segment's connectors
20306
20782
  var movedConnectors = [];
20307
20783
  movedSegment.traverse(function (child) {
20308
- var _child$userData15;
20309
- if (((_child$userData15 = child.userData) === null || _child$userData15 === void 0 ? void 0 : _child$userData15.objectType) === 'segment-connector') {
20784
+ var _child$userData21;
20785
+ if (((_child$userData21 = child.userData) === null || _child$userData21 === void 0 ? void 0 : _child$userData21.objectType) === 'segment-connector') {
20310
20786
  movedConnectors.push(child);
20311
20787
  }
20312
20788
  });
@@ -20319,7 +20795,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20319
20795
  var newEndpoints = this.calculateSegmentEndpoints(movedSegment);
20320
20796
 
20321
20797
  // Check scene data for connections involving the moved segment's connectors
20322
- var connections = ((_this$sceneViewer9 = this.sceneViewer) === null || _this$sceneViewer9 === void 0 || (_this$sceneViewer9 = _this$sceneViewer9.currentSceneData) === null || _this$sceneViewer9 === void 0 ? void 0 : _this$sceneViewer9.connections) || [];
20798
+ var connections = ((_this$sceneViewer10 = this.sceneViewer) === null || _this$sceneViewer10 === void 0 || (_this$sceneViewer10 = _this$sceneViewer10.currentSceneData) === null || _this$sceneViewer10 === void 0 ? void 0 : _this$sceneViewer10.connections) || [];
20323
20799
  var movedConnectorIds = movedConnectors.map(function (c) {
20324
20800
  return c.uuid;
20325
20801
  });
@@ -20354,7 +20830,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20354
20830
  // Find the adjacent connector in the scene
20355
20831
  var adjacentConnector = null;
20356
20832
  var adjacentSegment = null;
20357
- _this3.sceneViewer.scene.traverse(function (object) {
20833
+ _this5.sceneViewer.scene.traverse(function (object) {
20358
20834
  var _object$userData;
20359
20835
  if (object.uuid === pair.adjacentConnectorId && ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.objectType) === 'segment-connector') {
20360
20836
  adjacentConnector = object;
@@ -20376,8 +20852,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20376
20852
  // Get all connectors of the adjacent segment
20377
20853
  var adjacentConnectors = [];
20378
20854
  adjacentSegment.traverse(function (child) {
20379
- var _child$userData16;
20380
- if (((_child$userData16 = child.userData) === null || _child$userData16 === void 0 ? void 0 : _child$userData16.objectType) === 'segment-connector') {
20855
+ var _child$userData22;
20856
+ if (((_child$userData22 = child.userData) === null || _child$userData22 === void 0 ? void 0 : _child$userData22.objectType) === 'segment-connector') {
20381
20857
  adjacentConnectors.push(child);
20382
20858
  }
20383
20859
  });
@@ -20403,12 +20879,12 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20403
20879
  // Constrain movement to maintain orthogonal alignment
20404
20880
  // Horizontal segments: only adjust X and Y, keep Z constant
20405
20881
  // Vertical segments: only adjust Z, keep X and Y constant
20406
- var constrainedPosition = _this3.constrainPositionToOrthogonal(pair.newPosition, stationaryWorldPos, adjacentSegment);
20882
+ var constrainedPosition = _this5.constrainPositionToOrthogonal(pair.newPosition, stationaryWorldPos, adjacentSegment);
20407
20883
 
20408
20884
  // Recreate the segment mesh with new length using explicit endpoint positions
20409
20885
  // Pass the intended positions, not the connector objects (which may have temporary positions)
20410
20886
  // Pass movedSegment as activeSegment context so zero-length removal knows which segment to extend
20411
- _this3.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, constrainedPosition, stationaryWorldPos, movedSegment);
20887
+ _this5.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, constrainedPosition, stationaryWorldPos, movedSegment);
20412
20888
 
20413
20889
  // CRITICAL: After recreating the mesh, BOTH connectors need to be repositioned
20414
20890
  // because the segment's center and rotation have changed
@@ -20417,13 +20893,13 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20417
20893
  var movingLocalPos = adjacentSegment.worldToLocal(constrainedPosition.clone());
20418
20894
  movingConnector.position.copy(movingLocalPos);
20419
20895
  movingConnector.updateMatrixWorld(true);
20420
- _this3.updateConnectorPositionInSceneData(movingConnector, constrainedPosition, adjacentSegment);
20896
+ _this5.updateConnectorPositionInSceneData(movingConnector, constrainedPosition, adjacentSegment);
20421
20897
 
20422
20898
  // Position stationary connector at its original world position
20423
20899
  var stationaryLocalPos = adjacentSegment.worldToLocal(stationaryWorldPos.clone());
20424
20900
  stationaryConnector.position.copy(stationaryLocalPos);
20425
20901
  stationaryConnector.updateMatrixWorld(true);
20426
- _this3.updateConnectorPositionInSceneData(stationaryConnector, stationaryWorldPos, adjacentSegment);
20902
+ _this5.updateConnectorPositionInSceneData(stationaryConnector, stationaryWorldPos, adjacentSegment);
20427
20903
 
20428
20904
  // Record this connection as satisfied
20429
20905
  satisfiedConnections.push({
@@ -20473,8 +20949,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20473
20949
  }, {
20474
20950
  key: "updateComponentPositionInSceneData",
20475
20951
  value: function updateComponentPositionInSceneData(component) {
20476
- var _this$sceneViewer0;
20477
- if (!((_this$sceneViewer0 = this.sceneViewer) !== null && _this$sceneViewer0 !== void 0 && (_this$sceneViewer0 = _this$sceneViewer0.currentSceneData) !== null && _this$sceneViewer0 !== void 0 && _this$sceneViewer0.scene)) {
20952
+ var _this$sceneViewer11;
20953
+ if (!((_this$sceneViewer11 = this.sceneViewer) !== null && _this$sceneViewer11 !== void 0 && (_this$sceneViewer11 = _this$sceneViewer11.currentSceneData) !== null && _this$sceneViewer11 !== void 0 && _this$sceneViewer11.scene)) {
20478
20954
  console.warn('⚠️ Cannot update component position: currentSceneData not available');
20479
20955
  return;
20480
20956
  }
@@ -20500,8 +20976,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20500
20976
  // Also update child connectors' positions if they exist
20501
20977
  if (sceneDataComponent.children) {
20502
20978
  sceneDataComponent.children.forEach(function (child) {
20503
- var _child$userData17;
20504
- if (((_child$userData17 = child.userData) === null || _child$userData17 === void 0 ? void 0 : _child$userData17.objectType) === 'connector') {
20979
+ var _child$userData23;
20980
+ if (((_child$userData23 = child.userData) === null || _child$userData23 === void 0 ? void 0 : _child$userData23.objectType) === 'connector') {
20505
20981
  // Find the actual connector object in the scene
20506
20982
  var connectorObj = component.children.find(function (c) {
20507
20983
  var _c$userData;
@@ -20537,9 +21013,9 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20537
21013
  }, {
20538
21014
  key: "updateConnectorPositionInSceneData",
20539
21015
  value: function updateConnectorPositionInSceneData(connector, worldPosition, parentSegment) {
20540
- var _this$sceneViewer1;
21016
+ var _this$sceneViewer12;
20541
21017
  // Update scene data if available
20542
- if (!((_this$sceneViewer1 = this.sceneViewer) !== null && _this$sceneViewer1 !== void 0 && (_this$sceneViewer1 = _this$sceneViewer1.currentSceneData) !== null && _this$sceneViewer1 !== void 0 && _this$sceneViewer1.scene)) {
21018
+ if (!((_this$sceneViewer12 = this.sceneViewer) !== null && _this$sceneViewer12 !== void 0 && (_this$sceneViewer12 = _this$sceneViewer12.currentSceneData) !== null && _this$sceneViewer12 !== void 0 && _this$sceneViewer12.scene)) {
20543
21019
  return;
20544
21020
  }
20545
21021
  var cleanPosition = function cleanPosition(value) {
@@ -20830,8 +21306,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20830
21306
  // Check if either external connector belongs to the active segment
20831
21307
  var activeSegmentConnectors = [];
20832
21308
  activeSegment.traverse(function (child) {
20833
- var _child$userData18;
20834
- if (((_child$userData18 = child.userData) === null || _child$userData18 === void 0 ? void 0 : _child$userData18.objectType) === 'segment-connector') {
21309
+ var _child$userData24;
21310
+ if (((_child$userData24 = child.userData) === null || _child$userData24 === void 0 ? void 0 : _child$userData24.objectType) === 'segment-connector') {
20835
21311
  activeSegmentConnectors.push(child.uuid);
20836
21312
  }
20837
21313
  });
@@ -20879,8 +21355,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20879
21355
  // Get all connectors of the active segment
20880
21356
  var activeConnectors = [];
20881
21357
  activeSegment.traverse(function (child) {
20882
- var _child$userData19;
20883
- if (((_child$userData19 = child.userData) === null || _child$userData19 === void 0 ? void 0 : _child$userData19.objectType) === 'segment-connector') {
21358
+ var _child$userData25;
21359
+ if (((_child$userData25 = child.userData) === null || _child$userData25 === void 0 ? void 0 : _child$userData25.objectType) === 'segment-connector') {
20884
21360
  activeConnectors.push(child);
20885
21361
  }
20886
21362
  });
@@ -25104,7 +25580,7 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
25104
25580
  sceneViewer.scene.add(cylinder);
25105
25581
 
25106
25582
  // Add smooth elbow joints only at actual direction changes (not at every point)
25107
- _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, sceneViewer.scene // Pass scene instead of polyline
25583
+ _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, cylinder // Pass the segment so elbow can be added as a child
25108
25584
  );
25109
25585
  }
25110
25586
  }
@@ -25119,11 +25595,11 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
25119
25595
  * @param {THREE.Material} pipeMaterial - Material for the elbow
25120
25596
  * @param {Object} pathData - Path data object with from/to information
25121
25597
  * @param {number} index - Path index for naming
25122
- * @param {THREE.Scene} scene - Scene object to add elbow to directly
25598
+ * @param {THREE.Object3D} segment - The parent segment to add the elbow to as a child
25123
25599
  */
25124
25600
  }, {
25125
25601
  key: "createAndAddElbowIfNeeded",
25126
- value: function createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, scene) {
25602
+ value: function createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, segment) {
25127
25603
  // Only check if there are at least 2 more points ahead
25128
25604
  if (j >= pathPoints.length - 2) {
25129
25605
  return;
@@ -25138,11 +25614,12 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
25138
25614
  if (angle <= minAngle) {
25139
25615
  return; // No significant direction change
25140
25616
  }
25141
- var elbowGeometry = this.createElbowGeometry(pathPoints[j], pathPoints[j + 1], pathPoints[j + 2], pipeRadius);
25142
- if (!elbowGeometry) {
25143
- return; // Failed to create geometry
25144
- }
25145
- var elbow = new THREE__namespace.Mesh(elbowGeometry, pipeMaterial);
25617
+
25618
+ // Create simple sphere elbow (efficient geometry)
25619
+ var sphereRadius = 0.1;
25620
+ var sphereSegments = 16;
25621
+ var sphereGeometry = new THREE__namespace.SphereGeometry(sphereRadius, sphereSegments, sphereSegments);
25622
+ var elbow = new THREE__namespace.Mesh(sphereGeometry, pipeMaterial);
25146
25623
  elbow.castShadow = true;
25147
25624
  elbow.receiveShadow = true;
25148
25625
 
@@ -25172,72 +25649,32 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
25172
25649
  }
25173
25650
  }
25174
25651
  };
25175
- scene.add(elbow);
25176
- }
25177
25652
 
25178
- /**
25179
- * Create smooth elbow geometry to connect two pipe segments (optimized for 90-degree angles)
25180
- * @param {THREE.Vector3} point1 - First point (before the joint)
25181
- * @param {THREE.Vector3} point2 - Joint point (center of the elbow)
25182
- * @param {THREE.Vector3} point3 - Third point (after the joint)
25183
- * @param {number} radius - Pipe radius
25184
- * @returns {THREE.BufferGeometry} Elbow geometry
25185
- */
25186
- }, {
25187
- key: "createElbowGeometry",
25188
- value: function createElbowGeometry(point1, point2, point3, radius) {
25189
- try {
25190
- // Fixed elbow radius for 90-degree bends (simplified)
25191
- var elbowRadius = radius * 3;
25192
-
25193
- // Create a curve for the 90-degree elbow
25194
- var curve = this.createElbowCurve(point1, point2, point3, elbowRadius);
25195
-
25196
- // Fixed tubular segments for 90-degree bends (quarter circle)
25197
- var tubularSegments = 12; // Good balance for smooth 90° curves
25198
- var radialSegments = 16;
25199
- var closed = false;
25200
- var elbowGeometry = new THREE__namespace.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
25201
- return elbowGeometry;
25202
- } catch (error) {
25203
- console.warn('Failed to create elbow geometry:', error);
25204
- return null;
25205
- }
25206
- }
25653
+ // Calculate elbow position in segment's local space
25654
+ // The elbow is at the end point of the segment (pathPoints[j + 1])
25655
+ // We need to convert this world position to segment's local space
25207
25656
 
25208
- /**
25209
- * Create a smooth curve for the 90-degree elbow joint (simplified)
25210
- * @param {THREE.Vector3} point1 - First point
25211
- * @param {THREE.Vector3} point2 - Joint point
25212
- * @param {THREE.Vector3} point3 - Third point
25213
- * @param {number} elbowRadius - Radius of the elbow curve
25214
- * @returns {THREE.Curve} Curve for the elbow
25215
- */
25216
- }, {
25217
- key: "createElbowCurve",
25218
- value: function createElbowCurve(point1, point2, point3, elbowRadius) {
25219
- // Calculate direction vectors
25220
- var dir1 = point2.clone().sub(point1).normalize();
25221
- var dir2 = point3.clone().sub(point2).normalize();
25657
+ // Get the elbow position in world space
25658
+ var elbowWorldPos = pathPoints[j + 1].clone();
25222
25659
 
25223
- // For 90-degree bends, we can use a simpler approach with a quarter circle
25224
- // The curve radius is proportional to the elbow radius
25225
- var curveRadius = elbowRadius * 0.001;
25660
+ // Convert world position to segment's local space
25661
+ // First, get the segment's world position
25662
+ var segmentWorldPos = segment.position.clone();
25226
25663
 
25227
- // Calculate the offset distance from the joint point
25228
- var offset = curveRadius;
25664
+ // Calculate the offset from segment center to elbow in world space
25665
+ var localOffset = elbowWorldPos.clone().sub(segmentWorldPos);
25229
25666
 
25230
- // Calculate start and end points of the curve (offset from the joint)
25231
- var curveStart = point2.clone().sub(dir1.clone().multiplyScalar(offset));
25232
- var curveEnd = point2.clone().add(dir2.clone().multiplyScalar(offset));
25667
+ // Convert the offset to segment's local coordinate system
25668
+ // We need to apply the inverse of the segment's rotation
25669
+ var inverseQuaternion = segment.quaternion.clone().invert();
25670
+ localOffset.applyQuaternion(inverseQuaternion);
25233
25671
 
25234
- // For a 90-degree bend, the control point is at the corner
25235
- // We can use a quadratic bezier curve for simplicity
25236
- var controlPoint = point2.clone();
25672
+ // Set elbow position in local space
25673
+ elbow.position.copy(localOffset);
25237
25674
 
25238
- // Create a quadratic bezier curve (simpler than cubic for 90° bends)
25239
- var curve = new THREE__namespace.QuadraticBezierCurve3(curveStart, controlPoint, curveEnd);
25240
- return curve;
25675
+ // Add elbow as a child of the segment
25676
+ segment.add(elbow);
25677
+ console.log("\u2705 Elbow sphere added as child of segment at local position (".concat(localOffset.x.toFixed(2), ", ").concat(localOffset.y.toFixed(2), ", ").concat(localOffset.z.toFixed(2), ")"));
25241
25678
  }
25242
25679
 
25243
25680
  /**
@@ -26405,7 +26842,7 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
26405
26842
  // ============================================================================
26406
26843
 
26407
26844
  /**
26408
- * Enrich sceneData with worldBoundingBox for segments
26845
+ * Enrich sceneData with worldBoundingBox for segments and components
26409
26846
  * Connectors remain with just position information
26410
26847
  * @param {Object} sceneData - Original scene data
26411
26848
  * @returns {Object} Enriched scene data (shallow copy with modifications)
@@ -26422,9 +26859,9 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
26422
26859
  return enriched;
26423
26860
  }
26424
26861
 
26425
- // Process children to add worldBoundingBox to segments
26862
+ // Process children to add worldBoundingBox to segments and components
26426
26863
  enriched.children = sceneData.children.map(function (child) {
26427
- // Only enrich segments (check if objectType is 'segment' in userData)
26864
+ // Enrich segments (check if objectType is 'segment' in userData)
26428
26865
  if (child.userData && child.userData.objectType === 'segment') {
26429
26866
  // Find the actual segment object in the scene
26430
26867
  var segmentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
@@ -26447,7 +26884,31 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
26447
26884
  }
26448
26885
  }
26449
26886
 
26450
- // For non-segments (including connectors), return as-is
26887
+ // Enrich components (check if objectType is 'component' in userData)
26888
+ if (child.userData && child.userData.objectType === 'component') {
26889
+ // Find the actual component object in the scene
26890
+ var componentObject = _this3.sceneViewer.scene.getObjectByProperty('uuid', child.uuid);
26891
+ if (componentObject) {
26892
+ // Compute world bounding box
26893
+ var _worldBBox = new THREE__namespace.Box3().setFromObject(componentObject);
26894
+ console.log("\uD83D\uDD04 Updated worldBoundingBox for component ".concat(child.uuid, ": min=[").concat(_worldBBox.min.x.toFixed(2), ", ").concat(_worldBBox.min.y.toFixed(2), ", ").concat(_worldBBox.min.z.toFixed(2), "], max=[").concat(_worldBBox.max.x.toFixed(2), ", ").concat(_worldBBox.max.y.toFixed(2), ", ").concat(_worldBBox.max.z.toFixed(2), "]"));
26895
+
26896
+ // Return enriched component data with worldBoundingBox in userData
26897
+ // Note: pathfinder expects arrays [x, y, z] format for min/max
26898
+ return _objectSpread2(_objectSpread2({}, child), {}, {
26899
+ userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
26900
+ worldBoundingBox: {
26901
+ min: [_worldBBox.min.x, _worldBBox.min.y, _worldBBox.min.z],
26902
+ max: [_worldBBox.max.x, _worldBBox.max.y, _worldBBox.max.z]
26903
+ }
26904
+ })
26905
+ });
26906
+ } else {
26907
+ console.warn("\u26A0\uFE0F Could not find component object in scene: ".concat(child.uuid));
26908
+ }
26909
+ }
26910
+
26911
+ // For non-segments and non-components (including connectors), return as-is
26451
26912
  return child;
26452
26913
  });
26453
26914
  return enriched;
@@ -33427,7 +33888,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
33427
33888
  * Initialize the CentralPlant manager
33428
33889
  *
33429
33890
  * @constructor
33430
- * @version 0.1.55
33891
+ * @version 0.1.60
33431
33892
  * @updated 2025-10-22
33432
33893
  *
33433
33894
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.