@2112-lab/central-plant 0.1.60 → 0.1.62

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.
@@ -20136,7 +20136,24 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20136
20136
  // Check if this is a valid T-junction (moving segment endpoint touching manual segment)
20137
20137
  var isTJunction = _this2.isTJunction(segmentEndpoints.start, segmentEndpoints.end, otherEndpoints.start, otherEndpoints.end, endpointTolerance);
20138
20138
  if (isTJunction) {
20139
- console.log("\u2705 T-junction detected but allowed - moving segment endpoint touches manual segment");
20139
+ // T-junction detected, but we need to verify segments are NOT parallel
20140
+ // Parallel segments with touching endpoints indicate overlapping along the same line
20141
+ var _dir = new THREE__namespace.Vector3().subVectors(segmentEndpoints.end, segmentEndpoints.start).normalize();
20142
+ var _dir2 = new THREE__namespace.Vector3().subVectors(otherEndpoints.end, otherEndpoints.start).normalize();
20143
+ var _dotProduct = Math.abs(_dir.dot(_dir2));
20144
+ var parallelThreshold = 0.9999; // Same threshold as collinear check above
20145
+
20146
+ if (_dotProduct > parallelThreshold) {
20147
+ // Segments are parallel - this is NOT a valid T-junction, it's an overlap
20148
+ console.log("\uD83D\uDD34 T-junction rejected - segments are parallel (dot product: ".concat(_dotProduct.toFixed(6), ")"));
20149
+ console.log(" Moving segment: [".concat(segmentEndpoints.start.x.toFixed(2), ", ").concat(segmentEndpoints.start.y.toFixed(2), ", ").concat(segmentEndpoints.start.z.toFixed(2), "] to [").concat(segmentEndpoints.end.x.toFixed(2), ", ").concat(segmentEndpoints.end.y.toFixed(2), ", ").concat(segmentEndpoints.end.z.toFixed(2), "]"));
20150
+ console.log(" Manual segment: [".concat(otherEndpoints.start.x.toFixed(2), ", ").concat(otherEndpoints.start.y.toFixed(2), ", ").concat(otherEndpoints.start.z.toFixed(2), "] to [").concat(otherEndpoints.end.x.toFixed(2), ", ").concat(otherEndpoints.end.y.toFixed(2), ", ").concat(otherEndpoints.end.z.toFixed(2), "]"));
20151
+ hasIntersection = true;
20152
+ return;
20153
+ }
20154
+
20155
+ // Valid non-parallel T-junction - allowed
20156
+ console.log("\u2705 T-junction detected and allowed - moving segment endpoint touches manual segment at different angle (dot product: ".concat(_dotProduct.toFixed(6), ")"));
20140
20157
  console.log(" Moving segment: [".concat(segmentEndpoints.start.x.toFixed(2), ", ").concat(segmentEndpoints.start.y.toFixed(2), ", ").concat(segmentEndpoints.start.z.toFixed(2), "] to [").concat(segmentEndpoints.end.x.toFixed(2), ", ").concat(segmentEndpoints.end.y.toFixed(2), ", ").concat(segmentEndpoints.end.z.toFixed(2), "]"));
20141
20158
  console.log(" Manual segment: [".concat(otherEndpoints.start.x.toFixed(2), ", ").concat(otherEndpoints.start.y.toFixed(2), ", ").concat(otherEndpoints.start.z.toFixed(2), "] to [").concat(otherEndpoints.end.x.toFixed(2), ", ").concat(otherEndpoints.end.y.toFixed(2), ", ").concat(otherEndpoints.end.z.toFixed(2), "]"));
20142
20159
  return;
@@ -20257,7 +20274,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20257
20274
  return null;
20258
20275
  }
20259
20276
  var collisionRadius = 0.5; // Radius around connector that triggers collision
20260
- var endpointTolerance = 0.55; // Tolerance for determining if connector is at segment endpoint
20277
+ var endpointTolerance = 0.1; // Tolerance for determining if connector is at segment endpoint
20261
20278
 
20262
20279
  // Get segment endpoints in world coordinates
20263
20280
  var endpoints = this.calculateSegmentEndpoints(segment);
@@ -20272,6 +20289,15 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20272
20289
 
20273
20290
  // Check if this is a component connector (not a segment-connector)
