@2112-lab/central-plant 0.3.38 → 0.3.39

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.
Files changed (29) hide show
  1. package/dist/bundle/index.js +1078 -562
  2. package/dist/cjs/src/core/centralPlant.js +115 -68
  3. package/dist/cjs/src/core/centralPlantInternals.js +20 -36
  4. package/dist/cjs/src/index.js +19 -0
  5. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +175 -234
  6. package/dist/cjs/src/managers/components/componentDataManager.js +63 -11
  7. package/dist/cjs/src/managers/scene/componentTooltipManager.js +95 -65
  8. package/dist/cjs/src/managers/scene/modelManager.js +93 -145
  9. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +8 -3
  10. package/dist/cjs/src/utils/animationTransformUtils.js +66 -0
  11. package/dist/cjs/src/utils/behaviorDispatch.js +62 -0
  12. package/dist/cjs/src/utils/behaviorRegistration.js +76 -0
  13. package/dist/cjs/src/utils/behaviorSceneUtils.js +155 -0
  14. package/dist/cjs/src/utils/behaviorSchema.js +209 -0
  15. package/dist/esm/src/core/centralPlant.js +115 -68
  16. package/dist/esm/src/core/centralPlantInternals.js +21 -37
  17. package/dist/esm/src/index.js +4 -0
  18. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +176 -235
  19. package/dist/esm/src/managers/components/componentDataManager.js +63 -11
  20. package/dist/esm/src/managers/scene/componentTooltipManager.js +95 -65
  21. package/dist/esm/src/managers/scene/modelManager.js +94 -146
  22. package/dist/esm/src/managers/scene/sceneOperationsManager.js +8 -3
  23. package/dist/esm/src/utils/animationTransformUtils.js +41 -0
  24. package/dist/esm/src/utils/behaviorDispatch.js +56 -0
  25. package/dist/esm/src/utils/behaviorRegistration.js +71 -0
  26. package/dist/esm/src/utils/behaviorSceneUtils.js +147 -0
  27. package/dist/esm/src/utils/behaviorSchema.js +201 -0
  28. package/dist/index.d.ts +169 -1
  29. package/package.json +1 -1
@@ -1,6 +1,9 @@
1
- import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, toConsumableArray as _toConsumableArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import * as THREE from 'three';
3
3
  import { BaseDisposable } from '../../core/baseDisposable.js';
4
+ import { normalizeBehavior } from '../../utils/behaviorSchema.js';
5
+ import { getScopedAttachmentKey } from '../../utils/behaviorDispatch.js';
6
+ import { applyModelRootTranslation } from '../../utils/animationTransformUtils.js';
4
7
 
5
8
  var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
