@2112-lab/central-plant 0.3.25 → 0.3.27
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 +948 -137
- package/dist/cjs/src/core/centralPlant.js +1 -1
- package/dist/cjs/src/core/centralPlantInternals.js +56 -14
- package/dist/cjs/src/core/sceneViewer.js +55 -2
- package/dist/cjs/src/managers/behaviors/IoAnimationManager.js +29 -2
- package/dist/cjs/src/managers/behaviors/IoOutlineManager.js +258 -0
- package/dist/cjs/src/managers/controls/transformControlsManager.js +319 -43
- package/dist/cjs/src/managers/scene/animationManager.js +9 -2
- package/dist/cjs/src/managers/scene/componentTooltipManager.js +198 -31
- package/dist/cjs/src/managers/scene/modelManager.js +17 -2
- package/dist/cjs/src/utils/boundingBoxUtils.js +38 -40
- package/dist/esm/src/core/centralPlant.js +1 -1
- package/dist/esm/src/core/centralPlantInternals.js +57 -15
- package/dist/esm/src/core/sceneViewer.js +55 -2
- package/dist/esm/src/managers/behaviors/IoAnimationManager.js +29 -2
- package/dist/esm/src/managers/behaviors/IoOutlineManager.js +234 -0
- package/dist/esm/src/managers/controls/transformControlsManager.js +319 -43
- package/dist/esm/src/managers/scene/animationManager.js +9 -2
- package/dist/esm/src/managers/scene/componentTooltipManager.js +199 -32
- package/dist/esm/src/managers/scene/modelManager.js +18 -3
- package/dist/esm/src/utils/boundingBoxUtils.js +39 -42
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createClass as _createClass, objectSpread2 as _objectSpread2, createForOfIteratorHelper as _createForOfIteratorHelper, typeof as _typeof, toConsumableArray as _toConsumableArray, classCallCheck as _classCallCheck, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
1
|
+
import { createClass as _createClass, objectSpread2 as _objectSpread2, createForOfIteratorHelper as _createForOfIteratorHelper, typeof as _typeof, slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray, classCallCheck as _classCallCheck, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import { CentralPlantValidator } from './centralPlantValidator.js';
|
|
4
4
|
import { createTransformControls } from '../managers/controls/transformControlsManager.js';
|
|
@@ -24,8 +24,10 @@ import { SceneTooltipsManager } from '../managers/scene/sceneTooltipsManager.js'
|
|
|
24
24
|
import { ComponentTooltipManager } from '../managers/scene/componentTooltipManager.js';
|
|
25
25
|
import { Viewport2DManager } from '../managers/scene/viewport2DManager.js';
|
|
26
26
|
import { IoAnimationManager } from '../managers/behaviors/IoAnimationManager.js';
|
|
27
|
+
import { IoOutlineManager } from '../managers/behaviors/IoOutlineManager.js';
|
|
27
28
|
import { generateUuidFromName, getHardcodedUuid, findObjectByHardcodedUuid, generateUniqueComponentId } from '../utils/nameUtils.js';
|
|
28
29
|
import { attachIODevicesToComponent } from '../utils/ioDeviceUtils.js';
|
|
30
|
+
import { computeFilteredBoundingBoxCached } from '../utils/boundingBoxUtils.js';
|
|
29
31
|
import modelPreloader from '../rendering/modelPreloader.js';
|
|
30
32
|
|
|
31
33
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -150,6 +152,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
150
152
|
this.centralPlant.managers.componentDragManager = new ComponentDragManager(this.centralPlant.sceneViewer);
|
|
151
153
|
this.centralPlant.managers.viewport2DManager = new Viewport2DManager(this.centralPlant.sceneViewer);
|
|
152
154
|
this.centralPlant.managers.ioAnimationManager = new IoAnimationManager(this.centralPlant.sceneViewer);
|
|
155
|
+
this.centralPlant.managers.ioOutlineManager = new IoOutlineManager(this.centralPlant.sceneViewer);
|
|
153
156
|
|
|
154
157
|
// All managers are now stored in the managers collection and will be attached via attachToComponent()
|
|
155
158
|
}
|
|
@@ -1124,15 +1127,40 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1124
1127
|
|
|
1125
1128
|
// Add attached IO device models for smart components
|
|
1126
1129
|
if (componentData.isSmart && componentData.attachedDevices) {
|
|
1130
|
+
var _this$centralPlant$sc8;
|
|
1127
1131
|
attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
|
|
1132
|
+
|
|
1133
|
+
// Register animation configs so IoAnimationManager can respond to state changes
|
|
1134
|
+
var ioAnimMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioAnimationManager;
|
|
1135
|
+
if (ioAnimMgr) {
|
|
1136
|
+
var _loop = function _loop() {
|
|
1137
|
+
var _modelPreloader$compo;
|
|
1138
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
1139
|
+
attachmentId = _Object$entries$_i[0],
|
|
1140
|
+
attachment = _Object$entries$_i[1];
|
|
1141
|
+
var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
|
|
1142
|
+
if (!(deviceData !== null && deviceData !== void 0 && deviceData.animationConfig)) return 1; // continue
|
|
1143
|
+
var deviceRoot = null;
|
|
1144
|
+
componentModel.traverse(function (obj) {
|
|
1145
|
+
var _obj$userData2;
|
|
1146
|
+
if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
|
|
1147
|
+
});
|
|
1148
|
+
if (deviceRoot) {
|
|
1149
|
+
ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, componentId);
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
|
|
1153
|
+
if (_loop()) continue;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1128
1156
|
}
|
|
1129
1157
|
|
|
1130
1158
|
// Register default behaviors for smart components so the BehaviorManager
|
|
1131
1159
|
// responds to tooltip-driven state changes immediately after drop.
|
|
1132
1160
|
// (The scene-load path uses _processBehaviors instead, which runs on loadSceneData.)
|
|
1133
1161
|
if ((_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length) {
|
|
1134
|
-
var _this$centralPlant$
|
|
1135
|
-
var som = (_this$centralPlant$
|
|
1162
|
+
var _this$centralPlant$sc9, _som$registerBehavior;
|
|
1163
|
+
var som = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.sceneOperationsManager;
|
|
1136
1164
|
som === null || som === void 0 || (_som$registerBehavior = som.registerBehaviorsForComponentInstance) === null || _som$registerBehavior === void 0 || _som$registerBehavior.call(som, componentData, componentId);
|
|
1137
1165
|
}
|
|
1138
1166
|
|
|
@@ -1141,6 +1169,20 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1141
1169
|
componentManager.registerComponent(componentModel);
|
|
1142
1170
|
}
|
|
1143
1171
|
|
|
1172
|
+
// Pre-warm the filtered bounding-box cache for smart components so the
|
|
1173
|
+
// first selection is instant. Deferred to idle time so it does not
|
|
1174
|
+
// block the current frame.
|
|
1175
|
+
if (componentData.isSmart) {
|
|
1176
|
+
var warmFn = function warmFn() {
|
|
1177
|
+
return computeFilteredBoundingBoxCached(componentModel, ['io-device', 'connector']);
|
|
1178
|
+
};
|
|
1179
|
+
if (typeof requestIdleCallback !== 'undefined') {
|
|
1180
|
+
requestIdleCallback(warmFn);
|
|
1181
|
+
} else {
|
|
1182
|
+
setTimeout(warmFn, 0);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1144
1186
|
// EMIT COMPONENT ADDED EVENT
|
|
1145
1187
|
// This allows UI components (like SceneHierarchy) to update reactively
|
|
1146
1188
|
if (this.centralPlant.sceneViewer.emit) {
|
|
@@ -1194,18 +1236,18 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1194
1236
|
}, {
|
|
1195
1237
|
key: "deleteComponent",
|
|
1196
1238
|
value: function deleteComponent(componentId) {
|
|
1197
|
-
var _this$centralPlant$
|
|
1239
|
+
var _this$centralPlant$sc0;
|
|
1198
1240
|
// Check if component manager is available
|
|
1199
|
-
var componentManager = (_this$centralPlant$
|
|
1241
|
+
var componentManager = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.componentManager;
|
|
1200
1242
|
if (!componentManager) {
|
|
1201
1243
|
console.error('❌ deleteComponent(): Component manager not available');
|
|
1202
1244
|
return false;
|
|
1203
1245
|
}
|
|
1204
1246
|
try {
|
|
1205
|
-
var _this$centralPlant$
|
|
1247
|
+
var _this$centralPlant$sc1, _this$centralPlant$sc10, _sceneData$scene2, _sceneData$scene3;
|
|
1206
1248
|
console.log("\uD83D\uDDD1\uFE0F deleteComponent(): Deleting component ".concat(componentId));
|
|
1207
|
-
var threeScene = (_this$centralPlant$
|
|
1208
|
-
var sceneData = (_this$centralPlant$
|
|
1249
|
+
var threeScene = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.scene;
|
|
1250
|
+
var sceneData = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.currentSceneData;
|
|
1209
1251
|
|
|
1210
1252
|
// Step 1: Resolve the actual Three.js UUID from componentId.
|
|
1211
1253
|
// The UI emits object.name (e.g. "Pump (PUMP-1)") as the selection ID, but
|
|
@@ -1249,8 +1291,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1249
1291
|
// the component (e.g., dynamically added but not yet synced to sceneData)
|
|
1250
1292
|
if (connectorIds.size === 0 && threeScene) {
|
|
1251
1293
|
threeScene.traverse(function (obj) {
|
|
1252
|
-
var _obj$
|
|
1253
|
-
if ((obj.uuid === resolvedUuid || ((_obj$
|
|
1294
|
+
var _obj$userData3, _obj$userData4;
|
|
1295
|
+
if ((obj.uuid === resolvedUuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === resolvedUuid) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'component') {
|
|
1254
1296
|
obj.children.forEach(function (child) {
|
|
1255
1297
|
var _child$userData3;
|
|
1256
1298
|
if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'connector') {
|
|
@@ -1300,12 +1342,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1300
1342
|
if (connectorIds.size > 0 && threeScene) {
|
|
1301
1343
|
var objectsToRemove = [];
|
|
1302
1344
|
threeScene.traverse(function (obj) {
|
|
1303
|
-
var _obj$uuid, _obj$
|
|
1304
|
-
var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$
|
|
1305
|
-
var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$
|
|
1345
|
+
var _obj$uuid, _obj$userData5, _obj$uuid2, _obj$userData6;
|
|
1346
|
+
var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
|
|
1347
|
+
var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.isDeclared) !== true;
|
|
1306
1348
|
if (isComputedSegment || isComputedGateway) {
|
|
1307
|
-
var _obj$
|
|
1308
|
-
if (connectorIds.has((_obj$
|
|
1349
|
+
var _obj$userData7, _obj$userData8;
|
|
1350
|
+
if (connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathFrom) || connectorIds.has((_obj$userData8 = obj.userData) === null || _obj$userData8 === void 0 ? void 0 : _obj$userData8.pathTo)) {
|
|
1309
1351
|
objectsToRemove.push(obj);
|
|
1310
1352
|
}
|
|
1311
1353
|
}
|
|
@@ -98,7 +98,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
98
98
|
this.centralPlant.attachToComponent();
|
|
99
99
|
|
|
100
100
|
// Sync our managers tracking object after attachment
|
|
101
|
-
managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
|
|
101
|
+
managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
|
|
102
102
|
managerKeys.forEach(function (key) {
|
|
103
103
|
if (_this2[key]) {
|
|
104
104
|
_this2.managers[key] = _this2[key];
|
|
@@ -286,7 +286,21 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
// Update camera aspect ratio
|
|
289
|
-
this.camera.
|
|
289
|
+
if (this.camera.isPerspectiveCamera) {
|
|
290
|
+
this.camera.aspect = width / height;
|
|
291
|
+
} else if (this.camera.isOrthographicCamera) {
|
|
292
|
+
var _this$camera$userData;
|
|
293
|
+
var aspect = width / height;
|
|
294
|
+
var extents = (_this$camera$userData = this.camera.userData) === null || _this$camera$userData === void 0 ? void 0 : _this$camera$userData._orthoHalfExtents;
|
|
295
|
+
if (extents) {
|
|
296
|
+
var frustumHalfH = Math.max(extents.halfH, extents.halfW / aspect);
|
|
297
|
+
var frustumHalfW = frustumHalfH * aspect;
|
|
298
|
+
this.camera.left = -frustumHalfW;
|
|
299
|
+
this.camera.right = frustumHalfW;
|
|
300
|
+
this.camera.top = frustumHalfH;
|
|
301
|
+
this.camera.bottom = -frustumHalfH;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
290
304
|
this.camera.updateProjectionMatrix();
|
|
291
305
|
|
|
292
306
|
// Update renderer size (updateStyle=true to sync canvas CSS)
|
|
@@ -413,6 +427,45 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
413
427
|
if (_this4.componentTooltipManager) {
|
|
414
428
|
_this4.componentTooltipManager.toggleIODeviceBinaryState(ioDeviceObject);
|
|
415
429
|
}
|
|
430
|
+
},
|
|
431
|
+
onIODeviceDrag: function onIODeviceDrag(ioDeviceObject, signedDelta, isStart) {
|
|
432
|
+
if (isStart) {
|
|
433
|
+
var _ioDeviceObject$userD, _this4$managers$ioAni, _this4$managers, _this4$managers2;
|
|
434
|
+
// Resolve parentUuid by walking up to the host component.
|
|
435
|
+
// Use userData.originalUuid (the custom componentId) because that
|
|
436
|
+
// is what IoAnimationManager uses as the map key — NOT obj.uuid.
|
|
437
|
+
var parentUuid = null;
|
|
438
|
+
var obj = ioDeviceObject.parent;
|
|
439
|
+
while (obj) {
|
|
440
|
+
var _obj$userData;
|
|
441
|
+
if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
|
|
442
|
+
parentUuid = obj.userData.originalUuid || obj.uuid;
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
obj = obj.parent;
|
|
446
|
+
}
|
|
447
|
+
var attachmentId = (_ioDeviceObject$userD = ioDeviceObject.userData) === null || _ioDeviceObject$userD === void 0 ? void 0 : _ioDeviceObject$userD.attachmentId;
|
|
448
|
+
// When animated meshes are available, outline ONLY them so their
|
|
449
|
+
// silhouette is isolated and the outline ring is visible around
|
|
450
|
+
// them specifically (not swallowed by the larger device body).
|
|
451
|
+
// Fall back to the whole device group when none are registered.
|
|
452
|
+
var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioAni = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioAnimationManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioAni !== void 0 ? _this4$managers$ioAni : [] : [];
|
|
453
|
+
var targets = animatedMeshes.length > 0 ? animatedMeshes : [ioDeviceObject];
|
|
454
|
+
(_this4$managers2 = _this4.managers) === null || _this4$managers2 === void 0 || (_this4$managers2 = _this4$managers2.ioOutlineManager) === null || _this4$managers2 === void 0 || _this4$managers2.setTargets(targets);
|
|
455
|
+
}
|
|
456
|
+
if (!_this4.componentTooltipManager) return;
|
|
457
|
+
if (isStart) {
|
|
458
|
+
_this4.componentTooltipManager.startIODeviceDrag(ioDeviceObject);
|
|
459
|
+
} else {
|
|
460
|
+
_this4.componentTooltipManager.updateIODeviceDrag(signedDelta);
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
onIODeviceDragEnd: function onIODeviceDragEnd(ioDeviceObject) {
|
|
464
|
+
var _this4$managers3;
|
|
465
|
+
(_this4$managers3 = _this4.managers) === null || _this4$managers3 === void 0 || (_this4$managers3 = _this4$managers3.ioOutlineManager) === null || _this4$managers3 === void 0 || _this4$managers3.setTargets([]);
|
|
466
|
+
if (_this4.componentTooltipManager) {
|
|
467
|
+
_this4.componentTooltipManager.endIODeviceDrag();
|
|
468
|
+
}
|
|
416
469
|
}
|
|
417
470
|
});
|
|
418
471
|
|
|
@@ -70,7 +70,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
70
70
|
}
|
|
71
71
|
if (entries.length) {
|
|
72
72
|
this._entries.set(key, entries);
|
|
73
|
-
console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ")"))
|
|
73
|
+
console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
|
|
74
|
+
return e.anim.stateVariable;
|
|
75
|
+
}).join(', ')));
|
|
76
|
+
} else {
|
|
77
|
+
console.warn("[IoAnimationManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 animationConfig had ").concat(anims.length, " entries but none matched a mesh"));
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -215,6 +219,26 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
215
219
|
return dps;
|
|
216
220
|
}
|
|
217
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Return the Three.js mesh objects that are animated for a given attachment.
|
|
224
|
+
* Used by IoOutlineManager to include animated meshes in the outline.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} parentUuid
|
|
227
|
+
* @param {string} attachmentId
|
|
228
|
+
* @returns {THREE.Object3D[]}
|
|
229
|
+
*/
|
|
230
|
+
}, {
|
|
231
|
+
key: "getAnimatedMeshes",
|
|
232
|
+
value: function getAnimatedMeshes(parentUuid, attachmentId) {
|
|
233
|
+
var key = this._key(parentUuid, attachmentId);
|
|
234
|
+
var entries = this._entries.get(key);
|
|
235
|
+
if (!(entries !== null && entries !== void 0 && entries.length)) return [];
|
|
236
|
+
// Deduplicate — multiple animations can target the same mesh
|
|
237
|
+
return _toConsumableArray(new Set(entries.map(function (e) {
|
|
238
|
+
return e.mesh;
|
|
239
|
+
})));
|
|
240
|
+
}
|
|
241
|
+
|
|
218
242
|
/**
|
|
219
243
|
* Remove all animation entries associated with a given host component.
|
|
220
244
|
* Call when a component is removed from the scene.
|
|
@@ -446,7 +470,10 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
446
470
|
value: function _applyTranslation(mesh, origPos, transform) {
|
|
447
471
|
var _transform$x, _transform$y, _transform$z;
|
|
448
472
|
if (!transform) return;
|
|
449
|
-
|
|
473
|
+
// X and Y are negated to match the sign convention used in the AnimateDevicesDialog
|
|
474
|
+
// preview (_syncViewerTransform negates x and y before calling setMeshPreviewOffset).
|
|
475
|
+
// Z is added directly (no negation) — matching the dialog's z handling.
|
|
476
|
+
mesh.position.set(origPos.x - ((_transform$x = transform.x) !== null && _transform$x !== void 0 ? _transform$x : 0), origPos.y - ((_transform$y = transform.y) !== null && _transform$y !== void 0 ? _transform$y : 0), origPos.z + ((_transform$z = transform.z) !== null && _transform$z !== void 0 ? _transform$z : 0));
|
|
450
477
|
}
|
|
451
478
|
|
|
452
479
|
/**
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { inherits as _inherits, createClass as _createClass, superPropGet as _superPropGet, createForOfIteratorHelper as _createForOfIteratorHelper, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { BaseDisposable } from '../../core/baseDisposable.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Three.js layer reserved exclusively for the outline mask pass.
|
|
7
|
+
* Layer 20 is high enough to be well clear of typical app layer usage.
|
|
8
|
+
*/
|
|
9
|
+
var OUTLINE_LAYER = 20;
|
|
10
|
+
|
|
11
|
+
// ── Shaders ──────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
var VERT_SHADER = /* glsl */"\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position.xy, 0.0, 1.0);\n}\n";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Screen-space outline shader.
|
|
17
|
+
*
|
|
18
|
+
* Samples in a circular kernel to find the nearest filled (silhouette) pixel.
|
|
19
|
+
* Applies a smooth alpha falloff at the outer edge using smoothstep so the
|
|
20
|
+
* outline fades cleanly rather than cutting off hard.
|
|
21
|
+
*
|
|
22
|
+
* RADIUS — maximum distance in pixels from the silhouette edge to draw.
|
|
23
|
+
* SAMPLES — kernel half-extent; must be an integer ≥ ceil(RADIUS).
|
|
24
|
+
*/
|
|
25
|
+
var FRAG_SHADER = /* glsl */"\nuniform sampler2D tMask;\nuniform vec2 uInvSize;\nvarying vec2 vUv;\n\nconst float RADIUS = 3.0; // total outline width in pixels\nconst int SAMPLES = 4; // kernel half-extent (\u2265 ceil(RADIUS))\n\nvoid main() {\n float center = texture2D(tMask, vUv).r;\n\n // Pixels inside the silhouette: the main render already drew them.\n if (center > 0.5) discard;\n\n // Find the closest filled neighbour within the circular kernel.\n float minDist = 99.0;\n for (int x = -SAMPLES; x <= SAMPLES; x++) {\n for (int y = -SAMPLES; y <= SAMPLES; y++) {\n float dist = length(vec2(float(x), float(y)));\n if (dist > RADIUS + 1.0) continue; // skip corners outside circle\n vec2 offset = vec2(float(x), float(y)) * uInvSize;\n if (texture2D(tMask, vUv + offset).r > 0.5) {\n minDist = min(minDist, dist);\n }\n }\n }\n\n if (minDist > RADIUS + 0.5) discard;\n\n // Smooth alpha: full opacity near the edge, fades to 0 at RADIUS pixels out.\n float alpha = 1.0 - smoothstep(RADIUS - 1.0, RADIUS + 0.5, minDist);\n gl_FragColor = vec4(1.0, 1.0, 1.0, alpha);\n}\n";
|
|
26
|
+
var IoOutlineManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
27
|
+
function IoOutlineManager(sceneViewer) {
|
|
28
|
+
var _this;
|
|
29
|
+
_classCallCheck(this, IoOutlineManager);
|
|
30
|
+
_this = _callSuper(this, IoOutlineManager);
|
|
31
|
+
_this.sceneViewer = sceneViewer;
|
|
32
|
+
_this._maskTarget = null;
|
|
33
|
+
_this._maskMat = null;
|
|
34
|
+
_this._overlayScene = null;
|
|
35
|
+
_this._overlayCamera = null;
|
|
36
|
+
_this._overlayMat = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Meshes that have been moved onto OUTLINE_LAYER, stored with their
|
|
40
|
+
* original layers.mask so they can be restored on clearance.
|
|
41
|
+
* @type {{ mesh: THREE.Mesh, originalMask: number }[]}
|
|
42
|
+
*/
|
|
43
|
+
_this._layeredMeshes = [];
|
|
44
|
+
|
|
45
|
+
/** @type {boolean} Whether an outline is currently active. */
|
|
46
|
+
_this.isActive = false;
|
|
47
|
+
return _this;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
51
|
+
// PUBLIC API
|
|
52
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Set the objects to outline. Pass an empty array (or call with no args)
|
|
56
|
+
* to remove the outline.
|
|
57
|
+
* @param {THREE.Object3D[]} objects
|
|
58
|
+
*/
|
|
59
|
+
_inherits(IoOutlineManager, _BaseDisposable);
|
|
60
|
+
return _createClass(IoOutlineManager, [{
|
|
61
|
+
key: "setTargets",
|
|
62
|
+
value: function setTargets() {
|
|
63
|
+
var _this2 = this;
|
|
64
|
+
var objects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
65
|
+
this._clearLayeredMeshes();
|
|
66
|
+
if (!objects.length) {
|
|
67
|
+
this.isActive = false;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
objects.forEach(function (obj) {
|
|
71
|
+
obj.traverse(function (child) {
|
|
72
|
+
if (!child.isMesh) return;
|
|
73
|
+
var originalMask = child.layers.mask;
|
|
74
|
+
child.layers.enable(OUTLINE_LAYER);
|
|
75
|
+
_this2._layeredMeshes.push({
|
|
76
|
+
mesh: child,
|
|
77
|
+
originalMask: originalMask
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
this.isActive = this._layeredMeshes.length > 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Render one frame: main scene → mask pass → outline composite.
|
|
86
|
+
* Must be called instead of renderer.render() when isActive is true.
|
|
87
|
+
*/
|
|
88
|
+
}, {
|
|
89
|
+
key: "render",
|
|
90
|
+
value: function render() {
|
|
91
|
+
var sv = this.sceneViewer;
|
|
92
|
+
var renderer = sv.renderer,
|
|
93
|
+
scene = sv.scene,
|
|
94
|
+
camera = sv.camera;
|
|
95
|
+
if (!renderer || !scene || !camera) return;
|
|
96
|
+
|
|
97
|
+
// ── Step 1: Normal scene render — sky, transparency, all unaffected ──
|
|
98
|
+
renderer.render(scene, camera);
|
|
99
|
+
if (!this.isActive) return;
|
|
100
|
+
this._ensureResources();
|
|
101
|
+
if (!this._maskTarget) return;
|
|
102
|
+
|
|
103
|
+
// Stash renderer / scene / camera state we are about to mutate
|
|
104
|
+
var prevBg = scene.background;
|
|
105
|
+
var prevOverride = scene.overrideMaterial;
|
|
106
|
+
var prevLayerMask = camera.layers.mask;
|
|
107
|
+
var prevAutoClear = renderer.autoClear;
|
|
108
|
+
var prevClearClr = renderer.getClearColor(new THREE.Color());
|
|
109
|
+
var prevClearA = renderer.getClearAlpha();
|
|
110
|
+
|
|
111
|
+
// ── Step 2: Render the device silhouette into the private mask target ──
|
|
112
|
+
scene.background = null; // transparent clear
|
|
113
|
+
scene.overrideMaterial = this._maskMat; // flat white for all geometry
|
|
114
|
+
camera.layers.set(OUTLINE_LAYER); // only see the device meshes
|
|
115
|
+
renderer.setClearColor(0x000000, 0);
|
|
116
|
+
renderer.autoClear = true;
|
|
117
|
+
renderer.setRenderTarget(this._maskTarget);
|
|
118
|
+
renderer.render(scene, camera);
|
|
119
|
+
renderer.setRenderTarget(null);
|
|
120
|
+
|
|
121
|
+
// Restore mutated state
|
|
122
|
+
scene.background = prevBg;
|
|
123
|
+
scene.overrideMaterial = prevOverride;
|
|
124
|
+
camera.layers.mask = prevLayerMask;
|
|
125
|
+
renderer.setClearColor(prevClearClr, prevClearA);
|
|
126
|
+
|
|
127
|
+
// ── Step 3: Composite the outline on top without clearing the framebuffer
|
|
128
|
+
renderer.autoClear = false;
|
|
129
|
+
renderer.render(this._overlayScene, this._overlayCamera);
|
|
130
|
+
renderer.autoClear = prevAutoClear;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Call when the canvas is resized to keep the mask target and shader in sync.
|
|
135
|
+
* @param {number} width
|
|
136
|
+
* @param {number} height
|
|
137
|
+
*/
|
|
138
|
+
}, {
|
|
139
|
+
key: "setSize",
|
|
140
|
+
value: function setSize(width, height) {
|
|
141
|
+
if (this._maskTarget) this._maskTarget.setSize(width, height);
|
|
142
|
+
if (this._overlayMat) {
|
|
143
|
+
this._overlayMat.uniforms.uInvSize.value.set(1 / width, 1 / height);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}, {
|
|
147
|
+
key: "dispose",
|
|
148
|
+
value: function dispose() {
|
|
149
|
+
this._clearLayeredMeshes();
|
|
150
|
+
if (this._maskTarget) {
|
|
151
|
+
this._maskTarget.dispose();
|
|
152
|
+
this._maskTarget = null;
|
|
153
|
+
}
|
|
154
|
+
if (this._maskMat) {
|
|
155
|
+
this._maskMat.dispose();
|
|
156
|
+
this._maskMat = null;
|
|
157
|
+
}
|
|
158
|
+
if (this._overlayMat) {
|
|
159
|
+
this._overlayMat.dispose();
|
|
160
|
+
this._overlayMat = null;
|
|
161
|
+
}
|
|
162
|
+
_superPropGet(IoOutlineManager, "dispose", this, 3)([]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
166
|
+
// PRIVATE
|
|
167
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
/** Lazy-initialise GPU resources on first use. */
|
|
170
|
+
}, {
|
|
171
|
+
key: "_ensureResources",
|
|
172
|
+
value: function _ensureResources() {
|
|
173
|
+
if (this._maskTarget) return;
|
|
174
|
+
var renderer = this.sceneViewer.renderer;
|
|
175
|
+
if (!renderer) return;
|
|
176
|
+
var size = renderer.getSize(new THREE.Vector2());
|
|
177
|
+
var w = size.x;
|
|
178
|
+
var h = size.y;
|
|
179
|
+
|
|
180
|
+
// Private render target — receives the flat white device silhouette
|
|
181
|
+
this._maskTarget = new THREE.WebGLRenderTarget(w, h);
|
|
182
|
+
|
|
183
|
+
// Flat white material used during the mask pass
|
|
184
|
+
this._maskMat = new THREE.MeshBasicMaterial({
|
|
185
|
+
color: 0xffffff
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Screen-space overlay: reads the mask and draws only the outline pixels
|
|
189
|
+
this._overlayMat = new THREE.ShaderMaterial({
|
|
190
|
+
uniforms: {
|
|
191
|
+
tMask: {
|
|
192
|
+
value: this._maskTarget.texture
|
|
193
|
+
},
|
|
194
|
+
uInvSize: {
|
|
195
|
+
value: new THREE.Vector2(1 / w, 1 / h)
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
vertexShader: VERT_SHADER,
|
|
199
|
+
fragmentShader: FRAG_SHADER,
|
|
200
|
+
transparent: true,
|
|
201
|
+
depthTest: false,
|
|
202
|
+
depthWrite: false
|
|
203
|
+
});
|
|
204
|
+
var quad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), this._overlayMat);
|
|
205
|
+
quad.frustumCulled = false;
|
|
206
|
+
this._overlayScene = new THREE.Scene();
|
|
207
|
+
this._overlayScene.add(quad);
|
|
208
|
+
this._overlayCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Remove OUTLINE_LAYER from all tracked meshes and clear the list. */
|
|
212
|
+
}, {
|
|
213
|
+
key: "_clearLayeredMeshes",
|
|
214
|
+
value: function _clearLayeredMeshes() {
|
|
215
|
+
var _iterator = _createForOfIteratorHelper(this._layeredMeshes),
|
|
216
|
+
_step;
|
|
217
|
+
try {
|
|
218
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
219
|
+
var _step$value = _step.value,
|
|
220
|
+
mesh = _step$value.mesh,
|
|
221
|
+
originalMask = _step$value.originalMask;
|
|
222
|
+
mesh.layers.mask = originalMask;
|
|
223
|
+
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
_iterator.e(err);
|
|
226
|
+
} finally {
|
|
227
|
+
_iterator.f();
|
|
228
|
+
}
|
|
229
|
+
this._layeredMeshes = [];
|
|
230
|
+
}
|
|
231
|
+
}]);
|
|
232
|
+
}(BaseDisposable);
|
|
233
|
+
|
|
234
|
+
export { IoOutlineManager };
|