20274
20291
  if (((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType) === 'connector') {
20292
+ var _segment$userData10, _segment$userData11;
20293
+ // Skip connectors that are connected to this segment (pathFrom or pathTo)
20294
+ var segmentPathFrom = (_segment$userData10 = segment.userData) === null || _segment$userData10 === void 0 ? void 0 : _segment$userData10.pathFrom;
20295
+ var segmentPathTo = (_segment$userData11 = segment.userData) === null || _segment$userData11 === void 0 ? void 0 : _segment$userData11.pathTo;
20296
+ var connectorId = child.uuid;
20297
+ if (connectorId === segmentPathFrom || connectorId === segmentPathTo) {
20298
+ return; // Skip this connector - it's connected to this segment
20299
+ }
20300
+
20275
20301
  // Get world position of connector
20276
20302
  var connectorWorldPos = new THREE__namespace.Vector3();
20277
20303
  child.getWorldPosition(connectorWorldPos);
@@ -21285,8 +21311,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21285
21311
  // Remove segment from scene data
21286
21312
  if (currentSceneData.scene && currentSceneData.scene.children) {
21287
21313
  var segmentIndex = currentSceneData.scene.children.findIndex(function (child) {
21288
- var _segment$userData10;
21289
- return child.uuid === segment.uuid || child.uuid === ((_segment$userData10 = segment.userData) === null || _segment$userData10 === void 0 ? void 0 : _segment$userData10.originalUuid);
21314
+ var _segment$userData12;
21315
+ return child.uuid === segment.uuid || child.uuid === ((_segment$userData12 = segment.userData) === null || _segment$userData12 === void 0 ? void 0 : _segment$userData12.originalUuid);
21290
21316
  });
21291
21317
  if (segmentIndex !== -1) {
21292
21318
  currentSceneData.scene.children.splice(segmentIndex, 1);
@@ -27027,15 +27053,12 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
27027
27053
  var objectsToRemove = [];
27028
27054
  sceneViewer.scene.traverse(function (obj) {
27029
27055
  // Remove computed Segments (uppercase SEGMENT-)
27056
+ // Note: Elbows are children of segments, so they'll be removed automatically
27030
27057
  if (obj.uuid && obj.uuid.startsWith("SEGMENT-") && obj.userData.isDeclared !== true) {
27031
27058
  console.log("[removeComputedObjects] to be removed: ".concat(obj.uuid, ")"));
27032
27059
  objectsToRemove.push(obj);
27033
27060
  }
27034
- // Remove computed Elbows
27035
- if (obj.userData && obj.userData.isPipeElbow && obj.userData.isDeclared !== true) {
27036
- console.log("[removeComputedObjects] to be removed: ".concat(obj.uuid, ")"));
27037
- objectsToRemove.push(obj);
27038
- }
27061
+ // Don't separately collect elbows - they're children of segments and will be handled below
27039
27062
  // Also remove computed Gateways
27040
27063
  if (obj.uuid && obj.uuid.includes("Gateway") && obj.userData && obj.userData.isDeclared !== true) {
27041
27064
  console.log("[removeComputedObjects] to be removed: ".concat(obj.uuid));
@@ -27044,9 +27067,30 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
27044
27067
  });
27045
27068
 
27046
27069
  // console.log(`[removeComputedObjects] objectsToRemove:`, objectsToRemove);
27047
-
27048
- for (var _i = 0, _objectsToRemove = objectsToRemove; _i < _objectsToRemove.length; _i++) {
27070
+ var _loop = function _loop() {
27049
27071
  var obj = _objectsToRemove[_i];
27072
+ // Dispose of children (elbows) before removing parent segment
27073
+ // Clone children array since we'll be modifying it during removal
27074
+ if (obj.children && obj.children.length > 0) {
27075
+ var childrenToRemove = _toConsumableArray(obj.children);
27076
+ childrenToRemove.forEach(function (child) {
27077
+ // Remove child from parent first
27078
+ obj.remove(child);
27079
+ // Then dispose of its resources
27080
+ if (child.geometry) child.geometry.dispose();
27081
+ if (child.material) {
27082
+ if (Array.isArray(child.material)) {
27083
+ child.material.forEach(function (mat) {
27084
+ return mat.dispose();
27085
+ });
27086
+ } else {
27087
+ child.material.dispose();
27088
+ }
27089
+ }
27090
+ });
27091
+ }
27092
+
27093
+ // Now remove the parent object from scene
27050
27094
  sceneViewer.scene.remove(obj);
27051
27095
  if (obj.geometry) obj.geometry.dispose();
27052
27096
  if (obj.material) {
@@ -27058,8 +27102,11 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
27058
27102
  obj.material.dispose();
27059
27103
  }
27060
27104
  }
27105
+ };
27106
+ for (var _i = 0, _objectsToRemove = objectsToRemove; _i < _objectsToRemove.length; _i++) {
27107
+ _loop();
27061
27108
  }
27062
- console.log("\u2705 Removed ".concat(objectsToRemove.length, " computed objects from scene (segments, elbows, and gateways)"));
27109
+ console.log("\u2705 Removed ".concat(objectsToRemove.length, " computed objects from scene (segments with elbows, and gateways)"));
27063
27110
  }
27064
27111
 
27065
27112
  /**
@@ -29645,6 +29692,28 @@ var SceneOperationsManager = /*#__PURE__*/function () {
29645
29692
  return;
29646
29693
  }
29647
29694
 
29695
+ // Remove any child elbows from computed segment before manualizing
29696
+ if (segment.children && segment.children.length > 0) {
29697
+ var childrenToRemove = _toConsumableArray(segment.children);
29698
+ childrenToRemove.forEach(function (child) {
29699
+ var _child$userData10;
29700
+ if ((_child$userData10 = child.userData) !== null && _child$userData10 !== void 0 && _child$userData10.isPipeElbow) {
29701
+ console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
29702
+ segment.remove(child);
29703
+ if (child.geometry) child.geometry.dispose();
29704
+ if (child.material) {
29705
+ if (Array.isArray(child.material)) {
29706
+ child.material.forEach(function (mat) {
29707
+ return mat.dispose();
29708
+ });
29709
+ } else {
29710
+ child.material.dispose();
29711
+ }
29712
+ }
29713
+ }
29714
+ });
29715
+ }
29716
+
29648
29717
  // CRITICAL: Clone the material BEFORE changing its color
29649
29718
  // This prevents the color change from affecting other segments that share the same material instance
29650
29719
  if (segment.material && !((_segment$material$use = segment.material.userData) !== null && _segment$material$use !== void 0 && _segment$material$use.isCloned)) {
@@ -33360,6 +33429,103 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33360
33429
  return this.centralPlant.managers.transformOperationsManager.translateGateway(gatewayId, axis, value);
33361
33430
  }
33362
33431
 
33432
+ /**
33433
+ * Translate currently selected objects
33434
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
33435
+ * @param {number} value - The value to translate by
33436
+ * @param {boolean} [skipPathUpdate=false] - If true, skip automatic path regeneration
33437
+ * @returns {Object} Result object with success status and details
33438
+ */
33439
+ }, {
33440
+ key: "translateSelectedObjects",
33441
+ value: function translateSelectedObjects(axis, value) {
33442
+ var _this$centralPlant$sc2;
33443
+ var skipPathUpdate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
33444
+ var result = {
33445
+ success: false,
33446
+ translatedCount: 0,
33447
+ totalCount: 0,
33448
+ errors: []
33449
+ };
33450
+
33451
+ // Get selected objects from transform manager
33452
+ var transformManager = (_this$centralPlant$sc2 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc2 === void 0 ? void 0 : _this$centralPlant$sc2.transformManager;
33453
+ if (!transformManager) {
33454
+ result.errors.push('Transform manager not available');
33455
+ console.error('❌ translateSelectedObjects(): Transform manager not available');
33456
+ return result;
33457
+ }
33458
+ var selectedObjects = transformManager.selectedObjects || [];
33459
+ result.totalCount = selectedObjects.length;
33460
+ if (selectedObjects.length === 0) {
33461
+ result.errors.push('No objects selected');
33462
+ console.warn('⚠️ translateSelectedObjects(): No objects selected');
33463
+ return result;
33464
+ }
33465
+ console.log("\uD83D\uDD27 Translating ".concat(selectedObjects.length, " selected object(s) on ").concat(axis, " axis by ").concat(value));
33466
+
33467
+ // Translate each selected object using the appropriate method
33468
+ var _iterator = _createForOfIteratorHelper(selectedObjects),
33469
+ _step;
33470
+ try {
33471
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
33472
+ var _obj$userData;
33473
+ var obj = _step.value;
33474
+ var objectType = (_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType;
33475
+ var objectId = obj.uuid;
33476
+ var success = false;
33477
+ try {
33478
+ if (objectType === 'segment') {
33479
+ success = this.translateSegment(objectId, axis, value, skipPathUpdate);
33480
+ } else if (objectType === 'gateway') {
33481
+ success = this.translateGateway(objectId, axis, value);
33482
+ } else if (objectType === 'component') {
33483
+ success = this.translateComponent(objectId, axis, value);
33484
+ } else {
33485
+ result.errors.push("Unknown object type: ".concat(objectType, " (").concat(objectId, ")"));
33486
+ console.warn("\u26A0\uFE0F Unknown object type: ".concat(objectType, " for ").concat(objectId));
33487
+ continue;
33488
+ }
33489
+ if (success) {
33490
+ result.translatedCount++;
33491
+ } else {
33492
+ result.errors.push("Failed to translate ".concat(objectType, ": ").concat(objectId));
33493
+ }
33494
+ } catch (error) {
33495
+ result.errors.push("Error translating ".concat(objectId, ": ").concat(error.message));
33496
+ console.error("\u274C Error translating ".concat(objectId, ":"), error);
33497
+ }
33498
+ }
33499
+ } catch (err) {
33500
+ _iterator.e(err);
33501
+ } finally {
33502
+ _iterator.f();
33503
+ }
33504
+ result.success = result.translatedCount === result.totalCount;
33505
+ if (result.success) {
33506
+ console.log("\u2705 Successfully translated all ".concat(result.translatedCount, " object(s)"));
33507
+ } else {
33508
+ console.warn("\u26A0\uFE0F Translated ".concat(result.translatedCount, "/").concat(result.totalCount, " objects. ").concat(result.errors.length, " error(s) occurred."));
33509
+ }
33510
+ return result;
33511
+ }
33512
+
33513
+ /**
33514
+ * Get currently selected objects
33515
+ * @returns {Array<THREE.Object3D>} Array of currently selected objects
33516
+ */
33517
+ }, {
33518
+ key: "getSelectedObjects",
33519
+ value: function getSelectedObjects() {
33520
+ var _this$centralPlant$sc3;
33521
+ var transformManager = (_this$centralPlant$sc3 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc3 === void 0 ? void 0 : _this$centralPlant$sc3.transformManager;
33522
+ if (!transformManager) {
33523
+ console.warn('⚠️ getSelectedObjects(): Transform manager not available');
33524
+ return [];
33525
+ }
33526
+ return transformManager.selectedObjects || [];
33527
+ }
33528
+
33363
33529
  /**
33364
33530
  * Rotate a component by componentId (delegates to TransformOperationsManager)
33365
33531
  * @param {string} componentId - The UUID of the component to rotate
@@ -33517,9 +33683,9 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33517
33683
  }, {
33518
33684
  key: "addConnection",
33519
33685
  value: function addConnection(fromConnectorId, toConnectorId) {
33520
- var _this$centralPlant$sc2;
33686
+ var _this$centralPlant$sc4;
33521
33687
  // Use centralized validation for connection parameters
33522
- var existingConnections = ((_this$centralPlant$sc2 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc2 === void 0 || (_this$centralPlant$sc2 = _this$centralPlant$sc2.currentSceneData) === null || _this$centralPlant$sc2 === void 0 ? void 0 : _this$centralPlant$sc2.connections) || [];
33688
+ var existingConnections = ((_this$centralPlant$sc4 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc4 === void 0 || (_this$centralPlant$sc4 = _this$centralPlant$sc4.currentSceneData) === null || _this$centralPlant$sc4 === void 0 ? void 0 : _this$centralPlant$sc4.connections) || [];
33523
33689
  var validation = this.validator.validateConnectionParams(fromConnectorId, toConnectorId, existingConnections);
33524
33690
  if (!validation.isValid) {
33525
33691
  return false; // Validator already logged the error
@@ -33645,7 +33811,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33645
33811
  }, {
33646
33812
  key: "addComponent",
33647
33813
  value: function addComponent(libraryId) {
33648
- var _this$centralPlant$sc3;
33814
+ var _this$centralPlant$sc5;
33649
33815
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
33650
33816
  // Use centralized validation for component addition parameters
33651
33817
  var existingIds = this.getComponentIds();
@@ -33655,7 +33821,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33655
33821
  }
33656
33822
 
33657
33823
  // Validate scene availability
33658
- var sceneValidation = this.validator.validateSceneViewer(this.centralPlant.sceneViewer, (_this$centralPlant$sc3 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc3 === void 0 ? void 0 : _this$centralPlant$sc3.scene);
33824
+ var sceneValidation = this.validator.validateSceneViewer(this.centralPlant.sceneViewer, (_this$centralPlant$sc5 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc5 === void 0 ? void 0 : _this$centralPlant$sc5.scene);
33659
33825
  if (!sceneValidation.isValid) {
33660
33826
  return false;
33661
33827
  }
@@ -33674,7 +33840,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33674
33840
  return false;
33675
33841
  }
33676
33842
  try {
33677
- var _this$centralPlant$sc4;
33843
+ var _this$centralPlant$sc6;
33678
33844
  // Generate a unique component ID if not provided
33679
33845
  var componentId = options.customId || this.generateUniqueComponentId(libraryId);
33680
33846
 
@@ -33748,7 +33914,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
33748
33914
  componentModel.updateMatrixWorld(true);
33749
33915
 
33750
33916
  // Check if component is underground and fix if needed (based on settings)
33751
- var checkUnderground = (_this$centralPlant$sc4 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc4 === void 0 || (_this$centralPlant$sc4 = _this$centralPlant$sc4.managers) === null || _this$centralPlant$sc4 === void 0 || (_this$centralPlant$sc4 = _this$centralPlant$sc4.settingsManager) === null || _this$centralPlant$sc4 === void 0 ? void 0 : _this$centralPlant$sc4.getSetting('scene', 'checkUnderground');
33917
+ var checkUnderground = (_this$centralPlant$sc6 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc6 === void 0 || (_this$centralPlant$sc6 = _this$centralPlant$sc6.managers) === null || _this$centralPlant$sc6 === void 0 || (_this$centralPlant$sc6 = _this$centralPlant$sc6.settingsManager) === null || _this$centralPlant$sc6 === void 0 ? void 0 : _this$centralPlant$sc6.getSetting('scene', 'checkUnderground');
33752
33918
  if (checkUnderground) {
33753
33919
  var wasFixed = this.fixUndergroundComponent(componentModel);
33754
33920
  if (wasFixed) {
@@ -33888,7 +34054,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
33888
34054
  * Initialize the CentralPlant manager
33889
34055
  *
33890
34056
  * @constructor
33891
- * @version 0.1.60
34057
+ * @version 0.1.62
33892
34058
  * @updated 2025-10-22
33893
34059
  *
33894
34060
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -34160,6 +34326,179 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
34160
34326
  return this.internals.translateSegment(segmentId, axis, value, skipPathUpdate);
34161
34327
  }
34162
34328
 
34329
+ /**
34330
+ * Select an object in the scene
34331
+ * @param {THREE.Object3D|string} objectOrId - The Three.js object to select, or its UUID
34332
+ * @returns {boolean} True if selection was successful, false otherwise
34333
+ * @description Selects an object in the scene, showing the transform controls and bounding box.
34334
+ * This is useful for programmatically selecting objects before performing operations like translation.
34335
+ * Objects marked as immutable (userData.immutable = true) cannot be selected.
34336
+ * @example
34337
+ * // Select by object reference
34338
+ * const segment = scene.getObjectByProperty('uuid', 'segment-uuid-123');
34339
+ * centralPlant.selectObject(segment);
34340
+ *
34341
+ * // Select by UUID
34342
+ * centralPlant.selectObject('segment-uuid-123');
34343
+ *
34344
+ * // Then translate the selected object
34345
+ * centralPlant.translateSegment('segment-uuid-123', 'x', 1.0);
34346
+ *
34347
+ * @since 0.1.37
34348
+ */
34349
+ }, {
34350
+ key: "selectObject",
34351
+ value: function selectObject(objectOrId) {
34352
+ var _this$sceneViewer, _object;
34353
+ if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.transformManager)) {
34354
+ console.warn('⚠️ Transform manager not initialized');
34355
+ return false;
34356
+ }
34357
+ var object = objectOrId;
34358
+
34359
+ // If a string UUID was provided, find the object in the scene
34360
+ if (typeof objectOrId === 'string') {
34361
+ object = this.sceneViewer.scene.getObjectByProperty('uuid', objectOrId);
34362
+ if (!object) {
34363
+ console.warn("\u26A0\uFE0F Object with UUID ".concat(objectOrId, " not found in scene"));
34364
+ return false;
34365
+ }
34366
+ }
34367
+
34368
+ // Check if object is immutable
34369
+ if (((_object = object) === null || _object === void 0 || (_object = _object.userData) === null || _object === void 0 ? void 0 : _object.immutable) === true) {
34370
+ console.warn("\u26A0\uFE0F Cannot select immutable object: ".concat(object.name || object.uuid));
34371
+ return false;
34372
+ }
34373
+ return this.sceneViewer.transformManager.selectObject(object);
34374
+ }
34375
+
34376
+ /**
34377
+ * Toggle object selection (add/remove from multi-selection)
34378
+ * @param {THREE.Object3D|string} objectOrId - The Three.js object to toggle, or its UUID
34379
+ * @returns {boolean} True if toggle was successful, false otherwise
34380
+ * @description Toggles an object in the multi-selection. If the object is already selected,
34381
+ * it will be removed from the selection. If not selected, it will be added to the selection.
34382
+ * This is useful for programmatic multi-selection (similar to shift+click behavior).
34383
+ * Objects marked as immutable (userData.immutable = true) cannot be toggled.
34384
+ * @example
34385
+ * // Toggle selection by UUID
34386
+ * centralPlant.toggleObject('segment-uuid-123');
34387
+ * centralPlant.toggleObject('segment-uuid-456'); // Now both selected
34388
+ * centralPlant.toggleObject('segment-uuid-123'); // Removes first, keeps second
34389
+ *
34390
+ * // Toggle by object reference
34391
+ * const segment = scene.getObjectByProperty('uuid', 'segment-uuid-789');
34392
+ * centralPlant.toggleObject(segment);
34393
+ *
34394
+ * @since 0.1.37
34395
+ */
34396
+ }, {
34397
+ key: "toggleObject",
34398
+ value: function toggleObject(objectOrId) {
34399
+ var _this$sceneViewer2, _object2;
34400
+ if (!((_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.transformManager)) {
34401
+ console.warn('⚠️ Transform manager not initialized');
34402
+ return false;
34403
+ }
34404
+ var object = objectOrId;
34405
+
34406
+ // If a string UUID was provided, find the object in the scene
34407
+ if (typeof objectOrId === 'string') {
34408
+ object = this.sceneViewer.scene.getObjectByProperty('uuid', objectOrId);
34409
+ if (!object) {
34410
+ console.warn("\u26A0\uFE0F Object with UUID ".concat(objectOrId, " not found in scene"));
34411
+ return false;
34412
+ }
34413
+ }
34414
+
34415
+ // Check if object is immutable
34416
+ if (((_object2 = object) === null || _object2 === void 0 || (_object2 = _object2.userData) === null || _object2 === void 0 ? void 0 : _object2.immutable) === true) {
34417
+ console.warn("\u26A0\uFE0F Cannot toggle immutable object: ".concat(object.name || object.uuid));
34418
+ return false;
34419
+ }
34420
+ this.sceneViewer.transformManager.toggleObjectSelection(object);
34421
+ return true;
34422
+ }
34423
+
34424
+ /**
34425
+ * Deselect the currently selected object(s)
34426
+ * @returns {boolean} True if deselection was successful
34427
+ * @description Clears the current selection, hiding transform controls and bounding boxes.
34428
+ * @example
34429
+ * centralPlant.deselectObject();
34430
+ *
34431
+ * @since 0.1.37
34432
+ */
34433
+ }, {
34434
+ key: "deselectObject",
34435
+ value: function deselectObject() {
34436
+ var _this$sceneViewer3;
34437
+ if (!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformManager)) {
34438
+ console.warn('⚠️ Transform manager not initialized');
34439
+ return false;
34440
+ }
34441
+ this.sceneViewer.transformManager.deselectObject();
34442
+ return true;
34443
+ }
34444
+
34445
+ /**
34446
+ * Translate currently selected object(s)
34447
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
34448
+ * @param {number} value - The value to translate by (must be a multiple of 0.5)
34449
+ * @param {boolean} [skipPathUpdate=false] - If true, skip automatic path regeneration (for batch operations)
34450
+ * @returns {Object} Result object with success status and details
34451
+ * @returns {boolean} returns.success - True if all translations were successful
34452
+ * @returns {number} returns.translatedCount - Number of objects successfully translated
34453
+ * @returns {number} returns.totalCount - Total number of selected objects
34454
+ * @returns {Array} returns.errors - Array of error messages if any failures occurred
34455
+ * @description Translates all currently selected objects along the specified axis.
34456
+ * Automatically determines the object type (component/segment/gateway) and calls the
34457
+ * appropriate translation method for each. Useful for batch operations on selections.
34458
+ * @example
34459
+ * // Select objects first
34460
+ * centralPlant.selectObject('segment-1')
34461
+ * centralPlant.toggleObjectSelection('segment-2')
34462
+ * centralPlant.toggleObjectSelection('component-1')
34463
+ *
34464
+ * // Translate all selected objects 2 units along X axis
34465
+ * const result = centralPlant.translateSelectedObjects('x', 2.0)
34466
+ * console.log(`Translated ${result.translatedCount}/${result.totalCount} objects`)
34467
+ *
34468
+ * // Batch operation with path update disabled
34469
+ * centralPlant.translateSelectedObjects('x', 1.0, true)
34470
+ * centralPlant.translateSelectedObjects('y', 0.5, true)
34471
+ * centralPlant.updatePaths() // Update once at the end
34472
+ *
34473
+ * @since 0.1.37
34474
+ */
34475
+ }, {
34476
+ key: "translateSelectedObjects",
34477
+ value: function translateSelectedObjects(axis, value) {
34478
+ var skipPathUpdate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
34479
+ return this.internals.translateSelectedObjects(axis, value, skipPathUpdate);
34480
+ }
34481
+
34482
+ /**
34483
+ * Get currently selected objects
34484
+ * @returns {Array<THREE.Object3D>} Array of currently selected Three.js objects
34485
+ * @description Returns the array of objects currently selected in the scene.
34486
+ * Useful for inspecting selection state or performing custom operations.
34487
+ * @example
34488
+ * const selected = centralPlant.getSelectedObjects()
34489
+ * console.log(`${selected.length} objects selected`)
34490
+ * selected.forEach(obj => {
34491
+ * console.log(`- ${obj.name} (${obj.userData.objectType}): ${obj.uuid}`)
34492
+ * })
34493
+ *
34494
+ * @since 0.1.37
34495
+ */
34496
+ }, {
34497
+ key: "getSelectedObjects",
34498
+ value: function getSelectedObjects() {
34499
+ return this.internals.getSelectedObjects();
34500
+ }
34501
+
34163
34502
  /**
34164
34503
  * Translate a gateway by gatewayId
34165
34504
  * @param {string} gatewayId - The UUID of the gateway to translate
@@ -34623,6 +34962,116 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
34623
34962
  return componentIds;
34624
34963
  }
34625
34964
 
34965
+ /**
34966
+ * Get object IDs from the scene by type
34967
+ * @param {string|Array<string>} objectTypes - Object type(s) to retrieve: 'components', 'segments', 'gateways', or array of types
34968
+ * @param {Object} [options={}] - Optional filtering options
34969
+ * @param {boolean} [options.isDeclared] - Filter by declared status (true for declared only, false for computed only, undefined for all)
34970
+ * @returns {Array<string>} Array of object UUID strings matching the specified type(s), or empty array if none exist
34971
+ * @description Retrieves UUIDs of objects currently in the 3D scene filtered by object type.
34972
+ * This traverses the Three.js scene graph to find objects with matching objectType in userData.
34973
+ * Supports single type as string or multiple types as array.
34974
+ * @example
34975
+ * // Get all component IDs
34976
+ * const componentIds = centralPlant.getObjectIds('components');
34977
+ * console.log(`Components: ${componentIds.length}`);
34978
+ *
34979
+ * // Get all segment IDs
34980
+ * const segmentIds = centralPlant.getObjectIds('segments');
34981
+ * console.log(`Segments: ${segmentIds.length}`);
34982
+ *
34983
+ * // Get all gateway IDs
34984
+ * const gatewayIds = centralPlant.getObjectIds('gateways');
34985
+ * console.log(`Gateways: ${gatewayIds.length}`);
34986
+ *
34987
+ * // Get multiple object types at once
34988
+ * const allIds = centralPlant.getObjectIds(['components', 'segments', 'gateways']);
34989
+ * console.log(`Total objects: ${allIds.length}`);
34990
+ *
34991
+ * // Get only declared segments
34992
+ * const declaredSegments = centralPlant.getObjectIds('segments', { isDeclared: true });
34993
+ * console.log(`Declared segments: ${declaredSegments.length}`);
34994
+ *
34995
+ * // Get only computed gateways
34996
+ * const computedGateways = centralPlant.getObjectIds('gateways', { isDeclared: false });
34997
+ * console.log(`Computed gateways: ${computedGateways.length}`);
34998
+ *
34999
+ * // Use for selection UI
35000
+ * const segments = centralPlant.getObjectIds('segments');
35001
+ * segments.forEach(id => {
35002
+ * console.log(`Segment: ${id}`);
35003
+ * });
35004
+ */
35005
+ }, {
35006
+ key: "getObjectIds",
35007
+ value: function getObjectIds() {
35008
+ var objectTypes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['components', 'segments', 'gateways'];
35009
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
35010
+ if (!this.sceneViewer) {
35011
+ console.warn('⚠️ getObjectIds(): Scene viewer not available - centralPlant not fully initialized');
35012
+ return [];
35013
+ }
35014
+ if (!this.sceneViewer.scene) {
35015
+ console.warn('⚠️ getObjectIds(): Scene not available - scene viewer not fully initialized');
35016
+ return [];
35017
+ }
35018
+ if (!this.sceneViewer.isInitialized) {
35019
+ console.warn('⚠️ getObjectIds(): Scene viewer initialization not complete');
35020
+ return [];
35021
+ }
35022
+
35023
+ // Validate objectTypes parameter
35024
+ if (!objectTypes) {
35025
+ console.warn('⚠️ getObjectIds(): objectTypes parameter is required');
35026
+ return [];
35027
+ }
35028
+
35029
+ // Convert to array for consistent handling
35030
+ var typesArray = Array.isArray(objectTypes) ? objectTypes : [objectTypes];
35031
+
35032
+ // Map plural types to singular (internal userData uses singular)
35033
+ var typeMapping = {
35034
+ 'components': 'component',
35035
+ 'segments': 'segment',
35036
+ 'gateways': 'gateway'
35037
+ };
35038
+
35039
+ // Validate each type and convert to internal format
35040
+ var validTypes = ['components', 'segments', 'gateways'];
35041
+ var invalidTypes = typesArray.filter(function (type) {
35042
+ return !validTypes.includes(type);
35043
+ });
35044
+ if (invalidTypes.length > 0) {
35045
+ console.warn("\u26A0\uFE0F getObjectIds(): Invalid object type(s): ".concat(invalidTypes.join(', '), ". Valid types are: ").concat(validTypes.join(', ')));
35046
+ return [];
35047
+ }
35048
+
35049
+ // Convert plural types to singular for internal lookup
35050
+ var internalTypes = typesArray.map(function (type) {
35051
+ return typeMapping[type];
35052
+ });
35053
+ var objectIds = [];
35054
+
35055
+ // Traverse the scene to find all objects matching the specified types
35056
+ this.sceneViewer.scene.traverse(function (child) {
35057
+ if (child.userData && child.userData.objectType && internalTypes.includes(child.userData.objectType)) {
35058
+ // Apply isDeclared filter if specified
35059
+ if (options.isDeclared !== undefined) {
35060
+ var childIsDeclared = child.userData.isDeclared === true;
35061
+ if (childIsDeclared !== options.isDeclared) {
35062
+ return; // Skip this object if it doesn't match the isDeclared filter
35063
+ }
35064
+ }
35065
+ var id = child.uuid || child.userData.originalUuid || child.name;
35066
+ if (id) {
35067
+ objectIds.push(id);
35068
+ }
35069
+ }
35070
+ });
35071
+ console.log("\uD83D\uDCCB getObjectIds(): Found ".concat(objectIds.length, " object IDs for type(s) [").concat(typesArray.join(', '), "]").concat(options.isDeclared !== undefined ? " (isDeclared: ".concat(options.isDeclared, ")") : '', ":"), objectIds);
35072
+ return objectIds;
35073
+ }
35074
+
34626
35075
  /**
34627
35076
  * Get components from the dictionary with detailed information and flexible filtering
34628
35077
  * @param {Object} [options={}] - Configuration options for component listing