@2112-lab/central-plant 0.1.44 → 0.1.45

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.
@@ -18765,7 +18765,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
18765
18765
  }, {
18766
18766
  key: "translateSegment",
18767
18767
  value: function translateSegment(segmentId, axis, value) {
18768
- var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man2, _this$sceneViewer$man3, _this$sceneViewer$man4;
18768
+ var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man3, _this$sceneViewer$man4, _this$sceneViewer$man5;
18769
18769
  // Validate parameters
18770
18770
  if (!segmentId || !axis || value === undefined || value === null) {
18771
18771
  console.error('❌ translateSegment(): Invalid parameters');
@@ -18842,7 +18842,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
18842
18842
  });
18843
18843
  console.log("\uD83D\uDD0D After filtering, ".concat(componentConnectorsAtEndpoints.length, " component connectors at endpoints"));
18844
18844
  if (componentConnectorsAtEndpoints.length > 0) {
18845
- var _this$sceneViewer2;
18845
+ var _this$sceneViewer2, _this$sceneViewer$man2;
18846
18846
  console.warn("\u26A0\uFE0F translateSegment(): Segment '".concat(segmentId, "' has component connectors at endpoints."));
18847
18847
  console.log(" Found ".concat(componentConnectorsAtEndpoints.length, " component connector(s):"), componentConnectorsAtEndpoints.map(function (c) {
18848
18848
  var _c$parent3;
@@ -18858,9 +18858,45 @@ var TransformOperationsManager = /*#__PURE__*/function () {
18858
18858
  console.warn('⚠️ Segment splitting is disabled. Cannot translate segments with component connectors at endpoints.');
18859
18859
  return false;
18860
18860
  }
18861
- console.warn('⚠️ Segment splitting is enabled, but splitting during translate is not supported due to undo complexity.');
18862
- console.warn('⚠️ Please manually split the segment first if you want to translate one half.');
18863
- return false;
18861
+
18862
+ // Split the segment and translate the free half
18863
+ console.log('✂️ Splitting segment to enable translation of the free half...');
18864
+
18865
+ // Get sceneOperationsManager
18866
+ var _sceneOperationsManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.sceneOperationsManager;
18867
+ if (!_sceneOperationsManager) {
18868
+ console.error('❌ translateSegment(): SceneOperationsManager not available for splitting');
18869
+ return false;
18870
+ }
18871
+ try {
18872
+ var splitResult = _sceneOperationsManager.splitSegment(segment);
18873
+ if (splitResult && splitResult.segment1 && splitResult.segment2) {
18874
+ console.log("\u2705 Segment split: ".concat(splitResult.segment1.uuid, " (").concat(splitResult.segment1.geometry.parameters.height, "), ").concat(splitResult.segment2.uuid, " (").concat(splitResult.segment2.geometry.parameters.height, ")"));
18875
+
18876
+ // Determine which segment is the longer one (free side) to translate
18877
+ var segment1Length = splitResult.segment1.geometry.parameters.height;
18878
+ var segment2Length = splitResult.segment2.geometry.parameters.height;
18879
+
18880
+ // The longer segment is the one WITHOUT the component connector
18881
+ var segmentToTranslate = segment1Length > segment2Length ? splitResult.segment1 : splitResult.segment2;
18882
+ console.log("\uD83C\uDFAF Translating ".concat(segmentToTranslate.uuid, " (length: ").concat(segmentToTranslate.geometry.parameters.height, ") - the free segment"));
18883
+
18884
+ // Now translate the chosen segment (recursive call with the new segment)
18885
+ var translateSuccess = this.translateSegment(segmentToTranslate.uuid, axis, value);
18886
+ if (!translateSuccess) {
18887
+ console.error('❌ translateSegment(): Failed to translate split segment half');
18888
+ return false;
18889
+ }
18890
+ console.log('✅ Split and translate operation completed successfully');
18891
+ return true;
18892
+ } else {
18893
+ console.error('❌ translateSegment(): Split operation failed');
18894
+ return false;
18895
+ }
18896
+ } catch (error) {
18897
+ console.error('❌ translateSegment(): Error splitting segment:', error);
18898
+ return false;
18899
+ }
18864
18900
  }
18865
18901
  console.log("\uD83D\uDD04 translateSegment(): Translating segment ".concat(segmentId, " on ").concat(axis, " axis by ").concat(value));
18866
18902
 
@@ -18925,8 +18961,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
18925
18961
  }
18926
18962
 
18927
18963
  // Validate PathfindingManager and SceneOperationsManager availability
18928
- var pathfindingManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.pathfindingManager;
18929
- var sceneOperationsManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.sceneOperationsManager;
18964
+ var pathfindingManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.pathfindingManager;
18965
+ var sceneOperationsManager = (_this$sceneViewer$man4 = this.sceneViewer.managers) === null || _this$sceneViewer$man4 === void 0 ? void 0 : _this$sceneViewer$man4.sceneOperationsManager;
18930
18966
  if (!pathfindingManager || !sceneOperationsManager) {
18931
18967
  console.error('❌ translateSegment(): PathfindingManager or SceneOperationsManager not available');
18932
18968
  return false;
@@ -18961,7 +18997,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
18961
18997
 
18962
18998
  // Store transform parameters using the OperationHistoryManager BEFORE updatePaths
18963
18999
  // This is critical so that intersection detection can undo the operation
18964
- if ((_this$sceneViewer$man4 = this.sceneViewer.managers) !== null && _this$sceneViewer$man4 !== void 0 && _this$sceneViewer$man4.operationHistory) {
19000
+ if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistory) {
18965
19001
  this.sceneViewer.managers.operationHistory.addToOperationHistory('translateSegment', {
18966
19002
  segmentId: segmentId,
18967
19003
  axis: axis,
@@ -19009,11 +19045,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19009
19045
  }, {
19010
19046
  key: "translateGateway",
19011
19047
  value: function translateGateway(gatewayId, axis, value) {
19012
- var _this$sceneViewer$man5, _this$sceneViewer$man6;
19048
+ var _this$sceneViewer$man6, _this$sceneViewer$man7;
19013
19049
  console.log("[Pathfinder] translateGateway started");
19014
19050
 
19015
19051
  // Store transform parameters using the OperationHistoryManager
19016
- if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistoryManager) {
19052
+ if ((_this$sceneViewer$man6 = this.sceneViewer.managers) !== null && _this$sceneViewer$man6 !== void 0 && _this$sceneViewer$man6.operationHistoryManager) {
19017
19053
  this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateGateway', {
19018
19054
  gatewayId: gatewayId,
19019
19055
  axis: axis,
@@ -19064,7 +19100,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19064
19100
 
19065
19101
  // Handle manual gateway transformation (convert to declared and process connections)
19066
19102
  console.log('🔧 Handling manual gateway transformation via SceneOperationsManager');
19067
- var sceneOperationsManager = (_this$sceneViewer$man6 = this.sceneViewer.managers) === null || _this$sceneViewer$man6 === void 0 ? void 0 : _this$sceneViewer$man6.sceneOperationsManager;
19103
+ var sceneOperationsManager = (_this$sceneViewer$man7 = this.sceneViewer.managers) === null || _this$sceneViewer$man7 === void 0 ? void 0 : _this$sceneViewer$man7.sceneOperationsManager;
19068
19104
  if (!sceneOperationsManager) {
19069
19105
  console.error('❌ translateGateway(): SceneOperationsManager not available');
19070
19106
  return false;
@@ -24629,6 +24665,10 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
24629
24665
 
24630
24666
  var pipeRadius = 0.1;
24631
24667
  var pipeMaterial = this.createPipeMaterial(crosscubeTextureSet);
24668
+
24669
+ // Shared geometry for all connector caps to improve performance
24670
+ var capGeometry = new THREE__namespace.SphereGeometry(pipeRadius, 16, 16);
24671
+ this.registerDisposable(capGeometry);
24632
24672
  paths.forEach(function (pathData, index) {
24633
24673
  if (pathData.path && pathData.path.length >= 2) {
24634
24674
  // Convert path points to Vector3 objects for consistent handling
@@ -24682,15 +24722,14 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
24682
24722
  pathTo: pathData.to
24683
24723
  };
24684
24724
 
24725
+ // Create caps at both ends of every segment
24726
+ _this3.createSegmentCaps(cylinder, capGeometry, pipeMaterial, globalSegmentIndex);
24727
+
24685
24728
  // Increment global segment counter
24686
24729
  globalSegmentIndex++;
24687
24730
 
24688
24731
  // Add segment directly to scene instead of to polyline
24689
24732
  sceneViewer.scene.add(cylinder);
24690
-
24691
- // Add smooth elbow joints only at actual direction changes (not at every point)
24692
- _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, sceneViewer.scene // Pass scene instead of polyline
24693
- );
24694
24733
  }
24695
24734
  console.log("\u2705 Created pipe path ".concat(pathData.from, "-").concat(pathData.to, " with ").concat(pathPoints.length - 1, " segments"));
24696
24735
  }
@@ -24698,130 +24737,49 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
24698
24737
  }
24699
24738
 
24700
24739
  /**
24701
- * Create and add elbow joint if there's a direction change at the current segment
24702
- * @param {Array<THREE.Vector3>} pathPoints - Array of path points
24703
- * @param {number} j - Current segment index
24704
- * @param {number} pipeRadius - Radius of the pipe
24705
- * @param {THREE.Material} pipeMaterial - Material for the elbow
24706
- * @param {Object} pathData - Path data object with from/to information
24707
- * @param {number} index - Path index for naming
24708
- * @param {THREE.Scene} scene - Scene object to add elbow to directly
24740
+ * Create connector caps at both segment endpoints as children of the segment
24741
+ * Caps are positioned in local coordinates to match manual segment connector positioning
24742
+ * @param {THREE.Mesh} segment - The parent segment mesh
24743
+ * @param {THREE.BufferGeometry} capGeometry - Shared geometry for caps
24744
+ * @param {THREE.Material} material - Material for the caps
24745
+ * @param {number} segmentIndex - Global segment index
24709
24746
  */
24710
24747
  }, {
24711
- key: "createAndAddElbowIfNeeded",
24712
- value: function createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, scene) {
24713
- // Only check if there are at least 2 more points ahead
24714
- if (j >= pathPoints.length - 2) {
24715
- return;
24716
- }
24717
- var currentSegment = pathPoints[j + 1].clone().sub(pathPoints[j]).normalize();
24718
- var nextSegment = pathPoints[j + 2].clone().sub(pathPoints[j + 1]).normalize();
24719
-
24720
- // Check if there's actually a direction change (not just continuing straight)
24721
- var angle = currentSegment.angleTo(nextSegment);
24722
- var minAngle = Math.PI / 36; // 5 degrees minimum for creating an elbow
24723
-
24724
- if (angle <= minAngle) {
24725
- return; // No significant direction change
24726
- }
24727
- var elbowGeometry = this.createElbowGeometry(pathPoints[j], pathPoints[j + 1], pathPoints[j + 2], pipeRadius);
24728
- if (!elbowGeometry) {
24729
- return; // Failed to create geometry
24730
- }
24731
- var elbow = new THREE__namespace.Mesh(elbowGeometry, pipeMaterial);
24732
- elbow.castShadow = true;
24733
- elbow.receiveShadow = true;
24734
-
24735
- // Make elbows selectable as well
24736
- var elbowId = "pipe-elbow-".concat(pathData.from, "-").concat(pathData.to, "-").concat(j);
24737
- elbow.uuid = "Pipe Elbow ".concat(j + 1, ": ").concat(pathData.from, "-").concat(pathData.to);
24738
-
24739
- // Add userData for elbows too
24740
- elbow.userData = {
24741
- isPipeElbow: true,
24742
- elbowId: elbowId,
24743
- elbowIndex: j,
24744
- pathFrom: pathData.from,
24745
- pathTo: pathData.to,
24746
- angle: (angle * 180 / Math.PI).toFixed(1),
24747
- // Add component data for tooltips
24748
- component: {
24749
- type: 'PipeElbow',
24750
- attributes: {
24751
- angle: {
24752
- key: 'Bend Angle',
24753
- value: (angle * 180 / Math.PI).toFixed(1),
24754
- unit: '°'
24755
- }
24756
- }
24757
- }
24758
- };
24759
- scene.add(elbow);
24760
- }
24761
-
24762
- /**
24763
- * Create smooth elbow geometry to connect two pipe segments (optimized for 90-degree angles)
24764
- * @param {THREE.Vector3} point1 - First point (before the joint)
24765
- * @param {THREE.Vector3} point2 - Joint point (center of the elbow)
24766
- * @param {THREE.Vector3} point3 - Third point (after the joint)
24767
- * @param {number} radius - Pipe radius
24768
- * @returns {THREE.BufferGeometry} Elbow geometry
24769
- */
24770
- }, {
24771
- key: "createElbowGeometry",
24772
- value: function createElbowGeometry(point1, point2, point3, radius) {
24773
- try {
24774
- // Fixed elbow radius for 90-degree bends (simplified)
24775
- var elbowRadius = radius * 3;
24776
-
24777
- // Create a curve for the 90-degree elbow
24778
- var curve = this.createElbowCurve(point1, point2, point3, elbowRadius);
24779
-
24780
- // Fixed tubular segments for 90-degree bends (quarter circle)
24781
- var tubularSegments = 12; // Good balance for smooth 90° curves
24782
- var radialSegments = 16;
24783
- var closed = false;
24784
- var elbowGeometry = new THREE__namespace.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
24785
- return elbowGeometry;
24786
- } catch (error) {
24787
- console.warn('Failed to create elbow geometry:', error);
24788
- return null;
24789
- }
24790
- }
24791
-
24792
- /**
24793
- * Create a smooth curve for the 90-degree elbow joint (simplified)
24794
- * @param {THREE.Vector3} point1 - First point
24795
- * @param {THREE.Vector3} point2 - Joint point
24796
- * @param {THREE.Vector3} point3 - Third point
24797
- * @param {number} elbowRadius - Radius of the elbow curve
24798
- * @returns {THREE.Curve} Curve for the elbow
24799
- */
24800
- }, {
24801
- key: "createElbowCurve",
24802
- value: function createElbowCurve(point1, point2, point3, elbowRadius) {
24803
- // Calculate direction vectors
24804
- var dir1 = point2.clone().sub(point1).normalize();
24805
- var dir2 = point3.clone().sub(point2).normalize();
24806
-
24807
- // For 90-degree bends, we can use a simpler approach with a quarter circle
24808
- // The curve radius is proportional to the elbow radius
24809
- var curveRadius = elbowRadius * 0.001;
24748
+ key: "createSegmentCaps",
24749
+ value: function createSegmentCaps(segment, capGeometry, material, segmentIndex) {
24750
+ var length = segment.geometry.parameters.height;
24810
24751
 
24811
- // Calculate the offset distance from the joint point
24812
- var offset = curveRadius;
24813
-
24814
- // Calculate start and end points of the curve (offset from the joint)
24815
- var curveStart = point2.clone().sub(dir1.clone().multiplyScalar(offset));
24816
- var curveEnd = point2.clone().add(dir2.clone().multiplyScalar(offset));
24817
-
24818
- // For a 90-degree bend, the control point is at the corner
24819
- // We can use a quadratic bezier curve for simplicity
24820
- var controlPoint = point2.clone();
24821
-
24822
- // Create a quadratic bezier curve (simpler than cubic for 90° bends)
24823
- var curve = new THREE__namespace.QuadraticBezierCurve3(curveStart, controlPoint, curveEnd);
24824
- return curve;
24752
+ // Create start cap
24753
+ var startCap = new THREE__namespace.Mesh(capGeometry, material);
24754
+ // Position at segment start in local space (matches manual segment connector positioning)
24755
+ startCap.position.set(0, -length / 2, 0);
24756
+ startCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-START");
24757
+ startCap.userData = {
24758
+ objectType: 'segment-cap',
24759
+ capType: 'start',
24760
+ segmentId: segment.uuid,
24761
+ segmentIndex: segmentIndex
24762
+ };
24763
+ startCap.castShadow = true;
24764
+ startCap.receiveShadow = true;
24765
+ segment.add(startCap);
24766
+ console.log("\uD83D\uDD35 Created START cap for segment ".concat(segmentIndex, " at local position (0, ").concat(-length / 2, ", 0)"));
24767
+
24768
+ // Create end cap
24769
+ var endCap = new THREE__namespace.Mesh(capGeometry, material);
24770
+ // Position at segment end in local space (matches manual segment connector positioning)
24771
+ endCap.position.set(0, length / 2, 0);
24772
+ endCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-END");
24773
+ endCap.userData = {
24774
+ objectType: 'segment-cap',
24775
+ capType: 'end',
24776
+ segmentId: segment.uuid,
24777
+ segmentIndex: segmentIndex
24778
+ };
24779
+ endCap.castShadow = true;
24780
+ endCap.receiveShadow = true;
24781
+ segment.add(endCap);
24782
+ console.log("\uD83D\uDD35 Created END cap for segment ".concat(segmentIndex, " at local position (0, ").concat(length / 2, ", 0)"));
24825
24783
  }
24826
24784
 
24827
24785
  /**
@@ -32488,7 +32446,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
32488
32446
  * Initialize the CentralPlant manager
32489
32447
  *
32490
32448
  * @constructor
32491
- * @version 0.1.44
32449
+ * @version 0.1.45
32492
32450
  * @updated 2025-10-22
32493
32451
  *
32494
32452
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -19,7 +19,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
19
19
  * Initialize the CentralPlant manager
20
20
  *
21
21
  * @constructor
22
- * @version 0.1.44
22
+ * @version 0.1.45
23
23
  * @updated 2025-10-22
24
24
  *
25
25
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -126,7 +126,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
126
126
  }, {
127
127
  key: "translateSegment",
128
128
  value: function translateSegment(segmentId, axis, value) {
129
- var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man2, _this$sceneViewer$man3, _this$sceneViewer$man4;
129
+ var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man3, _this$sceneViewer$man4, _this$sceneViewer$man5;
130
130
  // Validate parameters
131
131
  if (!segmentId || !axis || value === undefined || value === null) {
132
132
  console.error('❌ translateSegment(): Invalid parameters');
@@ -203,7 +203,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
203
203
  });
204
204
  console.log("\uD83D\uDD0D After filtering, ".concat(componentConnectorsAtEndpoints.length, " component connectors at endpoints"));
205
205
  if (componentConnectorsAtEndpoints.length > 0) {
206
- var _this$sceneViewer2;
206
+ var _this$sceneViewer2, _this$sceneViewer$man2;
207
207
  console.warn("\u26A0\uFE0F translateSegment(): Segment '".concat(segmentId, "' has component connectors at endpoints."));
208
208
  console.log(" Found ".concat(componentConnectorsAtEndpoints.length, " component connector(s):"), componentConnectorsAtEndpoints.map(function (c) {
209
209
  var _c$parent3;
@@ -219,9 +219,45 @@ var TransformOperationsManager = /*#__PURE__*/function () {
219
219
  console.warn('⚠️ Segment splitting is disabled. Cannot translate segments with component connectors at endpoints.');
220
220
  return false;
221
221
  }
222
- console.warn('⚠️ Segment splitting is enabled, but splitting during translate is not supported due to undo complexity.');
223
- console.warn('⚠️ Please manually split the segment first if you want to translate one half.');
224
- return false;
222
+
223
+ // Split the segment and translate the free half
224
+ console.log('✂️ Splitting segment to enable translation of the free half...');
225
+
226
+ // Get sceneOperationsManager
227
+ var _sceneOperationsManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.sceneOperationsManager;
228
+ if (!_sceneOperationsManager) {
229
+ console.error('❌ translateSegment(): SceneOperationsManager not available for splitting');
230
+ return false;
231
+ }
232
+ try {
233
+ var splitResult = _sceneOperationsManager.splitSegment(segment);
234
+ if (splitResult && splitResult.segment1 && splitResult.segment2) {
235
+ console.log("\u2705 Segment split: ".concat(splitResult.segment1.uuid, " (").concat(splitResult.segment1.geometry.parameters.height, "), ").concat(splitResult.segment2.uuid, " (").concat(splitResult.segment2.geometry.parameters.height, ")"));
236
+
237
+ // Determine which segment is the longer one (free side) to translate
238
+ var segment1Length = splitResult.segment1.geometry.parameters.height;
239
+ var segment2Length = splitResult.segment2.geometry.parameters.height;
240
+
241
+ // The longer segment is the one WITHOUT the component connector
242
+ var segmentToTranslate = segment1Length > segment2Length ? splitResult.segment1 : splitResult.segment2;
243
+ console.log("\uD83C\uDFAF Translating ".concat(segmentToTranslate.uuid, " (length: ").concat(segmentToTranslate.geometry.parameters.height, ") - the free segment"));
244
+
245
+ // Now translate the chosen segment (recursive call with the new segment)
246
+ var translateSuccess = this.translateSegment(segmentToTranslate.uuid, axis, value);
247
+ if (!translateSuccess) {
248
+ console.error('❌ translateSegment(): Failed to translate split segment half');
249
+ return false;
250
+ }
251
+ console.log('✅ Split and translate operation completed successfully');
252
+ return true;
253
+ } else {
254
+ console.error('❌ translateSegment(): Split operation failed');
255
+ return false;
256
+ }
257
+ } catch (error) {
258
+ console.error('❌ translateSegment(): Error splitting segment:', error);
259
+ return false;
260
+ }
225
261
  }
226
262
  console.log("\uD83D\uDD04 translateSegment(): Translating segment ".concat(segmentId, " on ").concat(axis, " axis by ").concat(value));
227
263
 
@@ -286,8 +322,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
286
322
  }
287
323
 
288
324
  // Validate PathfindingManager and SceneOperationsManager availability
289
- var pathfindingManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.pathfindingManager;
290
- var sceneOperationsManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.sceneOperationsManager;
325
+ var pathfindingManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.pathfindingManager;
326
+ var sceneOperationsManager = (_this$sceneViewer$man4 = this.sceneViewer.managers) === null || _this$sceneViewer$man4 === void 0 ? void 0 : _this$sceneViewer$man4.sceneOperationsManager;
291
327
  if (!pathfindingManager || !sceneOperationsManager) {
292
328
  console.error('❌ translateSegment(): PathfindingManager or SceneOperationsManager not available');
293
329
  return false;
@@ -322,7 +358,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
322
358
 
323
359
  // Store transform parameters using the OperationHistoryManager BEFORE updatePaths
324
360
  // This is critical so that intersection detection can undo the operation
325
- if ((_this$sceneViewer$man4 = this.sceneViewer.managers) !== null && _this$sceneViewer$man4 !== void 0 && _this$sceneViewer$man4.operationHistory) {
361
+ if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistory) {
326
362
  this.sceneViewer.managers.operationHistory.addToOperationHistory('translateSegment', {
327
363
  segmentId: segmentId,
328
364
  axis: axis,
@@ -370,11 +406,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
370
406
  }, {
371
407
  key: "translateGateway",
372
408
  value: function translateGateway(gatewayId, axis, value) {
373
- var _this$sceneViewer$man5, _this$sceneViewer$man6;
409
+ var _this$sceneViewer$man6, _this$sceneViewer$man7;
374
410
  console.log("[Pathfinder] translateGateway started");
375
411
 
376
412
  // Store transform parameters using the OperationHistoryManager
377
- if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistoryManager) {
413
+ if ((_this$sceneViewer$man6 = this.sceneViewer.managers) !== null && _this$sceneViewer$man6 !== void 0 && _this$sceneViewer$man6.operationHistoryManager) {
378
414
  this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateGateway', {
379
415
  gatewayId: gatewayId,
380
416
  axis: axis,
@@ -425,7 +461,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
425
461
 
426
462
  // Handle manual gateway transformation (convert to declared and process connections)
427
463
  console.log('🔧 Handling manual gateway transformation via SceneOperationsManager');
428
- var sceneOperationsManager = (_this$sceneViewer$man6 = this.sceneViewer.managers) === null || _this$sceneViewer$man6 === void 0 ? void 0 : _this$sceneViewer$man6.sceneOperationsManager;
464
+ var sceneOperationsManager = (_this$sceneViewer$man7 = this.sceneViewer.managers) === null || _this$sceneViewer$man7 === void 0 ? void 0 : _this$sceneViewer$man7.sceneOperationsManager;
429
465
  if (!sceneOperationsManager) {
430
466
  console.error('❌ translateGateway(): SceneOperationsManager not available');
431
467
  return false;
@@ -184,6 +184,10 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
184
184
 
185
185
  var pipeRadius = 0.1;
186
186
  var pipeMaterial = this.createPipeMaterial(crosscubeTextureSet);
187
+
188
+ // Shared geometry for all connector caps to improve performance
189
+ var capGeometry = new THREE__namespace.SphereGeometry(pipeRadius, 16, 16);
190
+ this.registerDisposable(capGeometry);
187
191
  paths.forEach(function (pathData, index) {
188
192
  if (pathData.path && pathData.path.length >= 2) {
189
193
  // Convert path points to Vector3 objects for consistent handling
@@ -237,15 +241,14 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
237
241
  pathTo: pathData.to
238
242
  };
239
243
 
244
+ // Create caps at both ends of every segment
245
+ _this3.createSegmentCaps(cylinder, capGeometry, pipeMaterial, globalSegmentIndex);
246
+
240
247
  // Increment global segment counter
241
248
  globalSegmentIndex++;
242
249
 
243
250
  // Add segment directly to scene instead of to polyline
244
251
  sceneViewer.scene.add(cylinder);
245
-
246
- // Add smooth elbow joints only at actual direction changes (not at every point)
247
- _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, sceneViewer.scene // Pass scene instead of polyline
248
- );
249
252
  }
250
253
  console.log("\u2705 Created pipe path ".concat(pathData.from, "-").concat(pathData.to, " with ").concat(pathPoints.length - 1, " segments"));
251
254
  }
@@ -253,130 +256,49 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
253
256
  }
254
257
 
255
258
  /**
256
- * Create and add elbow joint if there's a direction change at the current segment
257
- * @param {Array<THREE.Vector3>} pathPoints - Array of path points
258
- * @param {number} j - Current segment index
259
- * @param {number} pipeRadius - Radius of the pipe
260
- * @param {THREE.Material} pipeMaterial - Material for the elbow
261
- * @param {Object} pathData - Path data object with from/to information
262
- * @param {number} index - Path index for naming
263
- * @param {THREE.Scene} scene - Scene object to add elbow to directly
259
+ * Create connector caps at both segment endpoints as children of the segment
260
+ * Caps are positioned in local coordinates to match manual segment connector positioning
261
+ * @param {THREE.Mesh} segment - The parent segment mesh
262
+ * @param {THREE.BufferGeometry} capGeometry - Shared geometry for caps
263
+ * @param {THREE.Material} material - Material for the caps
264
+ * @param {number} segmentIndex - Global segment index
264
265
  */
265
266
  }, {
266
- key: "createAndAddElbowIfNeeded",
267
- value: function createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, scene) {
268
- // Only check if there are at least 2 more points ahead
269
- if (j >= pathPoints.length - 2) {
270
- return;
271
- }
272
- var currentSegment = pathPoints[j + 1].clone().sub(pathPoints[j]).normalize();
273
- var nextSegment = pathPoints[j + 2].clone().sub(pathPoints[j + 1]).normalize();
274
-
275
- // Check if there's actually a direction change (not just continuing straight)
276
- var angle = currentSegment.angleTo(nextSegment);
277
- var minAngle = Math.PI / 36; // 5 degrees minimum for creating an elbow
278
-
279
- if (angle <= minAngle) {
280
- return; // No significant direction change
281
- }
282
- var elbowGeometry = this.createElbowGeometry(pathPoints[j], pathPoints[j + 1], pathPoints[j + 2], pipeRadius);
283
- if (!elbowGeometry) {
284
- return; // Failed to create geometry
285
- }
286
- var elbow = new THREE__namespace.Mesh(elbowGeometry, pipeMaterial);
287
- elbow.castShadow = true;
288
- elbow.receiveShadow = true;
289
-
290
- // Make elbows selectable as well
291
- var elbowId = "pipe-elbow-".concat(pathData.from, "-").concat(pathData.to, "-").concat(j);
292
- elbow.uuid = "Pipe Elbow ".concat(j + 1, ": ").concat(pathData.from, "-").concat(pathData.to);
293
-
294
- // Add userData for elbows too
295
- elbow.userData = {
296
- isPipeElbow: true,
297
- elbowId: elbowId,
298
- elbowIndex: j,
299
- pathFrom: pathData.from,
300
- pathTo: pathData.to,
301
- angle: (angle * 180 / Math.PI).toFixed(1),
302
- // Add component data for tooltips
303
- component: {
304
- type: 'PipeElbow',
305
- attributes: {
306
- angle: {
307
- key: 'Bend Angle',
308
- value: (angle * 180 / Math.PI).toFixed(1),
309
- unit: '°'
310
- }
311
- }
312
- }
267
+ key: "createSegmentCaps",
268
+ value: function createSegmentCaps(segment, capGeometry, material, segmentIndex) {
269
+ var length = segment.geometry.parameters.height;
270
+
271
+ // Create start cap
272
+ var startCap = new THREE__namespace.Mesh(capGeometry, material);
273
+ // Position at segment start in local space (matches manual segment connector positioning)
274
+ startCap.position.set(0, -length / 2, 0);
275
+ startCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-START");
276
+ startCap.userData = {
277
+ objectType: 'segment-cap',
278
+ capType: 'start',
279
+ segmentId: segment.uuid,
280
+ segmentIndex: segmentIndex
313
281
  };
314
- scene.add(elbow);
315
- }
316
-
317
- /**
318
- * Create smooth elbow geometry to connect two pipe segments (optimized for 90-degree angles)
319
- * @param {THREE.Vector3} point1 - First point (before the joint)
320
- * @param {THREE.Vector3} point2 - Joint point (center of the elbow)
321
- * @param {THREE.Vector3} point3 - Third point (after the joint)
322
- * @param {number} radius - Pipe radius
323
- * @returns {THREE.BufferGeometry} Elbow geometry
324
- */
325
- }, {
326
- key: "createElbowGeometry",
327
- value: function createElbowGeometry(point1, point2, point3, radius) {
328
- try {
329
- // Fixed elbow radius for 90-degree bends (simplified)
330
- var elbowRadius = radius * 3;
331
-
332
- // Create a curve for the 90-degree elbow
333
- var curve = this.createElbowCurve(point1, point2, point3, elbowRadius);
334
-
335
- // Fixed tubular segments for 90-degree bends (quarter circle)
336
- var tubularSegments = 12; // Good balance for smooth 90° curves
337
- var radialSegments = 16;
338
- var closed = false;
339
- var elbowGeometry = new THREE__namespace.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
340
- return elbowGeometry;
341
- } catch (error) {
342
- console.warn('Failed to create elbow geometry:', error);
343
- return null;
344
- }
345
- }
346
-
347
- /**
348
- * Create a smooth curve for the 90-degree elbow joint (simplified)
349
- * @param {THREE.Vector3} point1 - First point
350
- * @param {THREE.Vector3} point2 - Joint point
351
- * @param {THREE.Vector3} point3 - Third point
352
- * @param {number} elbowRadius - Radius of the elbow curve
353
- * @returns {THREE.Curve} Curve for the elbow
354
- */
355
- }, {
356
- key: "createElbowCurve",
357
- value: function createElbowCurve(point1, point2, point3, elbowRadius) {
358
- // Calculate direction vectors
359
- var dir1 = point2.clone().sub(point1).normalize();
360
- var dir2 = point3.clone().sub(point2).normalize();
361
-
362
- // For 90-degree bends, we can use a simpler approach with a quarter circle
363
- // The curve radius is proportional to the elbow radius
364
- var curveRadius = elbowRadius * 0.001;
365
-
366
- // Calculate the offset distance from the joint point
367
- var offset = curveRadius;
368
-
369
- // Calculate start and end points of the curve (offset from the joint)
370
- var curveStart = point2.clone().sub(dir1.clone().multiplyScalar(offset));
371
- var curveEnd = point2.clone().add(dir2.clone().multiplyScalar(offset));
372
-
373
- // For a 90-degree bend, the control point is at the corner
374
- // We can use a quadratic bezier curve for simplicity
375
- var controlPoint = point2.clone();
376
-
377
- // Create a quadratic bezier curve (simpler than cubic for 90° bends)
378
- var curve = new THREE__namespace.QuadraticBezierCurve3(curveStart, controlPoint, curveEnd);
379
- return curve;
282
+ startCap.castShadow = true;
283
+ startCap.receiveShadow = true;
284
+ segment.add(startCap);
285
+ console.log("\uD83D\uDD35 Created START cap for segment ".concat(segmentIndex, " at local position (0, ").concat(-length / 2, ", 0)"));
286
+
287
+ // Create end cap
288
+ var endCap = new THREE__namespace.Mesh(capGeometry, material);
289
+ // Position at segment end in local space (matches manual segment connector positioning)
290
+ endCap.position.set(0, length / 2, 0);
291
+ endCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-END");
292
+ endCap.userData = {
293
+ objectType: 'segment-cap',
294
+ capType: 'end',
295
+ segmentId: segment.uuid,
296
+ segmentIndex: segmentIndex
297
+ };
298
+ endCap.castShadow = true;
299
+ endCap.receiveShadow = true;
300
+ segment.add(endCap);
301
+ console.log("\uD83D\uDD35 Created END cap for segment ".concat(segmentIndex, " at local position (0, ").concat(length / 2, ", 0)"));
380
302
  }
381
303
 
382
304
  /**
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.44
18
+ * @version 0.1.45
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -102,7 +102,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
102
102
  }, {
103
103
  key: "translateSegment",
104
104
  value: function translateSegment(segmentId, axis, value) {
105
- var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man2, _this$sceneViewer$man3, _this$sceneViewer$man4;
105
+ var _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man3, _this$sceneViewer$man4, _this$sceneViewer$man5;
106
106
  // Validate parameters
107
107
  if (!segmentId || !axis || value === undefined || value === null) {
108
108
  console.error('❌ translateSegment(): Invalid parameters');
@@ -179,7 +179,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
179
179
  });
180
180
  console.log("\uD83D\uDD0D After filtering, ".concat(componentConnectorsAtEndpoints.length, " component connectors at endpoints"));
181
181
  if (componentConnectorsAtEndpoints.length > 0) {
182
- var _this$sceneViewer2;
182
+ var _this$sceneViewer2, _this$sceneViewer$man2;
183
183
  console.warn("\u26A0\uFE0F translateSegment(): Segment '".concat(segmentId, "' has component connectors at endpoints."));
184
184
  console.log(" Found ".concat(componentConnectorsAtEndpoints.length, " component connector(s):"), componentConnectorsAtEndpoints.map(function (c) {
185
185
  var _c$parent3;
@@ -195,9 +195,45 @@ var TransformOperationsManager = /*#__PURE__*/function () {
195
195
  console.warn('⚠️ Segment splitting is disabled. Cannot translate segments with component connectors at endpoints.');
196
196
  return false;
197
197
  }
198
- console.warn('⚠️ Segment splitting is enabled, but splitting during translate is not supported due to undo complexity.');
199
- console.warn('⚠️ Please manually split the segment first if you want to translate one half.');
200
- return false;
198
+
199
+ // Split the segment and translate the free half
200
+ console.log('✂️ Splitting segment to enable translation of the free half...');
201
+
202
+ // Get sceneOperationsManager
203
+ var _sceneOperationsManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.sceneOperationsManager;
204
+ if (!_sceneOperationsManager) {
205
+ console.error('❌ translateSegment(): SceneOperationsManager not available for splitting');
206
+ return false;
207
+ }
208
+ try {
209
+ var splitResult = _sceneOperationsManager.splitSegment(segment);
210
+ if (splitResult && splitResult.segment1 && splitResult.segment2) {
211
+ console.log("\u2705 Segment split: ".concat(splitResult.segment1.uuid, " (").concat(splitResult.segment1.geometry.parameters.height, "), ").concat(splitResult.segment2.uuid, " (").concat(splitResult.segment2.geometry.parameters.height, ")"));
212
+
213
+ // Determine which segment is the longer one (free side) to translate
214
+ var segment1Length = splitResult.segment1.geometry.parameters.height;
215
+ var segment2Length = splitResult.segment2.geometry.parameters.height;
216
+
217
+ // The longer segment is the one WITHOUT the component connector
218
+ var segmentToTranslate = segment1Length > segment2Length ? splitResult.segment1 : splitResult.segment2;
219
+ console.log("\uD83C\uDFAF Translating ".concat(segmentToTranslate.uuid, " (length: ").concat(segmentToTranslate.geometry.parameters.height, ") - the free segment"));
220
+
221
+ // Now translate the chosen segment (recursive call with the new segment)
222
+ var translateSuccess = this.translateSegment(segmentToTranslate.uuid, axis, value);
223
+ if (!translateSuccess) {
224
+ console.error('❌ translateSegment(): Failed to translate split segment half');
225
+ return false;
226
+ }
227
+ console.log('✅ Split and translate operation completed successfully');
228
+ return true;
229
+ } else {
230
+ console.error('❌ translateSegment(): Split operation failed');
231
+ return false;
232
+ }
233
+ } catch (error) {
234
+ console.error('❌ translateSegment(): Error splitting segment:', error);
235
+ return false;
236
+ }
201
237
  }
202
238
  console.log("\uD83D\uDD04 translateSegment(): Translating segment ".concat(segmentId, " on ").concat(axis, " axis by ").concat(value));
203
239
 
@@ -262,8 +298,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
262
298
  }
263
299
 
264
300
  // Validate PathfindingManager and SceneOperationsManager availability
265
- var pathfindingManager = (_this$sceneViewer$man2 = this.sceneViewer.managers) === null || _this$sceneViewer$man2 === void 0 ? void 0 : _this$sceneViewer$man2.pathfindingManager;
266
- var sceneOperationsManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.sceneOperationsManager;
301
+ var pathfindingManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.pathfindingManager;
302
+ var sceneOperationsManager = (_this$sceneViewer$man4 = this.sceneViewer.managers) === null || _this$sceneViewer$man4 === void 0 ? void 0 : _this$sceneViewer$man4.sceneOperationsManager;
267
303
  if (!pathfindingManager || !sceneOperationsManager) {
268
304
  console.error('❌ translateSegment(): PathfindingManager or SceneOperationsManager not available');
269
305
  return false;
@@ -298,7 +334,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
298
334
 
299
335
  // Store transform parameters using the OperationHistoryManager BEFORE updatePaths
300
336
  // This is critical so that intersection detection can undo the operation
301
- if ((_this$sceneViewer$man4 = this.sceneViewer.managers) !== null && _this$sceneViewer$man4 !== void 0 && _this$sceneViewer$man4.operationHistory) {
337
+ if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistory) {
302
338
  this.sceneViewer.managers.operationHistory.addToOperationHistory('translateSegment', {
303
339
  segmentId: segmentId,
304
340
  axis: axis,
@@ -346,11 +382,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
346
382
  }, {
347
383
  key: "translateGateway",
348
384
  value: function translateGateway(gatewayId, axis, value) {
349
- var _this$sceneViewer$man5, _this$sceneViewer$man6;
385
+ var _this$sceneViewer$man6, _this$sceneViewer$man7;
350
386
  console.log("[Pathfinder] translateGateway started");
351
387
 
352
388
  // Store transform parameters using the OperationHistoryManager
353
- if ((_this$sceneViewer$man5 = this.sceneViewer.managers) !== null && _this$sceneViewer$man5 !== void 0 && _this$sceneViewer$man5.operationHistoryManager) {
389
+ if ((_this$sceneViewer$man6 = this.sceneViewer.managers) !== null && _this$sceneViewer$man6 !== void 0 && _this$sceneViewer$man6.operationHistoryManager) {
354
390
  this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateGateway', {
355
391
  gatewayId: gatewayId,
356
392
  axis: axis,
@@ -401,7 +437,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
401
437
 
402
438
  // Handle manual gateway transformation (convert to declared and process connections)
403
439
  console.log('🔧 Handling manual gateway transformation via SceneOperationsManager');
404
- var sceneOperationsManager = (_this$sceneViewer$man6 = this.sceneViewer.managers) === null || _this$sceneViewer$man6 === void 0 ? void 0 : _this$sceneViewer$man6.sceneOperationsManager;
440
+ var sceneOperationsManager = (_this$sceneViewer$man7 = this.sceneViewer.managers) === null || _this$sceneViewer$man7 === void 0 ? void 0 : _this$sceneViewer$man7.sceneOperationsManager;
405
441
  if (!sceneOperationsManager) {
406
442
  console.error('❌ translateGateway(): SceneOperationsManager not available');
407
443
  return false;
@@ -160,6 +160,10 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
160
160
 
161
161
  var pipeRadius = 0.1;
162
162
  var pipeMaterial = this.createPipeMaterial(crosscubeTextureSet);
163
+
164
+ // Shared geometry for all connector caps to improve performance
165
+ var capGeometry = new THREE.SphereGeometry(pipeRadius, 16, 16);
166
+ this.registerDisposable(capGeometry);
163
167
  paths.forEach(function (pathData, index) {
164
168
  if (pathData.path && pathData.path.length >= 2) {
165
169
  // Convert path points to Vector3 objects for consistent handling
@@ -213,15 +217,14 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
213
217
  pathTo: pathData.to
214
218
  };
215
219
 
220
+ // Create caps at both ends of every segment
221
+ _this3.createSegmentCaps(cylinder, capGeometry, pipeMaterial, globalSegmentIndex);
222
+
216
223
  // Increment global segment counter
217
224
  globalSegmentIndex++;
218
225
 
219
226
  // Add segment directly to scene instead of to polyline
220
227
  sceneViewer.scene.add(cylinder);
221
-
222
- // Add smooth elbow joints only at actual direction changes (not at every point)
223
- _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, sceneViewer.scene // Pass scene instead of polyline
224
- );
225
228
  }
226
229
  console.log("\u2705 Created pipe path ".concat(pathData.from, "-").concat(pathData.to, " with ").concat(pathPoints.length - 1, " segments"));
227
230
  }
@@ -229,130 +232,49 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
229
232
  }
230
233
 
231
234
  /**
232
- * Create and add elbow joint if there's a direction change at the current segment
233
- * @param {Array<THREE.Vector3>} pathPoints - Array of path points
234
- * @param {number} j - Current segment index
235
- * @param {number} pipeRadius - Radius of the pipe
236
- * @param {THREE.Material} pipeMaterial - Material for the elbow
237
- * @param {Object} pathData - Path data object with from/to information
238
- * @param {number} index - Path index for naming
239
- * @param {THREE.Scene} scene - Scene object to add elbow to directly
235
+ * Create connector caps at both segment endpoints as children of the segment
236
+ * Caps are positioned in local coordinates to match manual segment connector positioning
237
+ * @param {THREE.Mesh} segment - The parent segment mesh
238
+ * @param {THREE.BufferGeometry} capGeometry - Shared geometry for caps
239
+ * @param {THREE.Material} material - Material for the caps
240
+ * @param {number} segmentIndex - Global segment index
240
241
  */
241
242
  }, {
242
- key: "createAndAddElbowIfNeeded",
243
- value: function createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, scene) {
244
- // Only check if there are at least 2 more points ahead
245
- if (j >= pathPoints.length - 2) {
246
- return;
247
- }
248
- var currentSegment = pathPoints[j + 1].clone().sub(pathPoints[j]).normalize();
249
- var nextSegment = pathPoints[j + 2].clone().sub(pathPoints[j + 1]).normalize();
250
-
251
- // Check if there's actually a direction change (not just continuing straight)
252
- var angle = currentSegment.angleTo(nextSegment);
253
- var minAngle = Math.PI / 36; // 5 degrees minimum for creating an elbow
254
-
255
- if (angle <= minAngle) {
256
- return; // No significant direction change
257
- }
258
- var elbowGeometry = this.createElbowGeometry(pathPoints[j], pathPoints[j + 1], pathPoints[j + 2], pipeRadius);
259
- if (!elbowGeometry) {
260
- return; // Failed to create geometry
261
- }
262
- var elbow = new THREE.Mesh(elbowGeometry, pipeMaterial);
263
- elbow.castShadow = true;
264
- elbow.receiveShadow = true;
265
-
266
- // Make elbows selectable as well
267
- var elbowId = "pipe-elbow-".concat(pathData.from, "-").concat(pathData.to, "-").concat(j);
268
- elbow.uuid = "Pipe Elbow ".concat(j + 1, ": ").concat(pathData.from, "-").concat(pathData.to);
269
-
270
- // Add userData for elbows too
271
- elbow.userData = {
272
- isPipeElbow: true,
273
- elbowId: elbowId,
274
- elbowIndex: j,
275
- pathFrom: pathData.from,
276
- pathTo: pathData.to,
277
- angle: (angle * 180 / Math.PI).toFixed(1),
278
- // Add component data for tooltips
279
- component: {
280
- type: 'PipeElbow',
281
- attributes: {
282
- angle: {
283
- key: 'Bend Angle',
284
- value: (angle * 180 / Math.PI).toFixed(1),
285
- unit: '°'
286
- }
287
- }
288
- }
243
+ key: "createSegmentCaps",
244
+ value: function createSegmentCaps(segment, capGeometry, material, segmentIndex) {
245
+ var length = segment.geometry.parameters.height;
246
+
247
+ // Create start cap
248
+ var startCap = new THREE.Mesh(capGeometry, material);
249
+ // Position at segment start in local space (matches manual segment connector positioning)
250
+ startCap.position.set(0, -length / 2, 0);
251
+ startCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-START");
252
+ startCap.userData = {
253
+ objectType: 'segment-cap',
254
+ capType: 'start',
255
+ segmentId: segment.uuid,
256
+ segmentIndex: segmentIndex
289
257
  };
290
- scene.add(elbow);
291
- }
292
-
293
- /**
294
- * Create smooth elbow geometry to connect two pipe segments (optimized for 90-degree angles)
295
- * @param {THREE.Vector3} point1 - First point (before the joint)
296
- * @param {THREE.Vector3} point2 - Joint point (center of the elbow)
297
- * @param {THREE.Vector3} point3 - Third point (after the joint)
298
- * @param {number} radius - Pipe radius
299
- * @returns {THREE.BufferGeometry} Elbow geometry
300
- */
301
- }, {
302
- key: "createElbowGeometry",
303
- value: function createElbowGeometry(point1, point2, point3, radius) {
304
- try {
305
- // Fixed elbow radius for 90-degree bends (simplified)
306
- var elbowRadius = radius * 3;
307
-
308
- // Create a curve for the 90-degree elbow
309
- var curve = this.createElbowCurve(point1, point2, point3, elbowRadius);
310
-
311
- // Fixed tubular segments for 90-degree bends (quarter circle)
312
- var tubularSegments = 12; // Good balance for smooth 90° curves
313
- var radialSegments = 16;
314
- var closed = false;
315
- var elbowGeometry = new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
316
- return elbowGeometry;
317
- } catch (error) {
318
- console.warn('Failed to create elbow geometry:', error);
319
- return null;
320
- }
321
- }
322
-
323
- /**
324
- * Create a smooth curve for the 90-degree elbow joint (simplified)
325
- * @param {THREE.Vector3} point1 - First point
326
- * @param {THREE.Vector3} point2 - Joint point
327
- * @param {THREE.Vector3} point3 - Third point
328
- * @param {number} elbowRadius - Radius of the elbow curve
329
- * @returns {THREE.Curve} Curve for the elbow
330
- */
331
- }, {
332
- key: "createElbowCurve",
333
- value: function createElbowCurve(point1, point2, point3, elbowRadius) {
334
- // Calculate direction vectors
335
- var dir1 = point2.clone().sub(point1).normalize();
336
- var dir2 = point3.clone().sub(point2).normalize();
337
-
338
- // For 90-degree bends, we can use a simpler approach with a quarter circle
339
- // The curve radius is proportional to the elbow radius
340
- var curveRadius = elbowRadius * 0.001;
341
-
342
- // Calculate the offset distance from the joint point
343
- var offset = curveRadius;
344
-
345
- // Calculate start and end points of the curve (offset from the joint)
346
- var curveStart = point2.clone().sub(dir1.clone().multiplyScalar(offset));
347
- var curveEnd = point2.clone().add(dir2.clone().multiplyScalar(offset));
348
-
349
- // For a 90-degree bend, the control point is at the corner
350
- // We can use a quadratic bezier curve for simplicity
351
- var controlPoint = point2.clone();
352
-
353
- // Create a quadratic bezier curve (simpler than cubic for 90° bends)
354
- var curve = new THREE.QuadraticBezierCurve3(curveStart, controlPoint, curveEnd);
355
- return curve;
258
+ startCap.castShadow = true;
259
+ startCap.receiveShadow = true;
260
+ segment.add(startCap);
261
+ console.log("\uD83D\uDD35 Created START cap for segment ".concat(segmentIndex, " at local position (0, ").concat(-length / 2, ", 0)"));
262
+
263
+ // Create end cap
264
+ var endCap = new THREE.Mesh(capGeometry, material);
265
+ // Position at segment end in local space (matches manual segment connector positioning)
266
+ endCap.position.set(0, length / 2, 0);
267
+ endCap.uuid = "SEGMENT-".concat(segmentIndex, "-CAP-END");
268
+ endCap.userData = {
269
+ objectType: 'segment-cap',
270
+ capType: 'end',
271
+ segmentId: segment.uuid,
272
+ segmentIndex: segmentIndex
273
+ };
274
+ endCap.castShadow = true;
275
+ endCap.receiveShadow = true;
276
+ segment.add(endCap);
277
+ console.log("\uD83D\uDD35 Created END cap for segment ".concat(segmentIndex, " at local position (0, ").concat(length / 2, ", 0)"));
356
278
  }
357
279
 
358
280
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/index.js",