@2112-lab/central-plant 0.1.44 → 0.1.46

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;
@@ -19543,6 +19579,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19543
19579
  return null;
19544
19580
  }
19545
19581
  var collisionRadius = 0.5; // Radius around connector that triggers collision
19582
+ var endpointTolerance = 0.55; // Tolerance for determining if connector is at segment endpoint
19546
19583
 
19547
19584
  // Get segment endpoints in world coordinates
19548
19585
  var endpoints = this.calculateSegmentEndpoints(segment);
@@ -19561,6 +19598,13 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19561
19598
  var connectorWorldPos = new THREE__namespace.Vector3();
19562
19599
  child.getWorldPosition(connectorWorldPos);
19563
19600
 
19601
+ // Skip connectors that are at the segment's endpoints (legitimate connection points)
19602
+ var distanceToStart = connectorWorldPos.distanceTo(startPoint);
19603
+ var distanceToEnd = connectorWorldPos.distanceTo(endPoint);
19604
+ if (distanceToStart <= endpointTolerance || distanceToEnd <= endpointTolerance) {
19605
+ return; // Skip this connector - it's at an endpoint
19606
+ }
19607
+
19564
19608
  // Find the closest point on the segment to the connector
19565
19609
  var segmentVector = new THREE__namespace.Vector3().subVectors(endPoint, startPoint);
19566
19610
  var pointVector = new THREE__namespace.Vector3().subVectors(connectorWorldPos, startPoint);
@@ -19759,6 +19803,37 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19759
19803
  return c1.distanceTo(c2);
19760
19804
  }
19761
19805
 
19806
+ /**
19807
+ * Constrain a position to maintain orthogonal alignment with a reference position
19808
+ * Movement is restricted to only along the segment's direction vector
19809
+ * @param {THREE.Vector3} newPosition - The proposed new position
19810
+ * @param {THREE.Vector3} referencePosition - The stationary reference position
19811
+ * @param {THREE.Object3D} segment - The segment being adjusted
19812
+ * @returns {THREE.Vector3} Constrained position that maintains orthogonality
19813
+ * @private
19814
+ */
19815
+ }, {
19816
+ key: "constrainPositionToOrthogonal",
19817
+ value: function constrainPositionToOrthogonal(newPosition, referencePosition, segment) {
19818
+ // Get the segment's direction vector
19819
+ var direction = new THREE__namespace.Vector3(0, 1, 0);
19820
+ direction.applyQuaternion(segment.quaternion);
19821
+ direction.normalize();
19822
+
19823
+ // Calculate vector from reference position to new position
19824
+ var moveVector = new THREE__namespace.Vector3().subVectors(newPosition, referencePosition);
19825
+
19826
+ // Project the move vector onto the segment's direction
19827
+ // This gives us the component of movement along the segment's axis only
19828
+ var projectionLength = moveVector.dot(direction);
19829
+
19830
+ // Constrained position = reference + (projection along segment direction)
19831
+ var constrainedPos = new THREE__namespace.Vector3().copy(referencePosition).add(direction.clone().multiplyScalar(projectionLength));
19832
+ console.log("\uD83D\uDD12 Constrained movement along segment direction: [".concat(direction.x.toFixed(3), ", ").concat(direction.y.toFixed(3), ", ").concat(direction.z.toFixed(3), "]"));
19833
+ console.log(" Projection length: ".concat(projectionLength.toFixed(3)));
19834
+ return constrainedPos;
19835
+ }
19836
+
19762
19837
  /**
19763
19838
  * Update adjacent segment connectors to follow the moved segment's new position
19764
19839
  * Finds segments that were connected to the moved segment BEFORE the move,
@@ -19877,18 +19952,23 @@ var TransformOperationsManager = /*#__PURE__*/function () {
19877
19952
  var stationaryWorldPos = new THREE__namespace.Vector3();
19878
19953
  stationaryConnector.getWorldPosition(stationaryWorldPos);
19879
19954
 
19955
+ // Constrain movement to maintain orthogonal alignment
19956
+ // Horizontal segments: only adjust X and Y, keep Z constant
19957
+ // Vertical segments: only adjust Z, keep X and Y constant
19958
+ var constrainedPosition = _this2.constrainPositionToOrthogonal(pair.newPosition, stationaryWorldPos, adjacentSegment);
19959
+
19880
19960
  // Recreate the segment mesh with new length using explicit endpoint positions
19881
19961
  // Pass the intended positions, not the connector objects (which may have temporary positions)
19882
- _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, pair.newPosition, stationaryWorldPos);
19962
+ _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, constrainedPosition, stationaryWorldPos);
19883
19963
 