6
9
  function IoBehaviorManager(sceneViewer) {
@@ -19,12 +22,11 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
19
22
  */
20
23
  _this._entries = new Map();
21
24
  _this._crossComponentBehaviors = [];
22
-
23
- /**
24
- * Map: `${componentUuid}` → Array<{ id, input, outputs }>
25
- * Component-level behaviors for intra-component io-device linking
26
- */
27
25
  _this._componentBehaviors = new Map();
26
+ /** @type {Map<string, string>} parentUuid::attachmentId → parentUuid */
27
+ _this._attachmentParentMap = new Map();
28
+ /** @type {Map<string, Object[]>} cache key → data point definitions */
29
+ _this._dataPointsCache = new Map();
28
30
 
29
31
  /**
30
32
  * Injected by the host application to read and write I/O device state.
@@ -114,7 +116,10 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
114
116
  }
115
117
  if (entries.length) {
116
118
  this._entries.set(key, entries);
117
- // Loaded ${entries.length} animation(s) for attachment "${attachmentId}"
119
+ if (parentUuid && attachmentId) {
120
+ this._attachmentParentMap.set(this._key(parentUuid, attachmentId), parentUuid);
121
+ }
122
+ this._invalidateDataPointsCache(parentUuid, attachmentId);
118
123
  } else {
119
124
  console.warn("[IoBehaviorManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 behaviorConfig had ").concat(anims.length, " entries but none matched a mesh"));
120
125
  }
@@ -122,7 +127,6 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
122
127
 
123
128
  /**
124
129
  * Apply animations triggered by an IO device state change.
125
- * Should be called in parallel with BehaviorManager.triggerState().
126
130
  *
127
131
  * @param {string} attachmentId - Raw attachment key (not scoped)
128
132
  * @param {string} dataPointId - The data point / state variable id that changed
@@ -132,70 +136,36 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
132
136
  }, {
133
137
  key: "triggerState",
134
138
  value: function triggerState(attachmentId, dataPointId, value, parentUuid) {
135
- // triggerState: ${attachmentId}.${dataPointId} = ${value}
136
-
137
- if (parentUuid) {
138
- var key = this._key(parentUuid, attachmentId);
139
- var entries = this._entries.get(key);
140
- if (entries) {
141
- var _iterator2 = _createForOfIteratorHelper(entries),
142
- _step2;
143
- try {
144
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
145
- var entry = _step2.value;
146
- if (entry.anim.stateVariable !== dataPointId) continue;
147
- this._applyAnimation(entry, value);
148
- }
149
- } catch (err) {
150
- _iterator2.e(err);
151
- } finally {
152
- _iterator2.f();
153
- }
154
- }
155
- } else {
156
- // Fallback when parentUuid is not provided: match by attachmentId suffix or exact key match
157
- var suffix = "::".concat(attachmentId);
158
- var _iterator3 = _createForOfIteratorHelper(this._entries.entries()),
159
- _step3;
139
+ var _this$_crossComponent;
140
+ var resolvedParent = parentUuid || this._findComponentByAttachment(attachmentId);
141
+ if (!resolvedParent) return;
142
+ var key = this._key(resolvedParent, attachmentId);
143
+ var entries = this._entries.get(key);
144
+ if (entries) {
145
+ var _iterator2 = _createForOfIteratorHelper(entries),
146
+ _step2;
160
147
  try {
161
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
162
- var _step3$value = _slicedToArray(_step3.value, 2),
163
- _key2 = _step3$value[0],
164
- _entries = _step3$value[1];
165
- if (_key2 === attachmentId || _key2.endsWith(suffix)) {
166
- var _iterator4 = _createForOfIteratorHelper(_entries),
167
- _step4;
168
- try {
169
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
170
- var _entry = _step4.value;
171
- if (_entry.anim.stateVariable !== dataPointId) continue;
172
- this._applyAnimation(_entry, value);
173
- }
174
- } catch (err) {
175
- _iterator4.e(err);
176
- } finally {
177
- _iterator4.f();
178
- }
179
- }
148
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
149
+ var entry = _step2.value;
150
+ if (entry.anim.stateVariable !== dataPointId) continue;
151
+ this._applyAnimation(entry, value);
180
152
  }
181
153
  } catch (err) {
182
- _iterator3.e(err);
154
+ _iterator2.e(err);
183
155
  } finally {
184
- _iterator3.f();
156
+ _iterator2.f();
185
157
  }
186
158
  }
187
159
 
188
160
  // Evaluate component-level behaviors (intra-component io-device linking)
189
- if (parentUuid && this._componentBehaviors.has(parentUuid)) {
190
- var componentBehaviors = this._componentBehaviors.get(parentUuid);
191
- // Checking component-level behaviors (count: ${componentBehaviors.length})
192
- this.triggerCrossComponentBehaviors(componentBehaviors, parentUuid, attachmentId, dataPointId, value);
161
+ if (this._componentBehaviors.has(resolvedParent)) {
162
+ var componentBehaviors = this._componentBehaviors.get(resolvedParent);
163
+ this.triggerCrossComponentBehaviors(componentBehaviors, resolvedParent, attachmentId, dataPointId, value, {
164
+ sameComponent: true
165
+ });
193
166
  }
194
-
195
- // Evaluate cross-component behaviors if any are registered
196
- // Checking cross-component behaviors (count: ${this._crossComponentBehaviors?.length || 0})
197
- if (this._crossComponentBehaviors && this._crossComponentBehaviors.length > 0) {
198
- this.triggerCrossComponentBehaviors(this._crossComponentBehaviors, parentUuid, attachmentId, dataPointId, value);
167
+ if (((_this$_crossComponent = this._crossComponentBehaviors) === null || _this$_crossComponent === void 0 ? void 0 : _this$_crossComponent.length) > 0) {
168
+ this.triggerCrossComponentBehaviors(this._crossComponentBehaviors, resolvedParent, attachmentId, dataPointId, value);
199
169
  }
200
170
  }
201
171
 
@@ -208,9 +178,8 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
208
178
  }, {
209
179
  key: "setCrossComponentBehaviors",
210
180
  value: function setCrossComponentBehaviors(behaviors) {
211
- var _this2 = this;
212
181
  this._crossComponentBehaviors = (behaviors || []).map(function (b) {
213
- return _this2._normalizeBehavior(b);
182
+ return normalizeBehavior(b);
214
183
  });
215
184
  // Loaded ${this._crossComponentBehaviors.length} cross-component behavior(s)
216
185
  }
@@ -225,72 +194,17 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
225
194
  }, {
226
195
  key: "registerComponentBehaviors",
227
196
  value: function registerComponentBehaviors(componentUuid, behaviors) {
228
- var _this3 = this;
229
- if (!behaviors || behaviors.length === 0) return;
197
+ if (!behaviors || behaviors.length === 0) {
198
+ this._componentBehaviors.delete(componentUuid);
199
+ return;
200
+ }
230
201
  var normalized = behaviors.map(function (b) {
231
- return _this3._normalizeBehavior(b);
202
+ return normalizeBehavior(b);
232
203
  });
233
204
  this._componentBehaviors.set(componentUuid, normalized);
234
205
  // Registered ${normalized.length} component-level behavior(s) for component ${componentUuid}
235
206
  }
236
207
 
237
- /**
238
- * Normalize behavior from shorthand to full format.
239
- * Supports:
240
- * - input: "attachment.state" → { attachment, state }
241
- * - outputs: ["attachment.state", ...] → converted to individual behaviors
242
- *
243
- * @param {Object} behavior - Raw behavior from scene JSON
244
- * @returns {Object} Normalized behavior
245
- */
246
- }, {
247
- key: "_normalizeBehavior",
248
- value: function _normalizeBehavior(behavior) {
249
- var normalized = _objectSpread2({}, behavior);
250
-
251
- // Parse shorthand input: "attachment.state"
252
- if (typeof behavior.input === 'string') {
253
- var _behavior$input$split = behavior.input.split('.'),
254
- _behavior$input$split2 = _slicedToArray(_behavior$input$split, 2),
255
- attachment = _behavior$input$split2[0],
256
- state = _behavior$input$split2[1];
257
- normalized.input = {
258
- attachment: attachment,
259
- state: state
260
- };
261
- }
262
-
263
- // Parse shorthand output/outputs
264
- if (behavior.outputs) {
265
- // Multiple outputs - expand to array
266
- normalized._outputs = behavior.outputs.map(function (out) {
267
- if (typeof out === 'string') {
268
- var _out$split = out.split('.'),
269
- _out$split2 = _slicedToArray(_out$split, 2),
270
- _attachment = _out$split2[0],
271
- _state = _out$split2[1];
272
- return {
273
- attachment: _attachment,
274
- state: _state
275
- };
276
- }
277
- return out;
278
- });
279
- delete normalized.outputs;
280
- } else if (typeof behavior.output === 'string') {
281
- // Single output string
282
- var _behavior$output$spli = behavior.output.split('.'),
283
- _behavior$output$spli2 = _slicedToArray(_behavior$output$spli, 2),
284
- _attachment2 = _behavior$output$spli2[0],
285
- _state2 = _behavior$output$spli2[1];
286
- normalized.output = {
287
- attachment: _attachment2,
288
- state: _state2
289
- };
290
- }
291
- return normalized;
292
- }
293
-
294
208
  /**
295
209
  * Find the parent component UUID for a given attachment ID.
296
210
  * Searches the scene tree for the io-device with this attachment ID,
@@ -304,12 +218,10 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
304
218
  value: function _findComponentByAttachment(attachmentId) {
305
219
  var _this$sceneViewer;
306
220
  if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.scene)) return null;
307
- var scene = this.sceneViewer.scene;
308
221
  var found = null;
309
- scene.traverse(function (obj) {
222
+ this.sceneViewer.scene.traverse(function (obj) {
310
223
  var _obj$userData, _obj$userData2;
311
224
  if (found) return;
312
- // Find the io-device object with this attachmentId
313
225
  if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'io-device' && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) {
314
226
  found = obj.userData.parentComponentId;
315
227
  }
@@ -353,20 +265,21 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
353
265
  * @param {string} triggerAttachmentId - Attachment ID of the triggering device
354
266
  * @param {string} triggerStateId - The state variable ID that changed
355
267
  * @param {*} value - The new state value
268
+ * @param {{ sameComponent?: boolean }} [options] - Intra-component links share one parent instance
356
269
  */
357
270
  }, {
358
271
  key: "triggerCrossComponentBehaviors",
359
272
  value: function triggerCrossComponentBehaviors(behaviors, triggerParentUuid, triggerAttachmentId, triggerStateId, value) {
273
+ var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
360
274
  if (!behaviors || !Array.isArray(behaviors)) {
361
275
  return;
362
276
  }
363
-
364
- // Evaluating ${behaviors.length} behavior(s)
365
- var _iterator5 = _createForOfIteratorHelper(behaviors),
366
- _step5;
277
+ var sameComponent = options.sameComponent === true;
278
+ var _iterator3 = _createForOfIteratorHelper(behaviors),
279
+ _step3;
367
280
  try {
368
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
369
- var behavior = _step5.value;
281
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
282
+ var behavior = _step3.value;
370
283
  var input = behavior.input,
371
284
  output = behavior.output,
372
285
  _outputs = behavior._outputs,
@@ -375,94 +288,69 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
375
288
  console.warn('[Behavior] Skipping behavior - missing input or output(s):', behavior);
376
289
  continue;
377
290
  }
378
-
379
- // Auto-lookup component if not specified
380
- var inputComponent = input.component || this._findComponentByAttachment(input.attachment);
381
- console.log("[Behavior] Checking behavior \"".concat(behavior.id, "\":"), {
382
- inputMatch: inputComponent === triggerParentUuid,
383
- attachmentMatch: input.attachment === triggerAttachmentId,
384
- stateMatch: input.state === triggerStateId,
385
- expected: {
386
- component: inputComponent,
387
- attachment: input.attachment,
388
- state: input.state
389
- },
390
- actual: {
391
- component: triggerParentUuid,
392
- attachment: triggerAttachmentId,
393
- state: triggerStateId
394
- }
395
- });
396
-
397
- // Verify that the input matches the triggering source
398
- if (inputComponent === triggerParentUuid && input.attachment === triggerAttachmentId && input.state === triggerStateId) {
291
+ var inputComponent = input.component || (sameComponent ? triggerParentUuid : this._findComponentByAttachment(input.attachment));
292
+ var inputMatches = sameComponent ? input.attachment === triggerAttachmentId && input.state === triggerStateId : inputComponent === triggerParentUuid && input.attachment === triggerAttachmentId && input.state === triggerStateId;
293
+ if (inputMatches) {
399
294
  // Behavior "${behavior.id}" matched
400
295
 
401
296
  // Collect all outputs (single or multiple)
402
297
  var outputs = _outputs || (output ? [output] : []);
403
298
 
404
299
  // Process each output
405
- var _iterator6 = _createForOfIteratorHelper(outputs),
406
- _step6;
300
+ var _iterator4 = _createForOfIteratorHelper(outputs),
301
+ _step4;
407
302
  try {
408
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
409
- var out = _step6.value;
303
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
304
+ var out = _step4.value;
410
305
  // NEW: State-to-state pass-through pattern
411
306
  if (out.state) {
412
- // Auto-lookup output component if not specified
413
- var outputComponent = out.component || this._findComponentByAttachment(out.attachment);
414
-
415
- // Direct state mapping without conditions
307
+ var outputComponent = out.component || (sameComponent ? triggerParentUuid : this._findComponentByAttachment(out.attachment));
416
308
  if (this._stateAdapter) {
417
- // Dispatching state-to-state: ${out.attachment}.${out.state} = ${value}
418
- this._stateAdapter.setState(out.attachment, out.state, value);
419
-
420
- // Trigger animations on the output component
421
- // triggerState(attachmentId, dataPointId, value, parentUuid)
309
+ var scopedKey = getScopedAttachmentKey(out.attachment, outputComponent);
310
+ this._stateAdapter.setState(scopedKey, out.state, value);
422
311
  if (outputComponent) {
423
- // Triggering animations on output component
424
312
  this.triggerState(out.attachment, out.state, value, outputComponent);
425
313
  } else {
426
314
  console.warn("[Behavior] Could not find component for attachment \"".concat(out.attachment, "\""));
427
315
  }
428
316
  } else {
429
- console.warn('[Behavior] State adapter not configured for state-to-state behavior');
317
+ console.warn('[Behavior] State adapter not configured for state-to-state behavior');
430
318
  }
431
319
  }
432
320
  // LEGACY: Mesh-based pattern with conditions
433
321
  else if (conditions && out.child) {
434
322
  // Using legacy mesh-based pattern with conditions
435
323
  // Evaluate conditions
436
- var _iterator7 = _createForOfIteratorHelper(conditions),
437
- _step7;
324
+ var _iterator5 = _createForOfIteratorHelper(conditions),
325
+ _step5;
438
326
  try {
439
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
440
- var condition = _step7.value;
327
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
328
+ var condition = _step5.value;
441
329
  if (this._evaluateCondition(condition.when, value)) {
442
330
  // Apply actions to the target output component/attachment/child mesh
443
331
  this._applyCrossComponentActions(out, condition.actions);
444
332
  }
445
333
  }
446
334
  } catch (err) {
447
- _iterator7.e(err);
335
+ _iterator5.e(err);
448
336
  } finally {
449
- _iterator7.f();
337
+ _iterator5.f();
450
338
  }
451
339
  } else {
452
340
  console.warn('[Behavior] Output has neither state nor (child + conditions):', out);
453
341
  }
454
342
  } // end outputs loop
455
343
  } catch (err) {
456
- _iterator6.e(err);
344
+ _iterator4.e(err);
457
345
  } finally {
458
- _iterator6.f();
346
+ _iterator4.f();
459
347
  }
460
348
  }
461
349
  }
