@2112-lab/central-plant 0.3.5 → 0.3.7
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.
- package/dist/bundle/index.js +167 -176
- package/dist/cjs/src/core/centralPlant.js +1 -1
- package/dist/cjs/src/managers/scene/modelManager.js +7 -0
- package/dist/cjs/src/managers/scene/sceneOperationsManager.js +62 -132
- package/dist/cjs/src/managers/scene/viewport2DManager.js +88 -40
- package/dist/cjs/src/utils/sceneClearingUtility.js +7 -2
- package/dist/esm/src/core/centralPlant.js +1 -1
- package/dist/esm/src/managers/scene/modelManager.js +7 -0
- package/dist/esm/src/managers/scene/sceneOperationsManager.js +63 -133
- package/dist/esm/src/managers/scene/viewport2DManager.js +88 -40
- package/dist/esm/src/utils/sceneClearingUtility.js +7 -2
- package/package.json +1 -1
|
@@ -7,7 +7,6 @@ var THREE = require('three');
|
|
|
7
7
|
var textureConfig = require('../environment/textureConfig.js');
|
|
8
8
|
var modelManager = require('./modelManager.js');
|
|
9
9
|
var sceneClearingUtility = require('../../utils/sceneClearingUtility.js');
|
|
10
|
-
var boundingBoxUtils = require('../../utils/boundingBoxUtils.js');
|
|
11
10
|
|
|
12
11
|
function _interopNamespace(e) {
|
|
13
12
|
if (e && e.__esModule) return e;
|
|
@@ -647,123 +646,52 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
647
646
|
}
|
|
648
647
|
|
|
649
648
|
/**
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
*
|
|
649
|
+
* Sync world-space positions and isDeclared flags for gateways and connectors
|
|
650
|
+
* into the scene JSON data so the pathfinder can read them.
|
|
651
|
+
*
|
|
652
|
+
* Bounding boxes for components and segments are intentionally NOT computed here.
|
|
653
|
+
* They are computed (with matrix-hash caching) by
|
|
654
|
+
* PathfindingManager._enrichSceneDataWithBoundingBoxes(), which runs after GLB
|
|
655
|
+
* models are fully loaded and therefore produces correct values.
|
|
653
656
|
*/
|
|
654
657
|
}, {
|
|
655
|
-
key: "
|
|
656
|
-
value: function
|
|
657
|
-
var
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
// Recursively search children
|
|
677
|
-
if (child.children) {
|
|
678
|
-
var found = _findJsonObject(child.children);
|
|
679
|
-
if (found) return found;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
} catch (err) {
|
|
683
|
-
_iterator.e(err);
|
|
684
|
-
} finally {
|
|
685
|
-
_iterator.f();
|
|
686
|
-
}
|
|
687
|
-
return null;
|
|
688
|
-
};
|
|
689
|
-
jsonObject = _findJsonObject(data.scene.children);
|
|
690
|
-
if (jsonObject) {
|
|
691
|
-
// Store in JSON userData for pathfinder (skip for gateways - they're just routing points)
|
|
692
|
-
if (!jsonObject.userData) jsonObject.userData = {};
|
|
693
|
-
if (jsonObject.userData.objectType === 'component') {
|
|
694
|
-
// For components: compute filtered bounding box (excludes io-device and connector subtrees)
|
|
695
|
-
var filteredBBox = boundingBoxUtils.computeFilteredBoundingBox(object, ['io-device', 'connector']);
|
|
696
|
-
jsonObject.userData.worldBoundingBox = {
|
|
697
|
-
min: filteredBBox.min.toArray(),
|
|
698
|
-
max: filteredBBox.max.toArray()
|
|
699
|
-
};
|
|
700
|
-
console.log("Added filtered world bounding box for component:", jsonObject.userData.worldBoundingBox);
|
|
701
|
-
|
|
702
|
-
// Compute and inject separate io-device bounding boxes as children
|
|
703
|
-
var ioDeviceBBoxes = boundingBoxUtils.computeIODeviceBoundingBoxes(object);
|
|
704
|
-
if (ioDeviceBBoxes.length > 0) {
|
|
705
|
-
if (!jsonObject.children) jsonObject.children = [];
|
|
706
|
-
ioDeviceBBoxes.forEach(function (deviceBBox) {
|
|
707
|
-
var existingIndex = jsonObject.children.findIndex(function (c) {
|
|
708
|
-
return c.uuid === deviceBBox.uuid;
|
|
709
|
-
});
|
|
710
|
-
if (existingIndex >= 0) {
|
|
711
|
-
// Update existing entry
|
|
712
|
-
if (!jsonObject.children[existingIndex].userData) jsonObject.children[existingIndex].userData = {};
|
|
713
|
-
jsonObject.children[existingIndex].userData.objectType = 'io-device';
|
|
714
|
-
jsonObject.children[existingIndex].userData.worldBoundingBox = deviceBBox.worldBoundingBox;
|
|
715
|
-
} else {
|
|
716
|
-
// Create new entry
|
|
717
|
-
jsonObject.children.push({
|
|
718
|
-
uuid: deviceBBox.uuid,
|
|
719
|
-
userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, deviceBBox.userData), {}, {
|
|
720
|
-
worldBoundingBox: deviceBBox.worldBoundingBox
|
|
721
|
-
}),
|
|
722
|
-
children: []
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bbox(es) for component ").concat(jsonObject.uuid));
|
|
727
|
-
}
|
|
728
|
-
} else if (jsonObject.userData.objectType !== 'gateway') {
|
|
729
|
-
// For non-component, non-gateway objects: standard bounding box
|
|
730
|
-
var boundingBox = new THREE__namespace.Box3().setFromObject(object);
|
|
731
|
-
jsonObject.userData.worldBoundingBox = {
|
|
732
|
-
min: boundingBox.min.toArray(),
|
|
733
|
-
max: boundingBox.max.toArray()
|
|
734
|
-
};
|
|
735
|
-
console.log("Added world bounding box:", jsonObject.userData.worldBoundingBox);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// For gateways and connectors, ensure userData.position exists in scene data
|
|
739
|
-
// This is REQUIRED for pathfinder compatibility
|
|
740
|
-
if (jsonObject.userData.objectType === 'gateway' || jsonObject.userData.objectType === 'connector') {
|
|
741
|
-
// Use the object's world position (from Three.js mesh)
|
|
742
|
-
var worldPos = new THREE__namespace.Vector3();
|
|
743
|
-
object.getWorldPosition(worldPos);
|
|
744
|
-
|
|
745
|
-
// ALWAYS update userData.position with world position
|
|
746
|
-
// This is critical for manual segment connectors which start with local positions
|
|
747
|
-
jsonObject.userData.position = [worldPos.x, worldPos.y, worldPos.z];
|
|
748
|
-
console.log("\u2705 Set userData.position for ".concat(jsonObject.userData.objectType, " ").concat(jsonObject.uuid, ": [").concat(worldPos.x.toFixed(2), ", ").concat(worldPos.y.toFixed(2), ", ").concat(worldPos.z.toFixed(2), "]"));
|
|
749
|
-
|
|
750
|
-
// For gateways, ensure isDeclared flag is in scene data
|
|
751
|
-
if (jsonObject.userData.objectType === 'gateway') {
|
|
752
|
-
if (jsonObject.userData.isDeclared === undefined) {
|
|
753
|
-
jsonObject.userData.isDeclared = true;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// For manual segment connectors, ensure isDeclared is set in scene data
|
|
758
|
-
if (jsonObject.userData.objectType === 'segment-connector' && jsonObject.userData.isDeclared === undefined) {
|
|
759
|
-
jsonObject.userData.isDeclared = true;
|
|
760
|
-
console.log("\u2705 Set isDeclared=true for manual segment connector in scene data: ".concat(jsonObject.uuid));
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Also sync the mesh's userData.position (belt and suspenders approach)
|
|
764
|
-
object.userData.position = [worldPos.x, worldPos.y, worldPos.z];
|
|
765
|
-
}
|
|
658
|
+
key: "_syncPositionsForPathfinding",
|
|
659
|
+
value: function _syncPositionsForPathfinding(data) {
|
|
660
|
+
var scene = this.sceneViewer.scene;
|
|
661
|
+
var worldPos = new THREE__namespace.Vector3();
|
|
662
|
+
var syncPosition = function syncPosition(jsonObject) {
|
|
663
|
+
var _jsonObject$userData;
|
|
664
|
+
var object = scene.getObjectByProperty('uuid', jsonObject.uuid) || scene.getObjectByProperty('uuid', (_jsonObject$userData = jsonObject.userData) === null || _jsonObject$userData === void 0 ? void 0 : _jsonObject$userData.originalUuid);
|
|
665
|
+
if (!object) return;
|
|
666
|
+
object.getWorldPosition(worldPos);
|
|
667
|
+
var pos = [worldPos.x, worldPos.y, worldPos.z];
|
|
668
|
+
jsonObject.userData.position = pos;
|
|
669
|
+
object.userData.position = pos;
|
|
670
|
+
};
|
|
671
|
+
data.scene.children.forEach(function (jsonObject) {
|
|
672
|
+
var _jsonObject$userData2;
|
|
673
|
+
var type = (_jsonObject$userData2 = jsonObject.userData) === null || _jsonObject$userData2 === void 0 ? void 0 : _jsonObject$userData2.objectType;
|
|
674
|
+
if (type === 'gateway') {
|
|
675
|
+
syncPosition(jsonObject);
|
|
676
|
+
if (jsonObject.userData.isDeclared === undefined) {
|
|
677
|
+
jsonObject.userData.isDeclared = true;
|
|
766
678
|
}
|
|
679
|
+
} else if (type === 'connector') {
|
|
680
|
+
syncPosition(jsonObject);
|
|
681
|
+
} else if (type === 'segment-connector') {
|
|
682
|
+
syncPosition(jsonObject);
|
|
683
|
+
if (jsonObject.userData.isDeclared === undefined) {
|
|
684
|
+
jsonObject.userData.isDeclared = true;
|
|
685
|
+
}
|
|
686
|
+
} else if (type === 'component' && Array.isArray(jsonObject.children)) {
|
|
687
|
+
// Connectors are injected as JSON children by _injectConnectorChildrenFromDictionary
|
|
688
|
+
// and their Three.js objects exist in the scene, created recursively by createSceneObject
|
|
689
|
+
jsonObject.children.forEach(function (childJson) {
|
|
690
|
+
var _childJson$userData;
|
|
691
|
+
if (((_childJson$userData = childJson.userData) === null || _childJson$userData === void 0 ? void 0 : _childJson$userData.objectType) === 'connector') {
|
|
692
|
+
syncPosition(childJson);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
767
695
|
}
|
|
768
696
|
});
|
|
769
697
|
}
|
|
@@ -928,10 +856,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
928
856
|
var componentsProcessed = 0;
|
|
929
857
|
var connectorsInjected = 0;
|
|
930
858
|
data.scene.children.forEach(function (child) {
|
|
931
|
-
var _child$
|
|
932
|
-
var childType = ((_child$
|
|
859
|
+
var _child$userData4, _child$userData5, _child$userData6;
|
|
860
|
+
var childType = ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType);
|
|
933
861
|
// Only process components with libraryId
|
|
934
|
-
if (childType === 'component' && (_child$
|
|
862
|
+
if (childType === 'component' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.libraryId) {
|
|
935
863
|
var libraryId = child.userData.libraryId;
|
|
936
864
|
var dictEntry = componentDictionary[libraryId];
|
|
937
865
|
|
|
@@ -1088,23 +1016,25 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
1088
1016
|
geometries = this.createSceneGeometries(data, componentDictionary); // Create basic objects and track GLB replacements
|
|
1089
1017
|
libraryObjectsToReplace = [];
|
|
1090
1018
|
data.scene.children.forEach(function (child, index) {
|
|
1091
|
-
var _child$
|
|
1019
|
+
var _child$userData7, _child$userData8;
|
|
1092
1020
|
var createdObject = _this4.createSceneObject(child, geometries, materials, componentDictionary);
|
|
1093
1021
|
_this4.sceneViewer.scene.add(createdObject);
|
|
1094
1022
|
|
|
1095
1023
|
// Track objects that need GLB model replacement
|
|
1096
|
-
if ((_child$
|
|
1097
|
-
var _child$
|
|
1024
|
+
if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId && componentDictionary[(_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.libraryId]) {
|
|
1025
|
+
var _child$userData9;
|
|
1098
1026
|
libraryObjectsToReplace.push({
|
|
1099
1027
|
basicObject: createdObject,
|
|
1100
1028
|
jsonData: child,
|
|
1101
|
-
componentData: componentDictionary[(_child$
|
|
1029
|
+
componentData: componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]
|
|
1102
1030
|
});
|
|
1103
1031
|
}
|
|
1104
1032
|
});
|
|
1105
1033
|
|
|
1106
|
-
//
|
|
1107
|
-
|
|
1034
|
+
// Sync gateway/connector world positions into JSON before pathfinding.
|
|
1035
|
+
// Bounding boxes are computed later by PathfindingManager._enrichSceneDataWithBoundingBoxes
|
|
1036
|
+
// (after GLB models are loaded), so no bbox work is done here.
|
|
1037
|
+
this._syncPositionsForPathfinding(data);
|
|
1108
1038
|
this._saveOriginalWorldMatrices(this.sceneViewer.scene);
|
|
1109
1039
|
return _context6.a(2, {
|
|
1110
1040
|
componentDictionary: componentDictionary,
|
|
@@ -1250,8 +1180,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
1250
1180
|
var instanceBehaviors = [];
|
|
1251
1181
|
if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
|
|
1252
1182
|
data.scene.children.forEach(function (child) {
|
|
1253
|
-
var _child$
|
|
1254
|
-
var libraryId = (_child$
|
|
1183
|
+
var _child$userData0, _compData$defaultBeha;
|
|
1184
|
+
var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
|
|
1255
1185
|
if (!libraryId) return;
|
|
1256
1186
|
var instanceUuid = child.uuid;
|
|
1257
1187
|
// Skip instances whose defaults were already resolved by Step A
|
|
@@ -1447,8 +1377,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
1447
1377
|
key: "_saveOriginalWorldMatrices",
|
|
1448
1378
|
value: function _saveOriginalWorldMatrices(scene) {
|
|
1449
1379
|
scene.traverse(function (object) {
|
|
1450
|
-
var _object$
|
|
1451
|
-
if ((_object$
|
|
1380
|
+
var _object$userData;
|
|
1381
|
+
if ((_object$userData = object.userData) !== null && _object$userData !== void 0 && _object$userData.direction) {
|
|
1452
1382
|
var originalMatrix = new THREE__namespace.Matrix4();
|
|
1453
1383
|
originalMatrix.copy(object.matrixWorld);
|
|
1454
1384
|
}
|
|
@@ -1652,8 +1582,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
1652
1582
|
// Process children (connectors, etc.) if they exist
|
|
1653
1583
|
if (componentModel.children && componentModel.children.length > 0) {
|
|
1654
1584
|
componentModel.children.forEach(function (child) {
|
|
1655
|
-
var _child$
|
|
1656
|
-
var childType = ((_child$
|
|
1585
|
+
var _child$userData1, _child$userData10;
|
|
1586
|
+
var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
|
|
1657
1587
|
if (childType === 'connector') {
|
|
1658
1588
|
var _child$geometry;
|
|
1659
1589
|
var childBoundingBox = new THREE__namespace.Box3().setFromObject(child);
|
|
@@ -1738,8 +1668,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
|
|
|
1738
1668
|
if (segment.children && segment.children.length > 0) {
|
|
1739
1669
|
var childrenToRemove = _rollupPluginBabelHelpers.toConsumableArray(segment.children);
|
|
1740
1670
|
childrenToRemove.forEach(function (child) {
|
|
1741
|
-
var _child$
|
|
1742
|
-
if ((_child$
|
|
1671
|
+
var _child$userData11;
|
|
1672
|
+
if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
|
|
1743
1673
|
console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
|
|
1744
1674
|
segment.remove(child);
|
|
1745
1675
|
if (child.geometry) child.geometry.dispose();
|
|
@@ -111,6 +111,18 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
111
111
|
// Map of viewport instances by viewType or custom key
|
|
112
112
|
_this2.viewports = new Map();
|
|
113
113
|
|
|
114
|
+
// Per-refresh-cycle bbox cache: keyed by object.uuid, cleared each refresh()
|
|
115
|
+
// so each component bbox is computed once per cycle regardless of viewport count
|
|
116
|
+
_this2._bboxCache = new Map();
|
|
117
|
+
|
|
118
|
+
// Per-refresh-cycle component list cache: eliminates redundant scene traversals
|
|
119
|
+
// when all 3 viewports render in the same cycle
|
|
120
|
+
_this2._componentListCache = null;
|
|
121
|
+
|
|
122
|
+
// rAF debounce flag — prevents multiple same-frame refresh() calls from
|
|
123
|
+
// stacking up independent renderComponents() runs
|
|
124
|
+
_this2._refreshPending = false;
|
|
125
|
+
|
|
114
126
|
// Event listener reference for cleanup
|
|
115
127
|
_this2._objectTransformedListener = null;
|
|
116
128
|
console.log('🔲 Viewport2DManager initialized (multi-instance support)');
|
|
@@ -132,7 +144,6 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
132
144
|
|
|
133
145
|
// Listen for object transformations to refresh all viewports
|
|
134
146
|
this._objectTransformedListener = function (eventData) {
|
|
135
|
-
console.log('🔲 Viewport2DManager detected object transformation, refreshing all viewports');
|
|
136
147
|
_this3.refresh(); // Refresh all viewports
|
|
137
148
|
};
|
|
138
149
|
|
|
@@ -198,6 +209,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
198
209
|
case 3:
|
|
199
210
|
// Create new viewport instance
|
|
200
211
|
viewport = new Viewport2DInstance(this.sceneViewer, this.Konva, viewType, container);
|
|
212
|
+
viewport._instanceKey = key;
|
|
201
213
|
this.viewports.set(key, viewport);
|
|
202
214
|
|
|
203
215
|
// Initialize the stage for this viewport
|
|
@@ -375,9 +387,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
375
387
|
viewport.stage.width(width);
|
|
376
388
|
viewport.stage.height(height);
|
|
377
389
|
|
|
378
|
-
// Redraw
|
|
390
|
+
// Redraw grid immediately; schedule debounced component render
|
|
379
391
|
this.drawGrid(viewport);
|
|
380
|
-
this.
|
|
392
|
+
this.refresh(viewport._instanceKey);
|
|
381
393
|
}
|
|
382
394
|
}
|
|
383
395
|
|
|
@@ -560,7 +572,6 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
560
572
|
worldDepth = _this$getComponentDim.worldDepth,
|
|
561
573
|
worldHeight = _this$getComponentDim.worldHeight,
|
|
562
574
|
bboxCenter = _this$getComponentDim.bboxCenter;
|
|
563
|
-
console.log("[2D] ".concat(component.name, " | w=").concat(worldWidth.toFixed(3), " d=").concat(worldDepth.toFixed(3), " h=").concat(worldHeight.toFixed(3), " | center=(").concat(bboxCenter.x.toFixed(2), ",").concat(bboxCenter.y.toFixed(2), ",").concat(bboxCenter.z.toFixed(2), ")"));
|
|
564
575
|
|
|
565
576
|
// Project 3D bbox center to 2D based on view type
|
|
566
577
|
var _this$project3DTo2D = this.project3DTo2D(viewport, bboxCenter, worldWidth, worldDepth, worldHeight),
|
|
@@ -627,21 +638,45 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
627
638
|
}, {
|
|
628
639
|
key: "_getOrComputeWorldBoundingBox",
|
|
629
640
|
value: function _getOrComputeWorldBoundingBox(object) {
|
|
630
|
-
|
|
641
|
+
var _object$userData, _object$userData2;
|
|
642
|
+
// Fast path: offset the stored world bbox by the position delta since load time.
|
|
643
|
+
// Translation only shifts the bbox center — extents stay identical — so this is O(1)
|
|
644
|
+
// instead of O(meshes × vertices) from a full geometry traversal.
|
|
645
|
+
var stored = (_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.worldBoundingBox;
|
|
646
|
+
var basePos = (_object$userData2 = object.userData) === null || _object$userData2 === void 0 ? void 0 : _object$userData2._wbbBasePosition;
|
|
647
|
+
if (stored && basePos) {
|
|
648
|
+
var dx = object.position.x - basePos.x;
|
|
649
|
+
var dy = object.position.y - basePos.y;
|
|
650
|
+
var dz = object.position.z - basePos.z;
|
|
651
|
+
if (dx === 0 && dy === 0 && dz === 0) return stored;
|
|
652
|
+
return {
|
|
653
|
+
min: [stored.min[0] + dx, stored.min[1] + dy, stored.min[2] + dz],
|
|
654
|
+
max: [stored.max[0] + dx, stored.max[1] + dy, stored.max[2] + dz]
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Slow path: full vertex-accurate traversal (fallback when userData not populated)
|
|
659
|
+
if (this._bboxCache.has(object.uuid)) {
|
|
660
|
+
return this._bboxCache.get(object.uuid);
|
|
661
|
+
}
|
|
631
662
|
var box = boundingBoxUtils.computeFilteredBoundingBox(object, []);
|
|
663
|
+
var result;
|
|
632
664
|
if (box.isEmpty()) {
|
|
633
665
|
// Object has no geometry; fall back to a point at world position
|
|
634
666
|
var wp = new THREE__namespace.Vector3();
|
|
635
667
|
object.getWorldPosition(wp);
|
|
636
|
-
|
|
668
|
+
result = {
|
|
637
669
|
min: [wp.x, wp.y, wp.z],
|
|
638
670
|
max: [wp.x, wp.y, wp.z]
|
|
639
671
|
};
|
|
672
|
+
} else {
|
|
673
|
+
result = {
|
|
674
|
+
min: [box.min.x, box.min.y, box.min.z],
|
|
675
|
+
max: [box.max.x, box.max.y, box.max.z]
|
|
676
|
+
};
|
|
640
677
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
max: [box.max.x, box.max.y, box.max.z]
|
|
644
|
-
};
|
|
678
|
+
this._bboxCache.set(object.uuid, result);
|
|
679
|
+
return result;
|
|
645
680
|
}
|
|
646
681
|
|
|
647
682
|
/**
|
|
@@ -650,11 +685,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
650
685
|
}, {
|
|
651
686
|
key: "getComponentDimensions",
|
|
652
687
|
value: function getComponentDimensions(component) {
|
|
653
|
-
var _component$
|
|
654
|
-
//
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
|
|
688
|
+
var _component$getWorldPo;
|
|
689
|
+
// Always recompute from the live Three.js object so that the rect reflects
|
|
690
|
+
// the current world position after translate/drag operations.
|
|
691
|
+
// userData.worldBoundingBox is a load-time snapshot and goes stale whenever
|
|
692
|
+
// the object moves, so we cannot rely on it here.
|
|
693
|
+
var wbb = this._getOrComputeWorldBoundingBox(component);
|
|
658
694
|
if (wbb !== null && wbb !== void 0 && wbb.min && wbb !== null && wbb !== void 0 && wbb.max) {
|
|
659
695
|
var _wbb$min = _rollupPluginBabelHelpers.slicedToArray(wbb.min, 3),
|
|
660
696
|
minX = _wbb$min[0],
|
|
@@ -765,7 +801,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
765
801
|
rect.stroke('#007bff');
|
|
766
802
|
rect.strokeWidth(3);
|
|
767
803
|
viewport.stage.container().style.cursor = 'grab';
|
|
768
|
-
viewport.componentLayer.
|
|
804
|
+
viewport.componentLayer.batchDraw();
|
|
769
805
|
}
|
|
770
806
|
});
|
|
771
807
|
rect.on('mouseleave', function () {
|
|
@@ -774,7 +810,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
774
810
|
rect.stroke(colors.stroke);
|
|
775
811
|
rect.strokeWidth(2);
|
|
776
812
|
viewport.stage.container().style.cursor = 'default';
|
|
777
|
-
viewport.componentLayer.
|
|
813
|
+
viewport.componentLayer.batchDraw();
|
|
778
814
|
}
|
|
779
815
|
});
|
|
780
816
|
|
|
@@ -818,7 +854,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
818
854
|
// Snap to grid
|
|
819
855
|
var snappedPos = _this6.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
|
|
820
856
|
componentGroup.position(snappedPos);
|
|
821
|
-
viewport.componentLayer.
|
|
857
|
+
viewport.componentLayer.batchDraw();
|
|
822
858
|
});
|
|
823
859
|
|
|
824
860
|
// DRAG END
|
|
@@ -991,18 +1027,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
991
1027
|
}, {
|
|
992
1028
|
key: "getSceneComponents",
|
|
993
1029
|
value: function getSceneComponents() {
|
|
1030
|
+
if (this._componentListCache) return this._componentListCache;
|
|
994
1031
|
if (!this.sceneViewer || !this.sceneViewer.scene) {
|
|
995
1032
|
return [];
|
|
996
1033
|
}
|
|
997
1034
|
var components = [];
|
|
998
1035
|
this.sceneViewer.scene.traverse(function (object) {
|
|
999
|
-
var _object$
|
|
1036
|
+
var _object$userData3, _object$userData4;
|
|
1000
1037
|
// Only match the ROOT component object — must have both objectType:'component'
|
|
1001
1038
|
// AND libraryId (inner GLB mesh nodes don't have libraryId)
|
|
1002
|
-
if (((_object$
|
|
1039
|
+
if (((_object$userData3 = object.userData) === null || _object$userData3 === void 0 ? void 0 : _object$userData3.objectType) === 'component' && (_object$userData4 = object.userData) !== null && _object$userData4 !== void 0 && _object$userData4.libraryId) {
|
|
1003
1040
|
components.push(object);
|
|
1004
1041
|
}
|
|
1005
1042
|
});
|
|
1043
|
+
this._componentListCache = components;
|
|
1006
1044
|
return components;
|
|
1007
1045
|
}
|
|
1008
1046
|
|
|
@@ -1108,35 +1146,45 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1108
1146
|
}
|
|
1109
1147
|
|
|
1110
1148
|
/**
|
|
1111
|
-
* Refresh a specific viewport or all viewports
|
|
1149
|
+
* Refresh a specific viewport or all viewports.
|
|
1150
|
+
* Debounced via requestAnimationFrame so multiple calls within the same
|
|
1151
|
+
* frame (e.g. from Viewport2D mount + refreshAll2DViews) collapse into one.
|
|
1112
1152
|
* @param {string} key - Optional viewport key. If not provided, refreshes all viewports
|
|
1113
1153
|
*/
|
|
1114
1154
|
}, {
|
|
1115
1155
|
key: "refresh",
|
|
1116
1156
|
value: function refresh() {
|
|
1157
|
+
var _this7 = this;
|
|
1117
1158
|
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1159
|
+
if (this._refreshPending) return;
|
|
1160
|
+
this._refreshPending = true;
|
|
1161
|
+
requestAnimationFrame(function () {
|
|
1162
|
+
_this7._refreshPending = false;
|
|
1163
|
+
// Clear per-cycle caches so each component is measured/traversed once per paint
|
|
1164
|
+
_this7._bboxCache.clear();
|
|
1165
|
+
_this7._componentListCache = null;
|
|
1166
|
+
if (key) {
|
|
1167
|
+
var viewport = _this7.viewports.get(key);
|
|
1168
|
+
if (viewport && viewport.isReady) {
|
|
1169
|
+
_this7.renderComponents(viewport);
|
|
1170
|
+
}
|
|
1171
|
+
} else {
|
|
1172
|
+
var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(_this7.viewports.values()),
|
|
1173
|
+
_step;
|
|
1174
|
+
try {
|
|
1175
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1176
|
+
var _viewport = _step.value;
|
|
1177
|
+
if (_viewport.isReady) {
|
|
1178
|
+
_this7.renderComponents(_viewport);
|
|
1179
|
+
}
|
|
1132
1180
|
}
|
|
1181
|
+
} catch (err) {
|
|
1182
|
+
_iterator.e(err);
|
|
1183
|
+
} finally {
|
|
1184
|
+
_iterator.f();
|
|
1133
1185
|
}
|
|
1134
|
-
} catch (err) {
|
|
1135
|
-
_iterator.e(err);
|
|
1136
|
-
} finally {
|
|
1137
|
-
_iterator.f();
|
|
1138
1186
|
}
|
|
1139
|
-
}
|
|
1187
|
+
});
|
|
1140
1188
|
}
|
|
1141
1189
|
|
|
1142
1190
|
/**
|
|
@@ -226,10 +226,10 @@ var SceneClearingUtility = /*#__PURE__*/function () {
|
|
|
226
226
|
throw new Error('Scene not available for clearing');
|
|
227
227
|
case 1:
|
|
228
228
|
componentsToRemove = [];
|
|
229
|
-
scene = this.sceneViewer.scene; // Collect
|
|
229
|
+
scene = this.sceneViewer.scene; // Collect component, segment, and gateway objects
|
|
230
230
|
scene.traverse(function (child) {
|
|
231
231
|
if (child === scene) return;
|
|
232
|
-
var isComponent = child.userData && (child.userData.objectType === 'component' || child.userData.objectType === '
|
|
232
|
+
var isComponent = child.userData && (child.userData.objectType === 'component' || child.userData.objectType === 'segment' || child.userData.objectType === 'gateway' || child.userData.objectType === 'connector');
|
|
233
233
|
var isDirectChild = child.parent === scene;
|
|
234
234
|
if (isComponent && isDirectChild) {
|
|
235
235
|
componentsToRemove.push(child);
|
|
@@ -480,6 +480,11 @@ var SceneClearingUtility = /*#__PURE__*/function () {
|
|
|
480
480
|
this.sceneViewer.currentSceneData.connections = [];
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
// Clear the JSON data mirror so getAvailableConnections() returns nothing
|
|
484
|
+
if (this.sceneViewer.currentSceneData && this.sceneViewer.currentSceneData.scene) {
|
|
485
|
+
this.sceneViewer.currentSceneData.scene.children = [];
|
|
486
|
+
}
|
|
487
|
+
|
|
483
488
|
// Reset component counter for CentralPlant
|
|
484
489
|
if (this.sceneViewer.centralPlant && this.sceneViewer.centralPlant.componentCounter !== undefined) {
|
|
485
490
|
this.sceneViewer.centralPlant.componentCounter = 0;
|
|
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
31
31
|
* Initialize the CentralPlant manager
|
|
32
32
|
*
|
|
33
33
|
* @constructor
|
|
34
|
-
* @version 0.3.
|
|
34
|
+
* @version 0.3.7
|
|
35
35
|
* @updated 2025-10-22
|
|
36
36
|
*
|
|
37
37
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -571,6 +571,13 @@ var ModelManager = /*#__PURE__*/function () {
|
|
|
571
571
|
// Update both the JSON data object AND the live scene object
|
|
572
572
|
jsonData.userData.worldBoundingBox = worldBoundingBox;
|
|
573
573
|
glbModel.userData.worldBoundingBox = worldBoundingBox;
|
|
574
|
+
// Snapshot the object's local position so viewport2DManager can compute
|
|
575
|
+
// world-bbox updates via a fast O(1) position delta instead of re-traversing geometry
|
|
576
|
+
glbModel.userData._wbbBasePosition = {
|
|
577
|
+
x: glbModel.position.x,
|
|
578
|
+
y: glbModel.position.y,
|
|
579
|
+
z: glbModel.position.z
|
|
580
|
+
};
|
|
574
581
|
});
|
|
575
582
|
|
|
576
583
|
// Dispatch completion event
|