@2112-lab/central-plant 0.3.45 → 0.3.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.
@@ -17,6 +17,7 @@ import { KeyboardControlsManager } from '../managers/controls/keyboardControlsMa
17
17
  import { PathfindingManager } from '../managers/pathfinding/pathfindingManager.js';
18
18
  import { PathFlowManager } from '../managers/pathfinding/PathFlowManager.js';
19
19
  import { SceneOperationsManager } from '../managers/scene/sceneOperationsManager.js';
20
+ import { CollisionManager } from '../managers/scene/collisionManager.js';
20
21
  import { AnimationManager } from '../managers/scene/animationManager.js';
21
22
  import { CameraControlsManager } from '../managers/controls/cameraControlsManager.js';
22
23
  import { ComponentDragManager } from '../managers/controls/componentDragManager.js';
@@ -145,6 +146,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
145
146
  this.centralPlant.managers.keyboardControlsManager = new KeyboardControlsManager(this.centralPlant.sceneViewer);
146
147
  this.centralPlant.managers.pathfindingManager = new PathfindingManager(this.centralPlant.sceneViewer);
147
148
  this.centralPlant.managers.pathFlowManager = new PathFlowManager(this.centralPlant.sceneViewer);
149
+ this.centralPlant.managers.collisionManager = new CollisionManager(this.centralPlant.sceneViewer);
148
150
  this.centralPlant.managers.sceneOperationsManager = new SceneOperationsManager(this.centralPlant.sceneViewer);
149
151
  this.centralPlant.managers.animationManager = new AnimationManager(this.centralPlant.sceneViewer);
150
152
  this.centralPlant.managers.cameraControlsManager = new CameraControlsManager(this.centralPlant.sceneViewer);
@@ -643,6 +643,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
643
643
  // Find intersection with ground plane
644
644
  var intersection = this.raycaster.ray.intersectPlane(this.dropPlane, this.dropIntersection);