462
350
  } catch (err) {
463
- _iterator5.e(err);
351
+ _iterator3.e(err);
464
352
  } finally {
465
- _iterator5.f();
353
+ _iterator3.f();
466
354
  }
467
355
  }
468
356
 
@@ -532,17 +420,17 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
532
420
  }
533
421
 
534
422
  // 4. Apply actions to targetObj
535
- var _iterator8 = _createForOfIteratorHelper(actions),
536
- _step8;
423
+ var _iterator6 = _createForOfIteratorHelper(actions),
424
+ _step6;
537
425
  try {
538
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
539
- var action = _step8.value;
426
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
427
+ var action = _step6.value;
540
428
  this._applyCrossComponentAction(targetObj, action);
541
429
  }
542
430
  } catch (err) {
543
- _iterator8.e(err);
431
+ _iterator6.e(err);
544
432
  } finally {
545
- _iterator8.f();
433
+ _iterator6.f();
546
434
  }
547
435
  }
548
436
 
@@ -634,8 +522,12 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
634
522
  }, {
635
523
  key: "getAnimationDataPoints",
636
524
  value: function getAnimationDataPoints(parentUuid, attachmentId) {
637
- var _this4 = this;
525
+ var _this2 = this;
638
526
  var hitMesh = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
527
+ var cacheKey = "".concat(this._key(parentUuid, attachmentId), "::").concat((hitMesh === null || hitMesh === void 0 ? void 0 : hitMesh.uuid) || '');
528
+ if (this._dataPointsCache.has(cacheKey)) {
529
+ return this._dataPointsCache.get(cacheKey);
530
+ }
639
531
  var key = this._key(parentUuid, attachmentId);
640
532
  var entries = this._entries.get(key);
641
533
  if (!(entries !== null && entries !== void 0 && entries.length)) return [];
@@ -646,35 +538,35 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
646
538
  var filtered = entries;
647
539
  if (hitMesh) {
648
540
  var matching = entries.filter(function (e) {
649
- return _this4._isMeshOrDescendant(hitMesh, e.mesh);
541
+ return _this2._isMeshOrDescendant(hitMesh, e.mesh);
650
542
  });
651
543
  if (matching.length > 0) filtered = matching;
652
544
  }
653
545
 
654
546
  // Collapse multiple mesh entries that share the same stateVariable
655
547
  var seen = new Map(); // stateVariable → anim
656
- var _iterator9 = _createForOfIteratorHelper(filtered),
657
- _step9;
548
+ var _iterator7 = _createForOfIteratorHelper(filtered),
549
+ _step7;
658
550
  try {
659
- for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
660
- var anim = _step9.value.anim;
551
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
552
+ var anim = _step7.value.anim;
661
553
  if (!seen.has(anim.stateVariable)) {
662
554
  seen.set(anim.stateVariable, anim);
663
555
  }
664
556
  }
665
557
  } catch (err) {
666
- _iterator9.e(err);
558
+ _iterator7.e(err);
667
559
  } finally {
668
- _iterator9.f();
560
+ _iterator7.f();
669
561
  }
670
562
  var dps = [];
671
- var _iterator0 = _createForOfIteratorHelper(seen),
672
- _step0;
563
+ var _iterator8 = _createForOfIteratorHelper(seen),
564
+ _step8;
673
565
  try {
674
- for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
675
- var _step0$value = _slicedToArray(_step0.value, 2),
676
- stateVar = _step0$value[0],
677
- _anim = _step0$value[1];
566
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
567
+ var _step8$value = _slicedToArray(_step8.value, 2),
568
+ stateVar = _step8$value[0],
569
+ _anim = _step8$value[1];
678
570
  // Normalise stateType from AnimateDevicesDialog variants
679
571
  var stateType = void 0;
680
572
  var raw = (_anim.stateType || '').toLowerCase();
@@ -725,10 +617,11 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
725
617
  });
