@2112-lab/central-plant 0.3.12 → 0.3.14
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 +2736 -2643
- package/dist/cjs/src/core/centralPlant.js +1 -1
- package/dist/cjs/src/managers/scene/modelManager.js +12 -6
- package/dist/cjs/src/managers/scene/viewport2DManager.js +186 -102
- package/dist/cjs/src/utils/boundingBoxUtils.js +6 -1
- package/dist/esm/src/core/centralPlant.js +1 -1
- package/dist/esm/src/managers/scene/modelManager.js +12 -6
- package/dist/esm/src/managers/scene/viewport2DManager.js +186 -103
- package/dist/esm/src/utils/boundingBoxUtils.js +6 -1
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
35
35
|
* Initialize the CentralPlant manager
|
|
36
36
|
*
|
|
37
37
|
* @constructor
|
|
38
|
-
* @version 0.3.
|
|
38
|
+
* @version 0.3.14
|
|
39
39
|
* @updated 2025-10-22
|
|
40
40
|
*
|
|
41
41
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -7,6 +7,7 @@ var THREE = require('three');
|
|
|
7
7
|
var ioDeviceUtils = require('../../utils/ioDeviceUtils.js');
|
|
8
8
|
var modelPreloader = require('../../rendering/modelPreloader.js');
|
|
9
9
|
var boundingBoxUtils = require('../../utils/boundingBoxUtils.js');
|
|
10
|
+
var viewport2DManager = require('./viewport2DManager.js');
|
|
10
11
|
|
|
11
12
|
function _interopNamespace(e) {
|
|
12
13
|
if (e && e.__esModule) return e;
|
|
@@ -585,6 +586,15 @@ var ModelManager = /*#__PURE__*/function () {
|
|
|
585
586
|
var jsonData = _ref2.jsonData,
|
|
586
587
|
glbModel = _ref2.glbModel;
|
|
587
588
|
if (!glbModel) return;
|
|
589
|
+
|
|
590
|
+
// CRITICAL: Force matrix updates before computing bbox.
|
|
591
|
+
// After loadLibraryModel positions the model, the world matrices may not be
|
|
592
|
+
// invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
|
|
593
|
+
// which only updates if matrixWorldNeedsUpdate is true. Force the update here
|
|
594
|
+
// to ensure the bbox is computed with correct world-space coordinates.
|
|
595
|
+
glbModel.updateMatrix();
|
|
596
|
+
glbModel.updateMatrixWorld(true);
|
|
597
|
+
|
|
588
598
|
// Use filtered bbox (excludes connectors + io-devices) so it matches
|
|
589
599
|
// what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
|
|
590
600
|
var filteredBox = boundingBoxUtils.computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
|
|
@@ -595,13 +605,9 @@ var ModelManager = /*#__PURE__*/function () {
|
|
|
595
605
|
// Update both the JSON data object AND the live scene object
|
|
596
606
|
jsonData.userData.worldBoundingBox = worldBoundingBox;
|
|
597
607
|
glbModel.userData.worldBoundingBox = worldBoundingBox;
|
|
598
|
-
//
|
|
608
|
+
// Cache the object's position so viewport2DManager can compute
|
|
599
609
|
// world-bbox updates via a fast O(1) position delta instead of re-traversing geometry
|
|
600
|
-
glbModel
|
|
601
|
-
x: glbModel.position.x,
|
|
602
|
-
y: glbModel.position.y,
|
|
603
|
-
z: glbModel.position.z
|
|
604
|
-
};
|
|
610
|
+
viewport2DManager.cacheBasePosition(glbModel, worldBoundingBox);
|
|
605
611
|
});
|
|
606
612
|
|
|
607
613
|
// Dispatch completion event
|
|
@@ -27,6 +27,29 @@ function _interopNamespace(e) {
|
|
|
27
27
|
|
|
28
28
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* WeakMap cache for storing base position data per Object3D.
|
|
32
|
+
* Used by _getOrComputeWorldBoundingBox() for O(1) bbox offset calculations.
|
|
33
|
+
* WeakMap ensures automatic cleanup when objects are garbage collected.
|
|
34
|
+
* @type {WeakMap<THREE.Object3D, {x: number, y: number, z: number, worldBoundingBox: {min: number[], max: number[]}}>}
|
|
35
|
+
*/
|
|
36
|
+
var _basePositionCache = new WeakMap();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Store base position and worldBoundingBox for an Object3D.
|
|
40
|
+
* Called by modelManager after GLB loading to enable fast bbox updates.
|
|
41
|
+
* @param {THREE.Object3D} object - The loaded GLB model
|
|
42
|
+
* @param {{min: number[], max: number[]}} worldBoundingBox - Computed world bounding box
|
|
43
|
+
*/
|
|
44
|
+
function cacheBasePosition(object, worldBoundingBox) {
|
|
45
|
+
_basePositionCache.set(object, {
|
|
46
|
+
x: object.position.x,
|
|
47
|
+
y: object.position.y,
|
|
48
|
+
z: object.position.z,
|
|
49
|
+
worldBoundingBox: worldBoundingBox
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
/**
|
|
31
54
|
* Viewport2DInstance
|
|
32
55
|
* Represents a single 2D viewport with its own Konva stage and configuration
|
|
@@ -123,6 +146,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
123
146
|
// stacking up independent renderComponents() runs
|
|
124
147
|
_this2._refreshPending = false;
|
|
125
148
|
|
|
149
|
+
// Set of viewport keys pending refresh (collects keys across multiple refresh() calls in same frame)
|
|
150
|
+
_this2._pendingRefreshKeys = new Set();
|
|
151
|
+
|
|
126
152
|
// Event listener reference for cleanup
|
|
127
153
|
_this2._objectTransformedListener = null;
|
|
128
154
|
console.log('🔲 Viewport2DManager initialized (multi-instance support)');
|
|
@@ -212,8 +238,10 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
212
238
|
viewport._instanceKey = key;
|
|
213
239
|
this.viewports.set(key, viewport);
|
|
214
240
|
|
|
215
|
-
// Initialize the stage for this viewport
|
|
216
|
-
|
|
241
|
+
// Initialize the stage for this viewport (waits for DOM layout to settle)
|
|
242
|
+
_context.n = 4;
|
|
243
|
+
return this.initializeStage(viewport);
|
|
244
|
+
case 4:
|
|
217
245
|
return _context.a(2, viewport.isReady);
|
|
218
246
|
}
|
|
219
247
|
}, _callee, this);
|
|
@@ -307,50 +335,67 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
307
335
|
/**
|
|
308
336
|
* Initialize Konva stage for a specific viewport instance
|
|
309
337
|
* @param {Viewport2DInstance} viewport - The viewport instance to initialize
|
|
338
|
+
* @returns {Promise<void>} Resolves when viewport is fully ready
|
|
310
339
|
*/
|
|
311
340
|
)
|
|
312
341
|
}, {
|
|
313
342
|
key: "initializeStage",
|
|
314
343
|
value: function initializeStage(viewport) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
var width = rect.width || 800;
|
|
323
|
-
var height = rect.height || 600;
|
|
324
|
-
console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
|
|
325
|
-
|
|
326
|
-
// Create Konva stage for this viewport
|
|
327
|
-
viewport.stage = new this.Konva.Stage({
|
|
328
|
-
container: viewport.container,
|
|
329
|
-
width: width,
|
|
330
|
-
height: height
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// Create separate layers for grid and components
|
|
334
|
-
viewport.gridLayer = new this.Konva.Layer();
|
|
335
|
-
viewport.componentLayer = new this.Konva.Layer();
|
|
336
|
-
|
|
337
|
-
// Add layers to stage in order (grid first, then components)
|
|
338
|
-
viewport.stage.add(viewport.gridLayer);
|
|
339
|
-
viewport.stage.add(viewport.componentLayer);
|
|
340
|
-
|
|
341
|
-
// Setup resize handling
|
|
342
|
-
this.setupResizeListener(viewport);
|
|
343
|
-
|
|
344
|
-
// Setup zoom and pan handlers
|
|
345
|
-
this.setupZoomAndPanHandlers(viewport);
|
|
344
|
+
var _this4 = this;
|
|
345
|
+
return new Promise(function (resolve) {
|
|
346
|
+
if (!_this4.Konva || !viewport.container) {
|
|
347
|
+
console.error('❌ Cannot initialize stage: Konva or container missing');
|
|
348
|
+
resolve();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
346
351
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
352
|
+
// Get container dimensions
|
|
353
|
+
var rect = viewport.container.getBoundingClientRect();
|
|
354
|
+
var width = rect.width || 800;
|
|
355
|
+
var height = rect.height || 600;
|
|
356
|
+
console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
|
|
357
|
+
|
|
358
|
+
// Create Konva stage for this viewport
|
|
359
|
+
viewport.stage = new _this4.Konva.Stage({
|
|
360
|
+
container: viewport.container,
|
|
361
|
+
width: width,
|
|
362
|
+
height: height
|
|
363
|
+
});
|
|
350
364
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
365
|
+
// Create separate layers for grid and components
|
|
366
|
+
viewport.gridLayer = new _this4.Konva.Layer();
|
|
367
|
+
viewport.componentLayer = new _this4.Konva.Layer();
|
|
368
|
+
|
|
369
|
+
// Add layers to stage in order (grid first, then components)
|
|
370
|
+
viewport.stage.add(viewport.gridLayer);
|
|
371
|
+
viewport.stage.add(viewport.componentLayer);
|
|
372
|
+
|
|
373
|
+
// Setup resize handling
|
|
374
|
+
_this4.setupResizeListener(viewport);
|
|
375
|
+
|
|
376
|
+
// Setup zoom and pan handlers
|
|
377
|
+
_this4.setupZoomAndPanHandlers(viewport);
|
|
378
|
+
|
|
379
|
+
// Draw initial grid (lightweight, safe to do immediately)
|
|
380
|
+
_this4.drawGrid(viewport);
|
|
381
|
+
|
|
382
|
+
// Defer component rendering to next frame to ensure DOM layout is finalized.
|
|
383
|
+
// Without this, getBoundingClientRect() may return stale/zero dimensions
|
|
384
|
+
// if the container hasn't been fully laid out by CSS yet.
|
|
385
|
+
requestAnimationFrame(function () {
|
|
386
|
+
// Re-check container dimensions after layout settles
|
|
387
|
+
var finalRect = viewport.container.getBoundingClientRect();
|
|
388
|
+
if (finalRect.width > 0 && finalRect.height > 0) {
|
|
389
|
+
viewport.stage.width(finalRect.width);
|
|
390
|
+
viewport.stage.height(finalRect.height);
|
|
391
|
+
_this4.drawGrid(viewport);
|
|
392
|
+
}
|
|
393
|
+
_this4.renderComponents(viewport);
|
|
394
|
+
viewport.isReady = true;
|
|
395
|
+
console.log("\u2705 Viewport2DManager stage initialized (".concat(viewport.viewType, ")"));
|
|
396
|
+
resolve();
|
|
397
|
+
});
|
|
398
|
+
});
|
|
354
399
|
}
|
|
355
400
|
|
|
356
401
|
/**
|
|
@@ -360,12 +405,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
360
405
|
}, {
|
|
361
406
|
key: "setupResizeListener",
|
|
362
407
|
value: function setupResizeListener(viewport) {
|
|
363
|
-
var
|
|
408
|
+
var _this5 = this;
|
|
364
409
|
if (typeof window === 'undefined' || !window.ResizeObserver || !viewport.container) {
|
|
365
410
|
return;
|
|
366
411
|
}
|
|
367
412
|
viewport._resizeObserver = new ResizeObserver(function () {
|
|
368
|
-
|
|
413
|
+
_this5.resizeStage(viewport);
|
|
369
414
|
});
|
|
370
415
|
viewport._resizeObserver.observe(viewport.container);
|
|
371
416
|
}
|
|
@@ -400,7 +445,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
400
445
|
}, {
|
|
401
446
|
key: "setupZoomAndPanHandlers",
|
|
402
447
|
value: function setupZoomAndPanHandlers(viewport) {
|
|
403
|
-
var
|
|
448
|
+
var _this6 = this;
|
|
404
449
|
if (!viewport.stage) return;
|
|
405
450
|
|
|
406
451
|
// Mouse wheel zoom
|
|
@@ -426,7 +471,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
426
471
|
y: pointer.y - mousePointTo.y * clampedScale
|
|
427
472
|
};
|
|
428
473
|
viewport.stage.position(newPos);
|
|
429
|
-
|
|
474
|
+
_this6.drawGrid(viewport);
|
|
430
475
|
viewport.stage.batchDraw();
|
|
431
476
|
});
|
|
432
477
|
|
|
@@ -446,7 +491,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
446
491
|
viewport.stage.x(viewport.stage.x() + dx);
|
|
447
492
|
viewport.stage.y(viewport.stage.y() + dy);
|
|
448
493
|
viewport.lastPanPoint = pos;
|
|
449
|
-
|
|
494
|
+
_this6.drawGrid(viewport);
|
|
450
495
|
viewport.stage.batchDraw();
|
|
451
496
|
});
|
|
452
497
|
viewport.stage.on('mouseup', function () {
|
|
@@ -538,7 +583,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
538
583
|
}, {
|
|
539
584
|
key: "renderComponents",
|
|
540
585
|
value: function renderComponents(viewport) {
|
|
541
|
-
var
|
|
586
|
+
var _this7 = this;
|
|
542
587
|
if (!viewport.componentLayer || !viewport.stage || !this.sceneViewer) return;
|
|
543
588
|
viewport.componentLayer.destroyChildren();
|
|
544
589
|
|
|
@@ -549,19 +594,18 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
549
594
|
return;
|
|
550
595
|
}
|
|
551
596
|
|
|
552
|
-
//
|
|
553
|
-
if (
|
|
554
|
-
|
|
555
|
-
viewport._lastComponentCount = components.length;
|
|
556
|
-
}
|
|
597
|
+
// Track render count for debugging
|
|
598
|
+
if (viewport._renderCount === undefined) viewport._renderCount = 0;
|
|
599
|
+
viewport._renderCount++;
|
|
557
600
|
var width = viewport.stage.width();
|
|
558
601
|
var height = viewport.stage.height();
|
|
559
602
|
var centerX = width / 2;
|
|
560
603
|
var centerY = height / 2;
|
|
561
604
|
var scale = viewport.PIXELS_PER_UNIT;
|
|
605
|
+
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, ")"));
|
|
562
606
|
components.forEach(function (component) {
|
|
563
607
|
try {
|
|
564
|
-
|
|
608
|
+
_this7.renderComponent(viewport, component, centerX, centerY, scale);
|
|
565
609
|
} catch (err) {
|
|
566
610
|
console.warn('⚠️ Error rendering component in 2D:', component.name, err);
|
|
567
611
|
}
|
|
@@ -592,6 +636,11 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
592
636
|
var screenX = centerX + posX * scale;
|
|
593
637
|
var screenY = centerY - posY * scale; // Flip Y for screen coords
|
|
594
638
|
|
|
639
|
+
// Debug: Log ALL component positions on first render only
|
|
640
|
+
if (viewport._renderCount === 1) {
|
|
641
|
+
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)));
|
|
642
|
+
}
|
|
643
|
+
|
|
595
644
|
// Generate unique color for this component
|
|
596
645
|
var colors = this.generateComponentColor(component.id);
|
|
597
646
|
|
|
@@ -649,16 +698,15 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
649
698
|
}, {
|
|
650
699
|
key: "_getOrComputeWorldBoundingBox",
|
|
651
700
|
value: function _getOrComputeWorldBoundingBox(object) {
|
|
652
|
-
var _object$userData, _object$userData2;
|
|
653
701
|
// Fast path: offset the stored world bbox by the position delta since load time.
|
|
654
702
|
// Translation only shifts the bbox center — extents stay identical — so this is O(1)
|
|
655
703
|
// instead of O(meshes × vertices) from a full geometry traversal.
|
|
656
|
-
var
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
var
|
|
660
|
-
var
|
|
661
|
-
var
|
|
704
|
+
var cached = _basePositionCache.get(object);
|
|
705
|
+
if (cached) {
|
|
706
|
+
var dx = object.position.x - cached.x;
|
|
707
|
+
var dy = object.position.y - cached.y;
|
|
708
|
+
var dz = object.position.z - cached.z;
|
|
709
|
+
var stored = cached.worldBoundingBox;
|
|
662
710
|
if (dx === 0 && dy === 0 && dz === 0) return stored;
|
|
663
711
|
return {
|
|
664
712
|
min: [stored.min[0] + dx, stored.min[1] + dy, stored.min[2] + dz],
|
|
@@ -670,7 +718,14 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
670
718
|
if (this._bboxCache.has(object.uuid)) {
|
|
671
719
|
return this._bboxCache.get(object.uuid);
|
|
672
720
|
}
|
|
673
|
-
|
|
721
|
+
|
|
722
|
+
// Force matrix updates before computing bbox to ensure world-space accuracy
|
|
723
|
+
object.updateMatrix();
|
|
724
|
+
object.updateMatrixWorld(true);
|
|
725
|
+
|
|
726
|
+
// Exclude io-devices and connectors to match the stored worldBoundingBox
|
|
727
|
+
// computed in modelManager.replaceWithGLBModels()
|
|
728
|
+
var box = boundingBoxUtils.computeFilteredBoundingBox(object, ['io-device', 'connector']);
|
|
674
729
|
var result;
|
|
675
730
|
if (box.isEmpty()) {
|
|
676
731
|
// Object has no geometry; fall back to a point at world position
|
|
@@ -801,7 +856,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
801
856
|
}, {
|
|
802
857
|
key: "addComponentInteractions",
|
|
803
858
|
value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter, label) {
|
|
804
|
-
var
|
|
859
|
+
var _this8 = this;
|
|
805
860
|
if (!this.Konva) return;
|
|
806
861
|
var colors = this.generateComponentColor(component.id);
|
|
807
862
|
|
|
@@ -830,12 +885,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
830
885
|
// CLICK EVENT
|
|
831
886
|
rect.on('click', function () {
|
|
832
887
|
if (!viewport.isDragging) {
|
|
833
|
-
var
|
|
888
|
+
var _this8$sceneViewer;
|
|
834
889
|
console.log("\uD83C\uDFAF Component clicked: ".concat(component.name));
|
|
835
890
|
|
|
836
891
|
// Use centralPlant API to select component
|
|
837
|
-
if ((
|
|
838
|
-
|
|
892
|
+
if ((_this8$sceneViewer = _this8.sceneViewer) !== null && _this8$sceneViewer !== void 0 && _this8$sceneViewer.centralPlant && component.uuid) {
|
|
893
|
+
_this8.sceneViewer.centralPlant.selectComponent(component.uuid);
|
|
839
894
|
}
|
|
840
895
|
}
|
|
841
896
|
});
|
|
@@ -865,7 +920,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
865
920
|
var worldOriginY = stageHeight / 2;
|
|
866
921
|
|
|
867
922
|
// Snap to grid
|
|
868
|
-
var snappedPos =
|
|
923
|
+
var snappedPos = _this8.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
|
|
869
924
|
componentGroup.position(snappedPos);
|
|
870
925
|
viewport.componentLayer.batchDraw();
|
|
871
926
|
});
|
|
@@ -873,7 +928,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
873
928
|
// DRAG END
|
|
874
929
|
componentGroup.on('dragend', function () {
|
|
875
930
|
setTimeout(function () {
|
|
876
|
-
var
|
|
931
|
+
var _this8$sceneViewer2;
|
|
877
932
|
viewport.isDragging = false;
|
|
878
933
|
var finalPos = componentGroup.position();
|
|
879
934
|
var stageWidth = viewport.stage.width();
|
|
@@ -883,17 +938,17 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
883
938
|
var worldOriginY = stageHeight / 2;
|
|
884
939
|
|
|
885
940
|
// Convert screen to world coordinates
|
|
886
|
-
var worldCoords =
|
|
941
|
+
var worldCoords = _this8.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
|
|
887
942
|
|
|
888
943
|
// Calculate new position: delta from old bbox center to new bbox center
|
|
889
944
|
var currentPos = component.position;
|
|
890
|
-
var newPosition =
|
|
945
|
+
var newPosition = _this8.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
|
|
891
946
|
|
|
892
947
|
// Apply translation via centralPlant API
|
|
893
948
|
var deltaX = newPosition.x - currentPos.x;
|
|
894
949
|
var deltaY = newPosition.y - currentPos.y;
|
|
895
950
|
var deltaZ = newPosition.z - currentPos.z;
|
|
896
|
-
if ((
|
|
951
|
+
if ((_this8$sceneViewer2 = _this8.sceneViewer) !== null && _this8$sceneViewer2 !== void 0 && _this8$sceneViewer2.centralPlant && component.uuid) {
|
|
897
952
|
var success = true;
|
|
898
953
|
|
|
899
954
|
// Suppress per-axis path updates so the pathfinder only runs once,
|
|
@@ -901,32 +956,32 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
901
956
|
// Running updatePaths() after each individual axis (the default
|
|
902
957
|
// translateComponent behaviour) produces intermediate wrong results on
|
|
903
958
|
// the first drag because no cached fingerprint exists yet to skip them.
|
|
904
|
-
var wasAutoUpdate =
|
|
905
|
-
|
|
959
|
+
var wasAutoUpdate = _this8.sceneViewer.shouldUpdatePaths;
|
|
960
|
+
_this8.sceneViewer.shouldUpdatePaths = false;
|
|
906
961
|
try {
|
|
907
962
|
if (Math.abs(deltaX) > 0.01) {
|
|
908
|
-
success = success &&
|
|
963
|
+
success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'x', deltaX);
|
|
909
964
|
}
|
|
910
965
|
if (Math.abs(deltaY) > 0.01) {
|
|
911
|
-
success = success &&
|
|
966
|
+
success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'y', deltaY);
|
|
912
967
|
}
|
|
913
968
|
if (Math.abs(deltaZ) > 0.01) {
|
|
914
|
-
success = success &&
|
|
969
|
+
success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'z', deltaZ);
|
|
915
970
|
}
|
|
916
971
|
} finally {
|
|
917
|
-
|
|
972
|
+
_this8.sceneViewer.shouldUpdatePaths = wasAutoUpdate;
|
|
918
973
|
}
|
|
919
|
-
if (!success &&
|
|
974
|
+
if (!success && _this8.dragStartPosition) {
|
|
920
975
|
console.warn('⚠️ Failed to translate component, reverting position');
|
|
921
|
-
componentGroup.position(
|
|
976
|
+
componentGroup.position(_this8.dragStartPosition);
|
|
922
977
|
} else {
|
|
923
978
|
console.log("\u2705 Component ".concat(component.name, " translated successfully in 2D viewport"));
|
|
924
979
|
|
|
925
980
|
// Single path update with the final combined position
|
|
926
|
-
if (wasAutoUpdate &&
|
|
981
|
+
if (wasAutoUpdate && _this8.sceneViewer) {
|
|
927
982
|
console.log('🔄 Auto-updating paths after 2D viewport translation...');
|
|
928
983
|
try {
|
|
929
|
-
|
|
984
|
+
_this8.sceneViewer.updatePaths();
|
|
930
985
|
console.log('✅ Paths auto-updated successfully from 2D viewport');
|
|
931
986
|
} catch (error) {
|
|
932
987
|
console.error('❌ Error auto-updating paths from 2D viewport:', error);
|
|
@@ -1058,10 +1113,10 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1058
1113
|
}
|
|
1059
1114
|
var components = [];
|
|
1060
1115
|
this.sceneViewer.scene.traverse(function (object) {
|
|
1061
|
-
var _object$
|
|
1116
|
+
var _object$userData, _object$userData2;
|
|
1062
1117
|
// Only match the ROOT component object — must have both objectType:'component'
|
|
1063
1118
|
// AND libraryId (inner GLB mesh nodes don't have libraryId)
|
|
1064
|
-
if (((_object$
|
|
1119
|
+
if (((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.objectType) === 'component' && (_object$userData2 = object.userData) !== null && _object$userData2 !== void 0 && _object$userData2.libraryId) {
|
|
1065
1120
|
components.push(object);
|
|
1066
1121
|
}
|
|
1067
1122
|
});
|
|
@@ -1179,28 +1234,39 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1179
1234
|
}, {
|
|
1180
1235
|
key: "refresh",
|
|
1181
1236
|
value: function refresh() {
|
|
1182
|
-
var
|
|
1237
|
+
var _this9 = this;
|
|
1183
1238
|
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
1239
|
+
// Collect viewport keys to refresh; null means "all viewports"
|
|
1240
|
+
if (key) {
|
|
1241
|
+
this._pendingRefreshKeys.add(key);
|
|
1242
|
+
} else {
|
|
1243
|
+
// null key means refresh all — mark with special sentinel
|
|
1244
|
+
this._pendingRefreshKeys.add('__ALL__');
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// If already scheduled, the rAF callback will process our newly-added key
|
|
1184
1248
|
if (this._refreshPending) return;
|
|
1185
1249
|
this._refreshPending = true;
|
|
1186
1250
|
requestAnimationFrame(function () {
|
|
1187
|
-
|
|
1251
|
+
_this9._refreshPending = false;
|
|
1252
|
+
|
|
1188
1253
|
// Clear per-cycle caches so each component is measured/traversed once per paint
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1254
|
+
_this9._bboxCache.clear();
|
|
1255
|
+
_this9._componentListCache = null;
|
|
1256
|
+
|
|
1257
|
+
// Check if we need to refresh all viewports
|
|
1258
|
+
var refreshAll = _this9._pendingRefreshKeys.has('__ALL__');
|
|
1259
|
+
var keysToRefresh = new Set(_this9._pendingRefreshKeys);
|
|
1260
|
+
_this9._pendingRefreshKeys.clear();
|
|
1261
|
+
if (refreshAll) {
|
|
1262
|
+
// Refresh all viewports
|
|
1263
|
+
var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(_this9.viewports.values()),
|
|
1198
1264
|
_step;
|
|
1199
1265
|
try {
|
|
1200
1266
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1201
|
-
var
|
|
1202
|
-
if (
|
|
1203
|
-
|
|
1267
|
+
var viewport = _step.value;
|
|
1268
|
+
if (viewport.isReady) {
|
|
1269
|
+
_this9.renderComponents(viewport);
|
|
1204
1270
|
}
|
|
1205
1271
|
}
|
|
1206
1272
|
} catch (err) {
|
|
@@ -1208,6 +1274,23 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1208
1274
|
} finally {
|
|
1209
1275
|
_iterator.f();
|
|
1210
1276
|
}
|
|
1277
|
+
} else {
|
|
1278
|
+
// Refresh only the specific viewports that were requested
|
|
1279
|
+
var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(keysToRefresh),
|
|
1280
|
+
_step2;
|
|
1281
|
+
try {
|
|
1282
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
1283
|
+
var viewportKey = _step2.value;
|
|
1284
|
+
var _viewport = _this9.viewports.get(viewportKey);
|
|
1285
|
+
if (_viewport && _viewport.isReady) {
|
|
1286
|
+
_this9.renderComponents(_viewport);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
_iterator2.e(err);
|
|
1291
|
+
} finally {
|
|
1292
|
+
_iterator2.f();
|
|
1293
|
+
}
|
|
1211
1294
|
}
|
|
1212
1295
|
});
|
|
1213
1296
|
}
|
|
@@ -1228,20 +1311,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1228
1311
|
}
|
|
1229
1312
|
|
|
1230
1313
|
// Dispose all viewport instances
|
|
1231
|
-
var
|
|
1232
|
-
|
|
1314
|
+
var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this.viewports.entries()),
|
|
1315
|
+
_step3;
|
|
1233
1316
|
try {
|
|
1234
|
-
for (
|
|
1235
|
-
var
|
|
1236
|
-
key =
|
|
1237
|
-
viewport =
|
|
1317
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
1318
|
+
var _step3$value = _rollupPluginBabelHelpers.slicedToArray(_step3.value, 2),
|
|
1319
|
+
key = _step3$value[0],
|
|
1320
|
+
viewport = _step3$value[1];
|
|
1238
1321
|
console.log("\uD83D\uDDD1\uFE0F Disposing viewport: ".concat(key));
|
|
1239
1322
|
viewport.dispose();
|
|
1240
1323
|
}
|
|
1241
1324
|
} catch (err) {
|
|
1242
|
-
|
|
1325
|
+
_iterator3.e(err);
|
|
1243
1326
|
} finally {
|
|
1244
|
-
|
|
1327
|
+
_iterator3.f();
|
|
1245
1328
|
}
|
|
1246
1329
|
this.viewports.clear();
|
|
1247
1330
|
this.Konva = null;
|
|
@@ -1253,3 +1336,4 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
|
|
|
1253
1336
|
}(baseDisposable.BaseDisposable);
|
|
1254
1337
|
|
|
1255
1338
|
exports.Viewport2DManager = Viewport2DManager;
|
|
1339
|
+
exports.cacheBasePosition = cacheBasePosition;
|
|
@@ -119,7 +119,12 @@ function computeFilteredBoundingBox(object) {
|
|
|
119
119
|
|
|
120
120
|
// Build a Set for O(1) lookups
|
|
121
121
|
var excludeSet = new Set(excludeTypes);
|
|
122
|
-
|
|
122
|
+
|
|
123
|
+
// Force matrix updates to ensure world-space coordinates are accurate.
|
|
124
|
+
// Using force=true ensures matrices are updated even if matrixWorldNeedsUpdate is false,
|
|
125
|
+
// which can happen after positioning a model before the render loop runs.
|
|
126
|
+
object.updateMatrix();
|
|
127
|
+
object.updateMatrixWorld(true);
|
|
123
128
|
object.traverse(function (child) {
|
|
124
129
|
// Only process nodes with geometry (Mesh, SkinnedMesh, etc.)
|
|
125
130
|
if (!child.geometry) return;
|
|
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
31
31
|
* Initialize the CentralPlant manager
|
|
32
32
|
*
|
|
33
33
|
* @constructor
|
|
34
|
-
* @version 0.3.
|
|
34
|
+
* @version 0.3.14
|
|
35
35
|
* @updated 2025-10-22
|
|
36
36
|
*
|
|
37
37
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -3,6 +3,7 @@ import * as THREE from 'three';
|
|
|
3
3
|
import { attachIODevicesToComponent } from '../../utils/ioDeviceUtils.js';
|
|
4
4
|
import modelPreloader from '../../rendering/modelPreloader.js';
|
|
5
5
|
import { computeFilteredBoundingBox } from '../../utils/boundingBoxUtils.js';
|
|
6
|
+
import { cacheBasePosition } from './viewport2DManager.js';
|
|
6
7
|
|
|
7
8
|
var ModelManager = /*#__PURE__*/function () {
|
|
8
9
|
function ModelManager(sceneViewer) {
|
|
@@ -561,6 +562,15 @@ var ModelManager = /*#__PURE__*/function () {
|
|
|
561
562
|
var jsonData = _ref2.jsonData,
|
|
562
563
|
glbModel = _ref2.glbModel;
|
|
563
564
|
if (!glbModel) return;
|
|
565
|
+
|
|
566
|
+
// CRITICAL: Force matrix updates before computing bbox.
|
|
567
|
+
// After loadLibraryModel positions the model, the world matrices may not be
|
|
568
|
+
// invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
|
|
569
|
+
// which only updates if matrixWorldNeedsUpdate is true. Force the update here
|
|
570
|
+
// to ensure the bbox is computed with correct world-space coordinates.
|
|
571
|
+
glbModel.updateMatrix();
|
|
572
|
+
glbModel.updateMatrixWorld(true);
|
|
573
|
+
|
|
564
574
|
// Use filtered bbox (excludes connectors + io-devices) so it matches
|
|
565
575
|
// what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
|
|
566
576
|
var filteredBox = computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
|
|
@@ -571,13 +581,9 @@ var ModelManager = /*#__PURE__*/function () {
|
|
|
571
581
|
// Update both the JSON data object AND the live scene object
|
|
572
582
|
jsonData.userData.worldBoundingBox = worldBoundingBox;
|
|
573
583
|
glbModel.userData.worldBoundingBox = worldBoundingBox;
|
|
574
|
-
//
|
|
584
|
+
// Cache the object's position so viewport2DManager can compute
|
|
575
585
|
// world-bbox updates via a fast O(1) position delta instead of re-traversing geometry
|
|
576
|
-
glbModel
|
|
577
|
-
x: glbModel.position.x,
|
|
578
|
-
y: glbModel.position.y,
|
|
579
|
-
z: glbModel.position.z
|
|
580
|
-
};
|
|
586
|
+
cacheBasePosition(glbModel, worldBoundingBox);
|
|
581
587
|
});
|
|
582
588
|
|
|
583
589
|
// Dispatch completion event
|