@2112-lab/central-plant 0.3.12 → 0.3.13

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.
@@ -3497,7 +3497,12 @@ function computeFilteredBoundingBox(object) {
3497
3497
 
3498
3498
  // Build a Set for O(1) lookups
3499
3499
  var excludeSet = new Set(excludeTypes);
3500
- object.updateWorldMatrix(false, true);
3500
+
3501
+ // Force matrix updates to ensure world-space coordinates are accurate.
3502
+ // Using force=true ensures matrices are updated even if matrixWorldNeedsUpdate is false,
3503
+ // which can happen after positioning a model before the render loop runs.
3504
+ object.updateMatrix();
3505
+ object.updateMatrixWorld(true);
3501
3506
  object.traverse(function (child) {
3502
3507
  // Only process nodes with geometry (Mesh, SkinnedMesh, etc.)
3503
3508
  if (!child.geometry) return;
@@ -29818,6 +29823,15 @@ var ModelManager = /*#__PURE__*/function () {
29818
29823
  var jsonData = _ref2.jsonData,
29819
29824
  glbModel = _ref2.glbModel;
29820
29825
  if (!glbModel) return;
29826
+
29827
+ // CRITICAL: Force matrix updates before computing bbox.
29828
+ // After loadLibraryModel positions the model, the world matrices may not be
29829
+ // invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
29830
+ // which only updates if matrixWorldNeedsUpdate is true. Force the update here
29831
+ // to ensure the bbox is computed with correct world-space coordinates.
29832
+ glbModel.updateMatrix();
29833
+ glbModel.updateMatrixWorld(true);
29834
+
29821
29835
  // Use filtered bbox (excludes connectors + io-devices) so it matches
29822
29836
  // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
29823
29837
  var filteredBox = computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
@@ -35440,6 +35454,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35440
35454
  // stacking up independent renderComponents() runs
35441
35455
  _this2._refreshPending = false;
35442
35456
 
35457
+ // Set of viewport keys pending refresh (collects keys across multiple refresh() calls in same frame)
35458
+ _this2._pendingRefreshKeys = new Set();
35459
+
35443
35460
  // Event listener reference for cleanup
35444
35461
  _this2._objectTransformedListener = null;
35445
35462
  console.log('🔲 Viewport2DManager initialized (multi-instance support)');
@@ -35529,8 +35546,10 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35529
35546
  viewport._instanceKey = key;
35530
35547
  this.viewports.set(key, viewport);
35531
35548
 
35532
- // Initialize the stage for this viewport
35533
- this.initializeStage(viewport);
35549
+ // Initialize the stage for this viewport (waits for DOM layout to settle)
35550
+ _context.n = 4;
35551
+ return this.initializeStage(viewport);
35552
+ case 4:
35534
35553
  return _context.a(2, viewport.isReady);
35535
35554
  }
35536
35555
  }, _callee, this);
@@ -35624,50 +35643,67 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35624
35643
  /**
35625
35644
  * Initialize Konva stage for a specific viewport instance
35626
35645
  * @param {Viewport2DInstance} viewport - The viewport instance to initialize
35646
+ * @returns {Promise<void>} Resolves when viewport is fully ready
35627
35647
  */
35628
35648
  )