726
618
  }
727
619
  } catch (err) {
728
- _iterator0.e(err);
620
+ _iterator8.e(err);
729
621
  } finally {
730
- _iterator0.f();
622
+ _iterator8.f();
731
623
  }
624
+ this._dataPointsCache.set(cacheKey, dps);
732
625
  return dps;
733
626
  }
734
627
 
@@ -762,25 +655,46 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
762
655
  key: "unloadForComponent",
763
656
  value: function unloadForComponent(parentUuid) {
764
657
  var prefix = "".concat(parentUuid, "::");
765
- var _iterator1 = _createForOfIteratorHelper(this._entries.keys()),
766
- _step1;
767
- try {
768
- for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
769
- var key = _step1.value;
770
- if (key.startsWith(prefix)) {
771
- this._entries.delete(key);
772
- }
658
+ for (var _i = 0, _arr = _toConsumableArray(this._entries.keys()); _i < _arr.length; _i++) {
659
+ var key = _arr[_i];
660
+ if (key.startsWith(prefix)) {
661
+ this._attachmentParentMap.delete(key);
662
+ this._entries.delete(key);
773
663
  }
774
- } catch (err) {
775
- _iterator1.e(err);
776
- } finally {
777
- _iterator1.f();
778
664
  }
665
+ this._componentBehaviors.delete(parentUuid);
666
+ this._invalidateDataPointsCacheForParent(parentUuid);
667
+ }
668
+
669
+ /**
670
+ * Remove animation entries for a single attachment on a component instance.
671
+ */
672
+ }, {
673
+ key: "unloadForAttachment",
674
+ value: function unloadForAttachment(parentUuid, attachmentId) {
675
+ var key = this._key(parentUuid, attachmentId);
676
+ this._entries.delete(key);
677
+ this._attachmentParentMap.delete(key);
678
+ this._invalidateDataPointsCache(parentUuid, attachmentId);
679
+ }
680
+
681
+ /**
682
+ * Clear all runtime behavior state when the scene is cleared or replaced.
683
+ */
684
+ }, {
685
+ key: "resetForScene",
686
+ value: function resetForScene() {
687
+ this._entries.clear();
688
+ this._componentBehaviors.clear();
689
+ this._crossComponentBehaviors = [];
690
+ this._attachmentParentMap.clear();
691
+ this._dataPointsCache.clear();
779
692
  }
780
693
  }, {
781
694
  key: "dispose",
782
695
  value: function dispose() {
783
- this._entries.clear();
696
+ this.resetForScene();
697
+ this._stateAdapter = null;
784
698
  _superPropGet(IoBehaviorManager, "dispose", this, 3)([]);
785
699
  }
786
700
 
@@ -847,20 +761,20 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
847
761
  key: "_applyAnimation",
848
762
  value: function _applyAnimation(entry, value) {
849
763
  var anim = entry.anim,
850
- mesh = entry.mesh,
851
- origPos = entry.origPos;
764
+ mesh = entry.mesh;
765
+ entry.origPos;
852
766
  entry.origRot;
853
767
  var viewerMaxDim = entry.viewerMaxDim;
854
768
  var mapping = this._resolveMapping(anim, value);
855
769
  if (!mapping) return;
856
770
  var types = anim.transformTypes || [];
857
- var _iterator10 = _createForOfIteratorHelper(types),
858
- _step10;
771
+ var _iterator9 = _createForOfIteratorHelper(types),
772
+ _step9;
859
773
  try {
860
- for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
861
- var type = _step10.value;
774
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
775
+ var type = _step9.value;
862
776
  if (type === 'translation') {
863
- this._applyTranslation(mesh, origPos, mapping.transform);
777
+ this._applyTranslation(entry, mapping.transform);
864
778
  } else if (type === 'rotation') {
865
779
  this._applyRotation(entry, anim, mapping.rotationTransform, viewerMaxDim);
866
780
  } else if (type === 'color') {
@@ -868,9 +782,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
868
782
  }
869
783
  }
870
784
  } catch (err) {
871
- _iterator10.e(err);
785
+ _iterator9.e(err);
872
786
  } finally {
873
- _iterator10.f();
787
+ _iterator9.f();
874
788
  }
875
789
  }