645
645
  if (intersection) {
646
+ var _this$sceneViewer$col;
646
647
  // Apply 0.5 unit transform snapping to intersection point
647
648
  var snappedPosition = this._applyTransformSnap(intersection);
648
649
  this.dragData.previewObject.position.copy(snappedPosition);
@@ -650,7 +651,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
650
651
 
651
652
  // Check for overlap and update color accordingly
652
653
  var wasOverlapping = this.dragData.isOverlapping;
653
- this.dragData.isOverlapping = this._checkBoundingBoxOverlap();
654
+ this.dragData.isOverlapping = !!((_this$sceneViewer$col = this.sceneViewer.collisionManager) !== null && _this$sceneViewer$col !== void 0 && _this$sceneViewer$col.checkCollision(this.dragData.previewObject));
654
655
 
655
656
  // Update color if overlap state changed
656
657
  if (wasOverlapping !== this.dragData.isOverlapping) {
@@ -0,0 +1,119 @@
1
+ import { createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, classCallCheck as _classCallCheck } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import * as THREE from 'three';
3
+
4
+ var CollisionManager = /*#__PURE__*/function () {
5
+ /**
6
+ * @param {Object} sceneViewer - The scene viewer instance
7
+ */
8
+ function CollisionManager(sceneViewer) {
9
+ _classCallCheck(this, CollisionManager);
10
+ this.sceneViewer = sceneViewer;
11
+ }
12
+
13
+ /**
14
+ * Check if a given object overlaps with any other relevant objects in the scene.
15
+ * @param {THREE.Object3D} object - The object to check for collisions
16
+ * @param {Array<string>} excludeTypes - Object types to exclude from checking (e.g. ['ground'])
17
+ * @returns {Object|null} Collision info {object, objectType} if collision detected, null otherwise
18
+ */
19
+ return _createClass(CollisionManager, [{
20
+ key: "checkCollision",
21
+ value: function checkCollision(object) {
22
+ var _this$sceneViewer,
23
+ _this = this;
24
+ var excludeTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['isBaseGround', 'isBaseGrid', 'isBrickWall'];
25
+ if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.scene) || !object) return null;
26
+
27
+ // Compute high-quality bounding box for the object being checked
28
+ // We use setFromObject here because the target object just moved
29
+ var objectBBox = new THREE.Box3().setFromObject(object);
30
+
31
+ // Narrow down potential colliders
32
+ var collisionDetected = null;
33
+
34
+ // Optimization: Only check the root-level CP objects to avoid O(N^2) complexity with meshes
35
+ this.sceneViewer.scene.traverse(function (child) {
36
+ var _child$userData, _child$userData2, _child$userData3, _child$userData4, _child$userData5;
37
+ if (collisionDetected) return; // Short circuit
38
+
39
+ // Skip the object itself and its descendants
40
+ if (child === object || _this._isDescendantOf(object, child)) return;
41
+
42
+ // Filter by CP object types at the root level (skip internal meshes during traverse)
43
+ var isCPRootObject = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'component' || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment' || ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'gateway' || ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) === 'connector' || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.ioConfig); // IO Device
44
+
45
+ if (isCPRootObject && !_this._shouldExclude(child, excludeTypes)) {
46
+ // Use cached worldBoundingBox if available, otherwise compute it (and cache it)
47
+ var childBBox;
48
+ if (child.userData.worldBoundingBox && !child.userData.isMoving) {
49
+ var _min$x, _min$y, _min$z, _max$x, _max$y, _max$z;
50
+ // Use stored worldBoundingBox (handles array or Box3 format)
51
+ var min = child.userData.worldBoundingBox.min;
52
+ var max = child.userData.worldBoundingBox.max;
53
+ childBBox = new THREE.Box3(new THREE.Vector3((_min$x = min.x) !== null && _min$x !== void 0 ? _min$x : min[0], (_min$y = min.y) !== null && _min$y !== void 0 ? _min$y : min[1], (_min$z = min.z) !== null && _min$z !== void 0 ? _min$z : min[2]), new THREE.Vector3((_max$x = max.x) !== null && _max$x !== void 0 ? _max$x : max[0], (_max$y = max.y) !== null && _max$y !== void 0 ? _max$y : max[1], (_max$z = max.z) !== null && _max$z !== void 0 ? _max$z : max[2]));
54
+ } else {
55
+ // Fallback to high-quality computation for moving objects or first-time checks
56
+ childBBox = new THREE.Box3().setFromObject(child);
57
+
58
+ // Cache the result for next time (non-moving objects)
59
+ if (!child.userData.isMoving) {
60
+ child.userData.worldBoundingBox = {
61
+ min: [childBBox.min.x, childBBox.min.y, childBBox.min.z],
62
+ max: [childBBox.max.x, childBBox.max.y, childBBox.max.z]
63
+ };
64
+ }
65
+ }
66
+ if (objectBBox.intersectsBox(childBBox)) {
67
+ collisionDetected = {
68
+ object: child,
69
+ objectType: child.userData.objectType,
70
+ uuid: child.uuid,
71
+ name: child.name || child.userData.libraryId || child.uuid
72
+ };
73
+ }
74
+ }
75
+ });
76
+ return collisionDetected;
77
+ }
78
+
79
+ /**
80
+ * Helper to check if a node is a descendant of a specific parent
81
+ * @private
82
+ */
83
+ }, {
84
+ key: "_isDescendantOf",
85
+ value: function _isDescendantOf(parent, child) {
86
+ var node = child.parent;
87
+ while (node !== null) {
88
+ if (node === parent) return true;
89
+ node = node.parent;
90
+ }
91
+ return false;
92
+ }
93
+
94
+ /**
95
+ * Helper to determine if an object should be excluded from collision checking
96
+ * @private
97
+ */
98
+ }, {
99
+ key: "_shouldExclude",
100
+ value: function _shouldExclude(object, excludeTypes) {
101
+ if (object.userData.isPreview) return true;
102
+ var _iterator = _createForOfIteratorHelper(excludeTypes),
103
+ _step;
104
+ try {
105
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
106
+ var type = _step.value;
107
+ if (object.userData[type]) return true;
108
+ }
109
+ } catch (err) {
110
+ _iterator.e(err);
111
+ } finally {
112
+ _iterator.f();
113
+ }
114
+ return false;
115
+ }
116
+ }]);
117
+ }();
118
+
119
+ export { CollisionManager };
@@ -145,6 +145,8 @@ var SceneExportManager = /*#__PURE__*/function () {
145
145
  // Internal tracking - not needed in export
146
146
  'initialPosition',
147
147
  // Internal tracking - not needed in export
148
+ 'attachedDevices',
149
+ // Stored in smart component dictionary, not scene JSON
148
150
  // Exclude internal segment tracking properties
149
151
  'segmentId',
150
152
  // Internal tracking
@@ -1309,55 +1309,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1309
1309
  y: componentModel.scale.y,
1310
1310
  z: componentModel.scale.z
1311
1311
  },