19884
19964
  // CRITICAL: After recreating the mesh, BOTH connectors need to be repositioned
19885
19965
  // because the segment's center and rotation have changed
19886
19966
 
19887
- // Position moving connector at the new position
19888
- var movingLocalPos = adjacentSegment.worldToLocal(pair.newPosition.clone());
19967
+ // Position moving connector at the constrained position (not the raw new position)
19968
+ var movingLocalPos = adjacentSegment.worldToLocal(constrainedPosition.clone());
19889
19969
  movingConnector.position.copy(movingLocalPos);
19890
19970
  movingConnector.updateMatrixWorld(true);
19891
- _this2.updateConnectorPositionInSceneData(movingConnector, pair.newPosition, adjacentSegment);
19971
+ _this2.updateConnectorPositionInSceneData(movingConnector, constrainedPosition, adjacentSegment);
19892
19972
 
19893
19973
  // Position stationary connector at its original world position
19894
19974
  var stationaryLocalPos = adjacentSegment.worldToLocal(stationaryWorldPos.clone());
@@ -32488,7 +32568,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
32488
32568
  * Initialize the CentralPlant manager
32489
32569
  *
32490
32570
  * @constructor
32491
- * @version 0.1.44
32571
+ * @version 0.1.46
32492
32572
  * @updated 2025-10-22
32493
32573
  *
32494
32574
  * @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.46
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;
@@ -904,6 +940,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
904
940
  return null;
905
941
  }
906
942
  var collisionRadius = 0.5; // Radius around connector that triggers collision
943
+ var endpointTolerance = 0.55; // Tolerance for determining if connector is at segment endpoint
907
944
 
908
945
  // Get segment endpoints in world coordinates
909
946
  var endpoints = this.calculateSegmentEndpoints(segment);
@@ -922,6 +959,13 @@ var TransformOperationsManager = /*#__PURE__*/function () {
922
959
  var connectorWorldPos = new THREE__namespace.Vector3();
923
960
  child.getWorldPosition(connectorWorldPos);
924
961
 
962
+ // Skip connectors that are at the segment's endpoints (legitimate connection points)
963
+ var distanceToStart = connectorWorldPos.distanceTo(startPoint);
964
+ var distanceToEnd = connectorWorldPos.distanceTo(endPoint);
965
+ if (distanceToStart <= endpointTolerance || distanceToEnd <= endpointTolerance) {
966
+ return; // Skip this connector - it's at an endpoint
967
+ }
968
+
925
969
  // Find the closest point on the segment to the connector
926
970
  var segmentVector = new THREE__namespace.Vector3().subVectors(endPoint, startPoint);
927
971
  var pointVector = new THREE__namespace.Vector3().subVectors(connectorWorldPos, startPoint);
@@ -1120,6 +1164,37 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1120
1164
  return c1.distanceTo(c2);
1121
1165
  }
1122
1166
 
