@2112-lab/central-plant 0.3.28 → 0.3.29
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 +47 -47
- package/dist/cjs/src/core/centralPlant.js +3 -3
- package/dist/cjs/src/core/centralPlantInternals.js +7 -7
- package/dist/cjs/src/core/sceneViewer.js +4 -4
- package/dist/cjs/src/index.js +2 -2
- package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +571 -0
- package/dist/cjs/src/managers/scene/componentTooltipManager.js +8 -8
- package/dist/cjs/src/managers/scene/modelManager.js +8 -8
- package/dist/esm/src/core/centralPlant.js +3 -3
- package/dist/esm/src/core/centralPlantInternals.js +7 -7
- package/dist/esm/src/core/sceneViewer.js +4 -4
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +547 -0
- package/dist/esm/src/managers/scene/componentTooltipManager.js +8 -8
- package/dist/esm/src/managers/scene/modelManager.js +8 -8
- package/package.json +1 -1
|
@@ -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.29
|
|
35
35
|
* @updated 2025-10-22
|
|
36
36
|
*
|
|
37
37
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -1109,8 +1109,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
|
-
// 2. Apply io-
|
|
1113
|
-
(_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.
|
|
1112
|
+
// 2. Apply io-behavior changes
|
|
1113
|
+
(_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioBehaviorManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, stateId, value, parentUuid);
|
|
1114
1114
|
|
|
1115
1115
|
// 3. Emit event for host apps that don't use the state adapter (e.g. cp3d-viewer)
|
|
1116
1116
|
(_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || _this$sceneViewer6.emit('io-device-state-changed', {
|
|
@@ -22,7 +22,7 @@ import { ComponentDragManager } from '../managers/controls/componentDragManager.
|
|
|
22
22
|
import { SceneTooltipsManager } from '../managers/scene/sceneTooltipsManager.js';
|
|
23
23
|
import { ComponentTooltipManager } from '../managers/scene/componentTooltipManager.js';
|
|
24
24
|
import { Viewport2DManager } from '../managers/scene/viewport2DManager.js';
|
|
25
|
-
import {
|
|
25
|
+
import { IoBehaviorManager } from '../managers/behaviors/IoBehaviorManager.js';
|
|
26
26
|
import { IoOutlineManager } from '../managers/behaviors/IoOutlineManager.js';
|
|
27
27
|
import { generateUuidFromName, getHardcodedUuid, findObjectByHardcodedUuid, generateUniqueComponentId } from '../utils/nameUtils.js';
|
|
28
28
|
import { attachIODevicesToComponent } from '../utils/ioDeviceUtils.js';
|
|
@@ -149,7 +149,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
149
149
|
this.centralPlant.managers.cameraControlsManager = new CameraControlsManager(this.centralPlant.sceneViewer);
|
|
150
150
|
this.centralPlant.managers.componentDragManager = new ComponentDragManager(this.centralPlant.sceneViewer);
|
|
151
151
|
this.centralPlant.managers.viewport2DManager = new Viewport2DManager(this.centralPlant.sceneViewer);
|
|
152
|
-
this.centralPlant.managers.
|
|
152
|
+
this.centralPlant.managers.ioBehaviorManager = new IoBehaviorManager(this.centralPlant.sceneViewer);
|
|
153
153
|
this.centralPlant.managers.ioOutlineManager = new IoOutlineManager(this.centralPlant.sceneViewer);
|
|
154
154
|
|
|
155
155
|
// All managers are now stored in the managers collection and will be attached via attachToComponent()
|
|
@@ -1128,23 +1128,23 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1128
1128
|
var _this$centralPlant$sc8;
|
|
1129
1129
|
attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
|
|
1130
1130
|
|
|
1131
|
-
// Register
|
|
1132
|
-
var
|
|
1133
|
-
if (
|
|
1131
|
+
// Register behavior configs so IoBehaviorManager can respond to state changes
|
|
1132
|
+
var ioBehavMgr = (_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.ioBehaviorManager;
|
|
1133
|
+
if (ioBehavMgr) {
|
|
1134
1134
|
var _loop = function _loop() {
|
|
1135
1135
|
var _modelPreloader$compo;
|
|
1136
1136
|
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
1137
1137
|
attachmentId = _Object$entries$_i[0],
|
|
1138
1138
|
attachment = _Object$entries$_i[1];
|
|
1139
1139
|
var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
|
|
1140
|
-
if (!(deviceData !== null && deviceData !== void 0 && deviceData.
|
|
1140
|
+
if (!(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return 1; // continue
|
|
1141
1141
|
var deviceRoot = null;
|
|
1142
1142
|
componentModel.traverse(function (obj) {
|
|
1143
1143
|
var _obj$userData2;
|
|
1144
1144
|
if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
|
|
1145
1145
|
});
|
|
1146
1146
|
if (deviceRoot) {
|
|
1147
|
-
|
|
1147
|
+
ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, componentId);
|
|
1148
1148
|
}
|
|
1149
1149
|
};
|
|
1150
1150
|
for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
|
|
@@ -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', '
|
|
101
|
+
managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'ioBehaviorManager', '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];
|
|
@@ -430,10 +430,10 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
430
430
|
},
|
|
431
431
|
onIODeviceDrag: function onIODeviceDrag(ioDeviceObject, signedDelta, isStart) {
|
|
432
432
|
if (isStart) {
|
|
433
|
-
var _ioDeviceObject$userD, _this4$managers$
|
|
433
|
+
var _ioDeviceObject$userD, _this4$managers$ioBeh, _this4$managers, _this4$managers2;
|
|
434
434
|
// Resolve parentUuid by walking up to the host component.
|
|
435
435
|
// Use userData.originalUuid (the custom componentId) because that
|
|
436
|
-
// is what
|
|
436
|
+
// is what IoBehaviorManager uses as the map key — NOT obj.uuid.
|
|
437
437
|
var parentUuid = null;
|
|
438
438
|
var obj = ioDeviceObject.parent;
|
|
439
439
|
while (obj) {
|
|
@@ -449,7 +449,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
449
449
|
// silhouette is isolated and the outline ring is visible around
|
|
450
450
|
// them specifically (not swallowed by the larger device body).
|
|
451
451
|
// Fall back to the whole device group when none are registered.
|
|
452
|
-
var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$
|
|
452
|
+
var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioBeh = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioBehaviorManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioBeh !== void 0 ? _this4$managers$ioBeh : [] : [];
|
|
453
453
|
var targets = animatedMeshes.length > 0 ? animatedMeshes : [ioDeviceObject];
|
|
454
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
455
|
}
|
package/dist/esm/src/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export { SceneExportManager } from './managers/scene/sceneExportManager.js';
|
|
|
9
9
|
export { SceneTooltipsManager } from './managers/scene/sceneTooltipsManager.js';
|
|
10
10
|
export { ComponentTooltipManager } from './managers/scene/componentTooltipManager.js';
|
|
11
11
|
export { SceneHierarchyManager } from './managers/scene/sceneHierarchyManager.js';
|
|
12
|
-
export {
|
|
12
|
+
export { IoBehaviorManager } from './managers/behaviors/IoBehaviorManager.js';
|
|
13
13
|
export { ComponentManager } from './managers/components/componentManager.js';
|
|
14
14
|
export { AnimationManager } from './managers/scene/animationManager.js';
|
|
15
15
|
export { PathfindingManager } from './managers/pathfinding/pathfindingManager.js';
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray, superPropGet as _superPropGet, 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
|
+
var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
6
|
+
function IoBehaviorManager(sceneViewer) {
|
|
7
|
+
var _this;
|
|
8
|
+
_classCallCheck(this, IoBehaviorManager);
|
|
9
|
+
_this = _callSuper(this, IoBehaviorManager);
|
|
10
|
+
_this.sceneViewer = sceneViewer;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Map: `${parentUuid}::${attachmentId}` → Array<{
|
|
14
|
+
* anim: Object,
|
|
15
|
+
* mesh: THREE.Object3D,
|
|
16
|
+
* origPos: THREE.Vector3,
|
|
17
|
+
* origRot: THREE.Euler
|
|
18
|
+
* }>
|
|
19
|
+
*/
|
|
20
|
+
_this._entries = new Map();
|
|
21
|
+
return _this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
25
|
+
// PUBLIC API
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register animation entries for one attached I/O device.
|
|
30
|
+
* Should be called after the device GLB has been merged into the scene
|
|
31
|
+
* so that mesh references are live.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} attachmentId - The attachment key (e.g. 'attch-switch-01')
|
|
34
|
+
* @param {Object|Array} behaviorConfig - Serialized config; either the full object
|
|
35
|
+
* { behaviors: [...] } or a plain array of behavior entries.
|
|
36
|
+
* @param {THREE.Object3D} deviceModelRoot - The device's root Object3D as added to the scene
|
|
37
|
+
* @param {string} parentUuid - UUID of the host component Object3D
|
|
38
|
+
*/
|
|
39
|
+
_inherits(IoBehaviorManager, _BaseDisposable);
|
|
40
|
+
return _createClass(IoBehaviorManager, [{
|
|
41
|
+
key: "loadBehaviors",
|
|
42
|
+
value: function loadBehaviors(attachmentId, behaviorConfig, deviceModelRoot, parentUuid) {
|
|
43
|
+
var _behaviorConfig$behav;
|
|
44
|
+
if (!behaviorConfig || !deviceModelRoot) return;
|
|
45
|
+
var anims = Array.isArray(behaviorConfig) ? behaviorConfig : (_behaviorConfig$behav = behaviorConfig.behaviors) !== null && _behaviorConfig$behav !== void 0 ? _behaviorConfig$behav : [];
|
|
46
|
+
if (!anims.length) return;
|
|
47
|
+
var key = this._key(parentUuid, attachmentId);
|
|
48
|
+
var entries = [];
|
|
49
|
+
var _iterator = _createForOfIteratorHelper(anims),
|
|
50
|
+
_step;
|
|
51
|
+
try {
|
|
52
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
53
|
+
var anim = _step.value;
|
|
54
|
+
var mesh = this._resolveMesh(anim, deviceModelRoot);
|
|
55
|
+
if (!mesh) {
|
|
56
|
+
console.warn("[IoBehaviorManager] Could not find mesh for animation \"".concat(anim.name || anim.stateVariable, "\" (uuid: ").concat(anim.meshUuid, ", name: \"").concat(anim.meshName, "\")"));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
entries.push({
|
|
60
|
+
anim: anim,
|
|
61
|
+
mesh: mesh,
|
|
62
|
+
origPos: mesh.position.clone(),
|
|
63
|
+
origRot: mesh.rotation.clone()
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
_iterator.e(err);
|
|
68
|
+
} finally {
|
|
69
|
+
_iterator.f();
|
|
70
|
+
}
|
|
71
|
+
if (entries.length) {
|
|
72
|
+
this._entries.set(key, entries);
|
|
73
|
+
console.log("[IoBehaviorManager] 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("[IoBehaviorManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 behaviorConfig had ").concat(anims.length, " entries but none matched a mesh"));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Apply animations triggered by an IO device state change.
|
|
83
|
+
* Should be called in parallel with BehaviorManager.triggerState().
|
|
84
|
+
*
|
|
85
|
+
* @param {string} attachmentId - Raw attachment key (not scoped)
|
|
86
|
+
* @param {string} dataPointId - The data point / state variable id that changed
|
|
87
|
+
* @param {*} value - New state value
|
|
88
|
+
* @param {string} parentUuid - UUID of the host component
|
|
89
|
+
*/
|
|
90
|
+
}, {
|
|
91
|
+
key: "triggerState",
|
|
92
|
+
value: function triggerState(attachmentId, dataPointId, value, parentUuid) {
|
|
93
|
+
var key = this._key(parentUuid, attachmentId);
|
|
94
|
+
var entries = this._entries.get(key);
|
|
95
|
+
if (!(entries !== null && entries !== void 0 && entries.length)) return;
|
|
96
|
+
var _iterator2 = _createForOfIteratorHelper(entries),
|
|
97
|
+
_step2;
|
|
98
|
+
try {
|
|
99
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
100
|
+
var entry = _step2.value;
|
|
101
|
+
if (entry.anim.stateVariable !== dataPointId) continue;
|
|
102
|
+
this._applyAnimation(entry, value);
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
_iterator2.e(err);
|
|
106
|
+
} finally {
|
|
107
|
+
_iterator2.f();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Return tooltip-compatible data point definitions derived from the loaded
|
|
113
|
+
* animation entries for a given attachment. Used by componentTooltipManager
|
|
114
|
+
* to replace the static ioConfig.states[] snapshot with the richer animation
|
|
115
|
+
* state definitions created in the Animate window.
|
|
116
|
+
*
|
|
117
|
+
* One dp object is emitted per unique stateVariable; multiple mesh entries
|
|
118
|
+
* that share the same stateVariable are collapsed into one.
|
|
119
|
+
*
|
|
120
|
+
* Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
|
|
121
|
+
* {
|
|
122
|
+
* id: string, // stateVariable name
|
|
123
|
+
* name: string, // human-readable label (anim.name or stateVariable)
|
|
124
|
+
* stateType: string, // normalised to 'binary' | 'enum' | 'number'
|
|
125
|
+
* stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
|
|
126
|
+
* defaultValue: any, // sensible default for the stateType
|
|
127
|
+
* direction: 'input' // all animation-driven states are interactive
|
|
128
|
+
* }
|
|
129
|
+
*
|
|
130
|
+
* @param {string} parentUuid
|
|
131
|
+
* @param {string} attachmentId
|
|
132
|
+
* @returns {Object[]} Array of dp objects, or empty array if none loaded.
|
|
133
|
+
*/
|
|
134
|
+
}, {
|
|
135
|
+
key: "getAnimationDataPoints",
|
|
136
|
+
value: function getAnimationDataPoints(parentUuid, attachmentId) {
|
|
137
|
+
var key = this._key(parentUuid, attachmentId);
|
|
138
|
+
var entries = this._entries.get(key);
|
|
139
|
+
if (!(entries !== null && entries !== void 0 && entries.length)) return [];
|
|
140
|
+
|
|
141
|
+
// Collapse multiple mesh entries that share the same stateVariable
|
|
142
|
+
var seen = new Map(); // stateVariable → anim
|
|
143
|
+
var _iterator3 = _createForOfIteratorHelper(entries),
|
|
144
|
+
_step3;
|
|
145
|
+
try {
|
|
146
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
147
|
+
var anim = _step3.value.anim;
|
|
148
|
+
if (!seen.has(anim.stateVariable)) {
|
|
149
|
+
seen.set(anim.stateVariable, anim);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
_iterator3.e(err);
|
|
154
|
+
} finally {
|
|
155
|
+
_iterator3.f();
|
|
156
|
+
}
|
|
157
|
+
var dps = [];
|
|
158
|
+
var _iterator4 = _createForOfIteratorHelper(seen),
|
|
159
|
+
_step4;
|
|
160
|
+
try {
|
|
161
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
162
|
+
var _step4$value = _slicedToArray(_step4.value, 2),
|
|
163
|
+
stateVar = _step4$value[0],
|
|
164
|
+
_anim = _step4$value[1];
|
|
165
|
+
// Normalise stateType from AnimateDevicesDialog variants
|
|
166
|
+
var stateType = void 0;
|
|
167
|
+
var raw = (_anim.stateType || '').toLowerCase();
|
|
168
|
+
if (raw === 'binary' || raw === 'boolean') {
|
|
169
|
+
stateType = 'binary';
|
|
170
|
+
} else if (raw === 'enum') {
|
|
171
|
+
stateType = 'enum';
|
|
172
|
+
} else {
|
|
173
|
+
// 'continuous', 'range', 'number', or anything else → numeric slider
|
|
174
|
+
stateType = 'number';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Derive stateConfig from mappings
|
|
178
|
+
var stateConfig = {};
|
|
179
|
+
var mappingValues = (_anim.mappings || []).map(function (m) {
|
|
180
|
+
return m.stateValue;
|
|
181
|
+
});
|
|
182
|
+
if (stateType === 'enum') {
|
|
183
|
+
stateConfig.options = _toConsumableArray(new Set(mappingValues.map(String)));
|
|
184
|
+
} else if (stateType === 'number') {
|
|
185
|
+
var nums = mappingValues.map(Number).filter(function (n) {
|
|
186
|
+
return !isNaN(n);
|
|
187
|
+
});
|
|
188
|
+
if (nums.length) {
|
|
189
|
+
stateConfig.min = Math.min.apply(Math, _toConsumableArray(nums));
|
|
190
|
+
stateConfig.max = Math.max.apply(Math, _toConsumableArray(nums));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Sensible default values
|
|
195
|
+
var defaultValue = void 0;
|
|
196
|
+
if (stateType === 'binary') {
|
|
197
|
+
defaultValue = 0;
|
|
198
|
+
} else if (stateType === 'enum') {
|
|
199
|
+
var _stateConfig$options$, _stateConfig$options;
|
|
200
|
+
defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
|
|
201
|
+
} else {
|
|
202
|
+
var _stateConfig$min;
|
|
203
|
+
defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
|
|
204
|
+
}
|
|
205
|
+
dps.push({
|
|
206
|
+
id: stateVar,
|
|
207
|
+
name: _anim.name || stateVar,
|
|
208
|
+
stateType: stateType,
|
|
209
|
+
stateConfig: stateConfig,
|
|
210
|
+
defaultValue: defaultValue,
|
|
211
|
+
direction: 'input'
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
} catch (err) {
|
|
215
|
+
_iterator4.e(err);
|
|
216
|
+
} finally {
|
|
217
|
+
_iterator4.f();
|
|
218
|
+
}
|
|
219
|
+
return dps;
|
|
220
|
+
}
|
|
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
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Remove all animation entries associated with a given host component.
|
|
244
|
+
* Call when a component is removed from the scene.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} parentUuid
|
|
247
|
+
*/
|
|
248
|
+
}, {
|
|
249
|
+
key: "unloadForComponent",
|
|
250
|
+
value: function unloadForComponent(parentUuid) {
|
|
251
|
+
var prefix = "".concat(parentUuid, "::");
|
|
252
|
+
var _iterator5 = _createForOfIteratorHelper(this._entries.keys()),
|
|
253
|
+
_step5;
|
|
254
|
+
try {
|
|
255
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
256
|
+
var key = _step5.value;
|
|
257
|
+
if (key.startsWith(prefix)) {
|
|
258
|
+
this._entries.delete(key);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} catch (err) {
|
|
262
|
+
_iterator5.e(err);
|
|
263
|
+
} finally {
|
|
264
|
+
_iterator5.f();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}, {
|
|
268
|
+
key: "dispose",
|
|
269
|
+
value: function dispose() {
|
|
270
|
+
this._entries.clear();
|
|
271
|
+
_superPropGet(IoBehaviorManager, "dispose", this, 3)([]);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
275
|
+
// PRIVATE HELPERS
|
|
276
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
277
|
+
}, {
|
|
278
|
+
key: "_key",
|
|
279
|
+
value: function _key(parentUuid, attachmentId) {
|
|
280
|
+
return "".concat(parentUuid, "::").concat(attachmentId);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Find the mesh inside `root` using UUID first, then name as fallback.
|
|
285
|
+
* GLTFLoader assigns fresh UUIDs on every load, so name is the reliable key.
|
|
286
|
+
* @param {Object} anim
|
|
287
|
+
* @param {THREE.Object3D} root
|
|
288
|
+
* @returns {THREE.Object3D|null}
|
|
289
|
+
*/
|
|
290
|
+
}, {
|
|
291
|
+
key: "_resolveMesh",
|
|
292
|
+
value: function _resolveMesh(anim, root) {
|
|
293
|
+
var found = null;
|
|
294
|
+
root.traverse(function (obj) {
|
|
295
|
+
if (found) return;
|
|
296
|
+
|
|
297
|
+
// Prefer UUID match (works when the device was cloned from a cached instance
|
|
298
|
+
// whose UUIDs were preserved)
|
|
299
|
+
if (anim.meshUuid && obj.uuid === anim.meshUuid) {
|
|
300
|
+
found = obj;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Reliable fallback: mesh name
|
|
305
|
+
if (anim.meshName && obj.name === anim.meshName) {
|
|
306
|
+
found = obj;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
return found;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Resolve which mapping row to use and apply all declared transform types.
|
|
314
|
+
* @param {{ anim, mesh, origPos, origRot }} entry
|
|
315
|
+
* @param {*} value - Current state value
|
|
316
|
+
*/
|
|
317
|
+
}, {
|
|
318
|
+
key: "_applyAnimation",
|
|
319
|
+
value: function _applyAnimation(entry, value) {
|
|
320
|
+
var anim = entry.anim,
|
|
321
|
+
mesh = entry.mesh,
|
|
322
|
+
origPos = entry.origPos,
|
|
323
|
+
origRot = entry.origRot;
|
|
324
|
+
var mapping = this._resolveMapping(anim, value);
|
|
325
|
+
if (!mapping) return;
|
|
326
|
+
var types = anim.transformTypes || [];
|
|
327
|
+
var _iterator6 = _createForOfIteratorHelper(types),
|
|
328
|
+
_step6;
|
|
329
|
+
try {
|
|
330
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
331
|
+
var type = _step6.value;
|
|
332
|
+
if (type === 'translation') {
|
|
333
|
+
this._applyTranslation(mesh, origPos, mapping.transform);
|
|
334
|
+
} else if (type === 'rotation') {
|
|
335
|
+
this._applyRotation(mesh, origPos, origRot, anim, mapping.rotationTransform);
|
|
336
|
+
} else if (type === 'color') {
|
|
337
|
+
this._applyColor(mesh, mapping.colorTransform);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
_iterator6.e(err);
|
|
342
|
+
} finally {
|
|
343
|
+
_iterator6.f();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Find the mapping row that matches `value` for the given animation entry.
|
|
349
|
+
*
|
|
350
|
+
* - binary / enum: find where mapping.stateValue == value (loose equality so
|
|
351
|
+
* JSON-deserialised "true" matches boolean true)
|
|
352
|
+
* - continuous: linear interpolation between ordered anchor points
|
|
353
|
+
*
|
|
354
|
+
* @returns {Object|null} The matched/interpolated mapping row, or null.
|
|
355
|
+
*/
|
|
356
|
+
}, {
|
|
357
|
+
key: "_resolveMapping",
|
|
358
|
+
value: function _resolveMapping(anim, value) {
|
|
359
|
+
var mappings = anim.mappings;
|
|
360
|
+
if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
|
|
361
|
+
|
|
362
|
+
// Normalise stateType variants saved by AnimateDevicesDialog
|
|
363
|
+
// ('boolean' → 'binary', 'range' → 'continuous')
|
|
364
|
+
var raw = (anim.stateType || '').toLowerCase();
|
|
365
|
+
var stateType;
|
|
366
|
+
if (raw === 'binary' || raw === 'boolean') {
|
|
367
|
+
stateType = 'binary';
|
|
368
|
+
} else if (raw === 'continuous' || raw === 'range') {
|
|
369
|
+
stateType = 'continuous';
|
|
370
|
+
} else {
|
|
371
|
+
stateType = raw;
|
|
372
|
+
}
|
|
373
|
+
if (stateType === 'binary' || stateType === 'enum') {
|
|
374
|
+
var _mappings$find;
|
|
375
|
+
// eslint-disable-next-line eqeqeq
|
|
376
|
+
return (_mappings$find = mappings.find(function (m) {
|
|
377
|
+
return m.stateValue == value;
|
|
378
|
+
})) !== null && _mappings$find !== void 0 ? _mappings$find : null;
|
|
379
|
+
}
|
|
380
|
+
if (stateType === 'continuous') {
|
|
381
|
+
var num = Number(value);
|
|
382
|
+
if (isNaN(num)) return null;
|
|
383
|
+
|
|
384
|
+
// Sort anchors by numeric stateValue
|
|
385
|
+
var sorted = _toConsumableArray(mappings).filter(function (m) {
|
|
386
|
+
return m.stateValue != null && !isNaN(Number(m.stateValue));
|
|
387
|
+
}).sort(function (a, b) {
|
|
388
|
+
return Number(a.stateValue) - Number(b.stateValue);
|
|
389
|
+
});
|
|
390
|
+
if (!sorted.length) return null;
|
|
391
|
+
if (sorted.length === 1) return sorted[0];
|
|
392
|
+
|
|
393
|
+
// Clamp to range
|
|
394
|
+
if (num <= Number(sorted[0].stateValue)) return sorted[0];
|
|
395
|
+
if (num >= Number(sorted[sorted.length - 1].stateValue)) return sorted[sorted.length - 1];
|
|
396
|
+
|
|
397
|
+
// Find surrounding anchors and interpolate
|
|
398
|
+
for (var i = 0; i < sorted.length - 1; i++) {
|
|
399
|
+
var lo = sorted[i];
|
|
400
|
+
var hi = sorted[i + 1];
|
|
401
|
+
var loVal = Number(lo.stateValue);
|
|
402
|
+
var hiVal = Number(hi.stateValue);
|
|
403
|
+
if (num >= loVal && num <= hiVal) {
|
|
404
|
+
var t = (num - loVal) / (hiVal - loVal);
|
|
405
|
+
return this._lerpMapping(lo, hi, t);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Linear-interpolate between two mapping rows.
|
|
414
|
+
* @param {Object} lo - lower anchor mapping
|
|
415
|
+
* @param {Object} hi - upper anchor mapping
|
|
416
|
+
* @param {number} t - 0..1
|
|
417
|
+
*/
|
|
418
|
+
}, {
|
|
419
|
+
key: "_lerpMapping",
|
|
420
|
+
value: function _lerpMapping(lo, hi, t) {
|
|
421
|
+
var _lo$transform$x, _lo$transform, _hi$transform$x, _hi$transform, _lo$transform$y, _lo$transform2, _hi$transform$y, _hi$transform2, _lo$transform$z, _lo$transform3, _hi$transform$z, _hi$transform3;
|
|
422
|
+
var lerp = function lerp(a, b) {
|
|
423
|
+
return a + (b - a) * t;
|
|
424
|
+
};
|
|
425
|
+
var transform = {
|
|
426
|
+
x: lerp((_lo$transform$x = (_lo$transform = lo.transform) === null || _lo$transform === void 0 ? void 0 : _lo$transform.x) !== null && _lo$transform$x !== void 0 ? _lo$transform$x : 0, (_hi$transform$x = (_hi$transform = hi.transform) === null || _hi$transform === void 0 ? void 0 : _hi$transform.x) !== null && _hi$transform$x !== void 0 ? _hi$transform$x : 0),
|
|
427
|
+
y: lerp((_lo$transform$y = (_lo$transform2 = lo.transform) === null || _lo$transform2 === void 0 ? void 0 : _lo$transform2.y) !== null && _lo$transform$y !== void 0 ? _lo$transform$y : 0, (_hi$transform$y = (_hi$transform2 = hi.transform) === null || _hi$transform2 === void 0 ? void 0 : _hi$transform2.y) !== null && _hi$transform$y !== void 0 ? _hi$transform$y : 0),
|
|
428
|
+
z: lerp((_lo$transform$z = (_lo$transform3 = lo.transform) === null || _lo$transform3 === void 0 ? void 0 : _lo$transform3.z) !== null && _lo$transform$z !== void 0 ? _lo$transform$z : 0, (_hi$transform$z = (_hi$transform3 = hi.transform) === null || _hi$transform3 === void 0 ? void 0 : _hi$transform3.z) !== null && _hi$transform$z !== void 0 ? _hi$transform$z : 0)
|
|
429
|
+
};
|
|
430
|
+
var rotationTransform = lerp(typeof lo.rotationTransform === 'number' ? lo.rotationTransform : 0, typeof hi.rotationTransform === 'number' ? hi.rotationTransform : 0);
|
|
431
|
+
|
|
432
|
+
// Color interpolation: convert hex → RGB components → lerp → back to hex
|
|
433
|
+
var colorTransform = this._lerpHex(lo.colorTransform, hi.colorTransform, t);
|
|
434
|
+
return {
|
|
435
|
+
stateValue: null,
|
|
436
|
+
transform: transform,
|
|
437
|
+
rotationTransform: rotationTransform,
|
|
438
|
+
colorTransform: colorTransform
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Interpolate between two hex colour strings.
|
|
444
|
+
*/
|
|
445
|
+
}, {
|
|
446
|
+
key: "_lerpHex",
|
|
447
|
+
value: function _lerpHex(hexA, hexB, t) {
|
|
448
|
+
try {
|
|
449
|
+
var ca = new THREE.Color(hexA);
|
|
450
|
+
var cb = new THREE.Color(hexB);
|
|
451
|
+
ca.lerp(cb, t);
|
|
452
|
+
return "#".concat(ca.getHexString());
|
|
453
|
+
} catch (_unused) {
|
|
454
|
+
return hexA !== null && hexA !== void 0 ? hexA : '#ffffff';
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
459
|
+
// TRANSFORM APPLIERS
|
|
460
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Apply a position delta relative to the mesh's original position.
|
|
464
|
+
* @param {THREE.Object3D} mesh
|
|
465
|
+
* @param {THREE.Vector3} origPos
|
|
466
|
+
* @param {{ x, y, z }} transform - Deltas
|
|
467
|
+
*/
|
|
468
|
+
}, {
|
|
469
|
+
key: "_applyTranslation",
|
|
470
|
+
value: function _applyTranslation(mesh, origPos, transform) {
|
|
471
|
+
var _transform$x, _transform$y, _transform$z;
|
|
472
|
+
if (!transform) return;
|
|
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));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Apply a rotation around an arbitrary pivot point in device-local space,
|
|
481
|
+
* optionally also displacing the mesh position to simulate orbital motion.
|
|
482
|
+
*
|
|
483
|
+
* Math (all in device-local space):
|
|
484
|
+
* pivot = rotAxisOffset
|
|
485
|
+
* delta = origPos - pivot
|
|
486
|
+
* newDelta = rotate(delta, angle, axis)
|
|
487
|
+
* newPos = pivot + newDelta
|
|
488
|
+
* newRot[axis] = origRot[axis] + angle
|
|
489
|
+
*
|
|
490
|
+
* @param {THREE.Object3D} mesh
|
|
491
|
+
* @param {THREE.Vector3} origPos
|
|
492
|
+
* @param {THREE.Euler} origRot
|
|
493
|
+
* @param {Object} anim
|
|
494
|
+
* @param {number} angleDeg - Degrees
|
|
495
|
+
*/
|
|
496
|
+
}, {
|
|
497
|
+
key: "_applyRotation",
|
|
498
|
+
value: function _applyRotation(mesh, origPos, origRot, anim, angleDeg) {
|
|
499
|
+
var _anim$rotAxis, _anim$rotAxisOffset, _off$x, _off$y, _off$z;
|
|
500
|
+
var angle = THREE.MathUtils.degToRad(typeof angleDeg === 'number' ? angleDeg : 0);
|
|
501
|
+
var axis = ((_anim$rotAxis = anim.rotAxis) !== null && _anim$rotAxis !== void 0 ? _anim$rotAxis : 'x').toLowerCase();
|
|
502
|
+
var off = (_anim$rotAxisOffset = anim.rotAxisOffset) !== null && _anim$rotAxisOffset !== void 0 ? _anim$rotAxisOffset : {
|
|
503
|
+
x: 0,
|
|
504
|
+
y: 0,
|
|
505
|
+
z: 0
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// Unit vector for the chosen axis
|
|
509
|
+
var axisVec = new THREE.Vector3(axis === 'x' ? 1 : 0, axis === 'y' ? 1 : 0, axis === 'z' ? 1 : 0);
|
|
510
|
+
var pivot = new THREE.Vector3((_off$x = off.x) !== null && _off$x !== void 0 ? _off$x : 0, (_off$y = off.y) !== null && _off$y !== void 0 ? _off$y : 0, (_off$z = off.z) !== null && _off$z !== void 0 ? _off$z : 0);
|
|
511
|
+
var delta = origPos.clone().sub(pivot);
|
|
512
|
+
delta.applyAxisAngle(axisVec, angle);
|
|
513
|
+
mesh.position.copy(pivot).add(delta);
|
|
514
|
+
mesh.rotation[axis] = origRot[axis] + angle;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Apply a colour to all Mesh descendants of `mesh`.
|
|
519
|
+
* Creates a cloned material per mesh on first call to avoid shared-material
|
|
520
|
+
* cross-contamination between different device instances.
|
|
521
|
+
*
|
|
522
|
+
* @param {THREE.Object3D} mesh
|
|
523
|
+
* @param {string} colorHex - e.g. '#ff0000'
|
|
524
|
+
*/
|
|
525
|
+
}, {
|
|
526
|
+
key: "_applyColor",
|
|
527
|
+
value: function _applyColor(mesh, colorHex) {
|
|
528
|
+
if (!colorHex) return;
|
|
529
|
+
mesh.traverse(function (obj) {
|
|
530
|
+
if (!obj.isMesh || !obj.material) return;
|
|
531
|
+
|
|
532
|
+
// Ensure this mesh has its own material instance
|
|
533
|
+
if (!obj.userData._materialCloned) {
|
|
534
|
+
obj.material = obj.material.clone();
|
|
535
|
+
obj.userData._materialCloned = true;
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
obj.material.color.set(colorHex);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
// Material may not have a color property (e.g. MeshDepthMaterial)
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}]);
|
|
545
|
+
}(BaseDisposable);
|
|
546
|
+
|
|
547
|
+
export { IoBehaviorManager };
|