@2112-lab/central-plant 0.3.57 → 0.3.59
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 +1696 -34
- package/dist/cjs/src/core/centralPlant.js +80 -11
- package/dist/cjs/src/core/centralPlantInternals.js +4 -0
- package/dist/cjs/src/core/centralPlantValidator.js +10 -6
- package/dist/cjs/src/core/stateEngine.js +733 -0
- package/dist/cjs/src/core/stateEngineScene.js +97 -0
- package/dist/cjs/src/index.js +10 -0
- package/dist/cjs/src/managers/controls/transformControlsManager.js +61 -2
- package/dist/cjs/src/managers/pathfinding/PathFlowManager.js +94 -13
- package/dist/cjs/src/managers/pathfinding/PathRenderingManager.js +15 -0
- package/dist/cjs/src/managers/scene/componentTooltipManager.js +7 -1
- package/dist/cjs/src/managers/scene/controlPanelManager.js +377 -0
- package/dist/cjs/src/managers/scene/sceneExportManager.js +10 -1
- package/dist/cjs/src/managers/state/EffectiveVisualManager.js +270 -0
- package/dist/esm/src/core/centralPlant.js +80 -11
- package/dist/esm/src/core/centralPlantInternals.js +4 -0
- package/dist/esm/src/core/centralPlantValidator.js +10 -6
- package/dist/esm/src/core/stateEngine.js +725 -0
- package/dist/esm/src/core/stateEngineScene.js +90 -0
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/managers/controls/transformControlsManager.js +61 -2
- package/dist/esm/src/managers/pathfinding/PathFlowManager.js +94 -13
- package/dist/esm/src/managers/pathfinding/PathRenderingManager.js +15 -0
- package/dist/esm/src/managers/scene/componentTooltipManager.js +7 -1
- package/dist/esm/src/managers/scene/controlPanelManager.js +352 -0
- package/dist/esm/src/managers/scene/sceneExportManager.js +10 -1
- package/dist/esm/src/managers/state/EffectiveVisualManager.js +266 -0
- package/dist/index.d.ts +85 -0
- package/package.json +5 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { makeAddress } from './stateEngine.js';
|
|
3
|
+
import { getScopedAttachmentKey } from '../utils/behaviorDispatch.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Registry address for an I/O device data point.
|
|
7
|
+
* Shape: `${parentUuid}::${attachmentId}::${stateId}` (matches §17
|
|
8
|
+
* `pump-001::start-switch::mode`). The scoped key is `${parentUuid}::${attachmentId}`.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} attachmentId
|
|
11
|
+
* @param {string} stateId
|
|
12
|
+
* @param {string} [parentUuid]
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function ioAddress(attachmentId, stateId, parentUuid) {
|
|
16
|
+
return makeAddress(getScopedAttachmentKey(attachmentId, parentUuid), stateId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse an I/O device address back into its parts. Returns null for
|
|
21
|
+
* plant/group/object/pipe addresses (2-part) — I/O device data points are 3-part
|
|
22
|
+
* because a device always has a parent component (§8; free-floating I/O rejected).
|
|
23
|
+
*
|
|
24
|
+
* @param {string} address
|
|
25
|
+
* @returns {{ parentUuid: string, attachmentId: string, stateId: string, scopedKey: string } | null}
|
|
26
|
+
*/
|
|
27
|
+
function parseIoAddress(address) {
|
|
28
|
+
if (typeof address !== 'string') return null;
|
|
29
|
+
var parts = address.split('::');
|
|
30
|
+
if (parts.length !== 3) return null;
|
|
31
|
+
var _parts = _slicedToArray(parts, 3),
|
|
32
|
+
parentUuid = _parts[0],
|
|
33
|
+
attachmentId = _parts[1],
|
|
34
|
+
stateId = _parts[2];
|
|
35
|
+
return {
|
|
36
|
+
parentUuid: parentUuid,
|
|
37
|
+
attachmentId: attachmentId,
|
|
38
|
+
stateId: stateId,
|
|
39
|
+
scopedKey: "".concat(parentUuid, "::").concat(attachmentId)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Read a State Engine declaration out of scene data, tolerating legacy scenes.
|
|
45
|
+
* v3.0 scenes carry `plant` / `groups` / `chains` / `stateRegistry`; older v2.3
|
|
46
|
+
* scenes have none of these and load with safe defaults (plant power on, no
|
|
47
|
+
* groups, no chains) so nothing crashes on unknown/missing fields (§15).
|
|
48
|
+
*
|
|
49
|
+
* @param {object} [sceneData]
|
|
50
|
+
* @returns {{ plant: object, groups: object, chains: object[], stateRegistry: object }}
|
|
51
|
+
*/
|
|
52
|
+
function extractDeclaration() {
|
|
53
|
+
var sceneData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
54
|
+
return {
|
|
55
|
+
plant: sceneData.plant || {
|
|
56
|
+
states: {
|
|
57
|
+
power: {
|
|
58
|
+
type: 'Boolean',
|
|
59
|
+
default: true,
|
|
60
|
+
gate: true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
groups: sceneData.groups || {},
|
|
65
|
+
chains: Array.isArray(sceneData.chains) ? sceneData.chains : [],
|
|
66
|
+
stateRegistry: sceneData.stateRegistry || {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Merge a State Engine's current declaration (plant / groups / chains /
|
|
72
|
+
* stateRegistry) into an export object, bumping it to version 3.0. Mutates and
|
|
73
|
+
* returns `exportData`.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} exportData - the scene export object being assembled
|
|
76
|
+
* @param {{ toDeclaration: () => object }} engine
|
|
77
|
+
* @returns {object} exportData
|
|
78
|
+
*/
|
|
79
|
+
function applyDeclarationToExport(exportData, engine) {
|
|
80
|
+
if (!exportData || !engine || typeof engine.toDeclaration !== 'function') return exportData;
|
|
81
|
+
var declaration = engine.toDeclaration();
|
|
82
|
+
exportData.version = '3.0';
|
|
83
|
+
exportData.plant = declaration.plant;
|
|
84
|
+
exportData.groups = declaration.groups;
|
|
85
|
+
if (declaration.chains.length > 0) exportData.chains = declaration.chains;
|
|
86
|
+
exportData.stateRegistry = declaration.stateRegistry;
|
|
87
|
+
return exportData;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { applyDeclarationToExport, extractDeclaration, ioAddress, parseIoAddress };
|
package/dist/esm/src/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default as CentralPlant } from './core/centralPlant.js';
|
|
2
2
|
export { default as sceneViewer } from './core/sceneViewer.js';
|
|
3
|
+
export { StateEngine, makeAddress, offValue, valuesEqual } from './core/stateEngine.js';
|
|
4
|
+
export { applyDeclarationToExport, extractDeclaration, ioAddress, parseIoAddress } from './core/stateEngineScene.js';
|
|
3
5
|
export { findObjectByHardcodedUuid, generateUniqueComponentId, generateUuidFromName, getHardcodedUuid } from './utils/nameUtils.js';
|
|
4
6
|
export { FLOW_ATTRIBUTE_KEYS, PathData, createPathfindingRequest } from './core/pathfindingData.js';
|
|
5
7
|
export { getObjectTypeName, getObjectsByType, isComponent, isComputed, isConnector, isDeclared, isExportable, isGateway, isSegment, markAsComputed, markAsDeclared, shouldRemoveOnRegeneration } from './utils/objectTypes.js';
|
|
@@ -140,6 +140,12 @@ var TransformControlsManager = /*#__PURE__*/function () {
|
|
|
140
140
|
// Update bounding box if the transformed object is in current selection
|
|
141
141
|
if (_this.selectedObjects.includes(eventData.object)) {
|
|
142
142
|
console.log('🔲 Updating bounding boxes for selected objects after transform');
|
|
143
|
+
// Reposition the transform gizmo (and its selection group) to follow the
|
|
144
|
+
// object's new position. Programmatic transforms (e.g. the Transform tab's
|
|
145
|
+
// Translate/Rotate buttons) move the object directly without touching the
|
|
146
|
+
// multi-selection group the gizmo is attached to, so without this the widget
|
|
147
|
+
// would stay at the pre-transform centroid.
|
|
148
|
+
_this.syncSelectionToObjects();
|
|
143
149
|
_this.updateBoundingBox();
|
|
144
150
|
}
|
|
145
151
|
};
|
|
@@ -1246,6 +1252,59 @@ var TransformControlsManager = /*#__PURE__*/function () {
|
|
|
1246
1252
|
console.log("\uD83D\uDCE6 Multi-selection group created at centroid:", centroid.toArray());
|
|
1247
1253
|
}
|
|
1248
1254
|
|
|
1255
|
+
/**
|
|
1256
|
+
* Re-sync the multi-selection group (and the attached transform gizmo) to the
|
|
1257
|
+
* current world positions of the selected objects, in place.
|
|
1258
|
+
*
|
|
1259
|
+
* Used after a *programmatic* transform (e.g. the Transform tab's Translate /
|
|
1260
|
+
* Rotate buttons), which mutates the object directly and does not move the
|
|
1261
|
+
* selection group the gizmo is attached to. Unlike a full updateMultiSelection()
|
|
1262
|
+
* this keeps the same group instance so the gizmo doesn't detach/re-attach.
|
|
1263
|
+
*
|
|
1264
|
+
* It also refreshes the cached original transforms so a subsequent gizmo drag
|
|
1265
|
+
* computes its delta from the object's new position rather than jumping.
|
|
1266
|
+
*/
|
|
1267
|
+
}, {
|
|
1268
|
+
key: "syncSelectionToObjects",
|
|
1269
|
+
value: function syncSelectionToObjects() {
|
|
1270
|
+
var _this$transformContro;
|
|
1271
|
+
if (!this.multiSelectionGroup || this.selectedObjects.length === 0) {
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// Recompute the centroid from the objects' current world positions.
|
|
1276
|
+
var centroid = new THREE.Vector3();
|
|
1277
|
+
this.selectedObjects.forEach(function (obj) {
|
|
1278
|
+
obj.updateMatrixWorld(true);
|
|
1279
|
+
var worldPos = new THREE.Vector3();
|
|
1280
|
+
obj.getWorldPosition(worldPos);
|
|
1281
|
+
centroid.add(worldPos);
|
|
1282
|
+
});
|
|
1283
|
+
centroid.divideScalar(this.selectedObjects.length);
|
|
1284
|
+
|
|
1285
|
+
// Move the group (and thus the attached gizmo) to the new centroid. Reset any
|
|
1286
|
+
// rotation so the gizmo matches the freshly-created-group convention.
|
|
1287
|
+
this.multiSelectionGroup.position.copy(centroid);
|
|
1288
|
+
this.multiSelectionGroup.rotation.set(0, 0, 0);
|
|
1289
|
+
this.multiSelectionGroup.updateMatrixWorld(true);
|
|
1290
|
+
|
|
1291
|
+
// Refresh cached originals so applyRealtimeTransform's delta stays correct on
|
|
1292
|
+
// the next drag (its "original centroid" is derived from these values).
|
|
1293
|
+
this.selectedObjects.forEach(function (obj) {
|
|
1294
|
+
obj.userData._multiSelectOriginalPosition = obj.position.clone();
|
|
1295
|
+
obj.userData._multiSelectOriginalRotation = obj.rotation.clone();
|
|
1296
|
+
obj.userData._multiSelectOriginalScale = obj.scale.clone();
|
|
1297
|
+
var worldPos = new THREE.Vector3();
|
|
1298
|
+
obj.getWorldPosition(worldPos);
|
|
1299
|
+
obj.userData._multiSelectOriginalWorldPosition = worldPos;
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
// Keep the gizmo's world matrix in sync with the repositioned group.
|
|
1303
|
+
if ((_this$transformContro = this.transformControls) !== null && _this$transformContro !== void 0 && _this$transformContro.object) {
|
|
1304
|
+
this.transformControls.updateMatrixWorld(true);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1249
1308
|
/**
|
|
1250
1309
|
* Create a bounding box that encompasses all selected objects
|
|
1251
1310
|
*/
|
|
@@ -1525,10 +1584,10 @@ var TransformControlsManager = /*#__PURE__*/function () {
|
|
|
1525
1584
|
}, {
|
|
1526
1585
|
key: "toggleTransformMode",
|
|
1527
1586
|
value: function toggleTransformMode() {
|
|
1528
|
-
var _this$
|
|
1587
|
+
var _this$transformContro2;
|
|
1529
1588
|
var newMode = this.currentMode === 'translate' ? 'rotate' : 'translate';
|
|
1530
1589
|
this.setMode(newMode);
|
|
1531
|
-
if ((_this$
|
|
1590
|
+
if ((_this$transformContro2 = this.transformControls) !== null && _this$transformContro2 !== void 0 && _this$transformContro2.object) {
|
|
1532
1591
|
this.transformControls.updateMatrixWorld(true);
|
|
1533
1592
|
}
|
|
1534
1593
|
}
|
|
@@ -10,6 +10,9 @@ var TEMP_MIN = 0;
|
|
|
10
10
|
/** Maximum temperature in the colour ramp (maps to pure red). */
|
|
11
11
|
var TEMP_MAX = 100;
|
|
12
12
|
|
|
13
|
+
/** Colour a pipe is painted when its effective flow is gated off (§6.1). */
|
|
14
|
+
var PIPE_OFF_GRAY = '#3a3a3a';
|
|
15
|
+
|
|
13
16
|
/**
|
|
14
17
|
* Convert a temperature value to a CSS hex colour string using a blue→red HSL
|
|
15
18
|
* ramp. Values are clamped to [TEMP_MIN, TEMP_MAX].
|
|
@@ -47,22 +50,78 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
47
50
|
* @type {Map<string, Record<string, any>>}
|
|
48
51
|
*/
|
|
49
52
|
_this._derivedStore = new Map();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Paths whose effective flow is gated off (State Engine §6.1). While a path
|
|
56
|
+
* is in this set it is painted {@link PIPE_OFF_GRAY} and temperature updates
|
|
57
|
+
* are suppressed so they don't override the off-look.
|
|
58
|
+
* @type {Set<string>}
|
|
59
|
+
*/
|
|
60
|
+
_this._flowGatedOff = new Set();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Baseline (on-state) colour snapshotted the moment a path is gated off, so
|
|
64
|
+
* it can be restored exactly when flow returns — including pipes with no
|
|
65
|
+
* declared flowTemperature, whose colour is the base pipe material.
|
|
66
|
+
* @type {Map<string, string>}
|
|
67
|
+
*/
|
|
68
|
+
_this._flowBaseColor = new Map();
|
|
50
69
|
return _this;
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
// ──
|
|
72
|
+
// ── Flow gating (State Engine effective → 3D bridge, §6.1) ─────────────────
|
|
54
73
|
|
|
55
74
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
75
|
+
* Gate a path's flow visual on/off from the State Engine's effective `flow`.
|
|
76
|
+
* Off → paint the shared per-path material gray; on → restore the temperature
|
|
77
|
+
* colour. The off state is sticky (see {@link _flowGatedOff}).
|
|
59
78
|
*
|
|
60
|
-
* @param {string} pathId
|
|
61
|
-
* @param {
|
|
62
|
-
* @param {any} value
|
|
79
|
+
* @param {string} pathId - `${from}-->${to}`
|
|
80
|
+
* @param {boolean} enabled - Effective flow
|
|
63
81
|
*/
|
|
64
82
|
_inherits(PathFlowManager, _BaseDisposable);
|
|
65
83
|
return _createClass(PathFlowManager, [{
|
|
84
|
+
key: "setFlowEnabled",
|
|
85
|
+
value: function setFlowEnabled(pathId, enabled) {
|
|
86
|
+
var split = this._splitPathId(pathId);
|
|
87
|
+
if (!split) return;
|
|
88
|
+
var renderingManager = this._getRenderingManager();
|
|
89
|
+
if (enabled) {
|
|
90
|
+
this._flowGatedOff.delete(pathId);
|
|
91
|
+
// Prefer a temperature-derived colour if one is declared; otherwise
|
|
92
|
+
// restore the baseline snapshotted when the pipe was gated off (this is
|
|
93
|
+
// what fixes plain, non-temperature pipes getting stuck gray).
|
|
94
|
+
var temp = this.resolve(pathId, 'flowTemperature');
|
|
95
|
+
if (temp !== null && temp !== undefined) {
|
|
96
|
+
this.applyVisualizationForPath(pathId);
|
|
97
|
+
} else if (this._flowBaseColor.has(pathId)) {
|
|
98
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, this._flowBaseColor.get(pathId));
|
|
99
|
+
}
|
|
100
|
+
this._flowBaseColor.delete(pathId);
|
|
101
|
+
} else {
|
|
102
|
+
// Snapshot the current (on-state) colour once, before overpainting gray.
|
|
103
|
+
if (!this._flowGatedOff.has(pathId) && renderingManager) {
|
|
104
|
+
var _renderingManager$get;
|
|
105
|
+
var base = (_renderingManager$get = renderingManager.getPathColorHex) === null || _renderingManager$get === void 0 ? void 0 : _renderingManager$get.call(renderingManager, pathId);
|
|
106
|
+
if (base) this._flowBaseColor.set(pathId, base);
|
|
107
|
+
}
|
|
108
|
+
this._flowGatedOff.add(pathId);
|
|
109
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, PIPE_OFF_GRAY);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Public API — attribute write ──────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set a declared (persistent) flow attribute on a path.
|
|
117
|
+
* The value is stored in PathData and will be serialised with the scene.
|
|
118
|
+
* Triggers a visual update.
|
|
119
|
+
*
|
|
120
|
+
* @param {string} pathId - e.g. "PUMP-1-CONN-1-->CHILLER-1-CONN-2"
|
|
121
|
+
* @param {string} key - One of FLOW_ATTRIBUTE_KEYS
|
|
122
|
+
* @param {any} value
|
|
123
|
+
*/
|
|
124
|
+
}, {
|
|
66
125
|
key: "setDeclared",
|
|
67
126
|
value: function setDeclared(pathId, key, value) {
|
|
68
127
|
var pd = this._getPathData(pathId);
|
|
@@ -168,6 +227,13 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
168
227
|
}, {
|
|
169
228
|
key: "applyVisualizationForPath",
|
|
170
229
|
value: function applyVisualizationForPath(pathId) {
|
|
230
|
+
// Effective flow gated off (§6.1) — keep the off-look, ignore temperature.
|
|
231
|
+
if (this._flowGatedOff.has(pathId)) {
|
|
232
|
+
var _this$_getRenderingMa;
|
|
233
|
+
var _split = this._splitPathId(pathId);
|
|
234
|
+
if (_split) (_this$_getRenderingMa = this._getRenderingManager()) === null || _this$_getRenderingMa === void 0 || _this$_getRenderingMa.updatePathColor(_split.from, _split.to, PIPE_OFF_GRAY);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
171
237
|
var temp = this.resolve(pathId, 'flowTemperature');
|
|
172
238
|
if (temp === null || temp === undefined) {
|
|
173
239
|
return; // No temperature declared — leave default pipe color
|
|
@@ -175,17 +241,30 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
175
241
|
var color = temperatureToColor(temp);
|
|
176
242
|
var renderingManager = this._getRenderingManager();
|
|
177
243
|
if (!renderingManager) return;
|
|
244
|
+
var split = this._splitPathId(pathId);
|
|
245
|
+
if (!split) return;
|
|
246
|
+
renderingManager.updatePathColor(split.from, split.to, color);
|
|
247
|
+
console.log("\uD83C\uDF21\uFE0F PathFlowManager: \"".concat(pathId, "\" \u2192 flowTemperature ").concat(temp, " \u2192 ").concat(color));
|
|
248
|
+
}
|
|
178
249
|
|
|
179
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Split a `${from}-->${to}` pathId into its two connector halves.
|
|
252
|
+
* @param {string} pathId
|
|
253
|
+
* @returns {{ from: string, to: string }|null} null if malformed
|
|
254
|
+
* @private
|
|
255
|
+
*/
|
|
256
|
+
}, {
|
|
257
|
+
key: "_splitPathId",
|
|
258
|
+
value: function _splitPathId(pathId) {
|
|
180
259
|
var sepIdx = pathId.indexOf('-->');
|
|
181
260
|
if (sepIdx === -1) {
|
|
182
261
|
console.warn("PathFlowManager: malformed pathId \"".concat(pathId, "\""));
|
|
183
|
-
return;
|
|
262
|
+
return null;
|
|
184
263
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
264
|
+
return {
|
|
265
|
+
from: pathId.slice(0, sepIdx),
|
|
266
|
+
to: pathId.slice(sepIdx + 3)
|
|
267
|
+
};
|
|
189
268
|
}
|
|
190
269
|
|
|
191
270
|
/**
|
|
@@ -217,6 +296,8 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
217
296
|
key: "dispose",
|
|
218
297
|
value: function dispose() {
|
|
219
298
|
this._derivedStore.clear();
|
|
299
|
+
this._flowGatedOff.clear();
|
|
300
|
+
this._flowBaseColor.clear();
|
|
220
301
|
this.sceneViewer = null;
|
|
221
302
|
_superPropGet(PathFlowManager, "dispose", this, 3)([]);
|
|
222
303
|
}
|
|
@@ -64,6 +64,21 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Read the current color of a path's shared material as a `#rrggbb` string,
|
|
69
|
+
* or null if the path has no material. Used to snapshot a pipe's baseline
|
|
70
|
+
* colour before it is temporarily overpainted (e.g. flow gating).
|
|
71
|
+
*
|
|
72
|
+
* @param {string} pathId - "${from}-->${to}"
|
|
73
|
+
* @returns {string|null}
|
|
74
|
+
*/
|
|
75
|
+
}, {
|
|
76
|
+
key: "getPathColorHex",
|
|
77
|
+
value: function getPathColorHex(pathId) {
|
|
78
|
+
var mat = this._pathMaterials.get(pathId);
|
|
79
|
+
return mat ? "#".concat(mat.color.getHexString()) : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
67
82
|
/**
|
|
68
83
|
* Get path colors for visual distinction
|
|
69
84
|
* @param {number} index - Path index
|
|
@@ -120,7 +120,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
120
120
|
if (!ioDeviceObject || !this._stateAdapter) return;
|
|
121
121
|
var ud = ioDeviceObject.userData;
|
|
122
122
|
var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
|
|
123
|
-
var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
124
123
|
if (!attachmentId) return;
|
|
125
124
|
|
|
126
125
|
// Walk up to find parent component UUID for scoped state/behavior handling
|
|
@@ -135,6 +134,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
135
134
|
obj = obj.parent;
|
|
136
135
|
}
|
|
137
136
|
|
|
137
|
+
// Derive the device's data points from its behaviorConfig (same resolver the
|
|
138
|
+
// drag path uses). `userData.dataPoints` is not populated for attached
|
|
139
|
+
// devices, so reading it directly would find nothing — resolve instead, and
|
|
140
|
+
// keep the legacy field only as a fallback.
|
|
141
|
+
var resolved = resolveDataPoints(parentUuid, attachmentId, ud, getIoBehaviorManager(this.sceneViewer), null);
|
|
142
|
+
var dataPoints = resolved !== null && resolved !== void 0 && resolved.length ? resolved : (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
143
|
+
|
|
138
144
|
// Create a scoped attachment key to prevent state sharing between instances
|
|
139
145
|
// of the same smart component that share the same attachmentId
|
|
140
146
|
var scopedAttachmentId = getScopedAttachmentKey(attachmentId, parentUuid);
|