@2112-lab/central-plant 0.3.26 → 0.3.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { inherits as _inherits, createClass as _createClass, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import * as THREE from 'three';
3
3
  import { BaseDisposable } from '../../core/baseDisposable.js';
4
4
 
@@ -144,6 +144,169 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
144
144
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
145
145
  }
146
146
 
147
+ // ── IO device drag-to-state ─────────────────────────────────────────────
148
+
149
+ /**
150
+ * Begin tracking a drag gesture on an IO device mesh.
151
+ * Records the initial state of each animation data point so that
152
+ * `updateIODeviceDrag` can compute relative offsets from it.
153
+ *
154
+ * @param {THREE.Object3D} ioDeviceObject
155
+ */
156
+ }, {
157
+ key: "startIODeviceDrag",
158
+ value: function startIODeviceDrag(ioDeviceObject) {
159
+ var _this$sceneViewer3,
160
+ _this2 = this;
161
+ if (!ioDeviceObject || !this._stateAdapter) return;
162
+ var ud = ioDeviceObject.userData;
163
+ var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
164
+ if (!attachmentId) return;
165
+ var parentUuid = null;
166
+ var obj = ioDeviceObject.parent;
167
+ while (obj) {
168
+ var _obj$userData2;
169
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component') {
170
+ parentUuid = obj.uuid;
171
+ break;
172
+ }
173
+ obj = obj.parent;
174
+ }
175
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
176
+ var ioAnimMgr = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.ioAnimationManager;
177
+ var dataPoints = ((ioAnimMgr === null || ioAnimMgr === void 0 ? void 0 : ioAnimMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
178
+ // deduplicate by id
179
+ .filter(function (dp, i, arr) {
180
+ return arr.findIndex(function (d) {
181
+ return d.id === dp.id;
182
+ }) === i;
183
+ });
184
+ var dpSessions = [];
185
+ var _iterator = _createForOfIteratorHelper(dataPoints),
186
+ _step;
187
+ try {
188
+ var _loop = function _loop() {
189
+ var _this2$_stateAdapter$;
190
+ var dp = _step.value;
191
+ var stateType = (dp.stateType || '').toLowerCase();
192
+ if (stateType !== 'binary' && stateType !== 'boolean' && stateType !== 'enum') return 1; // continue
193
+ var curVal = (_this2$_stateAdapter$ = _this2._stateAdapter.getState(scopedAttachmentId, dp.id)) !== null && _this2$_stateAdapter$ !== void 0 ? _this2$_stateAdapter$ : dp.defaultValue;
194
+ if (stateType === 'binary' || stateType === 'boolean') {
195
+ dpSessions.push({
196
+ dp: dp,
197
+ scopedAttachmentId: scopedAttachmentId,
198
+ attachmentId: attachmentId,
199
+ parentUuid: parentUuid,
200
+ stateType: 'binary',
201
+ lastApplied: curVal
202
+ });
203
+ } else {
204
+ var _dp$stateConfig;
205
+ var opts = ((_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.options) || [];
206
+ var curIdx = opts.findIndex(function (o) {
207
+ return String(o) === String(curVal);
208
+ });
209
+ dpSessions.push({
210
+ dp: dp,
211
+ scopedAttachmentId: scopedAttachmentId,
212
+ attachmentId: attachmentId,
213
+ parentUuid: parentUuid,
214
+ stateType: 'enum',
215
+ opts: opts,
216
+ startIdx: curIdx >= 0 ? curIdx : 0,
217
+ lastAppliedIdx: curIdx >= 0 ? curIdx : 0
218
+ });
219
+ }
220
+ };
221
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
222
+ if (_loop()) continue;
223
+ }
224
+ } catch (err) {
225
+ _iterator.e(err);
226
+ } finally {
227
+ _iterator.f();
228
+ }
229
+ this._ioDragSession = dpSessions.length ? {
230
+ dpSessions: dpSessions
231
+ } : null;
232
+ }
233
+
234
+ /**
235
+ * Update animated mesh state while a drag is in progress.
236
+ * Called continuously during pointermove.
237
+ *
238
+ * Sign convention: up/right = positive `signedDelta`.
239
+ * - Binary: > +20 px → true/on state, < −20 px → false/off state.
240
+ * - Enum: each ±30 px step advances/retreats one option in the list.
241
+ *
242
+ * @param {number} signedDelta - Cumulative signed pixel displacement since drag start
243
+ */
244
+ }, {
245
+ key: "updateIODeviceDrag",
246
+ value: function updateIODeviceDrag(signedDelta) {
247
+ var session = this._ioDragSession;
248
+ if (!session) return;
249
+ var BINARY_THRESHOLD = 20;
250
+ var ENUM_STEP_PX = 30;
251
+ var _iterator2 = _createForOfIteratorHelper(session.dpSessions),
252
+ _step2;
253
+ try {
254
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
255
+ var dps = _step2.value;
256
+ if (dps.stateType === 'binary') {
257
+ var newVal = void 0;
258
+ if (signedDelta > BINARY_THRESHOLD) {
259
+ newVal = true;
260
+ } else if (signedDelta < -BINARY_THRESHOLD) {
261
+ newVal = false;
262
+ } else {
263
+ continue; // dead zone
264
+ }
265
+ if (newVal === dps.lastApplied) continue;
266
+ dps.lastApplied = newVal;
267
+ this._applyDpState(dps, newVal);
268
+ } else if (dps.stateType === 'enum') {
269
+ var steps = Math.round(signedDelta / ENUM_STEP_PX);
270
+ var newIdx = Math.max(0, Math.min(dps.opts.length - 1, dps.startIdx + steps));
271
+ if (newIdx === dps.lastAppliedIdx) continue;
272
+ dps.lastAppliedIdx = newIdx;
273
+ this._applyDpState(dps, dps.opts[newIdx]);
274
+ }
275
+ }
276
+ } catch (err) {
277
+ _iterator2.e(err);
278
+ } finally {
279
+ _iterator2.f();
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Clean up drag session state on pointerup.
285
+ */
286
+ }, {
287
+ key: "endIODeviceDrag",
288
+ value: function endIODeviceDrag() {
289
+ this._ioDragSession = null;
290
+ }
291
+
292
+ /**
293
+ * Apply a new value to a data point, updating Vuex state and firing behavior/animation triggers.
294
+ * @private
295
+ */
296
+ }, {
297
+ key: "_applyDpState",
298
+ value: function _applyDpState(_ref2, newVal) {
299
+ var _this$_stateAdapter, _this$sceneViewer4, _this$sceneViewer5;
300
+ var scopedAttachmentId = _ref2.scopedAttachmentId,
301
+ attachmentId = _ref2.attachmentId,
302
+ parentUuid = _ref2.parentUuid,
303
+ dp = _ref2.dp;
304
+ var dpId = dp.id;
305
+ (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
306
+ (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.behaviorManager) === null || _this$sceneViewer4 === void 0 || _this$sceneViewer4.triggerState(attachmentId, dpId, newVal, parentUuid);
307
+ (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioAnimationManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, dpId, newVal, parentUuid);
308
+ }
309
+
147
310
  /**
148
311
  * Should be called when an object is selected or deselected.
149
312
  * @param {THREE.Object3D|null} object
@@ -257,18 +420,18 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
257
420
  }, {
258
421
  key: "_getIODevices",
259
422
  value: function _getIODevices(object) {
260
- var _this2 = this;
423
+ var _this3 = this;
261
424
  var devices = [];
262
425
  var parentUuid = object.uuid; // The component's own UUID
263
426
  object.traverse(function (child) {
264
427
  var _child$userData;
265
428
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
266
- var _this2$sceneViewer$ma, _this2$sceneViewer;
429
+ var _this3$sceneViewer$ma, _this3$sceneViewer;
267
430
  var attachmentId = child.userData.attachmentId || '';
268
431
 
269
432
  // Use only data points from the animate window (animationConfig).
270
433
  // The static ioConfig.states[] snapshot on userData is intentionally ignored.
271
- var dataPoints = (_this2$sceneViewer$ma = (_this2$sceneViewer = _this2.sceneViewer) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.managers) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.ioAnimationManager) === null || _this2$sceneViewer === void 0 ? void 0 : _this2$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this2$sceneViewer$ma !== void 0 ? _this2$sceneViewer$ma : [];
434
+ var dataPoints = (_this3$sceneViewer$ma = (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.ioAnimationManager) === null || _this3$sceneViewer === void 0 ? void 0 : _this3$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this3$sceneViewer$ma !== void 0 ? _this3$sceneViewer$ma : [];
272
435
 
273
436
  // When data points come from animationConfig they already carry direction:'input'.
274
437
  // Pass null so _buildDataPointRow uses the per-dp direction instead of the
@@ -278,7 +441,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
278
441
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
279
442
  deviceId: child.userData.deviceId || '',
280
443
  attachmentId: attachmentId,
281
- scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
444
+ scopedAttachmentId: _this3._getScopedAttachmentKey(attachmentId, parentUuid),
282
445
  dataPoints: dataPoints,
283
446
  direction: deviceDirection
284
447
  });
@@ -294,7 +457,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
294
457
  }, {
295
458
  key: "_buildTooltip",
296
459
  value: function _buildTooltip(object) {
297
- var _this3 = this;
460
+ var _this4 = this;
298
461
  // Remove any existing tooltip first
299
462
  this.hide();
300
463
  // Re-assign selected object since hide() clears it
@@ -365,7 +528,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
365
528
  // Use scopedAttachmentId to ensure state is isolated per component instance
366
529
  if (device.scopedAttachmentId && device.dataPoints.length > 0) {
367
530
  device.dataPoints.forEach(function (dp) {
368
- var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
531
+ var row = _this4._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
369
532
  list.appendChild(row);
370
533
  });
371
534
  }
@@ -376,11 +539,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
376
539
  // Hover expand/collapse
377
540
  trigger.addEventListener('mouseenter', function () {
378
541
  ioSection.classList.add('expanded');
379
- _this3._ioExpanded = true;
542
+ _this4._ioExpanded = true;
380
543
  });
381
544
  ioSection.addEventListener('mouseleave', function () {
382
545
  ioSection.classList.remove('expanded');
383
- _this3._ioExpanded = false;
546
+ _this4._ioExpanded = false;
384
547
  });
385
548
  card.appendChild(ioSection);
386
549
  } else {
@@ -424,11 +587,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
424
587
  }, {
425
588
  key: "_positionTooltip",
426
589
  value: function _positionTooltip() {
427
- var _this$sceneViewer3, _this$sceneViewer4;
590
+ var _this$sceneViewer6, _this$sceneViewer7;
428
591
  if (!this.tooltipEl || !this.selectedObject) return;
429
592
  var container = this._getContainer();
430
- var camera = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.camera;
431
- var renderer = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.renderer;
593
+ var camera = (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.camera;
594
+ var renderer = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.renderer;
432
595
  if (!container || !camera || !renderer) return;
433
596
 
434
597
  // Compute bounding box to position above the component
@@ -465,8 +628,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
465
628
  }, {
466
629
  key: "_getContainer",
467
630
  value: function _getContainer() {
468
- var _this$sceneViewer5;
469
- return ((_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.renderer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.domElement) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.parentElement) || null;
631
+ var _this$sceneViewer8;
632
+ return ((_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.renderer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.domElement) === null || _this$sceneViewer8 === void 0 ? void 0 : _this$sceneViewer8.parentElement) || null;
470
633
  }
471
634
 
472
635
  // -----------------------------------------------------------------------
@@ -488,10 +651,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
488
651
  }, {
489
652
  key: "_buildDataPointRow",
490
653
  value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
491
- var _ref2,
654
+ var _ref3,
492
655
  _this$_stateAdapter$g,
493
- _this$_stateAdapter,
494
- _this4 = this;
656
+ _this$_stateAdapter2,
657
+ _this5 = this;
495
658
  var row = document.createElement('div');
496
659
  row.className = 'cp-tooltip__dp-row';
497
660
  var nameEl = document.createElement('span');
@@ -503,18 +666,18 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
503
666
  // Device-level direction takes precedence; fall back to per-dp direction
504
667
  var resolvedDirection = deviceDirection || dp.direction || 'output';
505
668
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
506
- var currentVal = (_ref2 = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : null;
669
+ var currentVal = (_ref3 = (_this$_stateAdapter$g = (_this$_stateAdapter2 = this._stateAdapter) === null || _this$_stateAdapter2 === void 0 ? void 0 : _this$_stateAdapter2.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : null;
507
670
  if (isInput) {
508
671
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
509
- var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer, _this4$sceneViewer2;
510
- (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
672
+ var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer, _this5$sceneViewer2;
673
+ (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
511
674
  // Also fire BehaviorManager so any wired behaviors react immediately.
512
675
  // Pass the parent component UUID so behaviors scoped to a specific instance
513
676
  // don't bleed across clones that share the same attachmentId.
514
677
  // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
515
- var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
516
- (_this4$sceneViewer = _this4.sceneViewer) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.managers) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.behaviorManager) === null || _this4$sceneViewer === void 0 || _this4$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
517
- (_this4$sceneViewer2 = _this4.sceneViewer) === null || _this4$sceneViewer2 === void 0 || (_this4$sceneViewer2 = _this4$sceneViewer2.managers) === null || _this4$sceneViewer2 === void 0 || (_this4$sceneViewer2 = _this4$sceneViewer2.ioAnimationManager) === null || _this4$sceneViewer2 === void 0 || _this4$sceneViewer2.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
678
+ var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
679
+ (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.behaviorManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
680
+ (_this5$sceneViewer2 = _this5.sceneViewer) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.managers) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.ioAnimationManager) === null || _this5$sceneViewer2 === void 0 || _this5$sceneViewer2.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
518
681
  });
519
682
  row.appendChild(ctrl);
520
683
  this._stateElements.set(key, {
@@ -523,9 +686,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
523
686
  isInput: true
524
687
  });
525
688
  } else {
526
- var _dp$stateConfig;
689
+ var _dp$stateConfig2;
527
690
  // unit suffix (optional, shown between name and badge)
528
- var unit = (_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.unit;
691
+ var unit = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.unit;
529
692
  if (unit) {
530
693
  var unitEl = document.createElement('span');
531
694
  unitEl.className = 'cp-tooltip__dp-unit';
@@ -670,7 +833,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
670
833
  }, {
671
834
  key: "_refreshStateDisplays",
672
835
  value: function _refreshStateDisplays() {
673
- var _this5 = this;
836
+ var _this6 = this;
674
837
  if (!this._stateAdapter || !this._stateElements.size) return;
675
838
  this._stateElements.forEach(function (entry, key) {
676
839
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
@@ -679,8 +842,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
679
842
  if (sepIdx === -1) return;
680
843
  var scopedAttachmentId = key.slice(0, sepIdx);
681
844
  var dataPointId = key.slice(sepIdx + 2);
682
- var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
683
- _this5._applyBadgeValue(entry.el, val, entry.dp);
845
+ var val = _this6._stateAdapter.getState(scopedAttachmentId, dataPointId);
846
+ _this6._applyBadgeValue(entry.el, val, entry.dp);
684
847
  });
685
848
  }
686
849
  }]);
@@ -2,7 +2,7 @@ import { createClass as _createClass, objectSpread2 as _objectSpread2, toConsuma
2
2
  import * as THREE from 'three';
3
3
  import { attachIODevicesToComponent } from '../../utils/ioDeviceUtils.js';
4
4
  import modelPreloader from '../../rendering/modelPreloader.js';
5
- import { computeFilteredBoundingBox } from '../../utils/boundingBoxUtils.js';
5
+ import { computeFilteredBoundingBox, computeFilteredBoundingBoxCached } from '../../utils/boundingBoxUtils.js';
6
6
  import { cacheBasePosition } from './viewport2DManager.js';
7
7
 
8
8
  var ModelManager = /*#__PURE__*/function () {
@@ -53,7 +53,7 @@ var ModelManager = /*#__PURE__*/function () {
53
53
  key: "loadLibraryModel",
54
54
  value: function () {
55
55
  var _loadLibraryModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(targetMesh, jsonEntry, componentData) {
56
- var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, _jsonEntry$userData4, _t;
56
+ var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
57
57
  return _regenerator().w(function (_context2) {
58
58
  while (1) switch (_context2.n) {
59
59
  case 0:
@@ -150,6 +150,20 @@ var ModelManager = /*#__PURE__*/function () {
150
150
  case 8:
151
151
  // Replace mesh in scene
152
152
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
153
+
154
+ // Pre-warm the filtered bounding-box cache for smart components so the
155
+ // first selection is instant. Deferred to idle time so it does not
156
+ // block the current frame.
157
+ if (componentData.isSmart) {
158
+ warmFn = function warmFn() {
159
+ return computeFilteredBoundingBoxCached(libraryModel, ['io-device', 'connector']);
160
+ };
161
+ if (typeof requestIdleCallback !== 'undefined') {
162
+ requestIdleCallback(warmFn);
163
+ } else {
164
+ setTimeout(warmFn, 0);
165
+ }
166
+ }
153
167
  console.log("\uD83C\uDF89 ".concat((_jsonEntry$userData3 = jsonEntry.userData) === null || _jsonEntry$userData3 === void 0 ? void 0 : _jsonEntry$userData3.libraryId, " GLB model successfully rendered in scene"));
154
168
  return _context2.a(2, libraryModel);
155
169
  case 9:
@@ -1,4 +1,4 @@
1
- import { createForOfIteratorHelper as _createForOfIteratorHelper } from '../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { createForOfIteratorHelper as _createForOfIteratorHelper, construct as _construct, toConsumableArray as _toConsumableArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import * as THREE from 'three';
3
3
 
4
4
  /**
@@ -209,6 +209,34 @@ function computeIODeviceBoundingBoxes(componentObject) {
209
209
  * const helpers = createSelectionBoxHelpers(pumpModel, 0x00ff00)
210
210
  * helpers.forEach(h => scene.add(h))
211
211
  */
212
+ /**
213
+ * Returns a filtered bounding box for `object`, using a cache stored on
214
+ * `object.userData._filteredBBoxCache`. The cache key is the serialised
215
+ * world-matrix elements string; if the object has moved the cache is
216
+ * automatically invalidated and recomputed.
217
+ *
218
+ * This avoids the expensive full-traverse on every selection event for
219
+ * large smart components with many child meshes.
220
+ *
221
+ * @param {THREE.Object3D} object
222
+ * @param {string[]} excludeTypes
223
+ * @returns {THREE.Box3}
224
+ */
225
+ function computeFilteredBoundingBoxCached(object, excludeTypes) {
226
+ object.updateMatrixWorld(true);
227
+ var matrixKey = object.matrixWorld.elements.join(',');
228
+ var cache = object.userData._filteredBBoxCache;
229
+ if (cache && cache.matrixKey === matrixKey) {
230
+ return new THREE.Box3(_construct(THREE.Vector3, _toConsumableArray(cache.min)), _construct(THREE.Vector3, _toConsumableArray(cache.max)));
231
+ }
232
+ var box = computeFilteredBoundingBox(object, excludeTypes);
233
+ object.userData._filteredBBoxCache = {
234
+ matrixKey: matrixKey,
235
+ min: box.min.toArray(),
236
+ max: box.max.toArray()
237
+ };
238
+ return box;
239
+ }
212
240
  function createSelectionBoxHelpers(object) {
213
241
  var _object$children;
214
242
  var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0x00ff00;
@@ -222,7 +250,7 @@ function createSelectionBoxHelpers(object) {
222
250
  });
223
251
  if (hasIODevices) {
224
252
  // 1. Create filtered helper for the component body
225
- var filteredBox = computeFilteredBoundingBox(object, excludeTypes);
253
+ var filteredBox = computeFilteredBoundingBoxCached(object, excludeTypes);
226
254
  var componentHelper = _createBoxHelperFromBox3(filteredBox, color);
227
255
  componentHelper.isHelper = true;
228
256
  componentHelper.userData = {
@@ -232,33 +260,6 @@ function createSelectionBoxHelpers(object) {
232
260
  excludeTypes: excludeTypes
233
261
  };
234
262
  helpers.push(componentHelper);
235
-
236
- // 2. Create individual helpers for each io-device
237
- var _iterator2 = _createForOfIteratorHelper(object.children),
238
- _step2;
239
- try {
240
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
241
- var _child$userData3;
242
- var child = _step2.value;
243
- if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) !== 'io-device') continue;
244
- var deviceBox = new THREE.Box3().setFromObject(child);
245
- if (deviceBox.isEmpty()) continue;
246
- var deviceHelper = _createBoxHelperFromBox3(deviceBox, color);
247
- deviceHelper.isHelper = true;
248
- deviceHelper.userData = {
249
- isBoundingBox: true,
250
- sourceObjectUuid: child.uuid,
251
- isFiltered: false,
252
- isIODevice: true,
253
- parentComponentUuid: object.uuid
254
- };
255
- helpers.push(deviceHelper);
256
- }
257
- } catch (err) {
258
- _iterator2.e(err);
259
- } finally {
260
- _iterator2.f();
261
- }
262
263
  } else {
263
264
  // Standard BoxHelper for non-smart objects
264
265
  var boxHelper = new THREE.BoxHelper(object, color);
@@ -282,11 +283,11 @@ function createSelectionBoxHelpers(object) {
282
283
  * @param {THREE.Scene} scene - The scene (for finding objects by uuid)
283
284
  */
284
285
  function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
285
- var _iterator3 = _createForOfIteratorHelper(helpers),
286
- _step3;
286
+ var _iterator2 = _createForOfIteratorHelper(helpers),
287
+ _step2;
287
288
  try {
288
289
  var _loop = function _loop() {
289
- var helper = _step3.value;
290
+ var helper = _step2.value;
290
291
  var _helper$userData = helper.userData,
291
292
  sourceObjectUuid = _helper$userData.sourceObjectUuid,
292
293
  isFiltered = _helper$userData.isFiltered,
@@ -309,26 +310,22 @@ function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
309
310
  if (!sourceObject) return 1; // continue
310
311
  sourceObject.updateMatrixWorld(true);
311
312
  if (isFiltered && excludeTypes) {
312
- // Recompute filtered bbox
313
- var box = computeFilteredBoundingBox(sourceObject, excludeTypes);
313
+ // Recompute filtered bbox (uses cache when the object hasn't moved)
314
+ var box = computeFilteredBoundingBoxCached(sourceObject, excludeTypes);
314
315
  _updateBoxHelperPositions(helper, box);
315
- } else if (isIODevice) {
316
- // Recompute io-device bbox
317
- var _box = new THREE.Box3().setFromObject(sourceObject);
318
- _updateBoxHelperPositions(helper, _box);
319
316
  } else if (helper.update) {
320
317
  // Standard BoxHelper — use built-in update
321
318
  helper.update();
322
319
  }
323
320
  };
324
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
321
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
325
322
  if (_loop()) continue;
326
323
  }
327
324
  } catch (err) {
328
- _iterator3.e(err);
325
+ _iterator2.e(err);
329
326
  } finally {
330
- _iterator3.f();
327
+ _iterator2.f();
331
328
  }
332
329
  }
333
330
 
334
- export { computeFilteredBoundingBox, computeIODeviceBoundingBoxes, createSelectionBoxHelpers, updateSelectionBoxHelpers };
331
+ export { computeFilteredBoundingBox, computeFilteredBoundingBoxCached, computeIODeviceBoundingBoxes, createSelectionBoxHelpers, updateSelectionBoxHelpers };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/src/index.js",