35629
35649
  }, {
35630
35650
  key: "initializeStage",
35631
35651
  value: function initializeStage(viewport) {
35632
- if (!this.Konva || !viewport.container) {
35633
- console.error('❌ Cannot initialize stage: Konva or container missing');
35634
- return;
35635
- }
35636
-
35637
- // Get container dimensions
35638
- var rect = viewport.container.getBoundingClientRect();
35639
- var width = rect.width || 800;
35640
- var height = rect.height || 600;
35641
- console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
35642
-
35643
- // Create Konva stage for this viewport
35644
- viewport.stage = new this.Konva.Stage({
35645
- container: viewport.container,
35646
- width: width,
35647
- height: height
35648
- });
35649
-
35650
- // Create separate layers for grid and components
35651
- viewport.gridLayer = new this.Konva.Layer();
35652
- viewport.componentLayer = new this.Konva.Layer();
35653
-
35654
- // Add layers to stage in order (grid first, then components)
35655
- viewport.stage.add(viewport.gridLayer);
35656
- viewport.stage.add(viewport.componentLayer);
35657
-
35658
- // Setup resize handling
35659
- this.setupResizeListener(viewport);
35652
+ var _this4 = this;
35653
+ return new Promise(function (resolve) {
35654
+ if (!_this4.Konva || !viewport.container) {
35655
+ console.error('❌ Cannot initialize stage: Konva or container missing');
35656
+ resolve();
35657
+ return;
35658
+ }
35660
35659
 
35661
- // Setup zoom and pan handlers
35662
- this.setupZoomAndPanHandlers(viewport);
35660
+ // Get container dimensions
35661
+ var rect = viewport.container.getBoundingClientRect();
35662
+ var width = rect.width || 800;
35663
+ var height = rect.height || 600;
35664
+ console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
35663
35665
 
35664
- // Draw initial content
35665
- this.drawGrid(viewport);
35666
- this.renderComponents(viewport);
35666
+ // Create Konva stage for this viewport
35667
+ viewport.stage = new _this4.Konva.Stage({
35668
+ container: viewport.container,
35669
+ width: width,
35670
+ height: height
35671
+ });
35667
35672
 
35668
- // Mark as ready
35669
- viewport.isReady = true;
35670
- console.log("\u2705 Viewport2DManager stage initialized (".concat(viewport.viewType, ")"));
35673
+ // Create separate layers for grid and components
35674
+ viewport.gridLayer = new _this4.Konva.Layer();
35675
+ viewport.componentLayer = new _this4.Konva.Layer();
35676
+
35677
+ // Add layers to stage in order (grid first, then components)
35678
+ viewport.stage.add(viewport.gridLayer);
35679
+ viewport.stage.add(viewport.componentLayer);
35680
+
35681
+ // Setup resize handling
35682
+ _this4.setupResizeListener(viewport);
35683
+
35684
+ // Setup zoom and pan handlers
35685
+ _this4.setupZoomAndPanHandlers(viewport);
35686
+
35687
+ // Draw initial grid (lightweight, safe to do immediately)
35688
+ _this4.drawGrid(viewport);
35689
+
35690
+ // Defer component rendering to next frame to ensure DOM layout is finalized.
35691
+ // Without this, getBoundingClientRect() may return stale/zero dimensions
35692
+ // if the container hasn't been fully laid out by CSS yet.
35693
+ requestAnimationFrame(function () {
35694
+ // Re-check container dimensions after layout settles
35695
+ var finalRect = viewport.container.getBoundingClientRect();
35696
+ if (finalRect.width > 0 && finalRect.height > 0) {
35697
+ viewport.stage.width(finalRect.width);
35698
+ viewport.stage.height(finalRect.height);
35699
+ _this4.drawGrid(viewport);
35700
+ }
35701
+ _this4.renderComponents(viewport);
35702
+ viewport.isReady = true;
35703
+ console.log("\u2705 Viewport2DManager stage initialized (".concat(viewport.viewType, ")"));
35704
+ resolve();
35705
+ });
35706
+ });
35671
35707
  }
35672
35708
 
35673
35709
  /**
@@ -35677,12 +35713,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35677
35713
  }, {
35678
35714
  key: "setupResizeListener",
35679
35715
  value: function setupResizeListener(viewport) {
35680
- var _this4 = this;
35716
+ var _this5 = this;
35681
35717
  if (typeof window === 'undefined' || !window.ResizeObserver || !viewport.container) {
35682
35718
  return;
35683
35719
  }
35684
35720
  viewport._resizeObserver = new ResizeObserver(function () {
35685
- _this4.resizeStage(viewport);
35721
+ _this5.resizeStage(viewport);
35686
35722
  });
35687
35723
  viewport._resizeObserver.observe(viewport.container);
35688
35724
  }
@@ -35717,7 +35753,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35717
35753
  }, {
35718
35754
  key: "setupZoomAndPanHandlers",
35719
35755
  value: function setupZoomAndPanHandlers(viewport) {
35720
- var _this5 = this;
35756
+ var _this6 = this;
35721
35757
  if (!viewport.stage) return;
35722
35758
 
35723
35759
  // Mouse wheel zoom
@@ -35743,7 +35779,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35743
35779
  y: pointer.y - mousePointTo.y * clampedScale
35744
35780
  };
35745
35781
  viewport.stage.position(newPos);
35746
- _this5.drawGrid(viewport);
35782
+ _this6.drawGrid(viewport);
35747
35783
  viewport.stage.batchDraw();
35748
35784
  });
35749
35785
 
@@ -35763,7 +35799,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35763
35799
  viewport.stage.x(viewport.stage.x() + dx);
35764
35800
  viewport.stage.y(viewport.stage.y() + dy);
35765
35801
  viewport.lastPanPoint = pos;
35766
- _this5.drawGrid(viewport);
35802
+ _this6.drawGrid(viewport);
35767
35803
  viewport.stage.batchDraw();
35768
35804
  });
35769
35805
  viewport.stage.on('mouseup', function () {
@@ -35855,7 +35891,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35855
35891
  }, {
35856
35892
  key: "renderComponents",
35857
35893
  value: function renderComponents(viewport) {
35858
- var _this6 = this;
35894
+ var _this7 = this;
35859
35895
  if (!viewport.componentLayer || !viewport.stage || !this.sceneViewer) return;
35860
35896
  viewport.componentLayer.destroyChildren();
35861
35897
 
@@ -35866,19 +35902,18 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35866
35902
  return;
35867
35903
  }
35868
35904
 
35869
- // Only log on significant changes
35870
- if (components.length !== viewport._lastComponentCount) {
35871
- console.log("\uD83D\uDCE6 Rendering ".concat(components.length, " components in 2D view (").concat(viewport.viewType, ")"));
35872
- viewport._lastComponentCount = components.length;
35873
- }
35905
+ // Track render count for debugging
35906
+ if (viewport._renderCount === undefined) viewport._renderCount = 0;
35907
+ viewport._renderCount++;
35874
35908
  var width = viewport.stage.width();
35875
35909
  var height = viewport.stage.height();
35876
35910
  var centerX = width / 2;
35877
35911
  var centerY = height / 2;
35878
35912
  var scale = viewport.PIXELS_PER_UNIT;
35913
+ console.log("\uD83C\uDFA8 RENDER #".concat(viewport._renderCount, " (").concat(viewport.viewType, "): ").concat(components.length, " components, stage=").concat(width, "x").concat(height, ", center=(").concat(centerX, ", ").concat(centerY, ")"));
35879
35914
  components.forEach(function (component) {
35880
35915
  try {
35881
- _this6.renderComponent(viewport, component, centerX, centerY, scale);
35916
+ _this7.renderComponent(viewport, component, centerX, centerY, scale);
35882
35917
  } catch (err) {
35883
35918
  console.warn('⚠️ Error rendering component in 2D:', component.name, err);
35884
35919
  }
@@ -35909,6 +35944,11 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35909
35944
  var screenX = centerX + posX * scale;
35910
35945
  var screenY = centerY - posY * scale; // Flip Y for screen coords
35911
35946
 
35947
+ // Debug: Log ALL component positions on first render only
35948
+ if (viewport._renderCount === 1) {
35949
+ console.log("\uD83D\uDD0D [".concat(viewport.viewType, "] ").concat(component.name, ": bbox.z=").concat(bboxCenter.z.toFixed(2), ", posY=").concat(posY.toFixed(2), ", screenY=").concat(screenY.toFixed(0), ", centerY=").concat(centerY.toFixed(0)));
35950
+ }
35951
+
35912
35952
  // Generate unique color for this component
35913
35953
  var colors = this.generateComponentColor(component.id);
35914
35954
 
@@ -35987,7 +36027,14 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35987
36027
  if (this._bboxCache.has(object.uuid)) {
35988
36028
  return this._bboxCache.get(object.uuid);
35989
36029
  }
35990
- var box = computeFilteredBoundingBox(object, []);
36030
+
36031
+ // Force matrix updates before computing bbox to ensure world-space accuracy
36032
+ object.updateMatrix();
36033
+ object.updateMatrixWorld(true);
36034
+
36035
+ // Exclude io-devices and connectors to match the stored worldBoundingBox
36036
+ // computed in modelManager.replaceWithGLBModels()
36037
+ var box = computeFilteredBoundingBox(object, ['io-device', 'connector']);
35991
36038
  var result;
35992
36039
  if (box.isEmpty()) {
35993
36040
  // Object has no geometry; fall back to a point at world position
@@ -36118,7 +36165,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36118
36165
  }, {
36119
36166
  key: "addComponentInteractions",
36120
36167
  value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter, label) {
36121
- var _this7 = this;
36168
+ var _this8 = this;
36122
36169
  if (!this.Konva) return;
36123
36170
  var colors = this.generateComponentColor(component.id);
36124
36171
 
@@ -36147,12 +36194,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36147
36194
  // CLICK EVENT
36148
36195
  rect.on('click', function () {
36149
36196
  if (!viewport.isDragging) {
36150
- var _this7$sceneViewer;
36197
+ var _this8$sceneViewer;
36151
36198
  console.log("\uD83C\uDFAF Component clicked: ".concat(component.name));
36152
36199
 
36153
36200
  // Use centralPlant API to select component
36154
- if ((_this7$sceneViewer = _this7.sceneViewer) !== null && _this7$sceneViewer !== void 0 && _this7$sceneViewer.centralPlant && component.uuid) {
36155
- _this7.sceneViewer.centralPlant.selectComponent(component.uuid);
36201
+ if ((_this8$sceneViewer = _this8.sceneViewer) !== null && _this8$sceneViewer !== void 0 && _this8$sceneViewer.centralPlant && component.uuid) {
36202
+ _this8.sceneViewer.centralPlant.selectComponent(component.uuid);
36156
36203
  }
36157
36204
  }
36158
36205
  });
@@ -36182,7 +36229,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36182
36229
  var worldOriginY = stageHeight / 2;
36183
36230
 
36184
36231
  // Snap to grid
36185
- var snappedPos = _this7.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
36232
+ var snappedPos = _this8.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
36186
36233
  componentGroup.position(snappedPos);
36187
36234
  viewport.componentLayer.batchDraw();
36188
36235
  });
@@ -36190,7 +36237,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36190
36237
  // DRAG END
36191
36238
  componentGroup.on('dragend', function () {
36192
36239
  setTimeout(function () {
36193
- var _this7$sceneViewer2;
36240
+ var _this8$sceneViewer2;
36194
36241
  viewport.isDragging = false;
36195
36242
  var finalPos = componentGroup.position();
36196
36243
  var stageWidth = viewport.stage.width();
@@ -36200,17 +36247,17 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36200
36247
  var worldOriginY = stageHeight / 2;
36201
36248
 
36202
36249
  // Convert screen to world coordinates
36203
- var worldCoords = _this7.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
36250
+ var worldCoords = _this8.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
36204
36251
 
36205
36252
  // Calculate new position: delta from old bbox center to new bbox center
36206
36253
  var currentPos = component.position;
36207
- var newPosition = _this7.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
36254
+ var newPosition = _this8.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
36208
36255
 
36209
36256
  // Apply translation via centralPlant API
36210
36257
  var deltaX = newPosition.x - currentPos.x;
36211
36258
  var deltaY = newPosition.y - currentPos.y;
36212
36259
  var deltaZ = newPosition.z - currentPos.z;
36213
- if ((_this7$sceneViewer2 = _this7.sceneViewer) !== null && _this7$sceneViewer2 !== void 0 && _this7$sceneViewer2.centralPlant && component.uuid) {
36260
+ if ((_this8$sceneViewer2 = _this8.sceneViewer) !== null && _this8$sceneViewer2 !== void 0 && _this8$sceneViewer2.centralPlant && component.uuid) {
36214
36261
  var success = true;
36215
36262
 
36216
36263
  // Suppress per-axis path updates so the pathfinder only runs once,
@@ -36218,32 +36265,32 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36218
36265
  // Running updatePaths() after each individual axis (the default
36219
36266
  // translateComponent behaviour) produces intermediate wrong results on
36220
36267
  // the first drag because no cached fingerprint exists yet to skip them.
36221
- var wasAutoUpdate = _this7.sceneViewer.shouldUpdatePaths;
36222
- _this7.sceneViewer.shouldUpdatePaths = false;
36268
+ var wasAutoUpdate = _this8.sceneViewer.shouldUpdatePaths;
36269
+ _this8.sceneViewer.shouldUpdatePaths = false;
36223
36270
  try {
36224
36271
  if (Math.abs(deltaX) > 0.01) {
36225
- success = success && _this7.sceneViewer.centralPlant.translate(component.uuid, 'x', deltaX);
36272
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'x', deltaX);
36226
36273
  }
36227
36274
  if (Math.abs(deltaY) > 0.01) {
36228
- success = success && _this7.sceneViewer.centralPlant.translate(component.uuid, 'y', deltaY);
36275
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'y', deltaY);
36229
36276
  }
36230
36277
  if (Math.abs(deltaZ) > 0.01) {
36231
- success = success && _this7.sceneViewer.centralPlant.translate(component.uuid, 'z', deltaZ);
36278
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'z', deltaZ);
36232
36279
  }
36233
36280
  } finally {
36234
- _this7.sceneViewer.shouldUpdatePaths = wasAutoUpdate;
36281
+ _this8.sceneViewer.shouldUpdatePaths = wasAutoUpdate;
36235
36282
  }
36236
- if (!success && _this7.dragStartPosition) {
36283
+ if (!success && _this8.dragStartPosition) {
36237
36284
  console.warn('⚠️ Failed to translate component, reverting position');
36238
- componentGroup.position(_this7.dragStartPosition);
36285
+ componentGroup.position(_this8.dragStartPosition);
36239
36286
  } else {
36240
36287
  console.log("\u2705 Component ".concat(component.name, " translated successfully in 2D viewport"));
36241
36288
 
36242
36289
  // Single path update with the final combined position
36243
- if (wasAutoUpdate && _this7.sceneViewer) {
36290
+ if (wasAutoUpdate && _this8.sceneViewer) {
36244
36291
  console.log('🔄 Auto-updating paths after 2D viewport translation...');
36245
36292
  try {
36246
- _this7.sceneViewer.updatePaths();
36293
+ _this8.sceneViewer.updatePaths();
36247
36294
  console.log('✅ Paths auto-updated successfully from 2D viewport');
36248
36295
  } catch (error) {
36249
36296
  console.error('❌ Error auto-updating paths from 2D viewport:', error);
@@ -36496,28 +36543,39 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36496
36543
  }, {
36497
36544
  key: "refresh",
36498
36545
  value: function refresh() {
36499
- var _this8 = this;
36546
+ var _this9 = this;
36500
36547
  var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
36548
+ // Collect viewport keys to refresh; null means "all viewports"
36549
+ if (key) {
36550
+ this._pendingRefreshKeys.add(key);
36551
+ } else {
36552
+ // null key means refresh all — mark with special sentinel
36553
+ this._pendingRefreshKeys.add('__ALL__');
36554
+ }
36555
+
36556
+ // If already scheduled, the rAF callback will process our newly-added key
36501
36557
  if (this._refreshPending) return;
36502
36558
  this._refreshPending = true;
36503
36559
  requestAnimationFrame(function () {
36504
- _this8._refreshPending = false;
36560
+ _this9._refreshPending = false;
36561
+
36505
36562
  // Clear per-cycle caches so each component is measured/traversed once per paint
36506
- _this8._bboxCache.clear();
36507
- _this8._componentListCache = null;
36508
- if (key) {
36509
- var viewport = _this8.viewports.get(key);
36510
- if (viewport && viewport.isReady) {
36511
- _this8.renderComponents(viewport);
36512
- }
36513
- } else {
36514
- var _iterator = _createForOfIteratorHelper(_this8.viewports.values()),
36563
+ _this9._bboxCache.clear();
36564
+ _this9._componentListCache = null;
36565
+
36566
+ // Check if we need to refresh all viewports
36567
+ var refreshAll = _this9._pendingRefreshKeys.has('__ALL__');
36568
+ var keysToRefresh = new Set(_this9._pendingRefreshKeys);
36569
+ _this9._pendingRefreshKeys.clear();
36570
+ if (refreshAll) {
36571
+ // Refresh all viewports
36572
+ var _iterator = _createForOfIteratorHelper(_this9.viewports.values()),
36515
36573
  _step;
36516
36574
  try {
36517
36575
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
36518
- var _viewport = _step.value;
36519
- if (_viewport.isReady) {
36520
- _this8.renderComponents(_viewport);
36576
+ var viewport = _step.value;
36577
+ if (viewport.isReady) {
36578
+ _this9.renderComponents(viewport);
36521
36579
  }
36522
36580
  }
36523
36581
  } catch (err) {
@@ -36525,6 +36583,23 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36525
36583
  } finally {
36526
36584
  _iterator.f();
36527
36585
  }
36586
+ } else {
36587
+ // Refresh only the specific viewports that were requested
36588
+ var _iterator2 = _createForOfIteratorHelper(keysToRefresh),
36589
+ _step2;
36590
+ try {
36591
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
36592
+ var viewportKey = _step2.value;
36593
+ var _viewport = _this9.viewports.get(viewportKey);
36594
+ if (_viewport && _viewport.isReady) {
36595
+ _this9.renderComponents(_viewport);
36596
+ }
36597
+ }
36598
+ } catch (err) {
36599
+ _iterator2.e(err);
36600
+ } finally {
36601
+ _iterator2.f();
36602
+ }
36528
36603
  }
36529
36604
  });
36530
36605
  }
@@ -36545,20 +36620,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36545
36620
  }
36546
36621
 
36547
36622
  // Dispose all viewport instances
36548
- var _iterator2 = _createForOfIteratorHelper(this.viewports.entries()),
36549
- _step2;
36623
+ var _iterator3 = _createForOfIteratorHelper(this.viewports.entries()),
36624
+ _step3;
36550
36625
  try {
36551
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
36552
- var _step2$value = _slicedToArray(_step2.value, 2),
36553
- key = _step2$value[0],
36554
- viewport = _step2$value[1];
36626
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
36627
+ var _step3$value = _slicedToArray(_step3.value, 2),
36628
+ key = _step3$value[0],
36629
+ viewport = _step3$value[1];
36555
36630
  console.log("\uD83D\uDDD1\uFE0F Disposing viewport: ".concat(key));
36556
36631
  viewport.dispose();
36557
36632
  }
36558
36633
  } catch (err) {
36559
- _iterator2.e(err);
36634
+ _iterator3.e(err);
36560
36635
  } finally {
36561
- _iterator2.f();
36636
+ _iterator3.f();
36562
36637
  }
36563
36638
  this.viewports.clear();
36564
36639
  this.Konva = null;
@@ -37959,7 +38034,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
37959
38034
  * Initialize the CentralPlant manager
37960
38035
  *
37961
38036
  * @constructor
37962
- * @version 0.3.12
38037
+ * @version 0.3.13
37963
38038
  * @updated 2025-10-22
37964
38039
  *
37965
38040
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
35
35
  * Initialize the CentralPlant manager
36
36
  *
37
37
  * @constructor
38
- * @version 0.3.12
38
+ * @version 0.3.13
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -585,6 +585,15 @@ var ModelManager = /*#__PURE__*/function () {
585
585
  var jsonData = _ref2.jsonData,
586
586
  glbModel = _ref2.glbModel;
587
587
  if (!glbModel) return;
588
+
589
+ // CRITICAL: Force matrix updates before computing bbox.
590
+ // After loadLibraryModel positions the model, the world matrices may not be
591
+ // invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
592
+ // which only updates if matrixWorldNeedsUpdate is true. Force the update here
593
+ // to ensure the bbox is computed with correct world-space coordinates.
594
+ glbModel.updateMatrix();
595
+ glbModel.updateMatrixWorld(true);
596
+
588
597
  // Use filtered bbox (excludes connectors + io-devices) so it matches
589
598
  // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
590
599
  var filteredBox = boundingBoxUtils.computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);