@2112-lab/central-plant 0.3.58 → 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 +1635 -32
- 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/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/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
package/dist/bundle/index.js
CHANGED
|
@@ -2143,20 +2143,24 @@ var CentralPlantValidator = /*#__PURE__*/function () {
|
|
|
2143
2143
|
key: "validateImportedSceneData",
|
|
2144
2144
|
value: function validateImportedSceneData(sceneData) {
|
|
2145
2145
|
var results = [];
|
|
2146
|
-
var hardcodedVersion = "2.3";
|
|
2147
2146
|
|
|
2148
|
-
//
|
|
2149
|
-
|
|
2147
|
+
// Supported scene formats: 2.3 (legacy I/O behaviors) and 3.0 (State Engine:
|
|
2148
|
+
// adds plant/groups/chains/stateRegistry). Both load; older scenes keep working.
|
|
2149
|
+
var supportedVersions = ["2.3", "3.0"];
|
|
2150
|
+
|
|
2151
|
+
// Version validation - MUST be one of the supported versions
|
|
2152
|
+
if (!sceneData.version || !supportedVersions.includes(sceneData.version)) {
|
|
2150
2153
|
var providedVersion = sceneData.version || 'unknown';
|
|
2151
|
-
|
|
2154
|
+
var supportedList = supportedVersions.join(' or ');
|
|
2155
|
+
alert("The central-plant requires the input file to be version ".concat(supportedList));
|
|
2152
2156
|
results.push({
|
|
2153
2157
|
isValid: false,
|
|
2154
2158
|
severity: ValidationSeverity.ERROR,
|
|
2155
|
-
message: "Invalid version: \"".concat(providedVersion, "\".
|
|
2159
|
+
message: "Invalid version: \"".concat(providedVersion, "\". Supported versions are ").concat(supportedList),
|
|
2156
2160
|
code: 'INVALID_VERSION',
|
|
2157
2161
|
metadata: {
|
|
2158
2162
|
providedVersion: providedVersion,
|
|
2159
|
-
|
|
2163
|
+
supportedVersions: supportedVersions
|
|
2160
2164
|
}
|
|
2161
2165
|
});
|
|
2162
2166
|
return this._combineValidationResults(results, 'scene_import_validation');
|
|
@@ -12114,6 +12118,813 @@ function generateUniqueComponentId(libraryId, scene) {
|
|
|
12114
12118
|
return id;
|
|
12115
12119
|
}
|
|
12116
12120
|
|
|
12121
|
+
/**
|
|
12122
|
+
* @file stateEngine.js
|
|
12123
|
+
* @description Portable State Engine core — registry, desired/effective resolution,
|
|
12124
|
+
* plant (and later group) gates, change notifications. No Vue/Three.js.
|
|
12125
|
+
*
|
|
12126
|
+
* Phase 1 scaffold per STATE_ENGINE_FRAMEWORK_0.3.md / milestones doc.
|
|
12127
|
+
*/
|
|
12128
|
+
|
|
12129
|
+
/**
|
|
12130
|
+
* @typedef {'desired' | 'effective'} StateLayer
|
|
12131
|
+
*/
|
|
12132
|
+
|
|
12133
|
+
/**
|
|
12134
|
+
* Build a scoped registry address.
|
|
12135
|
+
* @param {string} scope - e.g. "plant", "group:chiller-loop-a", "pump-001"
|
|
12136
|
+
* @param {string} attribute
|
|
12137
|
+
* @returns {string}
|
|
12138
|
+
*/
|
|
12139
|
+
function makeAddress(scope, attribute) {
|
|
12140
|
+
return "".concat(scope, "::").concat(attribute);
|
|
12141
|
+
}
|
|
12142
|
+
|
|
12143
|
+
/**
|
|
12144
|
+
* Shallow equality for registry values (boolean, number, string, null).
|
|
12145
|
+
* @param {*} a
|
|
12146
|
+
* @param {*} b
|
|
12147
|
+
* @returns {boolean}
|
|
12148
|
+
*/
|
|
12149
|
+
function valuesEqual(a, b) {
|
|
12150
|
+
return Object.is(a, b);
|
|
12151
|
+
}
|
|
12152
|
+
|
|
12153
|
+
/**
|
|
12154
|
+
* Safe-off value for a gated operational attribute: 0 for numbers, false for
|
|
12155
|
+
* booleans, otherwise the value unchanged.
|
|
12156
|
+
* @param {*} desired
|
|
12157
|
+
* @returns {*}
|
|
12158
|
+
*/
|
|
12159
|
+
function offValue(desired) {
|
|
12160
|
+
if (typeof desired === 'number') return 0;
|
|
12161
|
+
if (typeof desired === 'boolean') return false;
|
|
12162
|
+
return desired;
|
|
12163
|
+
}
|
|
12164
|
+
|
|
12165
|
+
/**
|
|
12166
|
+
* Central Plant State Engine (logic only).
|
|
12167
|
+
*
|
|
12168
|
+
* Desired values are stored in the registry. Effective values are computed
|
|
12169
|
+
* from gates (plant / groups / object) without erasing desired.
|
|
12170
|
+
*/
|
|
12171
|
+
var StateEngine = /*#__PURE__*/function () {
|
|
12172
|
+
/**
|
|
12173
|
+
* @param {object} [options]
|
|
12174
|
+
* @param {number} [options.maxChainDepth=8] - Ripple hop limit (Phase 3)
|
|
12175
|
+
*/
|
|
12176
|
+
function StateEngine() {
|
|
12177
|
+
var _options$maxChainDept, _options$gatedOperati;
|
|
12178
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
12179
|
+
_classCallCheck(this, StateEngine);
|
|
12180
|
+
/** @type {Map<string, *>} address → desired value */
|
|
12181
|
+
this._desired = new Map();
|
|
12182
|
+
|
|
12183
|
+
/** @type {Map<string, Set<string>>} groupId → member object ids */
|
|
12184
|
+
this._groupMembers = new Map();
|
|
12185
|
+
|
|
12186
|
+
/** @type {Map<string, *>} groupId → group desired power (and later other attrs) */
|
|
12187
|
+
this._groupDesired = new Map();
|
|
12188
|
+
|
|
12189
|
+
/** @type {boolean} */
|
|
12190
|
+
this._plantPowerDesired = true;
|
|
12191
|
+
|
|
12192
|
+
/** @type {Set<(change: { address: string, desired: *, effective: * }) => void>} */
|
|
12193
|
+
this._listeners = new Set();
|
|
12194
|
+
|
|
12195
|
+
/** @type {number} */
|
|
12196
|
+
this.maxChainDepth = (_options$maxChainDept = options.maxChainDepth) !== null && _options$maxChainDept !== void 0 ? _options$maxChainDept : 8;
|
|
12197
|
+
|
|
12198
|
+
/**
|
|
12199
|
+
* Operational attributes that read a safe-off value when their object's
|
|
12200
|
+
* power is not effectively on (Framework §6/§10: fan level 5 + plant off → 0).
|
|
12201
|
+
* Passive/meta attributes stay ungated.
|
|
12202
|
+
* @type {Set<string>}
|
|
12203
|
+
*/
|
|
12204
|
+
this._gatedOperationalAttributes = new Set((_options$gatedOperati = options.gatedOperationalAttributes) !== null && _options$gatedOperati !== void 0 ? _options$gatedOperati : ['fan-level', 'rpm', 'level', 'speed']);
|
|
12205
|
+
|
|
12206
|
+
/** @type {Array<object>} declared chains (Phase 3) */
|
|
12207
|
+
this._chains = [];
|
|
12208
|
+
|
|
12209
|
+
/** @type {boolean} */
|
|
12210
|
+
this._batching = false;
|
|
12211
|
+
|
|
12212
|
+
/** @type {Map<string, { address: string, desired: *, effective: * }>} */
|
|
12213
|
+
this._pendingNotifications = new Map();
|
|
12214
|
+
}
|
|
12215
|
+
|
|
12216
|
+
// ─── Load / reset ─────────────────────────────────────────────────────────
|
|
12217
|
+
|
|
12218
|
+
/**
|
|
12219
|
+
* Load plant, groups, and initial desired values from a scene-like declaration.
|
|
12220
|
+
* @param {object} declaration
|
|
12221
|
+
* @param {object} [declaration.plant]
|
|
12222
|
+
* @param {object} [declaration.groups]
|
|
12223
|
+
* @param {object} [declaration.stateRegistry]
|
|
12224
|
+
* @param {Array<object>} [declaration.chains]
|
|
12225
|
+
*/
|
|
12226
|
+
return _createClass(StateEngine, [{
|
|
12227
|
+
key: "load",
|
|
12228
|
+
value: function load() {
|
|
12229
|
+
var _declaration$plant;
|
|
12230
|
+
var declaration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
12231
|
+
this._desired.clear();
|
|
12232
|
+
this._groupMembers.clear();
|
|
12233
|
+
this._groupDesired.clear();
|
|
12234
|
+
this._chains = Array.isArray(declaration.chains) ? _toConsumableArray(declaration.chains) : [];
|
|
12235
|
+
var plantDefault = ((_declaration$plant = declaration.plant) === null || _declaration$plant === void 0 || (_declaration$plant = _declaration$plant.states) === null || _declaration$plant === void 0 || (_declaration$plant = _declaration$plant.power) === null || _declaration$plant === void 0 ? void 0 : _declaration$plant.default) !== undefined ? declaration.plant.states.power.default : true;
|
|
12236
|
+
this._plantPowerDesired = Boolean(plantDefault);
|
|
12237
|
+
this._desired.set(makeAddress('plant', 'power'), this._plantPowerDesired);
|
|
12238
|
+
var groups = declaration.groups || {};
|
|
12239
|
+
for (var _i = 0, _Object$entries = Object.entries(groups); _i < _Object$entries.length; _i++) {
|
|
12240
|
+
var _group$states;
|
|
12241
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
12242
|
+
groupId = _Object$entries$_i[0],
|
|
12243
|
+
group = _Object$entries$_i[1];
|
|
12244
|
+
var members = Array.isArray(group.members) ? group.members : [];
|
|
12245
|
+
this._groupMembers.set(groupId, new Set(members));
|
|
12246
|
+
var groupPower = ((_group$states = group.states) === null || _group$states === void 0 || (_group$states = _group$states.power) === null || _group$states === void 0 ? void 0 : _group$states.default) !== undefined ? group.states.power.default : true;
|
|
12247
|
+
this._groupDesired.set(groupId, Boolean(groupPower));
|
|
12248
|
+
this._desired.set(makeAddress("group:".concat(groupId), 'power'), Boolean(groupPower));
|
|
12249
|
+
}
|
|
12250
|
+
var registry = declaration.stateRegistry || {};
|
|
12251
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(registry); _i2 < _Object$entries2.length; _i2++) {
|
|
12252
|
+
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
|
|
12253
|
+
address = _Object$entries2$_i[0],
|
|
12254
|
+
value = _Object$entries2$_i[1];
|
|
12255
|
+
this._desired.set(address, value);
|
|
12256
|
+
if (address === makeAddress('plant', 'power')) {
|
|
12257
|
+
this._plantPowerDesired = Boolean(value);
|
|
12258
|
+
}
|
|
12259
|
+
var groupMatch = /^group:([^:]+)::power$/.exec(address);
|
|
12260
|
+
if (groupMatch) {
|
|
12261
|
+
this._groupDesired.set(groupMatch[1], Boolean(value));
|
|
12262
|
+
}
|
|
12263
|
+
}
|
|
12264
|
+
}
|
|
12265
|
+
}, {
|
|
12266
|
+
key: "reset",
|
|
12267
|
+
value: function reset() {
|
|
12268
|
+
this._desired.clear();
|
|
12269
|
+
this._groupMembers.clear();
|
|
12270
|
+
this._groupDesired.clear();
|
|
12271
|
+
this._chains = [];
|
|
12272
|
+
this._plantPowerDesired = true;
|
|
12273
|
+
this._pendingNotifications.clear();
|
|
12274
|
+
this._batching = false;
|
|
12275
|
+
}
|
|
12276
|
+
|
|
12277
|
+
// ─── Desired / effective ──────────────────────────────────────────────────
|
|
12278
|
+
|
|
12279
|
+
/**
|
|
12280
|
+
* @param {string} address
|
|
12281
|
+
* @returns {*}
|
|
12282
|
+
*/
|
|
12283
|
+
}, {
|
|
12284
|
+
key: "getDesired",
|
|
12285
|
+
value: function getDesired(address) {
|
|
12286
|
+
return this._desired.has(address) ? this._desired.get(address) : undefined;
|
|
12287
|
+
}
|
|
12288
|
+
|
|
12289
|
+
/**
|
|
12290
|
+
* Compute effective value for an address.
|
|
12291
|
+
* Power-style gates: plant AND all containing groups AND object desired.
|
|
12292
|
+
* Non-power attributes: return desired when power gates allow, else a safe off value.
|
|
12293
|
+
*
|
|
12294
|
+
* @param {string} address
|
|
12295
|
+
* @returns {*}
|
|
12296
|
+
*/
|
|
12297
|
+
}, {
|
|
12298
|
+
key: "getEffective",
|
|
12299
|
+
value: function getEffective(address) {
|
|
12300
|
+
var desired = this.getDesired(address);
|
|
12301
|
+
if (desired === undefined) return undefined;
|
|
12302
|
+
if (address === makeAddress('plant', 'power')) {
|
|
12303
|
+
return Boolean(desired);
|
|
12304
|
+
}
|
|
12305
|
+
var groupPowerMatch = /^group:([^:]+)::power$/.exec(address);
|
|
12306
|
+
if (groupPowerMatch) {
|
|
12307
|
+
return this._plantPowerDesired && Boolean(desired);
|
|
12308
|
+
}
|
|
12309
|
+
var objectPowerMatch = /^([^:]+)::power$/.exec(address);
|
|
12310
|
+
if (objectPowerMatch && !address.startsWith('group:') && !address.startsWith('plant::')) {
|
|
12311
|
+
var objectId = objectPowerMatch[1];
|
|
12312
|
+
// Skip nested I/O addresses like pump-001::start-switch::mode
|
|
12313
|
+
if (address.split('::').length !== 2) {
|
|
12314
|
+
return desired;
|
|
12315
|
+
}
|
|
12316
|
+
return this._isPowerAllowedForObject(objectId) && Boolean(desired);
|
|
12317
|
+
}
|
|
12318
|
+
|
|
12319
|
+
// Flow and other attrs: when tied to an object id prefix, gate on that object's power path
|
|
12320
|
+
var parts = address.split('::');
|
|
12321
|
+
if (parts.length === 2) {
|
|
12322
|
+
var _parts = _slicedToArray(parts, 2),
|
|
12323
|
+
_objectId = _parts[0],
|
|
12324
|
+
attribute = _parts[1];
|
|
12325
|
+
if (attribute === 'flow') {
|
|
12326
|
+
var powerAllowed = this._isPowerAllowedForObject(_objectId);
|
|
12327
|
+
if (!powerAllowed) return false;
|
|
12328
|
+
return Boolean(desired);
|
|
12329
|
+
}
|
|
12330
|
+
// Operational values (fan level, rpm, …) collapse to a safe-off value when
|
|
12331
|
+
// the object's power is not effectively on; restored when power returns (§6, §10).
|
|
12332
|
+
if (this._gatedOperationalAttributes.has(attribute)) {
|
|
12333
|
+
if (!this._isPowerAllowedForObject(_objectId)) return offValue(desired);
|
|
12334
|
+
return desired;
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
return desired;
|
|
12338
|
+
}
|
|
12339
|
+
|
|
12340
|
+
/**
|
|
12341
|
+
* @param {string} objectId
|
|
12342
|
+
* @returns {boolean}
|
|
12343
|
+
*/
|
|
12344
|
+
}, {
|
|
12345
|
+
key: "_isPowerAllowedForObject",
|
|
12346
|
+
value: function _isPowerAllowedForObject(objectId) {
|
|
12347
|
+
if (!this._plantPowerDesired) return false;
|
|
12348
|
+
var _iterator = _createForOfIteratorHelper(this._groupMembers),
|
|
12349
|
+
_step;
|
|
12350
|
+
try {
|
|
12351
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
12352
|
+
var _step$value = _slicedToArray(_step.value, 2),
|
|
12353
|
+
groupId = _step$value[0],
|
|
12354
|
+
members = _step$value[1];
|
|
12355
|
+
if (members.has(objectId)) {
|
|
12356
|
+
var groupOn = this._groupDesired.get(groupId);
|
|
12357
|
+
if (!groupOn) return false;
|
|
12358
|
+
}
|
|
12359
|
+
}
|
|
12360
|
+
} catch (err) {
|
|
12361
|
+
_iterator.e(err);
|
|
12362
|
+
} finally {
|
|
12363
|
+
_iterator.f();
|
|
12364
|
+
}
|
|
12365
|
+
return true;
|
|
12366
|
+
}
|
|
12367
|
+
|
|
12368
|
+
/**
|
|
12369
|
+
* Groups that contain an object.
|
|
12370
|
+
* @param {string} objectId
|
|
12371
|
+
* @returns {string[]}
|
|
12372
|
+
*/
|
|
12373
|
+
}, {
|
|
12374
|
+
key: "getGroupsForObject",
|
|
12375
|
+
value: function getGroupsForObject(objectId) {
|
|
12376
|
+
var result = [];
|
|
12377
|
+
var _iterator2 = _createForOfIteratorHelper(this._groupMembers),
|
|
12378
|
+
_step2;
|
|
12379
|
+
try {
|
|
12380
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
12381
|
+
var _step2$value = _slicedToArray(_step2.value, 2),
|
|
12382
|
+
groupId = _step2$value[0],
|
|
12383
|
+
members = _step2$value[1];
|
|
12384
|
+
if (members.has(objectId)) result.push(groupId);
|
|
12385
|
+
}
|
|
12386
|
+
} catch (err) {
|
|
12387
|
+
_iterator2.e(err);
|
|
12388
|
+
} finally {
|
|
12389
|
+
_iterator2.f();
|
|
12390
|
+
}
|
|
12391
|
+
return result;
|
|
12392
|
+
}
|
|
12393
|
+
|
|
12394
|
+
// ─── Writes ───────────────────────────────────────────────────────────────
|
|
12395
|
+
|
|
12396
|
+
/**
|
|
12397
|
+
* Set a desired value through the single write door and run one bounded
|
|
12398
|
+
* propagation pass: no-op stop, gate recompute, declared chains with depth
|
|
12399
|
+
* and visited-set guards, then batched notifications (Framework §11, §12).
|
|
12400
|
+
*
|
|
12401
|
+
* @param {string} address
|
|
12402
|
+
* @param {*} value
|
|
12403
|
+
* @returns {boolean} true if the desired value changed
|
|
12404
|
+
*/
|
|
12405
|
+
}, {
|
|
12406
|
+
key: "setDesired",
|
|
12407
|
+
value: function setDesired(address, value) {
|
|
12408
|
+
this._beginBatch();
|
|
12409
|
+
var ctx = {
|
|
12410
|
+
visited: new Map()
|
|
12411
|
+
};
|
|
12412
|
+
var changed = this._writeHop(address, value, 0, ctx);
|
|
12413
|
+
this._endBatch();
|
|
12414
|
+
return changed;
|
|
12415
|
+
}
|
|
12416
|
+
|
|
12417
|
+
/**
|
|
12418
|
+
* One hop of a propagation pass: apply the write, then walk declared chains.
|
|
12419
|
+
* @param {string} address
|
|
12420
|
+
* @param {*} value
|
|
12421
|
+
* @param {number} depth
|
|
12422
|
+
* @param {{ visited: Map<string, *> }} ctx
|
|
12423
|
+
* @returns {boolean} whether this hop changed the desired value
|
|
12424
|
+
*/
|
|
12425
|
+
}, {
|
|
12426
|
+
key: "_writeHop",
|
|
12427
|
+
value: function _writeHop(address, value, depth, ctx) {
|
|
12428
|
+
var result = this._applyWrite(address, value, ctx);
|
|
12429
|
+
if (!result.changed) return false;
|
|
12430
|
+
this._walkChains(result, depth, ctx);
|
|
12431
|
+
return true;
|
|
12432
|
+
}
|
|
12433
|
+
|
|
12434
|
+
/**
|
|
12435
|
+
* Atomic write door: no-op stop (§11.1), visited-set cycle guard (§11.5),
|
|
12436
|
+
* store desired, update gate mirrors, compute which effectives changed, and
|
|
12437
|
+
* queue notifications for changed addresses only.
|
|
12438
|
+
*
|
|
12439
|
+
* @param {string} address
|
|
12440
|
+
* @param {*} value
|
|
12441
|
+
* @param {{ visited: Map<string, *> }} ctx
|
|
12442
|
+
* @returns {{ changed: boolean, desiredChanged?: string, effectiveChanged?: Set<string> }}
|
|
12443
|
+
*/
|
|
12444
|
+
}, {
|
|
12445
|
+
key: "_applyWrite",
|
|
12446
|
+
value: function _applyWrite(address, value, ctx) {
|
|
12447
|
+
var previous = this.getDesired(address);
|
|
12448
|
+
if (previous !== undefined && valuesEqual(previous, value)) {
|
|
12449
|
+
return {
|
|
12450
|
+
changed: false
|
|
12451
|
+
}; // §11.1 no-op stop
|
|
12452
|
+
}
|
|
12453
|
+
|
|
12454
|
+
// §11.5 visited-set: writing the same pair a different value this pass = cycle conflict
|
|
12455
|
+
if (ctx.visited.has(address) && !valuesEqual(ctx.visited.get(address), value)) {
|
|
12456
|
+
console.warn("[StateEngine] cycle conflict: ".concat(address, " rewritten to a different value in one pass; stopping branch"));
|
|
12457
|
+
return {
|
|
12458
|
+
changed: false
|
|
12459
|
+
};
|
|
12460
|
+
}
|
|
12461
|
+
ctx.visited.set(address, value);
|
|
12462
|
+
|
|
12463
|
+
// Snapshot effective for every address this write might gate, before mutating.
|
|
12464
|
+
var affected = this._affectedByWrite(address);
|
|
12465
|
+
var before = new Map();
|
|
12466
|
+
var _iterator3 = _createForOfIteratorHelper(affected),
|
|
12467
|
+
_step3;
|
|
12468
|
+
try {
|
|
12469
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
12470
|
+
var a = _step3.value;
|
|
12471
|
+
before.set(a, this.getEffective(a));
|
|
12472
|
+
}
|
|
12473
|
+
|
|
12474
|
+
// Store desired + keep plant/group gate mirrors in sync.
|
|
12475
|
+
} catch (err) {
|
|
12476
|
+
_iterator3.e(err);
|
|
12477
|
+
} finally {
|
|
12478
|
+
_iterator3.f();
|
|
12479
|
+
}
|
|
12480
|
+
this._desired.set(address, value);
|
|
12481
|
+
if (address === makeAddress('plant', 'power')) {
|
|
12482
|
+
this._plantPowerDesired = Boolean(value);
|
|
12483
|
+
}
|
|
12484
|
+
var groupMatch = /^group:([^:]+)::power$/.exec(address);
|
|
12485
|
+
if (groupMatch) {
|
|
12486
|
+
this._groupDesired.set(groupMatch[1], Boolean(value));
|
|
12487
|
+
}
|
|
12488
|
+
|
|
12489
|
+
// Which effectives actually changed (§11.4 dedupe: notify once, changed only).
|
|
12490
|
+
var effectiveChanged = new Set();
|
|
12491
|
+
var _iterator4 = _createForOfIteratorHelper(affected),
|
|
12492
|
+
_step4;
|
|
12493
|
+
try {
|
|
12494
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
12495
|
+
var _a = _step4.value;
|
|
12496
|
+
if (!valuesEqual(before.get(_a), this.getEffective(_a))) effectiveChanged.add(_a);
|
|
12497
|
+
}
|
|
12498
|
+
} catch (err) {
|
|
12499
|
+
_iterator4.e(err);
|
|
12500
|
+
} finally {
|
|
12501
|
+
_iterator4.f();
|
|
12502
|
+
}
|
|
12503
|
+
this._queueNotification(address); // desired changed
|
|
12504
|
+
var _iterator5 = _createForOfIteratorHelper(effectiveChanged),
|
|
12505
|
+
_step5;
|
|
12506
|
+
try {
|
|
12507
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
12508
|
+
var _a2 = _step5.value;
|
|
12509
|
+
if (_a2 !== address) this._queueNotification(_a2);
|
|
12510
|
+
}
|
|
12511
|
+
} catch (err) {
|
|
12512
|
+
_iterator5.e(err);
|
|
12513
|
+
} finally {
|
|
12514
|
+
_iterator5.f();
|
|
12515
|
+
}
|
|
12516
|
+
return {
|
|
12517
|
+
changed: true,
|
|
12518
|
+
desiredChanged: address,
|
|
12519
|
+
effectiveChanged: effectiveChanged
|
|
12520
|
+
};
|
|
12521
|
+
}
|
|
12522
|
+
|
|
12523
|
+
/**
|
|
12524
|
+
* Addresses whose effective value a write to `address` could change: the
|
|
12525
|
+
* address itself, plus gate dependents (all keys for plant power, member
|
|
12526
|
+
* keys for group power).
|
|
12527
|
+
* @param {string} address
|
|
12528
|
+
* @returns {string[]}
|
|
12529
|
+
*/
|
|
12530
|
+
}, {
|
|
12531
|
+
key: "_affectedByWrite",
|
|
12532
|
+
value: function _affectedByWrite(address) {
|
|
12533
|
+
var affected = new Set([address]);
|
|
12534
|
+
if (address === makeAddress('plant', 'power')) {
|
|
12535
|
+
var _iterator6 = _createForOfIteratorHelper(this._desired.keys()),
|
|
12536
|
+
_step6;
|
|
12537
|
+
try {
|
|
12538
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
12539
|
+
var key = _step6.value;
|
|
12540
|
+
affected.add(key);
|
|
12541
|
+
}
|
|
12542
|
+
} catch (err) {
|
|
12543
|
+
_iterator6.e(err);
|
|
12544
|
+
} finally {
|
|
12545
|
+
_iterator6.f();
|
|
12546
|
+
}
|
|
12547
|
+
return _toConsumableArray(affected);
|
|
12548
|
+
}
|
|
12549
|
+
var groupMatch = /^group:([^:]+)::power$/.exec(address);
|
|
12550
|
+
if (groupMatch) {
|
|
12551
|
+
var members = this._groupMembers.get(groupMatch[1]);
|
|
12552
|
+
if (members) {
|
|
12553
|
+
var _iterator7 = _createForOfIteratorHelper(members),
|
|
12554
|
+
_step7;
|
|
12555
|
+
try {
|
|
12556
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
12557
|
+
var memberId = _step7.value;
|
|
12558
|
+
var _iterator8 = _createForOfIteratorHelper(this._desired.keys()),
|
|
12559
|
+
_step8;
|
|
12560
|
+
try {
|
|
12561
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
12562
|
+
var _key = _step8.value;
|
|
12563
|
+
if (_key.startsWith("".concat(memberId, "::"))) affected.add(_key);
|
|
12564
|
+
}
|
|
12565
|
+
} catch (err) {
|
|
12566
|
+
_iterator8.e(err);
|
|
12567
|
+
} finally {
|
|
12568
|
+
_iterator8.f();
|
|
12569
|
+
}
|
|
12570
|
+
}
|
|
12571
|
+
} catch (err) {
|
|
12572
|
+
_iterator7.e(err);
|
|
12573
|
+
} finally {
|
|
12574
|
+
_iterator7.f();
|
|
12575
|
+
}
|
|
12576
|
+
}
|
|
12577
|
+
}
|
|
12578
|
+
return _toConsumableArray(affected);
|
|
12579
|
+
}
|
|
12580
|
+
|
|
12581
|
+
/**
|
|
12582
|
+
* Walk declared chains triggered by this write (§10, §11.3). Desired-source
|
|
12583
|
+
* chains fire when the trigger's desired changed; effective-source chains fire
|
|
12584
|
+
* when the trigger's effective changed (so plant/group-off ripples through
|
|
12585
|
+
* `pump effective → pipe flow`, §17). Only `copy` is supported.
|
|
12586
|
+
*
|
|
12587
|
+
* @param {{ desiredChanged?: string, effectiveChanged?: Set<string> }} result
|
|
12588
|
+
* @param {number} depth
|
|
12589
|
+
* @param {{ visited: Map<string, *> }} ctx
|
|
12590
|
+
*/
|
|
12591
|
+
}, {
|
|
12592
|
+
key: "_walkChains",
|
|
12593
|
+
value: function _walkChains(result, depth, ctx) {
|
|
12594
|
+
if (this._chains.length === 0) return;
|
|
12595
|
+
var _iterator9 = _createForOfIteratorHelper(this._chains),
|
|
12596
|
+
_step9;
|
|
12597
|
+
try {
|
|
12598
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
12599
|
+
var _result$effectiveChan;
|
|
12600
|
+
var chain = _step9.value;
|
|
12601
|
+
if (chain.action && chain.action !== 'copy') continue;
|
|
12602
|
+
var trigger = makeAddress(chain.when.address, chain.when.attribute);
|
|
12603
|
+
var useEffective = chain.when.source === 'effective';
|
|
12604
|
+
var fires = useEffective ? (_result$effectiveChan = result.effectiveChanged) === null || _result$effectiveChan === void 0 ? void 0 : _result$effectiveChan.has(trigger) : result.desiredChanged === trigger;
|
|
12605
|
+
if (!fires) continue;
|
|
12606
|
+
if (depth + 1 > this.maxChainDepth) {
|
|
12607
|
+
var _chain$id;
|
|
12608
|
+
console.warn("[StateEngine] ripple depth limit (".concat(this.maxChainDepth, ") reached at chain ") + "\"".concat((_chain$id = chain.id) !== null && _chain$id !== void 0 ? _chain$id : '<unnamed>', "\" (").concat(trigger, " \u2192 ").concat(makeAddress(chain.set.address, chain.set.attribute), "); stopping path"));
|
|
12609
|
+
continue;
|
|
12610
|
+
}
|
|
12611
|
+
var nextValue = useEffective ? this.getEffective(trigger) : this.getDesired(trigger);
|
|
12612
|
+
var target = makeAddress(chain.set.address, chain.set.attribute);
|
|
12613
|
+
this._writeHop(target, nextValue, depth + 1, ctx);
|
|
12614
|
+
}
|
|
12615
|
+
} catch (err) {
|
|
12616
|
+
_iterator9.e(err);
|
|
12617
|
+
} finally {
|
|
12618
|
+
_iterator9.f();
|
|
12619
|
+
}
|
|
12620
|
+
}
|
|
12621
|
+
|
|
12622
|
+
// ─── Pipe lifecycle (Framework §9, §15) ─────────────────────────────────────
|
|
12623
|
+
|
|
12624
|
+
/**
|
|
12625
|
+
* Register a pipe/path in the registry so its flow state exists and is tied to
|
|
12626
|
+
* pipe lifecycle (no orphan keys). Keyed on the pipe/path id, never on segment
|
|
12627
|
+
* declared/computed status (§9). No-op if already registered.
|
|
12628
|
+
* @param {string} pipeId
|
|
12629
|
+
* @param {object} [options]
|
|
12630
|
+
* @param {boolean} [options.defaultFlow=false]
|
|
12631
|
+
* @returns {string} the flow address
|
|
12632
|
+
*/
|
|
12633
|
+
}, {
|
|
12634
|
+
key: "registerPipe",
|
|
12635
|
+
value: function registerPipe(pipeId) {
|
|
12636
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
12637
|
+
var address = makeAddress(pipeId, 'flow');
|
|
12638
|
+
if (!this._desired.has(address)) {
|
|
12639
|
+
var _options$defaultFlow;
|
|
12640
|
+
this._beginBatch();
|
|
12641
|
+
this._desired.set(address, Boolean((_options$defaultFlow = options.defaultFlow) !== null && _options$defaultFlow !== void 0 ? _options$defaultFlow : false));
|
|
12642
|
+
this._queueNotification(address);
|
|
12643
|
+
this._endBatch();
|
|
12644
|
+
}
|
|
12645
|
+
return address;
|
|
12646
|
+
}
|
|
12647
|
+
|
|
12648
|
+
/**
|
|
12649
|
+
* Remove a pipe and all of its registry entries when the pipe is destroyed, so
|
|
12650
|
+
* keys do not linger (§15). Emits a final notification (desired/effective
|
|
12651
|
+
* undefined) per removed key.
|
|
12652
|
+
* @param {string} pipeId
|
|
12653
|
+
* @returns {number} number of registry keys removed
|
|
12654
|
+
*/
|
|
12655
|
+
}, {
|
|
12656
|
+
key: "unregisterPipe",
|
|
12657
|
+
value: function unregisterPipe(pipeId) {
|
|
12658
|
+
var prefix = "".concat(pipeId, "::");
|
|
12659
|
+
var removed = _toConsumableArray(this._desired.keys()).filter(function (k) {
|
|
12660
|
+
return k.startsWith(prefix);
|
|
12661
|
+
});
|
|
12662
|
+
if (removed.length === 0) return 0;
|
|
12663
|
+
this._beginBatch();
|
|
12664
|
+
var _iterator0 = _createForOfIteratorHelper(removed),
|
|
12665
|
+
_step0;
|
|
12666
|
+
try {
|
|
12667
|
+
for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
|
|
12668
|
+
var key = _step0.value;
|
|
12669
|
+
this._desired.delete(key);
|
|
12670
|
+
}
|
|
12671
|
+
} catch (err) {
|
|
12672
|
+
_iterator0.e(err);
|
|
12673
|
+
} finally {
|
|
12674
|
+
_iterator0.f();
|
|
12675
|
+
}
|
|
12676
|
+
var _iterator1 = _createForOfIteratorHelper(removed),
|
|
12677
|
+
_step1;
|
|
12678
|
+
try {
|
|
12679
|
+
for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
|
|
12680
|
+
var _key2 = _step1.value;
|
|
12681
|
+
this._queueNotification(_key2);
|
|
12682
|
+
}
|
|
12683
|
+
} catch (err) {
|
|
12684
|
+
_iterator1.e(err);
|
|
12685
|
+
} finally {
|
|
12686
|
+
_iterator1.f();
|
|
12687
|
+
}
|
|
12688
|
+
this._endBatch();
|
|
12689
|
+
return removed.length;
|
|
12690
|
+
}
|
|
12691
|
+
}, {
|
|
12692
|
+
key: "_beginBatch",
|
|
12693
|
+
value: function _beginBatch() {
|
|
12694
|
+
this._batching = true;
|
|
12695
|
+
this._pendingNotifications.clear();
|
|
12696
|
+
}
|
|
12697
|
+
}, {
|
|
12698
|
+
key: "_queueNotification",
|
|
12699
|
+
value: function _queueNotification(address) {
|
|
12700
|
+
var change = {
|
|
12701
|
+
address: address,
|
|
12702
|
+
desired: this.getDesired(address),
|
|
12703
|
+
effective: this.getEffective(address)
|
|
12704
|
+
};
|
|
12705
|
+
if (this._batching) {
|
|
12706
|
+
this._pendingNotifications.set(address, change);
|
|
12707
|
+
} else {
|
|
12708
|
+
this._emit(change);
|
|
12709
|
+
}
|
|
12710
|
+
}
|
|
12711
|
+
}, {
|
|
12712
|
+
key: "_endBatch",
|
|
12713
|
+
value: function _endBatch() {
|
|
12714
|
+
this._batching = false;
|
|
12715
|
+
var changes = _toConsumableArray(this._pendingNotifications.values());
|
|
12716
|
+
this._pendingNotifications.clear();
|
|
12717
|
+
var _iterator10 = _createForOfIteratorHelper(changes),
|
|
12718
|
+
_step10;
|
|
12719
|
+
try {
|
|
12720
|
+
for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
|
|
12721
|
+
var change = _step10.value;
|
|
12722
|
+
this._emit(change);
|
|
12723
|
+
}
|
|
12724
|
+
} catch (err) {
|
|
12725
|
+
_iterator10.e(err);
|
|
12726
|
+
} finally {
|
|
12727
|
+
_iterator10.f();
|
|
12728
|
+
}
|
|
12729
|
+
}
|
|
12730
|
+
}, {
|
|
12731
|
+
key: "_emit",
|
|
12732
|
+
value: function _emit(change) {
|
|
12733
|
+
var _iterator11 = _createForOfIteratorHelper(this._listeners),
|
|
12734
|
+
_step11;
|
|
12735
|
+
try {
|
|
12736
|
+
for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
|
|
12737
|
+
var listener = _step11.value;
|
|
12738
|
+
try {
|
|
12739
|
+
listener(change);
|
|
12740
|
+
} catch (err) {
|
|
12741
|
+
console.error('[StateEngine] listener error:', err);
|
|
12742
|
+
}
|
|
12743
|
+
}
|
|
12744
|
+
} catch (err) {
|
|
12745
|
+
_iterator11.e(err);
|
|
12746
|
+
} finally {
|
|
12747
|
+
_iterator11.f();
|
|
12748
|
+
}
|
|
12749
|
+
}
|
|
12750
|
+
|
|
12751
|
+
/**
|
|
12752
|
+
* @param {(change: { address: string, desired: *, effective: * }) => void} listener
|
|
12753
|
+
* @returns {() => void} unsubscribe
|
|
12754
|
+
*/
|
|
12755
|
+
}, {
|
|
12756
|
+
key: "subscribe",
|
|
12757
|
+
value: function subscribe(listener) {
|
|
12758
|
+
var _this = this;
|
|
12759
|
+
this._listeners.add(listener);
|
|
12760
|
+
return function () {
|
|
12761
|
+
return _this._listeners.delete(listener);
|
|
12762
|
+
};
|
|
12763
|
+
}
|
|
12764
|
+
|
|
12765
|
+
/**
|
|
12766
|
+
* Snapshot of all desired values (for save / debug).
|
|
12767
|
+
* @returns {Record<string, *>}
|
|
12768
|
+
*/
|
|
12769
|
+
}, {
|
|
12770
|
+
key: "toRegistry",
|
|
12771
|
+
value: function toRegistry() {
|
|
12772
|
+
var out = {};
|
|
12773
|
+
var _iterator12 = _createForOfIteratorHelper(this._desired),
|
|
12774
|
+
_step12;
|
|
12775
|
+
try {
|
|
12776
|
+
for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
|
|
12777
|
+
var _step12$value = _slicedToArray(_step12.value, 2),
|
|
12778
|
+
k = _step12$value[0],
|
|
12779
|
+
v = _step12$value[1];
|
|
12780
|
+
out[k] = v;
|
|
12781
|
+
}
|
|
12782
|
+
} catch (err) {
|
|
12783
|
+
_iterator12.e(err);
|
|
12784
|
+
} finally {
|
|
12785
|
+
_iterator12.f();
|
|
12786
|
+
}
|
|
12787
|
+
return out;
|
|
12788
|
+
}
|
|
12789
|
+
|
|
12790
|
+
/**
|
|
12791
|
+
* Reconstruct the §17 scene declaration (plant / groups / chains /
|
|
12792
|
+
* stateRegistry) from current engine state, for scene save. Round-trips with
|
|
12793
|
+
* {@link StateEngine#load}.
|
|
12794
|
+
* @returns {{ version: string, plant: object, groups: object, chains: object[], stateRegistry: Record<string, *> }}
|
|
12795
|
+
*/
|
|
12796
|
+
}, {
|
|
12797
|
+
key: "toDeclaration",
|
|
12798
|
+
value: function toDeclaration() {
|
|
12799
|
+
var groups = {};
|
|
12800
|
+
var _iterator13 = _createForOfIteratorHelper(this._groupMembers),
|
|
12801
|
+
_step13;
|
|
12802
|
+
try {
|
|
12803
|
+
for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) {
|
|
12804
|
+
var _this$_groupDesired$g;
|
|
12805
|
+
var _step13$value = _slicedToArray(_step13.value, 2),
|
|
12806
|
+
groupId = _step13$value[0],
|
|
12807
|
+
members = _step13$value[1];
|
|
12808
|
+
groups[groupId] = {
|
|
12809
|
+
members: _toConsumableArray(members),
|
|
12810
|
+
states: {
|
|
12811
|
+
power: {
|
|
12812
|
+
type: 'Boolean',
|
|
12813
|
+
default: (_this$_groupDesired$g = this._groupDesired.get(groupId)) !== null && _this$_groupDesired$g !== void 0 ? _this$_groupDesired$g : true,
|
|
12814
|
+
gate: true
|
|
12815
|
+
}
|
|
12816
|
+
}
|
|
12817
|
+
};
|
|
12818
|
+
}
|
|
12819
|
+
} catch (err) {
|
|
12820
|
+
_iterator13.e(err);
|
|
12821
|
+
} finally {
|
|
12822
|
+
_iterator13.f();
|
|
12823
|
+
}
|
|
12824
|
+
return {
|
|
12825
|
+
version: '3.0',
|
|
12826
|
+
plant: {
|
|
12827
|
+
states: {
|
|
12828
|
+
power: {
|
|
12829
|
+
type: 'Boolean',
|
|
12830
|
+
default: this._plantPowerDesired,
|
|
12831
|
+
gate: true
|
|
12832
|
+
}
|
|
12833
|
+
}
|
|
12834
|
+
},
|
|
12835
|
+
groups: groups,
|
|
12836
|
+
chains: _toConsumableArray(this._chains),
|
|
12837
|
+
stateRegistry: this.toRegistry()
|
|
12838
|
+
};
|
|
12839
|
+
}
|
|
12840
|
+
}]);
|
|
12841
|
+
}();
|
|
12842
|
+
|
|
12843
|
+
/**
|
|
12844
|
+
* Registry address for an I/O device data point.
|
|
12845
|
+
* Shape: `${parentUuid}::${attachmentId}::${stateId}` (matches §17
|
|
12846
|
+
* `pump-001::start-switch::mode`). The scoped key is `${parentUuid}::${attachmentId}`.
|
|
12847
|
+
*
|
|
12848
|
+
* @param {string} attachmentId
|
|
12849
|
+
* @param {string} stateId
|
|
12850
|
+
* @param {string} [parentUuid]
|
|
12851
|
+
* @returns {string}
|
|
12852
|
+
*/
|
|
12853
|
+
function ioAddress(attachmentId, stateId, parentUuid) {
|
|
12854
|
+
return makeAddress(getScopedAttachmentKey(attachmentId, parentUuid), stateId);
|
|
12855
|
+
}
|
|
12856
|
+
|
|
12857
|
+
/**
|
|
12858
|
+
* Parse an I/O device address back into its parts. Returns null for
|
|
12859
|
+
* plant/group/object/pipe addresses (2-part) — I/O device data points are 3-part
|
|
12860
|
+
* because a device always has a parent component (§8; free-floating I/O rejected).
|
|
12861
|
+
*
|
|
12862
|
+
* @param {string} address
|
|
12863
|
+
* @returns {{ parentUuid: string, attachmentId: string, stateId: string, scopedKey: string } | null}
|
|
12864
|
+
*/
|
|
12865
|
+
function parseIoAddress(address) {
|
|
12866
|
+
if (typeof address !== 'string') return null;
|
|
12867
|
+
var parts = address.split('::');
|
|
12868
|
+
if (parts.length !== 3) return null;
|
|
12869
|
+
var _parts = _slicedToArray(parts, 3),
|
|
12870
|
+
parentUuid = _parts[0],
|
|
12871
|
+
attachmentId = _parts[1],
|
|
12872
|
+
stateId = _parts[2];
|
|
12873
|
+
return {
|
|
12874
|
+
parentUuid: parentUuid,
|
|
12875
|
+
attachmentId: attachmentId,
|
|
12876
|
+
stateId: stateId,
|
|
12877
|
+
scopedKey: "".concat(parentUuid, "::").concat(attachmentId)
|
|
12878
|
+
};
|
|
12879
|
+
}
|
|
12880
|
+
|
|
12881
|
+
/**
|
|
12882
|
+
* Read a State Engine declaration out of scene data, tolerating legacy scenes.
|
|
12883
|
+
* v3.0 scenes carry `plant` / `groups` / `chains` / `stateRegistry`; older v2.3
|
|
12884
|
+
* scenes have none of these and load with safe defaults (plant power on, no
|
|
12885
|
+
* groups, no chains) so nothing crashes on unknown/missing fields (§15).
|
|
12886
|
+
*
|
|
12887
|
+
* @param {object} [sceneData]
|
|
12888
|
+
* @returns {{ plant: object, groups: object, chains: object[], stateRegistry: object }}
|
|
12889
|
+
*/
|
|
12890
|
+
function extractDeclaration() {
|
|
12891
|
+
var sceneData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
12892
|
+
return {
|
|
12893
|
+
plant: sceneData.plant || {
|
|
12894
|
+
states: {
|
|
12895
|
+
power: {
|
|
12896
|
+
type: 'Boolean',
|
|
12897
|
+
default: true,
|
|
12898
|
+
gate: true
|
|
12899
|
+
}
|
|
12900
|
+
}
|
|
12901
|
+
},
|
|
12902
|
+
groups: sceneData.groups || {},
|
|
12903
|
+
chains: Array.isArray(sceneData.chains) ? sceneData.chains : [],
|
|
12904
|
+
stateRegistry: sceneData.stateRegistry || {}
|
|
12905
|
+
};
|
|
12906
|
+
}
|
|
12907
|
+
|
|
12908
|
+
/**
|
|
12909
|
+
* Merge a State Engine's current declaration (plant / groups / chains /
|
|
12910
|
+
* stateRegistry) into an export object, bumping it to version 3.0. Mutates and
|
|
12911
|
+
* returns `exportData`.
|
|
12912
|
+
*
|
|
12913
|
+
* @param {object} exportData - the scene export object being assembled
|
|
12914
|
+
* @param {{ toDeclaration: () => object }} engine
|
|
12915
|
+
* @returns {object} exportData
|
|
12916
|
+
*/
|
|
12917
|
+
function applyDeclarationToExport(exportData, engine) {
|
|
12918
|
+
if (!exportData || !engine || typeof engine.toDeclaration !== 'function') return exportData;
|
|
12919
|
+
var declaration = engine.toDeclaration();
|
|
12920
|
+
exportData.version = '3.0';
|
|
12921
|
+
exportData.plant = declaration.plant;
|
|
12922
|
+
exportData.groups = declaration.groups;
|
|
12923
|
+
if (declaration.chains.length > 0) exportData.chains = declaration.chains;
|
|
12924
|
+
exportData.stateRegistry = declaration.stateRegistry;
|
|
12925
|
+
return exportData;
|
|
12926
|
+
}
|
|
12927
|
+
|
|
12117
12928
|
var SceneExportManager = /*#__PURE__*/function () {
|
|
12118
12929
|
function SceneExportManager(sceneViewer) {
|
|
12119
12930
|
_classCallCheck(this, SceneExportManager);
|
|
@@ -12167,7 +12978,8 @@ var SceneExportManager = /*#__PURE__*/function () {
|
|
|
12167
12978
|
key: "exportSceneData",
|
|
12168
12979
|
value: function exportSceneData() {
|
|
12169
12980
|
var _this = this,
|
|
12170
|
-
_this$sceneViewer$cur2
|
|
12981
|
+
_this$sceneViewer$cur2,
|
|
12982
|
+
_this$sceneViewer$cen;
|
|
12171
12983
|
console.log('📤 Starting scene export...');
|
|
12172
12984
|
if (!this.sceneViewer.scene) {
|
|
12173
12985
|
console.warn('⚠️ No scene available for export');
|
|
@@ -12400,6 +13212,13 @@ var SceneExportManager = /*#__PURE__*/function () {
|
|
|
12400
13212
|
if (sceneBehaviors && sceneBehaviors.length > 0) {
|
|
12401
13213
|
exportData.behaviors = sceneBehaviors;
|
|
12402
13214
|
}
|
|
13215
|
+
|
|
13216
|
+
// Persist plant / groups / chains / stateRegistry from the State Engine and
|
|
13217
|
+
// bump the format to 3.0 (Framework §12; no-op if no engine present).
|
|
13218
|
+
var stateEngine = (_this$sceneViewer$cen = this.sceneViewer.centralPlant) === null || _this$sceneViewer$cen === void 0 ? void 0 : _this$sceneViewer$cen.stateEngine;
|
|
13219
|
+
if (stateEngine) {
|
|
13220
|
+
applyDeclarationToExport(exportData, stateEngine);
|
|
13221
|
+
}
|
|
12403
13222
|
console.log('✅ Scene export completed:', exportData);
|
|
12404
13223
|
console.log("\uD83D\uDCCA Exported ".concat(sceneChildren.length, " components and ").concat(exportData.connections.length, " connections"));
|
|
12405
13224
|
return exportData;
|
|
@@ -28239,6 +29058,21 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
28239
29058
|
}
|
|
28240
29059
|
}
|
|
28241
29060
|
|
|
29061
|
+
/**
|
|
29062
|
+
* Read the current color of a path's shared material as a `#rrggbb` string,
|
|
29063
|
+
* or null if the path has no material. Used to snapshot a pipe's baseline
|
|
29064
|
+
* colour before it is temporarily overpainted (e.g. flow gating).
|
|
29065
|
+
*
|
|
29066
|
+
* @param {string} pathId - "${from}-->${to}"
|
|
29067
|
+
* @returns {string|null}
|
|
29068
|
+
*/
|
|
29069
|
+
}, {
|
|
29070
|
+
key: "getPathColorHex",
|
|
29071
|
+
value: function getPathColorHex(pathId) {
|
|
29072
|
+
var mat = this._pathMaterials.get(pathId);
|
|
29073
|
+
return mat ? "#".concat(mat.color.getHexString()) : null;
|
|
29074
|
+
}
|
|
29075
|
+
|
|
28242
29076
|
/**
|
|
28243
29077
|
* Get path colors for visual distinction
|
|
28244
29078
|
* @param {number} index - Path index
|
|
@@ -30770,6 +31604,9 @@ var TEMP_MIN = 0;
|
|
|
30770
31604
|
/** Maximum temperature in the colour ramp (maps to pure red). */
|
|
30771
31605
|
var TEMP_MAX = 100;
|
|
30772
31606
|
|
|
31607
|
+
/** Colour a pipe is painted when its effective flow is gated off (§6.1). */
|
|
31608
|
+
var PIPE_OFF_GRAY = '#3a3a3a';
|
|
31609
|
+
|
|
30773
31610
|
/**
|
|
30774
31611
|
* Convert a temperature value to a CSS hex colour string using a blue→red HSL
|
|
30775
31612
|
* ramp. Values are clamped to [TEMP_MIN, TEMP_MAX].
|
|
@@ -30807,22 +31644,78 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
30807
31644
|
* @type {Map<string, Record<string, any>>}
|
|
30808
31645
|
*/
|
|
30809
31646
|
_this._derivedStore = new Map();
|
|
31647
|
+
|
|
31648
|
+
/**
|
|
31649
|
+
* Paths whose effective flow is gated off (State Engine §6.1). While a path
|
|
31650
|
+
* is in this set it is painted {@link PIPE_OFF_GRAY} and temperature updates
|
|
31651
|
+
* are suppressed so they don't override the off-look.
|
|
31652
|
+
* @type {Set<string>}
|
|
31653
|
+
*/
|
|
31654
|
+
_this._flowGatedOff = new Set();
|
|
31655
|
+
|
|
31656
|
+
/**
|
|
31657
|
+
* Baseline (on-state) colour snapshotted the moment a path is gated off, so
|
|
31658
|
+
* it can be restored exactly when flow returns — including pipes with no
|
|
31659
|
+
* declared flowTemperature, whose colour is the base pipe material.
|
|
31660
|
+
* @type {Map<string, string>}
|
|
31661
|
+
*/
|
|
31662
|
+
_this._flowBaseColor = new Map();
|
|
30810
31663
|
return _this;
|
|
30811
31664
|
}
|
|
30812
31665
|
|
|
30813
|
-
// ──
|
|
31666
|
+
// ── Flow gating (State Engine effective → 3D bridge, §6.1) ─────────────────
|
|
30814
31667
|
|
|
30815
31668
|
/**
|
|
30816
|
-
*
|
|
30817
|
-
*
|
|
30818
|
-
*
|
|
31669
|
+
* Gate a path's flow visual on/off from the State Engine's effective `flow`.
|
|
31670
|
+
* Off → paint the shared per-path material gray; on → restore the temperature
|
|
31671
|
+
* colour. The off state is sticky (see {@link _flowGatedOff}).
|
|
30819
31672
|
*
|
|
30820
|
-
* @param {string} pathId
|
|
30821
|
-
* @param {
|
|
30822
|
-
* @param {any} value
|
|
31673
|
+
* @param {string} pathId - `${from}-->${to}`
|
|
31674
|
+
* @param {boolean} enabled - Effective flow
|
|
30823
31675
|
*/
|
|
30824
31676
|
_inherits(PathFlowManager, _BaseDisposable);
|
|
30825
31677
|
return _createClass(PathFlowManager, [{
|
|
31678
|
+
key: "setFlowEnabled",
|
|
31679
|
+
value: function setFlowEnabled(pathId, enabled) {
|
|
31680
|
+
var split = this._splitPathId(pathId);
|
|
31681
|
+
if (!split) return;
|
|
31682
|
+
var renderingManager = this._getRenderingManager();
|
|
31683
|
+
if (enabled) {
|
|
31684
|
+
this._flowGatedOff.delete(pathId);
|
|
31685
|
+
// Prefer a temperature-derived colour if one is declared; otherwise
|
|
31686
|
+
// restore the baseline snapshotted when the pipe was gated off (this is
|
|
31687
|
+
// what fixes plain, non-temperature pipes getting stuck gray).
|
|
31688
|
+
var temp = this.resolve(pathId, 'flowTemperature');
|
|
31689
|
+
if (temp !== null && temp !== undefined) {
|
|
31690
|
+
this.applyVisualizationForPath(pathId);
|
|
31691
|
+
} else if (this._flowBaseColor.has(pathId)) {
|
|
31692
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, this._flowBaseColor.get(pathId));
|
|
31693
|
+
}
|
|
31694
|
+
this._flowBaseColor.delete(pathId);
|
|
31695
|
+
} else {
|
|
31696
|
+
// Snapshot the current (on-state) colour once, before overpainting gray.
|
|
31697
|
+
if (!this._flowGatedOff.has(pathId) && renderingManager) {
|
|
31698
|
+
var _renderingManager$get;
|
|
31699
|
+
var base = (_renderingManager$get = renderingManager.getPathColorHex) === null || _renderingManager$get === void 0 ? void 0 : _renderingManager$get.call(renderingManager, pathId);
|
|
31700
|
+
if (base) this._flowBaseColor.set(pathId, base);
|
|
31701
|
+
}
|
|
31702
|
+
this._flowGatedOff.add(pathId);
|
|
31703
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, PIPE_OFF_GRAY);
|
|
31704
|
+
}
|
|
31705
|
+
}
|
|
31706
|
+
|
|
31707
|
+
// ── Public API — attribute write ──────────────────────────────────────────
|
|
31708
|
+
|
|
31709
|
+
/**
|
|
31710
|
+
* Set a declared (persistent) flow attribute on a path.
|
|
31711
|
+
* The value is stored in PathData and will be serialised with the scene.
|
|
31712
|
+
* Triggers a visual update.
|
|
31713
|
+
*
|
|
31714
|
+
* @param {string} pathId - e.g. "PUMP-1-CONN-1-->CHILLER-1-CONN-2"
|
|
31715
|
+
* @param {string} key - One of FLOW_ATTRIBUTE_KEYS
|
|
31716
|
+
* @param {any} value
|
|
31717
|
+
*/
|
|
31718
|
+
}, {
|
|
30826
31719
|
key: "setDeclared",
|
|
30827
31720
|
value: function setDeclared(pathId, key, value) {
|
|
30828
31721
|
var pd = this._getPathData(pathId);
|
|
@@ -30928,6 +31821,13 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
30928
31821
|
}, {
|
|
30929
31822
|
key: "applyVisualizationForPath",
|
|
30930
31823
|
value: function applyVisualizationForPath(pathId) {
|
|
31824
|
+
// Effective flow gated off (§6.1) — keep the off-look, ignore temperature.
|
|
31825
|
+
if (this._flowGatedOff.has(pathId)) {
|
|
31826
|
+
var _this$_getRenderingMa;
|
|
31827
|
+
var _split = this._splitPathId(pathId);
|
|
31828
|
+
if (_split) (_this$_getRenderingMa = this._getRenderingManager()) === null || _this$_getRenderingMa === void 0 || _this$_getRenderingMa.updatePathColor(_split.from, _split.to, PIPE_OFF_GRAY);
|
|
31829
|
+
return;
|
|
31830
|
+
}
|
|
30931
31831
|
var temp = this.resolve(pathId, 'flowTemperature');
|
|
30932
31832
|
if (temp === null || temp === undefined) {
|
|
30933
31833
|
return; // No temperature declared — leave default pipe color
|
|
@@ -30935,17 +31835,30 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
30935
31835
|
var color = temperatureToColor(temp);
|
|
30936
31836
|
var renderingManager = this._getRenderingManager();
|
|
30937
31837
|
if (!renderingManager) return;
|
|
31838
|
+
var split = this._splitPathId(pathId);
|
|
31839
|
+
if (!split) return;
|
|
31840
|
+
renderingManager.updatePathColor(split.from, split.to, color);
|
|
31841
|
+
console.log("\uD83C\uDF21\uFE0F PathFlowManager: \"".concat(pathId, "\" \u2192 flowTemperature ").concat(temp, " \u2192 ").concat(color));
|
|
31842
|
+
}
|
|
30938
31843
|
|
|
30939
|
-
|
|
31844
|
+
/**
|
|
31845
|
+
* Split a `${from}-->${to}` pathId into its two connector halves.
|
|
31846
|
+
* @param {string} pathId
|
|
31847
|
+
* @returns {{ from: string, to: string }|null} null if malformed
|
|
31848
|
+
* @private
|
|
31849
|
+
*/
|
|
31850
|
+
}, {
|
|
31851
|
+
key: "_splitPathId",
|
|
31852
|
+
value: function _splitPathId(pathId) {
|
|
30940
31853
|
var sepIdx = pathId.indexOf('-->');
|
|
30941
31854
|
if (sepIdx === -1) {
|
|
30942
31855
|
console.warn("PathFlowManager: malformed pathId \"".concat(pathId, "\""));
|
|
30943
|
-
return;
|
|
31856
|
+
return null;
|
|
30944
31857
|
}
|
|
30945
|
-
|
|
30946
|
-
|
|
30947
|
-
|
|
30948
|
-
|
|
31858
|
+
return {
|
|
31859
|
+
from: pathId.slice(0, sepIdx),
|
|
31860
|
+
to: pathId.slice(sepIdx + 3)
|
|
31861
|
+
};
|
|
30949
31862
|
}
|
|
30950
31863
|
|
|
30951
31864
|
/**
|
|
@@ -30977,6 +31890,8 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
30977
31890
|
key: "dispose",
|
|
30978
31891
|
value: function dispose() {
|
|
30979
31892
|
this._derivedStore.clear();
|
|
31893
|
+
this._flowGatedOff.clear();
|
|
31894
|
+
this._flowBaseColor.clear();
|
|
30980
31895
|
this.sceneViewer = null;
|
|
30981
31896
|
_superPropGet(PathFlowManager, "dispose", this, 3)([]);
|
|
30982
31897
|
}
|
|
@@ -37899,7 +38814,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
37899
38814
|
if (!ioDeviceObject || !this._stateAdapter) return;
|
|
37900
38815
|
var ud = ioDeviceObject.userData;
|
|
37901
38816
|
var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
|
|
37902
|
-
var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
37903
38817
|
if (!attachmentId) return;
|
|
37904
38818
|
|
|
37905
38819
|
// Walk up to find parent component UUID for scoped state/behavior handling
|
|
@@ -37914,6 +38828,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
37914
38828
|
obj = obj.parent;
|
|
37915
38829
|
}
|
|
37916
38830
|
|
|
38831
|
+
// Derive the device's data points from its behaviorConfig (same resolver the
|
|
38832
|
+
// drag path uses). `userData.dataPoints` is not populated for attached
|
|
38833
|
+
// devices, so reading it directly would find nothing — resolve instead, and
|
|
38834
|
+
// keep the legacy field only as a fallback.
|
|
38835
|
+
var resolved = resolveDataPoints(parentUuid, attachmentId, ud, getIoBehaviorManager(this.sceneViewer), null);
|
|
38836
|
+
var dataPoints = resolved !== null && resolved !== void 0 && resolved.length ? resolved : (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
38837
|
+
|
|
37917
38838
|
// Create a scoped attachment key to prevent state sharing between instances
|
|
37918
38839
|
// of the same smart component that share the same attachmentId
|
|
37919
38840
|
var scopedAttachmentId = getScopedAttachmentKey(attachmentId, parentUuid);
|
|
@@ -40328,6 +41249,611 @@ var IoOutlineManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
40328
41249
|
}]);
|
|
40329
41250
|
}(BaseDisposable);
|
|
40330
41251
|
|
|
41252
|
+
/** How far the albedo is pulled toward black for a gated-off mesh. */
|
|
41253
|
+
var OFF_COLOR_SCALE = 0.2;
|
|
41254
|
+
var EffectiveVisualManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
41255
|
+
/**
|
|
41256
|
+
* @param {Object} sceneViewer - The central sceneViewer hub
|
|
41257
|
+
*/
|
|
41258
|
+
function EffectiveVisualManager(sceneViewer) {
|
|
41259
|
+
var _this;
|
|
41260
|
+
_classCallCheck(this, EffectiveVisualManager);
|
|
41261
|
+
_this = _callSuper(this, EffectiveVisualManager);
|
|
41262
|
+
_this.sceneViewer = sceneViewer;
|
|
41263
|
+
|
|
41264
|
+
/**
|
|
41265
|
+
* Materials this manager has gated (cloned + cached), for restore on dispose.
|
|
41266
|
+
* @type {Set<THREE.Material>}
|
|
41267
|
+
*/
|
|
41268
|
+
_this._gatedMaterials = new Set();
|
|
41269
|
+
|
|
41270
|
+
// Bound handlers so we can unsubscribe in _doDispose().
|
|
41271
|
+
_this._onChange = _this._onChange.bind(_this);
|
|
41272
|
+
_this._regateAll = _this._regateAll.bind(_this);
|
|
41273
|
+
if (sceneViewer !== null && sceneViewer !== void 0 && sceneViewer.on) {
|
|
41274
|
+
sceneViewer.on('engine-state-changed', _this._onChange);
|
|
41275
|
+
sceneViewer.on('scene-loaded', _this._regateAll);
|
|
41276
|
+
}
|
|
41277
|
+
return _this;
|
|
41278
|
+
}
|
|
41279
|
+
|
|
41280
|
+
// ── Engine change handling ──────────────────────────────────────────────────
|
|
41281
|
+
|
|
41282
|
+
/**
|
|
41283
|
+
* React to one committed registry change. Only 2-part object / pipe addresses
|
|
41284
|
+
* are gated; everything else is ignored.
|
|
41285
|
+
* @param {{ address: string, desired: *, effective: * }} change
|
|
41286
|
+
* @private
|
|
41287
|
+
*/
|
|
41288
|
+
_inherits(EffectiveVisualManager, _BaseDisposable);
|
|
41289
|
+
return _createClass(EffectiveVisualManager, [{
|
|
41290
|
+
key: "_onChange",
|
|
41291
|
+
value: function _onChange(change) {
|
|
41292
|
+
if (!change || typeof change.address !== 'string') return;
|
|
41293
|
+
var parts = change.address.split('::');
|
|
41294
|
+
if (parts.length !== 2) return; // 3-part I/O is desired-driven; skip
|
|
41295
|
+
|
|
41296
|
+
var _parts = _slicedToArray(parts, 2),
|
|
41297
|
+
scope = _parts[0],
|
|
41298
|
+
attr = _parts[1];
|
|
41299
|
+
if (scope === 'plant' || scope.startsWith('group:')) return; // members self-report
|
|
41300
|
+
|
|
41301
|
+
if (attr === 'power') {
|
|
41302
|
+
this._gateEquipment(scope, change.effective);
|
|
41303
|
+
} else if (attr === 'flow') {
|
|
41304
|
+
this._gatePipe(scope, change.effective);
|
|
41305
|
+
}
|
|
41306
|
+
}
|
|
41307
|
+
|
|
41308
|
+
/**
|
|
41309
|
+
* Re-gate the whole scene from current effective state. Used after scene load,
|
|
41310
|
+
* where StateEngine.load() populates the registry without emitting per-address
|
|
41311
|
+
* events (and the meshes have just been (re)built).
|
|
41312
|
+
* @private
|
|
41313
|
+
*/
|
|
41314
|
+
}, {
|
|
41315
|
+
key: "_regateAll",
|
|
41316
|
+
value: function _regateAll() {
|
|
41317
|
+
var _this$sceneViewer;
|
|
41318
|
+
var engine = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.centralPlant) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.stateEngine;
|
|
41319
|
+
if (!engine) return;
|
|
41320
|
+
var registry = engine.toRegistry();
|
|
41321
|
+
for (var _i = 0, _Object$keys = Object.keys(registry); _i < _Object$keys.length; _i++) {
|
|
41322
|
+
var address = _Object$keys[_i];
|
|
41323
|
+
var parts = address.split('::');
|
|
41324
|
+
if (parts.length !== 2) continue;
|
|
41325
|
+
var _parts2 = _slicedToArray(parts, 2),
|
|
41326
|
+
scope = _parts2[0],
|
|
41327
|
+
attr = _parts2[1];
|
|
41328
|
+
if (scope === 'plant' || scope.startsWith('group:')) continue;
|
|
41329
|
+
if (attr === 'power') {
|
|
41330
|
+
this._gateEquipment(scope, engine.getEffective(address));
|
|
41331
|
+
} else if (attr === 'flow') {
|
|
41332
|
+
this._gatePipe(scope, engine.getEffective(address));
|
|
41333
|
+
}
|
|
41334
|
+
}
|
|
41335
|
+
}
|
|
41336
|
+
|
|
41337
|
+
// ── Equipment gating ────────────────────────────────────────────────────────
|
|
41338
|
+
|
|
41339
|
+
/**
|
|
41340
|
+
* Darken (off) or restore (on) every mesh of an equipment object.
|
|
41341
|
+
* @param {string} objectId - Hardcoded object id (address scope)
|
|
41342
|
+
* @param {boolean} on - Effective power
|
|
41343
|
+
* @private
|
|
41344
|
+
*/
|
|
41345
|
+
}, {
|
|
41346
|
+
key: "_gateEquipment",
|
|
41347
|
+
value: function _gateEquipment(objectId, on) {
|
|
41348
|
+
var _this$sceneViewer2,
|
|
41349
|
+
_this2 = this;
|
|
41350
|
+
var scene = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.scene;
|
|
41351
|
+
if (!scene) return;
|
|
41352
|
+
var obj = findObjectByHardcodedUuid(scene, objectId);
|
|
41353
|
+
if (!obj || typeof obj.traverse !== 'function') return;
|
|
41354
|
+
obj.traverse(function (mesh) {
|
|
41355
|
+
if (!mesh.isMesh && !mesh.material) return;
|
|
41356
|
+
// Leave attached I/O control panels (switches) lit and operable even when
|
|
41357
|
+
// their host is powered off — you must be able to see and click the switch
|
|
41358
|
+
// that turns it back on. (traverse can't prune, so guard per mesh.)
|
|
41359
|
+
if (_this2._isUnderIoDevice(mesh, obj)) return;
|
|
41360
|
+
var materials = _this2._prepareMeshMaterials(mesh);
|
|
41361
|
+
var _iterator = _createForOfIteratorHelper(materials),
|
|
41362
|
+
_step;
|
|
41363
|
+
try {
|
|
41364
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
41365
|
+
var material = _step.value;
|
|
41366
|
+
_this2._applyGate(material, on);
|
|
41367
|
+
}
|
|
41368
|
+
} catch (err) {
|
|
41369
|
+
_iterator.e(err);
|
|
41370
|
+
} finally {
|
|
41371
|
+
_iterator.f();
|
|
41372
|
+
}
|
|
41373
|
+
});
|
|
41374
|
+
}
|
|
41375
|
+
|
|
41376
|
+
/**
|
|
41377
|
+
* True if `mesh` is an io-device or nested under one, walking up to (but not
|
|
41378
|
+
* past) the equipment root `stopAt`.
|
|
41379
|
+
* @param {THREE.Object3D} mesh
|
|
41380
|
+
* @param {THREE.Object3D} stopAt - equipment root to stop the upward walk at
|
|
41381
|
+
* @returns {boolean}
|
|
41382
|
+
* @private
|
|
41383
|
+
*/
|
|
41384
|
+
}, {
|
|
41385
|
+
key: "_isUnderIoDevice",
|
|
41386
|
+
value: function _isUnderIoDevice(mesh, stopAt) {
|
|
41387
|
+
var node = mesh;
|
|
41388
|
+
while (node) {
|
|
41389
|
+
var _node$userData;
|
|
41390
|
+
if (((_node$userData = node.userData) === null || _node$userData === void 0 ? void 0 : _node$userData.objectType) === 'io-device') return true;
|
|
41391
|
+
if (node === stopAt) break;
|
|
41392
|
+
node = node.parent;
|
|
41393
|
+
}
|
|
41394
|
+
return false;
|
|
41395
|
+
}
|
|
41396
|
+
|
|
41397
|
+
/**
|
|
41398
|
+
* Ensure a mesh's material(s) are cloned-on-write and have their baseline
|
|
41399
|
+
* cached, then return the material list to gate.
|
|
41400
|
+
* @param {THREE.Mesh} mesh
|
|
41401
|
+
* @returns {THREE.Material[]}
|
|
41402
|
+
* @private
|
|
41403
|
+
*/
|
|
41404
|
+
}, {
|
|
41405
|
+
key: "_prepareMeshMaterials",
|
|
41406
|
+
value: function _prepareMeshMaterials(mesh) {
|
|
41407
|
+
if (!mesh.material) return [];
|
|
41408
|
+
|
|
41409
|
+
// Clone-on-write once per mesh so shared library materials aren't shared.
|
|
41410
|
+
if (!mesh.userData._effectiveGateCloned) {
|
|
41411
|
+
if (Array.isArray(mesh.material)) {
|
|
41412
|
+
mesh.material = mesh.material.map(function (m) {
|
|
41413
|
+
return m !== null && m !== void 0 && m.clone ? m.clone() : m;
|
|
41414
|
+
});
|
|
41415
|
+
} else if (mesh.material.clone) {
|
|
41416
|
+
mesh.material = mesh.material.clone();
|
|
41417
|
+
}
|
|
41418
|
+
mesh.userData._effectiveGateCloned = true;
|
|
41419
|
+
}
|
|
41420
|
+
var materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
41421
|
+
var _iterator2 = _createForOfIteratorHelper(materials),
|
|
41422
|
+
_step2;
|
|
41423
|
+
try {
|
|
41424
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
41425
|
+
var _material$userData, _material$color$clone, _material$color, _material$color$clone2, _material$emissive$cl, _material$emissive, _material$emissive$cl2;
|
|
41426
|
+
var material = _step2.value;
|
|
41427
|
+
if (!material || (_material$userData = material.userData) !== null && _material$userData !== void 0 && _material$userData._effectiveGate) continue;
|
|
41428
|
+
material.userData = material.userData || {};
|
|
41429
|
+
material.userData._effectiveGate = {
|
|
41430
|
+
color: (_material$color$clone = (_material$color = material.color) === null || _material$color === void 0 || (_material$color$clone2 = _material$color.clone) === null || _material$color$clone2 === void 0 ? void 0 : _material$color$clone2.call(_material$color)) !== null && _material$color$clone !== void 0 ? _material$color$clone : null,
|
|
41431
|
+
emissive: (_material$emissive$cl = (_material$emissive = material.emissive) === null || _material$emissive === void 0 || (_material$emissive$cl2 = _material$emissive.clone) === null || _material$emissive$cl2 === void 0 ? void 0 : _material$emissive$cl2.call(_material$emissive)) !== null && _material$emissive$cl !== void 0 ? _material$emissive$cl : null,
|
|
41432
|
+
emissiveIntensity: typeof material.emissiveIntensity === 'number' ? material.emissiveIntensity : null
|
|
41433
|
+
};
|
|
41434
|
+
this._gatedMaterials.add(material);
|
|
41435
|
+
}
|
|
41436
|
+
} catch (err) {
|
|
41437
|
+
_iterator2.e(err);
|
|
41438
|
+
} finally {
|
|
41439
|
+
_iterator2.f();
|
|
41440
|
+
}
|
|
41441
|
+
return materials.filter(Boolean);
|
|
41442
|
+
}
|
|
41443
|
+
|
|
41444
|
+
/**
|
|
41445
|
+
* Darken or restore a single material from its cached baseline.
|
|
41446
|
+
* @param {THREE.Material} material
|
|
41447
|
+
* @param {boolean} on
|
|
41448
|
+
* @private
|
|
41449
|
+
*/
|
|
41450
|
+
}, {
|
|
41451
|
+
key: "_applyGate",
|
|
41452
|
+
value: function _applyGate(material, on) {
|
|
41453
|
+
var _material$userData2;
|
|
41454
|
+
var base = (_material$userData2 = material.userData) === null || _material$userData2 === void 0 ? void 0 : _material$userData2._effectiveGate;
|
|
41455
|
+
if (!base) return;
|
|
41456
|
+
if (on) {
|
|
41457
|
+
if (base.color && material.color) material.color.copy(base.color);
|
|
41458
|
+
if (base.emissive && material.emissive) material.emissive.copy(base.emissive);
|
|
41459
|
+
if (base.emissiveIntensity !== null) material.emissiveIntensity = base.emissiveIntensity;
|
|
41460
|
+
} else {
|
|
41461
|
+
if (base.color && material.color) material.color.copy(base.color).multiplyScalar(OFF_COLOR_SCALE);
|
|
41462
|
+
if (material.emissive) material.emissive.setScalar(0);
|
|
41463
|
+
if (typeof material.emissiveIntensity === 'number') material.emissiveIntensity = 0;
|
|
41464
|
+
}
|
|
41465
|
+
material.needsUpdate = true;
|
|
41466
|
+
}
|
|
41467
|
+
|
|
41468
|
+
// ── Pipe flow gating ────────────────────────────────────────────────────────
|
|
41469
|
+
|
|
41470
|
+
/**
|
|
41471
|
+
* Delegate pipe-flow gating to PathFlowManager (grays the shared per-path
|
|
41472
|
+
* material when off, restores the temperature colour when on).
|
|
41473
|
+
* @param {string} pathId - `${from}-->${to}`
|
|
41474
|
+
* @param {boolean} on - Effective flow
|
|
41475
|
+
* @private
|
|
41476
|
+
*/
|
|
41477
|
+
}, {
|
|
41478
|
+
key: "_gatePipe",
|
|
41479
|
+
value: function _gatePipe(pathId, on) {
|
|
41480
|
+
var _this$sceneViewer3, _pathFlowManager$setF;
|
|
41481
|
+
var pathFlowManager = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.pathFlowManager;
|
|
41482
|
+
pathFlowManager === null || pathFlowManager === void 0 || (_pathFlowManager$setF = pathFlowManager.setFlowEnabled) === null || _pathFlowManager$setF === void 0 || _pathFlowManager$setF.call(pathFlowManager, pathId, on);
|
|
41483
|
+
}
|
|
41484
|
+
|
|
41485
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
|
41486
|
+
}, {
|
|
41487
|
+
key: "_doDispose",
|
|
41488
|
+
value: function _doDispose() {
|
|
41489
|
+
var _this$sceneViewer4;
|
|
41490
|
+
if ((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.off) {
|
|
41491
|
+
this.sceneViewer.off('engine-state-changed', this._onChange);
|
|
41492
|
+
this.sceneViewer.off('scene-loaded', this._regateAll);
|
|
41493
|
+
}
|
|
41494
|
+
// Restore any still-gated materials to their cached baseline.
|
|
41495
|
+
var _iterator3 = _createForOfIteratorHelper(this._gatedMaterials),
|
|
41496
|
+
_step3;
|
|
41497
|
+
try {
|
|
41498
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
41499
|
+
var material = _step3.value;
|
|
41500
|
+
this._applyGate(material, true);
|
|
41501
|
+
}
|
|
41502
|
+
} catch (err) {
|
|
41503
|
+
_iterator3.e(err);
|
|
41504
|
+
} finally {
|
|
41505
|
+
_iterator3.f();
|
|
41506
|
+
}
|
|
41507
|
+
this._gatedMaterials.clear();
|
|
41508
|
+
this.sceneViewer = null;
|
|
41509
|
+
}
|
|
41510
|
+
}]);
|
|
41511
|
+
}(BaseDisposable);
|
|
41512
|
+
|
|
41513
|
+
/**
|
|
41514
|
+
* X offsets for `count` switches evenly spaced and centered on 0.
|
|
41515
|
+
* @param {number} count
|
|
41516
|
+
* @param {number} spacing
|
|
41517
|
+
* @returns {number[]}
|
|
41518
|
+
*/
|
|
41519
|
+
function computeSwitchRowX(count, spacing) {
|
|
41520
|
+
var start = -((count - 1) * spacing) / 2;
|
|
41521
|
+
return Array.from({
|
|
41522
|
+
length: count
|
|
41523
|
+
}, function (_, i) {
|
|
41524
|
+
return start + i * spacing;
|
|
41525
|
+
});
|
|
41526
|
+
}
|
|
41527
|
+
var ControlPanelManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
41528
|
+
/**
|
|
41529
|
+
* @param {Object} sceneViewer - The central sceneViewer hub
|
|
41530
|
+
*/
|
|
41531
|
+
function ControlPanelManager(sceneViewer) {
|
|
41532
|
+
var _this;
|
|
41533
|
+
_classCallCheck(this, ControlPanelManager);
|
|
41534
|
+
_this = _callSuper(this, ControlPanelManager);
|
|
41535
|
+
_this.sceneViewer = sceneViewer;
|
|
41536
|
+
_this._panel = null;
|
|
41537
|
+
_this._labels = [];
|
|
41538
|
+
_this._panelId = null;
|
|
41539
|
+
_this._build = _this._build.bind(_this);
|
|
41540
|
+
if (sceneViewer !== null && sceneViewer !== void 0 && sceneViewer.on) {
|
|
41541
|
+
sceneViewer.on('scene-loaded', _this._build);
|
|
41542
|
+
}
|
|
41543
|
+
return _this;
|
|
41544
|
+
}
|
|
41545
|
+
|
|
41546
|
+
/**
|
|
41547
|
+
* Build (or rebuild) the panel from the current scene's `controlPanel` spec.
|
|
41548
|
+
* @private
|
|
41549
|
+
*/
|
|
41550
|
+
_inherits(ControlPanelManager, _BaseDisposable);
|
|
41551
|
+
return _createClass(ControlPanelManager, [{
|
|
41552
|
+
key: "_build",
|
|
41553
|
+
value: (function () {
|
|
41554
|
+
var _build2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
41555
|
+
var _this$sceneViewer,
|
|
41556
|
+
_centralPlant$importe,
|
|
41557
|
+
_this$sceneViewer2,
|
|
41558
|
+
_b$width,
|
|
41559
|
+
_b$height,
|
|
41560
|
+
_b$thickness,
|
|
41561
|
+
_desk$tilt,
|
|
41562
|
+
_desk$height,
|
|
41563
|
+
_desk$legThickness,
|
|
41564
|
+
_desk$legInset,
|
|
41565
|
+
_spec$switchSpacing,
|
|
41566
|
+
_this$sceneViewer3,
|
|
41567
|
+
_spec$labelSize,
|
|
41568
|
+
_this2 = this;
|
|
41569
|
+
var centralPlant, spec, scene, panelId, panel, p, r, d2r, scale, b, width, depth, thickness, desk, tilt, deskHeight, legThickness, legInset, legColor, top, board, legMat, halfW, halfD, _i, _arr, ly, cornerZ, legH, yPos, _i2, _arr2, lx, leg, switches, spacing, xs, topZ, attachedDevices, componentData, switchScale, ioBehavMgr, engine, _iterator, _step, sw, current, lo, labelSize;
|
|
41570
|
+
return _regenerator().w(function (_context) {
|
|
41571
|
+
while (1) switch (_context.n) {
|
|
41572
|
+
case 0:
|
|
41573
|
+
this._teardown();
|
|
41574
|
+
centralPlant = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.centralPlant;
|
|
41575
|
+
spec = centralPlant === null || centralPlant === void 0 || (_centralPlant$importe = centralPlant.importedSceneData) === null || _centralPlant$importe === void 0 ? void 0 : _centralPlant$importe.controlPanel;
|
|
41576
|
+
scene = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.scene;
|
|
41577
|
+
if (!(!spec || !scene)) {
|
|
41578
|
+
_context.n = 1;
|
|
41579
|
+
break;
|
|
41580
|
+
}
|
|
41581
|
+
return _context.a(2);
|
|
41582
|
+
case 1:
|
|
41583
|
+
panelId = spec.id || 'CONTROL-PANEL-1';
|
|
41584
|
+
this._panelId = panelId;
|
|
41585
|
+
panel = new THREE__namespace.Group();
|
|
41586
|
+
panel.name = 'Control Panel';
|
|
41587
|
+
panel.uuid = panelId;
|
|
41588
|
+
panel.userData = {
|
|
41589
|
+
objectType: 'component',
|
|
41590
|
+
isControlPanel: true
|
|
41591
|
+
};
|
|
41592
|
+
p = spec.position || {};
|
|
41593
|
+
panel.position.set(p.x || 0, p.y || 0, p.z || 0);
|
|
41594
|
+
r = spec.rotation || {};
|
|
41595
|
+
d2r = Math.PI / 180;
|
|
41596
|
+
panel.rotation.set((r.x || 0) * d2r, (r.y || 0) * d2r, (r.z || 0) * d2r, 'XYZ');
|
|
41597
|
+
scale = Number.isFinite(spec.scale) && spec.scale > 0 ? spec.scale : 1;
|
|
41598
|
+
panel.scale.setScalar(scale);
|
|
41599
|
+
|
|
41600
|
+
// ── Drawing-desk geometry (Z-up scene) ───────────────────────────────────
|
|
41601
|
+
// A tilted top surface (the drafting board) raised on legs. Switches +
|
|
41602
|
+
// labels live on the tilted `top` group so they sit on the angled surface.
|
|
41603
|
+
b = spec.board || {};
|
|
41604
|
+
width = (_b$width = b.width) !== null && _b$width !== void 0 ? _b$width : 7; // x-extent of the top
|
|
41605
|
+
depth = (_b$height = b.height) !== null && _b$height !== void 0 ? _b$height : 2; // y-extent of the top (spec key kept as `height`)
|
|
41606
|
+
thickness = (_b$thickness = b.thickness) !== null && _b$thickness !== void 0 ? _b$thickness : 0.15;
|
|
41607
|
+
desk = spec.desk || {};
|
|
41608
|
+
tilt = ((_desk$tilt = desk.tilt) !== null && _desk$tilt !== void 0 ? _desk$tilt : 30) * d2r; // surface tilt; +raises the back (+y) edge
|
|
41609
|
+
deskHeight = (_desk$height = desk.height) !== null && _desk$height !== void 0 ? _desk$height : 1.3; // world height of the top's center
|
|
41610
|
+
legThickness = (_desk$legThickness = desk.legThickness) !== null && _desk$legThickness !== void 0 ? _desk$legThickness : 0.18;
|
|
41611
|
+
legInset = (_desk$legInset = desk.legInset) !== null && _desk$legInset !== void 0 ? _desk$legInset : 0.35;
|
|
41612
|
+
legColor = desk.legColor || '#22303c'; // Tilted top group: raised to deskHeight, rotated about X.
|
|
41613
|
+
top = new THREE__namespace.Group();
|
|
41614
|
+
top.position.set(0, 0, deskHeight);
|
|
41615
|
+
top.rotation.set(tilt, 0, 0, 'XYZ');
|
|
41616
|
+
panel.add(top);
|
|
41617
|
+
board = new THREE__namespace.Mesh(new THREE__namespace.BoxGeometry(width, depth, thickness), new THREE__namespace.MeshStandardMaterial({
|
|
41618
|
+
color: b.color || '#2c3e50',
|
|
41619
|
+
roughness: 0.6,
|
|
41620
|
+
metalness: 0.2
|
|
41621
|
+
}));
|
|
41622
|
+
board.userData = {
|
|
41623
|
+
objectType: 'control-panel-board'
|
|
41624
|
+
};
|
|
41625
|
+
top.add(board);
|
|
41626
|
+
|
|
41627
|
+
// Legs: one per corner, from the ground up to the tilted top's underside.
|
|
41628
|
+
legMat = new THREE__namespace.MeshStandardMaterial({
|
|
41629
|
+
color: legColor,
|
|
41630
|
+
roughness: 0.7,
|
|
41631
|
+
metalness: 0.1
|
|
41632
|
+
});
|
|
41633
|
+
halfW = Math.max(0.1, width / 2 - legInset);
|
|
41634
|
+
halfD = Math.max(0.1, depth / 2 - legInset);
|
|
41635
|
+
for (_i = 0, _arr = [halfD, -halfD]; _i < _arr.length; _i++) {
|
|
41636
|
+
ly = _arr[_i];
|
|
41637
|
+
// Underside height at this front/back row after the tilt.
|
|
41638
|
+
cornerZ = deskHeight + ly * Math.sin(tilt) - thickness / 2 * Math.cos(tilt);
|
|
41639
|
+
legH = Math.max(0.1, cornerZ);
|
|
41640
|
+
yPos = ly * Math.cos(tilt); // corner's y in panel space after tilt
|
|
41641
|
+
for (_i2 = 0, _arr2 = [halfW, -halfW]; _i2 < _arr2.length; _i2++) {
|
|
41642
|
+
lx = _arr2[_i2];
|
|
41643
|
+
leg = new THREE__namespace.Mesh(new THREE__namespace.BoxGeometry(legThickness, legThickness, legH), legMat);
|
|
41644
|
+
leg.position.set(lx, yPos, legH / 2);
|
|
41645
|
+
panel.add(leg);
|
|
41646
|
+
}
|
|
41647
|
+
}
|
|
41648
|
+
|
|
41649
|
+
// ── Switch layout: a row along X on the top face (+Z) of the board ───────
|
|
41650
|
+
switches = Array.isArray(spec.switches) ? spec.switches : [];
|
|
41651
|
+
spacing = (_spec$switchSpacing = spec.switchSpacing) !== null && _spec$switchSpacing !== void 0 ? _spec$switchSpacing : 1.3;
|
|
41652
|
+
xs = computeSwitchRowX(switches.length, spacing);
|
|
41653
|
+
topZ = thickness / 2;
|
|
41654
|
+
attachedDevices = {};
|
|
41655
|
+
switches.forEach(function (sw, i) {
|
|
41656
|
+
attachedDevices[sw.attachmentId] = {
|
|
41657
|
+
deviceId: spec.deviceId,
|
|
41658
|
+
attachmentLabel: sw.label || sw.attachmentId,
|
|
41659
|
+
attachmentPoint: {
|
|
41660
|
+
position: {
|
|
41661
|
+
x: xs[i],
|
|
41662
|
+
y: 0,
|
|
41663
|
+
z: topZ
|
|
41664
|
+
},
|
|
41665
|
+
rotation: spec.switchRotation || {
|
|
41666
|
+
x: 0,
|
|
41667
|
+
y: 0,
|
|
41668
|
+
z: 0
|
|
41669
|
+
}
|
|
41670
|
+
}
|
|
41671
|
+
};
|
|
41672
|
+
});
|
|
41673
|
+
componentData = {
|
|
41674
|
+
isSmart: true,
|
|
41675
|
+
attachedDevices: attachedDevices
|
|
41676
|
+
}; // Clone + tag the switch devices onto the tilted top (reuses smart-component path).
|
|
41677
|
+
_context.n = 2;
|
|
41678
|
+
return attachIODevicesToComponent(top, componentData, modelPreloader, panelId);
|
|
41679
|
+
case 2:
|
|
41680
|
+
// Optional switch-only scale (attach path always leaves devices at 1:1).
|
|
41681
|
+
switchScale = Number.isFinite(spec.switchScale) && spec.switchScale > 0 ? spec.switchScale : 1;
|
|
41682
|
+
if (switchScale !== 1) {
|
|
41683
|
+
top.traverse(function (obj) {
|
|
41684
|
+
var _obj$userData;
|
|
41685
|
+
if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'io-device') {
|
|
41686
|
+
obj.scale.setScalar(switchScale);
|
|
41687
|
+
}
|
|
41688
|
+
});
|
|
41689
|
+
}
|
|
41690
|
+
|
|
41691
|
+
// Panel must be in the scene before behaviors register (default-state seeding
|
|
41692
|
+
// scans the live scene graph for io-devices).
|
|
41693
|
+
scene.add(panel);
|
|
41694
|
+
this._panel = panel;
|
|
41695
|
+
ioBehavMgr = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.ioBehaviorManager;
|
|
41696
|
+
if (ioBehavMgr) {
|
|
41697
|
+
registerBehaviorsForComponent(ioBehavMgr, panelId, componentData, panel, modelPreloader.componentDictionary, centralPlant);
|
|
41698
|
+
|
|
41699
|
+
// Drive each switch's mesh to its current engine value so the initial
|
|
41700
|
+
// pose/colour reflects the state. `registerBehaviorsForComponent`'s
|
|
41701
|
+
// default-seeding writes through the engine, which no-ops when the value
|
|
41702
|
+
// already matches the seeded `stateRegistry` — leaving the button at its
|
|
41703
|
+
// neutral GLB rest pose. Trigger the animation directly to avoid that.
|
|
41704
|
+
engine = centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.stateEngine;
|
|
41705
|
+
_iterator = _createForOfIteratorHelper(switches);
|
|
41706
|
+
try {
|
|
41707
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
41708
|
+
sw = _step.value;
|
|
41709
|
+
current = engine === null || engine === void 0 ? void 0 : engine.getDesired("".concat(panelId, "::").concat(sw.attachmentId, "::mode"));
|
|
41710
|
+
ioBehavMgr.triggerState(sw.attachmentId, 'mode', current !== null && current !== void 0 ? current : true, panelId);
|
|
41711
|
+
}
|
|
41712
|
+
} catch (err) {
|
|
41713
|
+
_iterator.e(err);
|
|
41714
|
+
} finally {
|
|
41715
|
+
_iterator.f();
|
|
41716
|
+
}
|
|
41717
|
+
}
|
|
41718
|
+
|
|
41719
|
+
// ── Floating labels above each switch (on the tilted top) ────────────────
|
|
41720
|
+
lo = spec.labelOffset || {
|
|
41721
|
+
x: 0,
|
|
41722
|
+
y: 0,
|
|
41723
|
+
z: 0.7
|
|
41724
|
+
};
|
|
41725
|
+
labelSize = (_spec$labelSize = spec.labelSize) !== null && _spec$labelSize !== void 0 ? _spec$labelSize : 0.5;
|
|
41726
|
+
switches.forEach(function (sw, i) {
|
|
41727
|
+
var _lo$z;
|
|
41728
|
+
var label = _this2._makeLabel(sw.label || sw.attachmentId, labelSize);
|
|
41729
|
+
if (!label) return;
|
|
41730
|
+
label.position.set(xs[i] + (lo.x || 0), lo.y || 0, topZ + ((_lo$z = lo.z) !== null && _lo$z !== void 0 ? _lo$z : 0.7));
|
|
41731
|
+
top.add(label);
|
|
41732
|
+
_this2._labels.push(label);
|
|
41733
|
+
});
|
|
41734
|
+
case 3:
|
|
41735
|
+
return _context.a(2);
|
|
41736
|
+
}
|
|
41737
|
+
}, _callee, this);
|
|
41738
|
+
}));
|
|
41739
|
+
function _build() {
|
|
41740
|
+
return _build2.apply(this, arguments);
|
|
41741
|
+
}
|
|
41742
|
+
return _build;
|
|
41743
|
+
}()
|
|
41744
|
+
/**
|
|
41745
|
+
* Build one text label as a camera-facing `THREE.Sprite` (canvas texture),
|
|
41746
|
+
* drawn by the main WebGL renderer alongside the scene — no dependency on the
|
|
41747
|
+
* tooltip CSS2D renderer. Returns null when there is no DOM (headless tests).
|
|
41748
|
+
* @param {string} text
|
|
41749
|
+
* @param {number} worldHeight - label height in world units
|
|
41750
|
+
* @returns {THREE.Sprite|null}
|
|
41751
|
+
* @private
|
|
41752
|
+
*/
|
|
41753
|
+
)
|
|
41754
|
+
}, {
|
|
41755
|
+
key: "_makeLabel",
|
|
41756
|
+
value: function _makeLabel(text, worldHeight) {
|
|
41757
|
+
if (typeof document === 'undefined') return null;
|
|
41758
|
+
var dpr = 4; // supersample for crisp text
|
|
41759
|
+
var fontPx = 40;
|
|
41760
|
+
var padX = 14;
|
|
41761
|
+
var padY = 8;
|
|
41762
|
+
var font = "600 ".concat(fontPx, "px -apple-system, \"Segoe UI\", Roboto, sans-serif");
|
|
41763
|
+
var canvas = document.createElement('canvas');
|
|
41764
|
+
var ctx = canvas.getContext('2d');
|
|
41765
|
+
ctx.font = font;
|
|
41766
|
+
var textW = ctx.measureText(text).width;
|
|
41767
|
+
canvas.width = Math.ceil((textW + padX * 2) * dpr);
|
|
41768
|
+
canvas.height = Math.ceil((fontPx + padY * 2) * dpr);
|
|
41769
|
+
|
|
41770
|
+
// Re-apply after the resize (which clears the canvas), scaled by dpr.
|
|
41771
|
+
ctx.scale(dpr, dpr);
|
|
41772
|
+
ctx.font = font;
|
|
41773
|
+
var wCss = canvas.width / dpr;
|
|
41774
|
+
var hCss = canvas.height / dpr;
|
|
41775
|
+
ctx.fillStyle = 'rgba(20,28,38,0.86)';
|
|
41776
|
+
this._roundRect(ctx, 0, 0, wCss, hCss, 8);
|
|
41777
|
+
ctx.fill();
|
|
41778
|
+
ctx.fillStyle = '#eaf2f8';
|
|
41779
|
+
ctx.textAlign = 'center';
|
|
41780
|
+
ctx.textBaseline = 'middle';
|
|
41781
|
+
ctx.fillText(text, wCss / 2, hCss / 2);
|
|
41782
|
+
var texture = new THREE__namespace.CanvasTexture(canvas);
|
|
41783
|
+
texture.minFilter = THREE__namespace.LinearFilter;
|
|
41784
|
+
texture.anisotropy = 4;
|
|
41785
|
+
var material = new THREE__namespace.SpriteMaterial({
|
|
41786
|
+
map: texture,
|
|
41787
|
+
transparent: true,
|
|
41788
|
+
depthTest: false,
|
|
41789
|
+
// always readable, never occluded by the board/switch
|
|
41790
|
+
depthWrite: false
|
|
41791
|
+
});
|
|
41792
|
+
var sprite = new THREE__namespace.Sprite(material);
|
|
41793
|
+
var aspect = canvas.width / canvas.height;
|
|
41794
|
+
sprite.scale.set(worldHeight * aspect, worldHeight, 1);
|
|
41795
|
+
sprite.renderOrder = 999;
|
|
41796
|
+
return sprite;
|
|
41797
|
+
}
|
|
41798
|
+
|
|
41799
|
+
/** Trace a rounded-rect path (fill applied by caller). @private */
|
|
41800
|
+
}, {
|
|
41801
|
+
key: "_roundRect",
|
|
41802
|
+
value: function _roundRect(ctx, x, y, w, h, r) {
|
|
41803
|
+
var rr = Math.min(r, w / 2, h / 2);
|
|
41804
|
+
ctx.beginPath();
|
|
41805
|
+
ctx.moveTo(x + rr, y);
|
|
41806
|
+
ctx.arcTo(x + w, y, x + w, y + h, rr);
|
|
41807
|
+
ctx.arcTo(x + w, y + h, x, y + h, rr);
|
|
41808
|
+
ctx.arcTo(x, y + h, x, y, rr);
|
|
41809
|
+
ctx.arcTo(x, y, x + w, y, rr);
|
|
41810
|
+
ctx.closePath();
|
|
41811
|
+
}
|
|
41812
|
+
|
|
41813
|
+
/**
|
|
41814
|
+
* Remove the panel, its labels, and its registered behaviors (scene reload).
|
|
41815
|
+
* @private
|
|
41816
|
+
*/
|
|
41817
|
+
}, {
|
|
41818
|
+
key: "_teardown",
|
|
41819
|
+
value: function _teardown() {
|
|
41820
|
+
this._labels = [];
|
|
41821
|
+
if (this._panelId) {
|
|
41822
|
+
var _this$sceneViewer4;
|
|
41823
|
+
(_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.ioBehaviorManager) === null || _this$sceneViewer4 === void 0 || _this$sceneViewer4.unloadForComponent(this._panelId);
|
|
41824
|
+
}
|
|
41825
|
+
if (this._panel) {
|
|
41826
|
+
var _this$sceneViewer5;
|
|
41827
|
+
(_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.scene) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.remove(this._panel);
|
|
41828
|
+
this._panel.traverse(function (obj) {
|
|
41829
|
+
if (obj.isMesh || obj.isSprite) {
|
|
41830
|
+
var _obj$geometry, _obj$geometry$dispose;
|
|
41831
|
+
(_obj$geometry = obj.geometry) === null || _obj$geometry === void 0 || (_obj$geometry$dispose = _obj$geometry.dispose) === null || _obj$geometry$dispose === void 0 || _obj$geometry$dispose.call(_obj$geometry);
|
|
41832
|
+
var mats = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
41833
|
+
mats.forEach(function (m) {
|
|
41834
|
+
var _m$map, _m$map$dispose, _m$dispose;
|
|
41835
|
+
m === null || m === void 0 || (_m$map = m.map) === null || _m$map === void 0 || (_m$map$dispose = _m$map.dispose) === null || _m$map$dispose === void 0 || _m$map$dispose.call(_m$map); // sprite canvas texture
|
|
41836
|
+
m === null || m === void 0 || (_m$dispose = m.dispose) === null || _m$dispose === void 0 || _m$dispose.call(m);
|
|
41837
|
+
});
|
|
41838
|
+
}
|
|
41839
|
+
});
|
|
41840
|
+
this._panel = null;
|
|
41841
|
+
}
|
|
41842
|
+
this._panelId = null;
|
|
41843
|
+
}
|
|
41844
|
+
}, {
|
|
41845
|
+
key: "_doDispose",
|
|
41846
|
+
value: function _doDispose() {
|
|
41847
|
+
var _this$sceneViewer6;
|
|
41848
|
+
if ((_this$sceneViewer6 = this.sceneViewer) !== null && _this$sceneViewer6 !== void 0 && _this$sceneViewer6.off) {
|
|
41849
|
+
this.sceneViewer.off('scene-loaded', this._build);
|
|
41850
|
+
}
|
|
41851
|
+
this._teardown();
|
|
41852
|
+
this.sceneViewer = null;
|
|
41853
|
+
}
|
|
41854
|
+
}]);
|
|
41855
|
+
}(BaseDisposable);
|
|
41856
|
+
|
|
40331
41857
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
40332
41858
|
// Flow-direction helpers (module-level)
|
|
40333
41859
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -40451,6 +41977,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
40451
41977
|
this.centralPlant.managers.viewport2DManager = new Viewport2DManager(this.centralPlant.sceneViewer);
|
|
40452
41978
|
this.centralPlant.managers.ioBehaviorManager = new IoBehaviorManager(this.centralPlant.sceneViewer);
|
|
40453
41979
|
this.centralPlant.managers.ioOutlineManager = new IoOutlineManager(this.centralPlant.sceneViewer);
|
|
41980
|
+
this.centralPlant.managers.effectiveVisualManager = new EffectiveVisualManager(this.centralPlant.sceneViewer);
|
|
41981
|
+
this.centralPlant.managers.controlPanelManager = new ControlPanelManager(this.centralPlant.sceneViewer);
|
|
40454
41982
|
|
|
40455
41983
|
// All managers are now stored in the managers collection and will be attached via attachToComponent()
|
|
40456
41984
|
}
|
|
@@ -41884,7 +43412,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
41884
43412
|
* Initialize the CentralPlant manager
|
|
41885
43413
|
*
|
|
41886
43414
|
* @constructor
|
|
41887
|
-
* @version 0.3.
|
|
43415
|
+
* @version 0.3.59
|
|
41888
43416
|
* @updated 2025-10-22
|
|
41889
43417
|
*
|
|
41890
43418
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -41923,6 +43451,14 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
41923
43451
|
// Initialize internals handler
|
|
41924
43452
|
_this.internals = new CentralPlantInternals(_this);
|
|
41925
43453
|
|
|
43454
|
+
// Portable State Engine: single source of truth for desired/effective state
|
|
43455
|
+
// (plant/group/object/pipe). Owned here; the write door and scene load/save
|
|
43456
|
+
// route through it. The 3D layer/store mirror its output (Framework §12–§13).
|
|
43457
|
+
_this.stateEngine = new StateEngine();
|
|
43458
|
+
_this._unsubscribeStateEngine = _this.stateEngine.subscribe(function (change) {
|
|
43459
|
+
return _this._onEngineStateChange(change);
|
|
43460
|
+
});
|
|
43461
|
+
|
|
41926
43462
|
// Optional cache primer function (dependency injection)
|
|
41927
43463
|
_this.cachePrimer = null;
|
|
41928
43464
|
|
|
@@ -42099,6 +43635,16 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
42099
43635
|
timestamp: new Date().toISOString()
|
|
42100
43636
|
});
|
|
42101
43637
|
|
|
43638
|
+
// Load plant / groups / chains / stateRegistry into the State Engine. v3.0
|
|
43639
|
+
// scenes carry these; legacy v2.3 scenes load with safe defaults (§15).
|
|
43640
|
+
if (this.stateEngine) {
|
|
43641
|
+
try {
|
|
43642
|
+
this.stateEngine.load(extractDeclaration(sceneData));
|
|
43643
|
+
} catch (err) {
|
|
43644
|
+
console.warn('⚠️ setImportedSceneData(): stateEngine.load() failed:', err);
|
|
43645
|
+
}
|
|
43646
|
+
}
|
|
43647
|
+
|
|
42102
43648
|
// Scene behaviors loaded
|
|
42103
43649
|
|
|
42104
43650
|
// Cross-component behaviors are registered by sceneOperationsManager.loadSceneData()
|
|
@@ -43047,12 +44593,32 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
43047
44593
|
}
|
|
43048
44594
|
|
|
43049
44595
|
/**
|
|
43050
|
-
* Internal single path for I/O state changes
|
|
44596
|
+
* Internal single path for I/O state changes. Routes the write through the
|
|
44597
|
+
* State Engine (the one write door, Framework §12); the engine subscription
|
|
44598
|
+
* ({@link CentralPlant#_onEngineStateChange}) mirrors to the state adapter,
|
|
44599
|
+
* animates meshes, and emits. Falls back to a direct write only if the engine
|
|
44600
|
+
* is unavailable.
|
|
43051
44601
|
* @private
|
|
43052
44602
|
*/
|
|
43053
44603
|
}, {
|
|
43054
44604
|
key: "_dispatchIoState",
|
|
43055
44605
|
value: function _dispatchIoState(attachmentId, stateId, value, parentUuid) {
|
|
44606
|
+
if (this.stateEngine) {
|
|
44607
|
+
this.stateEngine.setDesired(ioAddress(attachmentId, stateId, parentUuid), value);
|
|
44608
|
+
return;
|
|
44609
|
+
}
|
|
44610
|
+
this._dispatchIoStateDirect(attachmentId, stateId, value, parentUuid);
|
|
44611
|
+
}
|
|
44612
|
+
|
|
44613
|
+
/**
|
|
44614
|
+
* Persist to the state adapter, animate meshes, and emit for one I/O data
|
|
44615
|
+
* point. This is the mirror side of the engine (driven from desired, since I/O
|
|
44616
|
+
* data points are ungated) and the legacy fallback when no engine is present.
|
|
44617
|
+
* @private
|
|
44618
|
+
*/
|
|
44619
|
+
}, {
|
|
44620
|
+
key: "_dispatchIoStateDirect",
|
|
44621
|
+
value: function _dispatchIoStateDirect(attachmentId, stateId, value, parentUuid) {
|
|
43056
44622
|
var _this$managers, _this$sceneViewer5, _this$sceneViewer6, _this$managers2, _this$sceneViewer7, _this$sceneViewer8, _this$sceneViewer9;
|
|
43057
44623
|
var tooltipMgr = ((_this$managers = this.managers) === null || _this$managers === void 0 ? void 0 : _this$managers.componentTooltipManager) || ((_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.componentTooltipManager) || ((_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.managers) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.componentTooltipManager);
|
|
43058
44624
|
var stateAdapter = tooltipMgr === null || tooltipMgr === void 0 ? void 0 : tooltipMgr._stateAdapter;
|
|
@@ -43076,6 +44642,25 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
43076
44642
|
});
|
|
43077
44643
|
}
|
|
43078
44644
|
|
|
44645
|
+
/**
|
|
44646
|
+
* State Engine subscriber: fan a committed registry change out to the 3D/store
|
|
44647
|
+
* layer. I/O device data points (3-part addresses) mirror to the adapter and
|
|
44648
|
+
* animate; every change (incl. plant/group/object/pipe effective) is surfaced
|
|
44649
|
+
* as `engine-state-changed` for gating-aware visual subscribers (§13).
|
|
44650
|
+
* @private
|
|
44651
|
+
* @param {{ address: string, desired: *, effective: * }} change
|
|
44652
|
+
*/
|
|
44653
|
+
}, {
|
|
44654
|
+
key: "_onEngineStateChange",
|
|
44655
|
+
value: function _onEngineStateChange(change) {
|
|
44656
|
+
var _this$sceneViewer0;
|
|
44657
|
+
var io = parseIoAddress(change.address);
|
|
44658
|
+
if (io) {
|
|
44659
|
+
this._dispatchIoStateDirect(io.attachmentId, io.stateId, change.desired, io.parentUuid);
|
|
44660
|
+
}
|
|
44661
|
+
(_this$sceneViewer0 = this.sceneViewer) === null || _this$sceneViewer0 === void 0 || _this$sceneViewer0.emit('engine-state-changed', change);
|
|
44662
|
+
}
|
|
44663
|
+
|
|
43079
44664
|
/**
|
|
43080
44665
|
* Configure the state adapter on both tooltip and behavior managers.
|
|
43081
44666
|
* @param {{ getState: Function, setState: Function }} stateAdapter
|
|
@@ -43083,16 +44668,16 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
43083
44668
|
}, {
|
|
43084
44669
|
key: "configureStateAdapter",
|
|
43085
44670
|
value: function configureStateAdapter(stateAdapter) {
|
|
43086
|
-
var _this$managers3, _this$
|
|
43087
|
-
var tooltipMgr = ((_this$managers3 = this.managers) === null || _this$managers3 === void 0 ? void 0 : _this$managers3.componentTooltipManager) || ((_this$
|
|
44671
|
+
var _this$managers3, _this$sceneViewer1, _this$managers4, _this$sceneViewer11, _this$sceneViewer12;
|
|
44672
|
+
var tooltipMgr = ((_this$managers3 = this.managers) === null || _this$managers3 === void 0 ? void 0 : _this$managers3.componentTooltipManager) || ((_this$sceneViewer1 = this.sceneViewer) === null || _this$sceneViewer1 === void 0 ? void 0 : _this$sceneViewer1.componentTooltipManager);
|
|
43088
44673
|
if (tooltipMgr !== null && tooltipMgr !== void 0 && tooltipMgr.configure) {
|
|
43089
|
-
var _this$
|
|
44674
|
+
var _this$sceneViewer10;
|
|
43090
44675
|
tooltipMgr.configure(stateAdapter);
|
|
43091
|
-
if ((_this$
|
|
44676
|
+
if ((_this$sceneViewer10 = this.sceneViewer) !== null && _this$sceneViewer10 !== void 0 && _this$sceneViewer10.managers) {
|
|
43092
44677
|
this.sceneViewer.managers.componentTooltipManager = tooltipMgr;
|
|
43093
44678
|
}
|
|
43094
44679
|
}
|
|
43095
|
-
var ioBehavMgr = ((_this$managers4 = this.managers) === null || _this$managers4 === void 0 ? void 0 : _this$managers4.ioBehaviorManager) || ((_this$
|
|
44680
|
+
var ioBehavMgr = ((_this$managers4 = this.managers) === null || _this$managers4 === void 0 ? void 0 : _this$managers4.ioBehaviorManager) || ((_this$sceneViewer11 = this.sceneViewer) === null || _this$sceneViewer11 === void 0 || (_this$sceneViewer11 = _this$sceneViewer11.managers) === null || _this$sceneViewer11 === void 0 ? void 0 : _this$sceneViewer11.ioBehaviorManager) || ((_this$sceneViewer12 = this.sceneViewer) === null || _this$sceneViewer12 === void 0 ? void 0 : _this$sceneViewer12.ioBehaviorManager);
|
|
43096
44681
|
if (ioBehavMgr !== null && ioBehavMgr !== void 0 && ioBehavMgr.configure) {
|
|
43097
44682
|
ioBehavMgr.configure(stateAdapter);
|
|
43098
44683
|
}
|
|
@@ -43127,8 +44712,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
43127
44712
|
}, {
|
|
43128
44713
|
key: "getSceneAttachments",
|
|
43129
44714
|
value: function getSceneAttachments() {
|
|
43130
|
-
var _this$
|
|
43131
|
-
var scene = (_this$
|
|
44715
|
+
var _this$sceneViewer13;
|
|
44716
|
+
var scene = (_this$sceneViewer13 = this.sceneViewer) === null || _this$sceneViewer13 === void 0 ? void 0 : _this$sceneViewer13.scene;
|
|
43132
44717
|
if (!scene) return [];
|
|
43133
44718
|
var results = [];
|
|
43134
44719
|
scene.traverse(function (obj) {
|
|
@@ -44909,12 +46494,14 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
44909
46494
|
}, {
|
|
44910
46495
|
key: "clearScene",
|
|
44911
46496
|
value: function clearScene() {
|
|
44912
|
-
var _this$
|
|
46497
|
+
var _this$sceneViewer14, _this$stateEngine;
|
|
44913
46498
|
this.importedSceneData = null;
|
|
44914
|
-
var ioBehavMgr = (_this$
|
|
46499
|
+
var ioBehavMgr = (_this$sceneViewer14 = this.sceneViewer) === null || _this$sceneViewer14 === void 0 || (_this$sceneViewer14 = _this$sceneViewer14.managers) === null || _this$sceneViewer14 === void 0 ? void 0 : _this$sceneViewer14.ioBehaviorManager;
|
|
44915
46500
|
if (ioBehavMgr) {
|
|
44916
46501
|
ioBehavMgr.setCrossComponentBehaviors([]);
|
|
44917
46502
|
}
|
|
46503
|
+
// Reset the registry so pipe/object keys from the old scene don't linger (§15).
|
|
46504
|
+
(_this$stateEngine = this.stateEngine) === null || _this$stateEngine === void 0 || _this$stateEngine.reset();
|
|
44918
46505
|
if (this.sceneViewer && this.sceneViewer.scene) {
|
|
44919
46506
|
this.sceneViewer.scene.clear();
|
|
44920
46507
|
}
|
|
@@ -44965,8 +46552,16 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
44965
46552
|
}, {
|
|
44966
46553
|
key: "_doDispose",
|
|
44967
46554
|
value: function _doDispose() {
|
|
46555
|
+
var _this$stateEngine2;
|
|
44968
46556
|
console.log('🧹 Disposing CentralPlant using enhanced disposal patterns...');
|
|
44969
46557
|
|
|
46558
|
+
// Tear down the State Engine subscription and registry.
|
|
46559
|
+
if (this._unsubscribeStateEngine) {
|
|
46560
|
+
this._unsubscribeStateEngine();
|
|
46561
|
+
this._unsubscribeStateEngine = null;
|
|
46562
|
+
}
|
|
46563
|
+
(_this$stateEngine2 = this.stateEngine) === null || _this$stateEngine2 === void 0 || _this$stateEngine2.reset();
|
|
46564
|
+
|
|
44970
46565
|
// Use batch disposal for all managers
|
|
44971
46566
|
var managers = Object.values(this.managers).filter(function (manager) {
|
|
44972
46567
|
return manager && typeof manager.dispose === 'function';
|
|
@@ -50840,8 +52435,10 @@ exports.SceneInitializationManager = SceneInitializationManager;
|
|
|
50840
52435
|
exports.SceneOperationsManager = SceneOperationsManager;
|
|
50841
52436
|
exports.SceneTooltipsManager = SceneTooltipsManager;
|
|
50842
52437
|
exports.SnapshotManager = SnapshotManager;
|
|
52438
|
+
exports.StateEngine = StateEngine;
|
|
50843
52439
|
exports.ThreeJSResourceManager = ThreeJSResourceManager;
|
|
50844
52440
|
exports.applyCrossComponentBehaviors = applyCrossComponentBehaviors;
|
|
52441
|
+
exports.applyDeclarationToExport = applyDeclarationToExport;
|
|
50845
52442
|
exports.applyDefaultIoDeviceStates = applyDefaultIoDeviceStates;
|
|
50846
52443
|
exports.applyModelRootTranslation = applyModelRootTranslation;
|
|
50847
52444
|
exports.applyModelRootTranslationFromWorldBase = applyModelRootTranslationFromWorldBase;
|
|
@@ -50855,6 +52452,7 @@ exports.clearS3Cache = clearS3Cache;
|
|
|
50855
52452
|
exports.collectSceneLibraryIds = collectSceneLibraryIds;
|
|
50856
52453
|
exports.createPathfindingRequest = createPathfindingRequest;
|
|
50857
52454
|
exports.createTransformControls = createTransformControls;
|
|
52455
|
+
exports.extractDeclaration = extractDeclaration;
|
|
50858
52456
|
exports.findObjectByHardcodedUuid = findObjectByHardcodedUuid;
|
|
50859
52457
|
exports.formatCacheExpiry = formatCacheExpiry;
|
|
50860
52458
|
exports.generateUniqueComponentId = generateUniqueComponentId;
|
|
@@ -50880,6 +52478,7 @@ exports.getObjectsByType = getObjectsByType;
|
|
|
50880
52478
|
exports.getScopedAttachmentKey = getScopedAttachmentKey;
|
|
50881
52479
|
exports.getThumbnailKey = getThumbnailKey;
|
|
50882
52480
|
exports.getUserOnlyCacheStats = getUserOnlyCacheStats;
|
|
52481
|
+
exports.ioAddress = ioAddress;
|
|
50883
52482
|
exports.isCached = isCached;
|
|
50884
52483
|
exports.isComponent = isComponent;
|
|
50885
52484
|
exports.isComputed = isComputed;
|
|
@@ -50891,14 +52490,17 @@ exports.isSegment = isSegment;
|
|
|
50891
52490
|
exports.isThumbnailCached = isThumbnailCached;
|
|
50892
52491
|
exports.loadCrossComponentBehaviors = loadCrossComponentBehaviors;
|
|
50893
52492
|
exports.loadTextureSetAndCreateMaterial = loadTextureSetAndCreateMaterial;
|
|
52493
|
+
exports.makeAddress = makeAddress;
|
|
50894
52494
|
exports.markAsComputed = markAsComputed;
|
|
50895
52495
|
exports.markAsDeclared = markAsDeclared;
|
|
50896
52496
|
exports.measureS3Transfer = measureS3Transfer;
|
|
50897
52497
|
exports.modelOffsetToWorldDelta = modelOffsetToWorldDelta;
|
|
50898
52498
|
exports.modelPreloader = modelPreloader;
|
|
50899
52499
|
exports.normalizeBehavior = normalizeBehavior;
|
|
52500
|
+
exports.offValue = offValue;
|
|
50900
52501
|
exports.parseCrossBehavior = parseCrossBehavior;
|
|
50901
52502
|
exports.parseIntraBehavior = parseIntraBehavior;
|
|
52503
|
+
exports.parseIoAddress = parseIoAddress;
|
|
50902
52504
|
exports.preloadLocalFiles = preloadLocalFiles;
|
|
50903
52505
|
exports.preloadS3Objects = preloadS3Objects;
|
|
50904
52506
|
exports.refreshSceneIntraBehaviors = refreshSceneIntraBehaviors;
|
|
@@ -50915,3 +52517,4 @@ exports.scanSceneIoEndpoints = scanSceneIoEndpoints;
|
|
|
50915
52517
|
exports.sceneViewer = sceneViewer;
|
|
50916
52518
|
exports.shouldRemoveOnRegeneration = shouldRemoveOnRegeneration;
|
|
50917
52519
|
exports.switchCachePartition = switchCachePartition;
|
|
52520
|
+
exports.valuesEqual = valuesEqual;
|