@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
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var THREE = require('three');
|
|
7
|
+
var baseDisposable = require('../../core/baseDisposable.js');
|
|
8
|
+
var modelPreloader = require('../../rendering/modelPreloader.js');
|
|
9
|
+
var ioDeviceUtils = require('../../utils/ioDeviceUtils.js');
|
|
10
|
+
var behaviorRegistration = require('../../utils/behaviorRegistration.js');
|
|
11
|
+
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n["default"] = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* X offsets for `count` switches evenly spaced and centered on 0.
|
|
34
|
+
* @param {number} count
|
|
35
|
+
* @param {number} spacing
|
|
36
|
+
* @returns {number[]}
|
|
37
|
+
*/
|
|
38
|
+
function computeSwitchRowX(count, spacing) {
|
|
39
|
+
var start = -((count - 1) * spacing) / 2;
|
|
40
|
+
return Array.from({
|
|
41
|
+
length: count
|
|
42
|
+
}, function (_, i) {
|
|
43
|
+
return start + i * spacing;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
var ControlPanelManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
47
|
+
/**
|
|
48
|
+
* @param {Object} sceneViewer - The central sceneViewer hub
|
|
49
|
+
*/
|
|
50
|
+
function ControlPanelManager(sceneViewer) {
|
|
51
|
+
var _this;
|
|
52
|
+
_rollupPluginBabelHelpers.classCallCheck(this, ControlPanelManager);
|
|
53
|
+
_this = _rollupPluginBabelHelpers.callSuper(this, ControlPanelManager);
|
|
54
|
+
_this.sceneViewer = sceneViewer;
|
|
55
|
+
_this._panel = null;
|
|
56
|
+
_this._labels = [];
|
|
57
|
+
_this._panelId = null;
|
|
58
|
+
_this._build = _this._build.bind(_this);
|
|
59
|
+
if (sceneViewer !== null && sceneViewer !== void 0 && sceneViewer.on) {
|
|
60
|
+
sceneViewer.on('scene-loaded', _this._build);
|
|
61
|
+
}
|
|
62
|
+
return _this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build (or rebuild) the panel from the current scene's `controlPanel` spec.
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
_rollupPluginBabelHelpers.inherits(ControlPanelManager, _BaseDisposable);
|
|
70
|
+
return _rollupPluginBabelHelpers.createClass(ControlPanelManager, [{
|
|
71
|
+
key: "_build",
|
|
72
|
+
value: (function () {
|
|
73
|
+
var _build2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee() {
|
|
74
|
+
var _this$sceneViewer,
|
|
75
|
+
_centralPlant$importe,
|
|
76
|
+
_this$sceneViewer2,
|
|
77
|
+
_b$width,
|
|
78
|
+
_b$height,
|
|
79
|
+
_b$thickness,
|
|
80
|
+
_desk$tilt,
|
|
81
|
+
_desk$height,
|
|
82
|
+
_desk$legThickness,
|
|
83
|
+
_desk$legInset,
|
|
84
|
+
_spec$switchSpacing,
|
|
85
|
+
_this$sceneViewer3,
|
|
86
|
+
_spec$labelSize,
|
|
87
|
+
_this2 = this;
|
|
88
|
+
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;
|
|
89
|
+
return _rollupPluginBabelHelpers.regenerator().w(function (_context) {
|
|
90
|
+
while (1) switch (_context.n) {
|
|
91
|
+
case 0:
|
|
92
|
+
this._teardown();
|
|
93
|
+
centralPlant = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.centralPlant;
|
|
94
|
+
spec = centralPlant === null || centralPlant === void 0 || (_centralPlant$importe = centralPlant.importedSceneData) === null || _centralPlant$importe === void 0 ? void 0 : _centralPlant$importe.controlPanel;
|
|
95
|
+
scene = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.scene;
|
|
96
|
+
if (!(!spec || !scene)) {
|
|
97
|
+
_context.n = 1;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
return _context.a(2);
|
|
101
|
+
case 1:
|
|
102
|
+
panelId = spec.id || 'CONTROL-PANEL-1';
|
|
103
|
+
this._panelId = panelId;
|
|
104
|
+
panel = new THREE__namespace.Group();
|
|
105
|
+
panel.name = 'Control Panel';
|
|
106
|
+
panel.uuid = panelId;
|
|
107
|
+
panel.userData = {
|
|
108
|
+
objectType: 'component',
|
|
109
|
+
isControlPanel: true
|
|
110
|
+
};
|
|
111
|
+
p = spec.position || {};
|
|
112
|
+
panel.position.set(p.x || 0, p.y || 0, p.z || 0);
|
|
113
|
+
r = spec.rotation || {};
|
|
114
|
+
d2r = Math.PI / 180;
|
|
115
|
+
panel.rotation.set((r.x || 0) * d2r, (r.y || 0) * d2r, (r.z || 0) * d2r, 'XYZ');
|
|
116
|
+
scale = Number.isFinite(spec.scale) && spec.scale > 0 ? spec.scale : 1;
|
|
117
|
+
panel.scale.setScalar(scale);
|
|
118
|
+
|
|
119
|
+
// ── Drawing-desk geometry (Z-up scene) ───────────────────────────────────
|
|
120
|
+
// A tilted top surface (the drafting board) raised on legs. Switches +
|
|
121
|
+
// labels live on the tilted `top` group so they sit on the angled surface.
|
|
122
|
+
b = spec.board || {};
|
|
123
|
+
width = (_b$width = b.width) !== null && _b$width !== void 0 ? _b$width : 7; // x-extent of the top
|
|
124
|
+
depth = (_b$height = b.height) !== null && _b$height !== void 0 ? _b$height : 2; // y-extent of the top (spec key kept as `height`)
|
|
125
|
+
thickness = (_b$thickness = b.thickness) !== null && _b$thickness !== void 0 ? _b$thickness : 0.15;
|
|
126
|
+
desk = spec.desk || {};
|
|
127
|
+
tilt = ((_desk$tilt = desk.tilt) !== null && _desk$tilt !== void 0 ? _desk$tilt : 30) * d2r; // surface tilt; +raises the back (+y) edge
|
|
128
|
+
deskHeight = (_desk$height = desk.height) !== null && _desk$height !== void 0 ? _desk$height : 1.3; // world height of the top's center
|
|
129
|
+
legThickness = (_desk$legThickness = desk.legThickness) !== null && _desk$legThickness !== void 0 ? _desk$legThickness : 0.18;
|
|
130
|
+
legInset = (_desk$legInset = desk.legInset) !== null && _desk$legInset !== void 0 ? _desk$legInset : 0.35;
|
|
131
|
+
legColor = desk.legColor || '#22303c'; // Tilted top group: raised to deskHeight, rotated about X.
|
|
132
|
+
top = new THREE__namespace.Group();
|
|
133
|
+
top.position.set(0, 0, deskHeight);
|
|
134
|
+
top.rotation.set(tilt, 0, 0, 'XYZ');
|
|
135
|
+
panel.add(top);
|
|
136
|
+
board = new THREE__namespace.Mesh(new THREE__namespace.BoxGeometry(width, depth, thickness), new THREE__namespace.MeshStandardMaterial({
|
|
137
|
+
color: b.color || '#2c3e50',
|
|
138
|
+
roughness: 0.6,
|
|
139
|
+
metalness: 0.2
|
|
140
|
+
}));
|
|
141
|
+
board.userData = {
|
|
142
|
+
objectType: 'control-panel-board'
|
|
143
|
+
};
|
|
144
|
+
top.add(board);
|
|
145
|
+
|
|
146
|
+
// Legs: one per corner, from the ground up to the tilted top's underside.
|
|
147
|
+
legMat = new THREE__namespace.MeshStandardMaterial({
|
|
148
|
+
color: legColor,
|
|
149
|
+
roughness: 0.7,
|
|
150
|
+
metalness: 0.1
|
|
151
|
+
});
|
|
152
|
+
halfW = Math.max(0.1, width / 2 - legInset);
|
|
153
|
+
halfD = Math.max(0.1, depth / 2 - legInset);
|
|
154
|
+
for (_i = 0, _arr = [halfD, -halfD]; _i < _arr.length; _i++) {
|
|
155
|
+
ly = _arr[_i];
|
|
156
|
+
// Underside height at this front/back row after the tilt.
|
|
157
|
+
cornerZ = deskHeight + ly * Math.sin(tilt) - thickness / 2 * Math.cos(tilt);
|
|
158
|
+
legH = Math.max(0.1, cornerZ);
|
|
159
|
+
yPos = ly * Math.cos(tilt); // corner's y in panel space after tilt
|
|
160
|
+
for (_i2 = 0, _arr2 = [halfW, -halfW]; _i2 < _arr2.length; _i2++) {
|
|
161
|
+
lx = _arr2[_i2];
|
|
162
|
+
leg = new THREE__namespace.Mesh(new THREE__namespace.BoxGeometry(legThickness, legThickness, legH), legMat);
|
|
163
|
+
leg.position.set(lx, yPos, legH / 2);
|
|
164
|
+
panel.add(leg);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Switch layout: a row along X on the top face (+Z) of the board ───────
|
|
169
|
+
switches = Array.isArray(spec.switches) ? spec.switches : [];
|
|
170
|
+
spacing = (_spec$switchSpacing = spec.switchSpacing) !== null && _spec$switchSpacing !== void 0 ? _spec$switchSpacing : 1.3;
|
|
171
|
+
xs = computeSwitchRowX(switches.length, spacing);
|
|
172
|
+
topZ = thickness / 2;
|
|
173
|
+
attachedDevices = {};
|
|
174
|
+
switches.forEach(function (sw, i) {
|
|
175
|
+
attachedDevices[sw.attachmentId] = {
|
|
176
|
+
deviceId: spec.deviceId,
|
|
177
|
+
attachmentLabel: sw.label || sw.attachmentId,
|
|
178
|
+
attachmentPoint: {
|
|
179
|
+
position: {
|
|
180
|
+
x: xs[i],
|
|
181
|
+
y: 0,
|
|
182
|
+
z: topZ
|
|
183
|
+
},
|
|
184
|
+
rotation: spec.switchRotation || {
|
|
185
|
+
x: 0,
|
|
186
|
+
y: 0,
|
|
187
|
+
z: 0
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
componentData = {
|
|
193
|
+
isSmart: true,
|
|
194
|
+
attachedDevices: attachedDevices
|
|
195
|
+
}; // Clone + tag the switch devices onto the tilted top (reuses smart-component path).
|
|
196
|
+
_context.n = 2;
|
|
197
|
+
return ioDeviceUtils.attachIODevicesToComponent(top, componentData, modelPreloader["default"], panelId);
|
|
198
|
+
case 2:
|
|
199
|
+
// Optional switch-only scale (attach path always leaves devices at 1:1).
|
|
200
|
+
switchScale = Number.isFinite(spec.switchScale) && spec.switchScale > 0 ? spec.switchScale : 1;
|
|
201
|
+
if (switchScale !== 1) {
|
|
202
|
+
top.traverse(function (obj) {
|
|
203
|
+
var _obj$userData;
|
|
204
|
+
if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'io-device') {
|
|
205
|
+
obj.scale.setScalar(switchScale);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Panel must be in the scene before behaviors register (default-state seeding
|
|
211
|
+
// scans the live scene graph for io-devices).
|
|
212
|
+
scene.add(panel);
|
|
213
|
+
this._panel = panel;
|
|
214
|
+
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;
|
|
215
|
+
if (ioBehavMgr) {
|
|
216
|
+
behaviorRegistration.registerBehaviorsForComponent(ioBehavMgr, panelId, componentData, panel, modelPreloader["default"].componentDictionary, centralPlant);
|
|
217
|
+
|
|
218
|
+
// Drive each switch's mesh to its current engine value so the initial
|
|
219
|
+
// pose/colour reflects the state. `registerBehaviorsForComponent`'s
|
|
220
|
+
// default-seeding writes through the engine, which no-ops when the value
|
|
221
|
+
// already matches the seeded `stateRegistry` — leaving the button at its
|
|
222
|
+
// neutral GLB rest pose. Trigger the animation directly to avoid that.
|
|
223
|
+
engine = centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.stateEngine;
|
|
224
|
+
_iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(switches);
|
|
225
|
+
try {
|
|
226
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
227
|
+
sw = _step.value;
|
|
228
|
+
current = engine === null || engine === void 0 ? void 0 : engine.getDesired("".concat(panelId, "::").concat(sw.attachmentId, "::mode"));
|
|
229
|
+
ioBehavMgr.triggerState(sw.attachmentId, 'mode', current !== null && current !== void 0 ? current : true, panelId);
|
|
230
|
+
}
|
|
231
|
+
} catch (err) {
|
|
232
|
+
_iterator.e(err);
|
|
233
|
+
} finally {
|
|
234
|
+
_iterator.f();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Floating labels above each switch (on the tilted top) ────────────────
|
|
239
|
+
lo = spec.labelOffset || {
|
|
240
|
+
x: 0,
|
|
241
|
+
y: 0,
|
|
242
|
+
z: 0.7
|
|
243
|
+
};
|
|
244
|
+
labelSize = (_spec$labelSize = spec.labelSize) !== null && _spec$labelSize !== void 0 ? _spec$labelSize : 0.5;
|
|
245
|
+
switches.forEach(function (sw, i) {
|
|
246
|
+
var _lo$z;
|
|
247
|
+
var label = _this2._makeLabel(sw.label || sw.attachmentId, labelSize);
|
|
248
|
+
if (!label) return;
|
|
249
|
+
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));
|
|
250
|
+
top.add(label);
|
|
251
|
+
_this2._labels.push(label);
|
|
252
|
+
});
|
|
253
|
+
case 3:
|
|
254
|
+
return _context.a(2);
|
|
255
|
+
}
|
|
256
|
+
}, _callee, this);
|
|
257
|
+
}));
|
|
258
|
+
function _build() {
|
|
259
|
+
return _build2.apply(this, arguments);
|
|
260
|
+
}
|
|
261
|
+
return _build;
|
|
262
|
+
}()
|
|
263
|
+
/**
|
|
264
|
+
* Build one text label as a camera-facing `THREE.Sprite` (canvas texture),
|
|
265
|
+
* drawn by the main WebGL renderer alongside the scene — no dependency on the
|
|
266
|
+
* tooltip CSS2D renderer. Returns null when there is no DOM (headless tests).
|
|
267
|
+
* @param {string} text
|
|
268
|
+
* @param {number} worldHeight - label height in world units
|
|
269
|
+
* @returns {THREE.Sprite|null}
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
)
|
|
273
|
+
}, {
|
|
274
|
+
key: "_makeLabel",
|
|
275
|
+
value: function _makeLabel(text, worldHeight) {
|
|
276
|
+
if (typeof document === 'undefined') return null;
|
|
277
|
+
var dpr = 4; // supersample for crisp text
|
|
278
|
+
var fontPx = 40;
|
|
279
|
+
var padX = 14;
|
|
280
|
+
var padY = 8;
|
|
281
|
+
var font = "600 ".concat(fontPx, "px -apple-system, \"Segoe UI\", Roboto, sans-serif");
|
|
282
|
+
var canvas = document.createElement('canvas');
|
|
283
|
+
var ctx = canvas.getContext('2d');
|
|
284
|
+
ctx.font = font;
|
|
285
|
+
var textW = ctx.measureText(text).width;
|
|
286
|
+
canvas.width = Math.ceil((textW + padX * 2) * dpr);
|
|
287
|
+
canvas.height = Math.ceil((fontPx + padY * 2) * dpr);
|
|
288
|
+
|
|
289
|
+
// Re-apply after the resize (which clears the canvas), scaled by dpr.
|
|
290
|
+
ctx.scale(dpr, dpr);
|
|
291
|
+
ctx.font = font;
|
|
292
|
+
var wCss = canvas.width / dpr;
|
|
293
|
+
var hCss = canvas.height / dpr;
|
|
294
|
+
ctx.fillStyle = 'rgba(20,28,38,0.86)';
|
|
295
|
+
this._roundRect(ctx, 0, 0, wCss, hCss, 8);
|
|
296
|
+
ctx.fill();
|
|
297
|
+
ctx.fillStyle = '#eaf2f8';
|
|
298
|
+
ctx.textAlign = 'center';
|
|
299
|
+
ctx.textBaseline = 'middle';
|
|
300
|
+
ctx.fillText(text, wCss / 2, hCss / 2);
|
|
301
|
+
var texture = new THREE__namespace.CanvasTexture(canvas);
|
|
302
|
+
texture.minFilter = THREE__namespace.LinearFilter;
|
|
303
|
+
texture.anisotropy = 4;
|
|
304
|
+
var material = new THREE__namespace.SpriteMaterial({
|
|
305
|
+
map: texture,
|
|
306
|
+
transparent: true,
|
|
307
|
+
depthTest: false,
|
|
308
|
+
// always readable, never occluded by the board/switch
|
|
309
|
+
depthWrite: false
|
|
310
|
+
});
|
|
311
|
+
var sprite = new THREE__namespace.Sprite(material);
|
|
312
|
+
var aspect = canvas.width / canvas.height;
|
|
313
|
+
sprite.scale.set(worldHeight * aspect, worldHeight, 1);
|
|
314
|
+
sprite.renderOrder = 999;
|
|
315
|
+
return sprite;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Trace a rounded-rect path (fill applied by caller). @private */
|
|
319
|
+
}, {
|
|
320
|
+
key: "_roundRect",
|
|
321
|
+
value: function _roundRect(ctx, x, y, w, h, r) {
|
|
322
|
+
var rr = Math.min(r, w / 2, h / 2);
|
|
323
|
+
ctx.beginPath();
|
|
324
|
+
ctx.moveTo(x + rr, y);
|
|
325
|
+
ctx.arcTo(x + w, y, x + w, y + h, rr);
|
|
326
|
+
ctx.arcTo(x + w, y + h, x, y + h, rr);
|
|
327
|
+
ctx.arcTo(x, y + h, x, y, rr);
|
|
328
|
+
ctx.arcTo(x, y, x + w, y, rr);
|
|
329
|
+
ctx.closePath();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Remove the panel, its labels, and its registered behaviors (scene reload).
|
|
334
|
+
* @private
|
|
335
|
+
*/
|
|
336
|
+
}, {
|
|
337
|
+
key: "_teardown",
|
|
338
|
+
value: function _teardown() {
|
|
339
|
+
this._labels = [];
|
|
340
|
+
if (this._panelId) {
|
|
341
|
+
var _this$sceneViewer4;
|
|
342
|
+
(_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);
|
|
343
|
+
}
|
|
344
|
+
if (this._panel) {
|
|
345
|
+
var _this$sceneViewer5;
|
|
346
|
+
(_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.scene) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.remove(this._panel);
|
|
347
|
+
this._panel.traverse(function (obj) {
|
|
348
|
+
if (obj.isMesh || obj.isSprite) {
|
|
349
|
+
var _obj$geometry, _obj$geometry$dispose;
|
|
350
|
+
(_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);
|
|
351
|
+
var mats = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
352
|
+
mats.forEach(function (m) {
|
|
353
|
+
var _m$map, _m$map$dispose, _m$dispose;
|
|
354
|
+
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
|
|
355
|
+
m === null || m === void 0 || (_m$dispose = m.dispose) === null || _m$dispose === void 0 || _m$dispose.call(m);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
this._panel = null;
|
|
360
|
+
}
|
|
361
|
+
this._panelId = null;
|
|
362
|
+
}
|
|
363
|
+
}, {
|
|
364
|
+
key: "_doDispose",
|
|
365
|
+
value: function _doDispose() {
|
|
366
|
+
var _this$sceneViewer6;
|
|
367
|
+
if ((_this$sceneViewer6 = this.sceneViewer) !== null && _this$sceneViewer6 !== void 0 && _this$sceneViewer6.off) {
|
|
368
|
+
this.sceneViewer.off('scene-loaded', this._build);
|
|
369
|
+
}
|
|
370
|
+
this._teardown();
|
|
371
|
+
this.sceneViewer = null;
|
|
372
|
+
}
|
|
373
|
+
}]);
|
|
374
|
+
}(baseDisposable.BaseDisposable);
|
|
375
|
+
|
|
376
|
+
exports.ControlPanelManager = ControlPanelManager;
|
|
377
|
+
exports.computeSwitchRowX = computeSwitchRowX;
|
|
@@ -6,6 +6,7 @@ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHel
|
|
|
6
6
|
require('three');
|
|
7
7
|
var GLTFExporter = require('../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js');
|
|
8
8
|
var nameUtils = require('../../utils/nameUtils.js');
|
|
9
|
+
var stateEngineScene = require('../../core/stateEngineScene.js');
|
|
9
10
|
|
|
10
11
|
function _interopNamespace(e) {
|
|
11
12
|
if (e && e.__esModule) return e;
|
|
@@ -78,7 +79,8 @@ var SceneExportManager = /*#__PURE__*/function () {
|
|
|
78
79
|
key: "exportSceneData",
|
|
79
80
|
value: function exportSceneData() {
|
|
80
81
|
var _this = this,
|
|
81
|
-
_this$sceneViewer$cur2
|
|
82
|
+
_this$sceneViewer$cur2,
|
|
83
|
+
_this$sceneViewer$cen;
|
|
82
84
|
console.log('📤 Starting scene export...');
|
|
83
85
|
if (!this.sceneViewer.scene) {
|
|
84
86
|
console.warn('⚠️ No scene available for export');
|
|
@@ -311,6 +313,13 @@ var SceneExportManager = /*#__PURE__*/function () {
|
|
|
311
313
|
if (sceneBehaviors && sceneBehaviors.length > 0) {
|
|
312
314
|
exportData.behaviors = sceneBehaviors;
|
|
313
315
|
}
|
|
316
|
+
|
|
317
|
+
// Persist plant / groups / chains / stateRegistry from the State Engine and
|
|
318
|
+
// bump the format to 3.0 (Framework §12; no-op if no engine present).
|
|
319
|
+
var stateEngine = (_this$sceneViewer$cen = this.sceneViewer.centralPlant) === null || _this$sceneViewer$cen === void 0 ? void 0 : _this$sceneViewer$cen.stateEngine;
|
|
320
|
+
if (stateEngine) {
|
|
321
|
+
stateEngineScene.applyDeclarationToExport(exportData, stateEngine);
|
|
322
|
+
}
|
|
314
323
|
console.log('✅ Scene export completed:', exportData);
|
|
315
324
|
console.log("\uD83D\uDCCA Exported ".concat(sceneChildren.length, " components and ").concat(exportData.connections.length, " connections"));
|
|
316
325
|
return exportData;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var baseDisposable = require('../../core/baseDisposable.js');
|
|
7
|
+
var nameUtils = require('../../utils/nameUtils.js');
|
|
8
|
+
|
|
9
|
+
/** How far the albedo is pulled toward black for a gated-off mesh. */
|
|
10
|
+
var OFF_COLOR_SCALE = 0.2;
|
|
11
|
+
var EffectiveVisualManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
12
|
+
/**
|
|
13
|
+
* @param {Object} sceneViewer - The central sceneViewer hub
|
|
14
|
+
*/
|
|
15
|
+
function EffectiveVisualManager(sceneViewer) {
|
|
16
|
+
var _this;
|
|
17
|
+
_rollupPluginBabelHelpers.classCallCheck(this, EffectiveVisualManager);
|
|
18
|
+
_this = _rollupPluginBabelHelpers.callSuper(this, EffectiveVisualManager);
|
|
19
|
+
_this.sceneViewer = sceneViewer;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Materials this manager has gated (cloned + cached), for restore on dispose.
|
|
23
|
+
* @type {Set<THREE.Material>}
|
|
24
|
+
*/
|
|
25
|
+
_this._gatedMaterials = new Set();
|
|
26
|
+
|
|
27
|
+
// Bound handlers so we can unsubscribe in _doDispose().
|
|
28
|
+
_this._onChange = _this._onChange.bind(_this);
|
|
29
|
+
_this._regateAll = _this._regateAll.bind(_this);
|
|
30
|
+
if (sceneViewer !== null && sceneViewer !== void 0 && sceneViewer.on) {
|
|
31
|
+
sceneViewer.on('engine-state-changed', _this._onChange);
|
|
32
|
+
sceneViewer.on('scene-loaded', _this._regateAll);
|
|
33
|
+
}
|
|
34
|
+
return _this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Engine change handling ──────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* React to one committed registry change. Only 2-part object / pipe addresses
|
|
41
|
+
* are gated; everything else is ignored.
|
|
42
|
+
* @param {{ address: string, desired: *, effective: * }} change
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
_rollupPluginBabelHelpers.inherits(EffectiveVisualManager, _BaseDisposable);
|
|
46
|
+
return _rollupPluginBabelHelpers.createClass(EffectiveVisualManager, [{
|
|
47
|
+
key: "_onChange",
|
|
48
|
+
value: function _onChange(change) {
|
|
49
|
+
if (!change || typeof change.address !== 'string') return;
|
|
50
|
+
var parts = change.address.split('::');
|
|
51
|
+
if (parts.length !== 2) return; // 3-part I/O is desired-driven; skip
|
|
52
|
+
|
|
53
|
+
var _parts = _rollupPluginBabelHelpers.slicedToArray(parts, 2),
|
|
54
|
+
scope = _parts[0],
|
|
55
|
+
attr = _parts[1];
|
|
56
|
+
if (scope === 'plant' || scope.startsWith('group:')) return; // members self-report
|
|
57
|
+
|
|
58
|
+
if (attr === 'power') {
|
|
59
|
+
this._gateEquipment(scope, change.effective);
|
|
60
|
+
} else if (attr === 'flow') {
|
|
61
|
+
this._gatePipe(scope, change.effective);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Re-gate the whole scene from current effective state. Used after scene load,
|
|
67
|
+
* where StateEngine.load() populates the registry without emitting per-address
|
|
68
|
+
* events (and the meshes have just been (re)built).
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
}, {
|
|
72
|
+
key: "_regateAll",
|
|
73
|
+
value: function _regateAll() {
|
|
74
|
+
var _this$sceneViewer;
|
|
75
|
+
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;
|
|
76
|
+
if (!engine) return;
|
|
77
|
+
var registry = engine.toRegistry();
|
|
78
|
+
for (var _i = 0, _Object$keys = Object.keys(registry); _i < _Object$keys.length; _i++) {
|
|
79
|
+
var address = _Object$keys[_i];
|
|
80
|
+
var parts = address.split('::');
|
|
81
|
+
if (parts.length !== 2) continue;
|
|
82
|
+
var _parts2 = _rollupPluginBabelHelpers.slicedToArray(parts, 2),
|
|
83
|
+
scope = _parts2[0],
|
|
84
|
+
attr = _parts2[1];
|
|
85
|
+
if (scope === 'plant' || scope.startsWith('group:')) continue;
|
|
86
|
+
if (attr === 'power') {
|
|
87
|
+
this._gateEquipment(scope, engine.getEffective(address));
|
|
88
|
+
} else if (attr === 'flow') {
|
|
89
|
+
this._gatePipe(scope, engine.getEffective(address));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Equipment gating ────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Darken (off) or restore (on) every mesh of an equipment object.
|
|
98
|
+
* @param {string} objectId - Hardcoded object id (address scope)
|
|
99
|
+
* @param {boolean} on - Effective power
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
}, {
|
|
103
|
+
key: "_gateEquipment",
|
|
104
|
+
value: function _gateEquipment(objectId, on) {
|
|
105
|
+
var _this$sceneViewer2,
|
|
106
|
+
_this2 = this;
|
|
107
|
+
var scene = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.scene;
|
|
108
|
+
if (!scene) return;
|
|
109
|
+
var obj = nameUtils.findObjectByHardcodedUuid(scene, objectId);
|
|
110
|
+
if (!obj || typeof obj.traverse !== 'function') return;
|
|
111
|
+
obj.traverse(function (mesh) {
|
|
112
|
+
if (!mesh.isMesh && !mesh.material) return;
|
|
113
|
+
// Leave attached I/O control panels (switches) lit and operable even when
|
|
114
|
+
// their host is powered off — you must be able to see and click the switch
|
|
115
|
+
// that turns it back on. (traverse can't prune, so guard per mesh.)
|
|
116
|
+
if (_this2._isUnderIoDevice(mesh, obj)) return;
|
|
117
|
+
var materials = _this2._prepareMeshMaterials(mesh);
|
|
118
|
+
var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(materials),
|
|
119
|
+
_step;
|
|
120
|
+
try {
|
|
121
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
122
|
+
var material = _step.value;
|
|
123
|
+
_this2._applyGate(material, on);
|
|
124
|
+
}
|
|
125
|
+
} catch (err) {
|
|
126
|
+
_iterator.e(err);
|
|
127
|
+
} finally {
|
|
128
|
+
_iterator.f();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* True if `mesh` is an io-device or nested under one, walking up to (but not
|
|
135
|
+
* past) the equipment root `stopAt`.
|
|
136
|
+
* @param {THREE.Object3D} mesh
|
|
137
|
+
* @param {THREE.Object3D} stopAt - equipment root to stop the upward walk at
|
|
138
|
+
* @returns {boolean}
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
}, {
|
|
142
|
+
key: "_isUnderIoDevice",
|
|
143
|
+
value: function _isUnderIoDevice(mesh, stopAt) {
|
|
144
|
+
var node = mesh;
|
|
145
|
+
while (node) {
|
|
146
|
+
var _node$userData;
|
|
147
|
+
if (((_node$userData = node.userData) === null || _node$userData === void 0 ? void 0 : _node$userData.objectType) === 'io-device') return true;
|
|
148
|
+
if (node === stopAt) break;
|
|
149
|
+
node = node.parent;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Ensure a mesh's material(s) are cloned-on-write and have their baseline
|
|
156
|
+
* cached, then return the material list to gate.
|
|
157
|
+
* @param {THREE.Mesh} mesh
|
|
158
|
+
* @returns {THREE.Material[]}
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
}, {
|
|
162
|
+
key: "_prepareMeshMaterials",
|
|
163
|
+
value: function _prepareMeshMaterials(mesh) {
|
|
164
|
+
if (!mesh.material) return [];
|
|
165
|
+
|
|
166
|
+
// Clone-on-write once per mesh so shared library materials aren't shared.
|
|
167
|
+
if (!mesh.userData._effectiveGateCloned) {
|
|
168
|
+
if (Array.isArray(mesh.material)) {
|
|
169
|
+
mesh.material = mesh.material.map(function (m) {
|
|
170
|
+
return m !== null && m !== void 0 && m.clone ? m.clone() : m;
|
|
171
|
+
});
|
|
172
|
+
} else if (mesh.material.clone) {
|
|
173
|
+
mesh.material = mesh.material.clone();
|
|
174
|
+
}
|
|
175
|
+
mesh.userData._effectiveGateCloned = true;
|
|
176
|
+
}
|
|
177
|
+
var materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
178
|
+
var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(materials),
|
|
179
|
+
_step2;
|
|
180
|
+
try {
|
|
181
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
182
|
+
var _material$userData, _material$color$clone, _material$color, _material$color$clone2, _material$emissive$cl, _material$emissive, _material$emissive$cl2;
|
|
183
|
+
var material = _step2.value;
|
|
184
|
+
if (!material || (_material$userData = material.userData) !== null && _material$userData !== void 0 && _material$userData._effectiveGate) continue;
|
|
185
|
+
material.userData = material.userData || {};
|
|
186
|
+
material.userData._effectiveGate = {
|
|
187
|
+
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,
|
|
188
|
+
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,
|
|
189
|
+
emissiveIntensity: typeof material.emissiveIntensity === 'number' ? material.emissiveIntensity : null
|
|
190
|
+
};
|
|
191
|
+
this._gatedMaterials.add(material);
|
|
192
|
+
}
|
|
193
|
+
} catch (err) {
|
|
194
|
+
_iterator2.e(err);
|
|
195
|
+
} finally {
|
|
196
|
+
_iterator2.f();
|
|
197
|
+
}
|
|
198
|
+
return materials.filter(Boolean);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Darken or restore a single material from its cached baseline.
|
|
203
|
+
* @param {THREE.Material} material
|
|
204
|
+
* @param {boolean} on
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
}, {
|
|
208
|
+
key: "_applyGate",
|
|
209
|
+
value: function _applyGate(material, on) {
|
|
210
|
+
var _material$userData2;
|
|
211
|
+
var base = (_material$userData2 = material.userData) === null || _material$userData2 === void 0 ? void 0 : _material$userData2._effectiveGate;
|
|
212
|
+
if (!base) return;
|
|
213
|
+
if (on) {
|
|
214
|
+
if (base.color && material.color) material.color.copy(base.color);
|
|
215
|
+
if (base.emissive && material.emissive) material.emissive.copy(base.emissive);
|
|
216
|
+
if (base.emissiveIntensity !== null) material.emissiveIntensity = base.emissiveIntensity;
|
|
217
|
+
} else {
|
|
218
|
+
if (base.color && material.color) material.color.copy(base.color).multiplyScalar(OFF_COLOR_SCALE);
|
|
219
|
+
if (material.emissive) material.emissive.setScalar(0);
|
|
220
|
+
if (typeof material.emissiveIntensity === 'number') material.emissiveIntensity = 0;
|
|
221
|
+
}
|
|
222
|
+
material.needsUpdate = true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Pipe flow gating ────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Delegate pipe-flow gating to PathFlowManager (grays the shared per-path
|
|
229
|
+
* material when off, restores the temperature colour when on).
|
|
230
|
+
* @param {string} pathId - `${from}-->${to}`
|
|
231
|
+
* @param {boolean} on - Effective flow
|
|
232
|
+
* @private
|
|
233
|
+
*/
|
|
234
|
+
}, {
|
|
235
|
+
key: "_gatePipe",
|
|
236
|
+
value: function _gatePipe(pathId, on) {
|
|
237
|
+
var _this$sceneViewer3, _pathFlowManager$setF;
|
|
238
|
+
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;
|
|
239
|
+
pathFlowManager === null || pathFlowManager === void 0 || (_pathFlowManager$setF = pathFlowManager.setFlowEnabled) === null || _pathFlowManager$setF === void 0 || _pathFlowManager$setF.call(pathFlowManager, pathId, on);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
|
243
|
+
}, {
|
|
244
|
+
key: "_doDispose",
|
|
245
|
+
value: function _doDispose() {
|
|
246
|
+
var _this$sceneViewer4;
|
|
247
|
+
if ((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.off) {
|
|
248
|
+
this.sceneViewer.off('engine-state-changed', this._onChange);
|
|
249
|
+
this.sceneViewer.off('scene-loaded', this._regateAll);
|
|
250
|
+
}
|
|
251
|
+
// Restore any still-gated materials to their cached baseline.
|
|
252
|
+
var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._gatedMaterials),
|
|
253
|
+
_step3;
|
|
254
|
+
try {
|
|
255
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
256
|
+
var material = _step3.value;
|
|
257
|
+
this._applyGate(material, true);
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
_iterator3.e(err);
|
|
261
|
+
} finally {
|
|
262
|
+
_iterator3.f();
|
|
263
|
+
}
|
|
264
|
+
this._gatedMaterials.clear();
|
|
265
|
+
this.sceneViewer = null;
|
|
266
|
+
}
|
|
267
|
+
}]);
|
|
268
|
+
}(baseDisposable.BaseDisposable);
|
|
269
|
+
|
|
270
|
+
exports.EffectiveVisualManager = EffectiveVisualManager;
|