876
790
 
@@ -990,20 +904,21 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
990
904
  // ─────────────────────────────────────────────────────────────────────────
991
905
 
992
906
  /**
993
- * Apply a position delta relative to the mesh's original position.
994
- * @param {THREE.Object3D} mesh
995
- * @param {THREE.Vector3} origPos
996
- * @param {{ x, y, z }} transform - Deltas
907
+ * Apply a translation offset in device-model-root space so every animated mesh
908
+ * shares the same axis directions regardless of intermediate parent transforms.
909
+ *
910
+ * @param {{ mesh, origPos, deviceModelRoot }} entry
911
+ * @param {{ x, y, z }} transform - Offset in model-root local space
997
912
  */
998
913
  }, {
999
914
  key: "_applyTranslation",
1000
- value: function _applyTranslation(mesh, origPos, transform) {
1001
- var _transform$x, _transform$y, _transform$z;
915
+ value: function _applyTranslation(entry, transform) {
1002
916
  if (!transform) return;
1003
- // X and Y are negated to match the sign convention used in the AnimateDevicesDialog
1004
- // preview (_syncViewerTransform negates x and y before calling setMeshPreviewOffset).
1005
- // Z is added directly (no negation) — matching the dialog's z handling.
1006
- mesh.position.set(origPos.x - ((_transform$x = transform.x) !== null && _transform$x !== void 0 ? _transform$x : 0), origPos.y - ((_transform$y = transform.y) !== null && _transform$y !== void 0 ? _transform$y : 0), origPos.z + ((_transform$z = transform.z) !== null && _transform$z !== void 0 ? _transform$z : 0));
917
+ var mesh = entry.mesh,
918
+ origPos = entry.origPos,
919
+ deviceModelRoot = entry.deviceModelRoot;
920
+ if (!mesh || !origPos || !deviceModelRoot) return;
921
+ applyModelRootTranslation(mesh, deviceModelRoot, origPos, transform);
1007
922
  }
1008
923
 
1009
924
  /**
@@ -1022,6 +937,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
1022
937
  origRot = entry.origRot,
1023
938
  deviceModelRoot = entry.deviceModelRoot;
1024
939
  if (!mesh || !origPos || !origRot) return null;
940
+ if (entry._restWorldCache) {
941
+ return entry._restWorldCache;
942
+ }
1025
943
  var savedPos = mesh.position.clone();
1026
944
  var savedQuat = mesh.quaternion.clone();
1027
945
  mesh.position.copy(origPos);
@@ -1040,12 +958,35 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
1040
958
  }
1041
959
  mesh.position.copy(savedPos);
1042
960
  mesh.quaternion.copy(savedQuat);
1043
- return {
961
+ entry._restWorldCache = {
1044
962
  origWorldPos: origWorldPos,
1045
963
  origWorldQuat: origWorldQuat,
1046
964
  origWorldCenter: origWorldCenter,
1047
965
  deviceWorldQuat: deviceWorldQuat
1048
966
  };
967
+ return entry._restWorldCache;
968
+ }
969
+ }, {
970
+ key: "_invalidateDataPointsCache",
971
+ value: function _invalidateDataPointsCache(parentUuid, attachmentId) {
972
+ var prefix = "".concat(this._key(parentUuid, attachmentId), "::");
973
+ for (var _i2 = 0, _arr2 = _toConsumableArray(this._dataPointsCache.keys()); _i2 < _arr2.length; _i2++) {
974
+ var key = _arr2[_i2];
975
+ if (key.startsWith(prefix)) {
976
+ this._dataPointsCache.delete(key);
977
+ }
978
+ }
979
+ }
980
+ }, {
981
+ key: "_invalidateDataPointsCacheForParent",
982
+ value: function _invalidateDataPointsCacheForParent(parentUuid) {
983
+ var prefix = "".concat(parentUuid, "::");
984
+ for (var _i3 = 0, _arr3 = _toConsumableArray(this._dataPointsCache.keys()); _i3 < _arr3.length; _i3++) {
985
+ var key = _arr3[_i3];
986
+ if (key.startsWith(prefix)) {
987
+ this._dataPointsCache.delete(key);
988
+ }
989
+ }
1049
990
  }
1050
991
 
1051
992
  /**