1312
- userData: _objectSpread2(_objectSpread2({}, componentModel.userData), {}, {
1312
+ userData: _objectSpread2(_objectSpread2({}, function () {
1313
+ var ud = _objectSpread2({}, componentModel.userData);
1314
+ delete ud.attachedDevices; // Instance data belongs in dictionary, not scene JSON
1315
+ delete ud.isDeclared; // Runtime flag
1316
+ delete ud.originalUuid; // Runtime tracking
1317
+ return ud;
1318
+ }()), {}, {
1313
1319
  worldBoundingBox: {
1314
1320
  min: boundingBox.min.toArray(),
1315
1321
  max: boundingBox.max.toArray()
1316
1322
  }
1317
- }),
1318
- children: []
1323
+ })
1319
1324
  };
1320
1325
 
1321
- // Process children (connectors, etc.) if they exist
1322
- if (componentModel.children && componentModel.children.length > 0) {
1323
- componentModel.children.forEach(function (child) {
1324
- var _child$userData0, _child$userData1;
1325
- var childType = ((_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.objectType) || ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType);
1326
- if (childType === 'connector') {
1327
- var _child$geometry;
1328
- var childBoundingBox = new THREE.Box3().setFromObject(child);
1329
- var childSceneData = {
1330
- uuid: child.uuid,
1331
- name: child.name,
1332
- type: child.type,
1333
- position: {
1334
- x: child.position.x,
1335
- y: child.position.y,
1336
- z: child.position.z
1337
- },
1338
- rotation: {
1339
- x: THREE.MathUtils.radToDeg(child.rotation.x),
1340
- y: THREE.MathUtils.radToDeg(child.rotation.y),
1341
- z: THREE.MathUtils.radToDeg(child.rotation.z)
1342
- },
1343
- scale: {
1344
- x: child.scale.x,
1345
- y: child.scale.y,
1346
- z: child.scale.z
1347
- },
1348
- userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
1349
- worldBoundingBox: {
1350
- min: childBoundingBox.min.toArray(),
1351
- max: childBoundingBox.max.toArray()
1352
- }
1353
- }),
1354
- geometry: ((_child$geometry = child.geometry) === null || _child$geometry === void 0 ? void 0 : _child$geometry.uuid) || 'CONNECTOR-GEO'
1355
- };
1356
- componentSceneData.children.push(childSceneData);
1357
- }
1358
- });
1359
- }
1360
-
1361
1326
  // Add the component to the scene data
1362
1327
  if (!currentSceneData.scene.children) {
1363
1328
  currentSceneData.scene.children = [];
@@ -1366,7 +1331,6 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1366
1331
  console.log('✅ addComponentToSceneData: Component added to scene data successfully', {
1367
1332
  componentId: componentModel.uuid,
1368
1333
  libraryId: (_componentModel$userD = componentModel.userData) === null || _componentModel$userD === void 0 ? void 0 : _componentModel$userD.libraryId,
1369
- childrenCount: componentSceneData.children.length,
1370
1334
  totalSceneChildren: currentSceneData.scene.children.length
1371
1335
  });
1372
1336
  return true;
@@ -1407,8 +1371,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1407
1371
  if (segment.children && segment.children.length > 0) {
1408
1372
  var childrenToRemove = _toConsumableArray(segment.children);
1409
1373
  childrenToRemove.forEach(function (child) {
1410
- var _child$userData10;
1411
- if ((_child$userData10 = child.userData) !== null && _child$userData10 !== void 0 && _child$userData10.isPipeElbow) {
1374
+ var _child$userData0;
1375
+ if ((_child$userData0 = child.userData) !== null && _child$userData0 !== void 0 && _child$userData0.isPipeElbow) {
1412
1376
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
1413
1377
  segment.remove(child);
1414
1378
  if (child.geometry) child.geometry.dispose();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.45",
3
+ "version": "0.3.46",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/src/index.js",