1167
+ /**
1168
+ * Constrain a position to maintain orthogonal alignment with a reference position
1169
+ * Movement is restricted to only along the segment's direction vector
1170
+ * @param {THREE.Vector3} newPosition - The proposed new position
1171
+ * @param {THREE.Vector3} referencePosition - The stationary reference position
1172
+ * @param {THREE.Object3D} segment - The segment being adjusted
1173
+ * @returns {THREE.Vector3} Constrained position that maintains orthogonality
1174
+ * @private
1175
+ */
1176
+ }, {
1177
+ key: "constrainPositionToOrthogonal",
1178
+ value: function constrainPositionToOrthogonal(newPosition, referencePosition, segment) {
1179
+ // Get the segment's direction vector
1180
+ var direction = new THREE__namespace.Vector3(0, 1, 0);
1181
+ direction.applyQuaternion(segment.quaternion);
1182
+ direction.normalize();
1183
+
1184
+ // Calculate vector from reference position to new position
1185
+ var moveVector = new THREE__namespace.Vector3().subVectors(newPosition, referencePosition);
1186
+
1187
+ // Project the move vector onto the segment's direction
1188
+ // This gives us the component of movement along the segment's axis only
1189
+ var projectionLength = moveVector.dot(direction);
1190
+
1191
+ // Constrained position = reference + (projection along segment direction)
1192
+ var constrainedPos = new THREE__namespace.Vector3().copy(referencePosition).add(direction.clone().multiplyScalar(projectionLength));
1193
+ console.log("\uD83D\uDD12 Constrained movement along segment direction: [".concat(direction.x.toFixed(3), ", ").concat(direction.y.toFixed(3), ", ").concat(direction.z.toFixed(3), "]"));
1194
+ console.log(" Projection length: ".concat(projectionLength.toFixed(3)));
1195
+ return constrainedPos;
1196
+ }
1197
+
1123
1198
  /**
1124
1199
  * Update adjacent segment connectors to follow the moved segment's new position
1125
1200
  * Finds segments that were connected to the moved segment BEFORE the move,
@@ -1238,18 +1313,23 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1238
1313
  var stationaryWorldPos = new THREE__namespace.Vector3();
1239
1314
  stationaryConnector.getWorldPosition(stationaryWorldPos);
1240
1315
 
1316
+ // Constrain movement to maintain orthogonal alignment
1317
+ // Horizontal segments: only adjust X and Y, keep Z constant
1318
+ // Vertical segments: only adjust Z, keep X and Y constant
1319
+ var constrainedPosition = _this2.constrainPositionToOrthogonal(pair.newPosition, stationaryWorldPos, adjacentSegment);
1320
+
1241
1321
  // Recreate the segment mesh with new length using explicit endpoint positions
1242
1322
  // Pass the intended positions, not the connector objects (which may have temporary positions)
1243
- _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, pair.newPosition, stationaryWorldPos);
1323
+ _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, constrainedPosition, stationaryWorldPos);
1244
1324
 
1245
1325
  // CRITICAL: After recreating the mesh, BOTH connectors need to be repositioned
1246
1326
  // because the segment's center and rotation have changed
1247
1327
 
1248
- // Position moving connector at the new position
1249
- var movingLocalPos = adjacentSegment.worldToLocal(pair.newPosition.clone());
1328
+ // Position moving connector at the constrained position (not the raw new position)
1329
+ var movingLocalPos = adjacentSegment.worldToLocal(constrainedPosition.clone());
1250
1330
  movingConnector.position.copy(movingLocalPos);
1251
1331
  movingConnector.updateMatrixWorld(true);
1252
- _this2.updateConnectorPositionInSceneData(movingConnector, pair.newPosition, adjacentSegment);
1332
+ _this2.updateConnectorPositionInSceneData(movingConnector, constrainedPosition, adjacentSegment);
1253
1333
 
1254
1334
  // Position stationary connector at its original world position
1255
1335
  var stationaryLocalPos = adjacentSegment.worldToLocal(stationaryWorldPos.clone());
@@ -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.46
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;
@@ -880,6 +916,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
880
916
  return null;
881
917
  }
882
918
  var collisionRadius = 0.5; // Radius around connector that triggers collision
919
+ var endpointTolerance = 0.55; // Tolerance for determining if connector is at segment endpoint
883
920
 
884
921
  // Get segment endpoints in world coordinates
885
922
  var endpoints = this.calculateSegmentEndpoints(segment);
@@ -898,6 +935,13 @@ var TransformOperationsManager = /*#__PURE__*/function () {
898
935
  var connectorWorldPos = new THREE.Vector3();
899
936
  child.getWorldPosition(connectorWorldPos);
900
937
 
938
+ // Skip connectors that are at the segment's endpoints (legitimate connection points)
939
+ var distanceToStart = connectorWorldPos.distanceTo(startPoint);
940
+ var distanceToEnd = connectorWorldPos.distanceTo(endPoint);
941
+ if (distanceToStart <= endpointTolerance || distanceToEnd <= endpointTolerance) {
942
+ return; // Skip this connector - it's at an endpoint
943
+ }
944
+
901
945
  // Find the closest point on the segment to the connector
902
946
  var segmentVector = new THREE.Vector3().subVectors(endPoint, startPoint);
903
947
  var pointVector = new THREE.Vector3().subVectors(connectorWorldPos, startPoint);
@@ -1096,6 +1140,37 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1096
1140
  return c1.distanceTo(c2);
1097
1141
  }
1098
1142
 
1143
+ /**
1144
+ * Constrain a position to maintain orthogonal alignment with a reference position
1145
+ * Movement is restricted to only along the segment's direction vector
1146
+ * @param {THREE.Vector3} newPosition - The proposed new position
1147
+ * @param {THREE.Vector3} referencePosition - The stationary reference position
1148
+ * @param {THREE.Object3D} segment - The segment being adjusted
1149
+ * @returns {THREE.Vector3} Constrained position that maintains orthogonality
1150
+ * @private
1151
+ */
1152
+ }, {
1153
+ key: "constrainPositionToOrthogonal",
1154
+ value: function constrainPositionToOrthogonal(newPosition, referencePosition, segment) {
1155
+ // Get the segment's direction vector
1156
+ var direction = new THREE.Vector3(0, 1, 0);
1157
+ direction.applyQuaternion(segment.quaternion);
1158
+ direction.normalize();
1159
+
1160
+ // Calculate vector from reference position to new position
1161
+ var moveVector = new THREE.Vector3().subVectors(newPosition, referencePosition);
1162
+
1163
+ // Project the move vector onto the segment's direction
1164
+ // This gives us the component of movement along the segment's axis only
1165
+ var projectionLength = moveVector.dot(direction);
1166
+
1167
+ // Constrained position = reference + (projection along segment direction)
1168
+ var constrainedPos = new THREE.Vector3().copy(referencePosition).add(direction.clone().multiplyScalar(projectionLength));
1169
+ console.log("\uD83D\uDD12 Constrained movement along segment direction: [".concat(direction.x.toFixed(3), ", ").concat(direction.y.toFixed(3), ", ").concat(direction.z.toFixed(3), "]"));
1170
+ console.log(" Projection length: ".concat(projectionLength.toFixed(3)));
1171
+ return constrainedPos;
1172
+ }
1173
+
1099
1174
  /**
1100
1175
  * Update adjacent segment connectors to follow the moved segment's new position
1101
1176
  * Finds segments that were connected to the moved segment BEFORE the move,
@@ -1214,18 +1289,23 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1214
1289
  var stationaryWorldPos = new THREE.Vector3();
1215
1290
  stationaryConnector.getWorldPosition(stationaryWorldPos);
1216
1291
 
1292
+ // Constrain movement to maintain orthogonal alignment
1293
+ // Horizontal segments: only adjust X and Y, keep Z constant
1294
+ // Vertical segments: only adjust Z, keep X and Y constant
1295
+ var constrainedPosition = _this2.constrainPositionToOrthogonal(pair.newPosition, stationaryWorldPos, adjacentSegment);
1296
+
1217
1297
  // Recreate the segment mesh with new length using explicit endpoint positions
1218
1298
  // Pass the intended positions, not the connector objects (which may have temporary positions)
1219
- _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, pair.newPosition, stationaryWorldPos);
1299
+ _this2.recreateSegmentMeshWithNewLength(adjacentSegment, adjacentConnectors, constrainedPosition, stationaryWorldPos);
1220
1300
 
1221
1301
  // CRITICAL: After recreating the mesh, BOTH connectors need to be repositioned
1222
1302
  // because the segment's center and rotation have changed
1223
1303
 
1224
- // Position moving connector at the new position
1225
- var movingLocalPos = adjacentSegment.worldToLocal(pair.newPosition.clone());
1304
+ // Position moving connector at the constrained position (not the raw new position)
1305
+ var movingLocalPos = adjacentSegment.worldToLocal(constrainedPosition.clone());
1226
1306
  movingConnector.position.copy(movingLocalPos);
1227
1307
  movingConnector.updateMatrixWorld(true);
1228
- _this2.updateConnectorPositionInSceneData(movingConnector, pair.newPosition, adjacentSegment);
1308
+ _this2.updateConnectorPositionInSceneData(movingConnector, constrainedPosition, adjacentSegment);
1229
1309
 
1230
1310
  // Position stationary connector at its original world position
1231
1311
  var stationaryLocalPos = adjacentSegment.worldToLocal(stationaryWorldPos.clone());
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.46",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/index.js",