@2112-lab/central-plant 0.3.2 → 0.3.5

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.
@@ -237,40 +237,17 @@ var SceneExportManager = /*#__PURE__*/function () {
237
237
  };
238
238
  }
239
239
 
240
- // For components: only export connector/gateway children (not mesh geometry)
241
- // For manual segments: export the segment's connector children
242
- // For connectors/gateways: no children (they're leaf nodes)
240
+ // For components: no children exported — connector positions are defined in the component
241
+ // dictionary GLB and will be re-injected at import time via _injectConnectorChildrenFromDictionary.
242
+ // Exporting them here would prevent that injection (it skips if children already exist)
243
+ // and would leave the pathfinder with connectors in incompatible local-position format.
243
244
  if (threeObject.children && threeObject.children.length > 0) {
244
245
  var exportableChildren = [];
245
- if (threeObject.userData.objectType === 'component') {
246
- // Export connector children (skip mesh geometry - it lives in the component dictionary GLB)
247
- threeObject.children.forEach(function (child) {
248
- var _child$userData;
249
- if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'connector') {
250
- var connectorJson = {
251
- // Use the raw Three.js UUID — connections in currentSceneData reference this exact value
252
- uuid: child.uuid,
253
- userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({
254
- objectType: 'connector'
255
- }, child.userData.direction ? {
256
- direction: child.userData.direction
257
- } : {}), child.userData.group ? {
258
- group: child.userData.group
259
- } : {}),
260
- position: {
261
- x: roundIfClose(child.position.x),
262
- y: roundIfClose(child.position.y),
263
- z: roundIfClose(child.position.z)
264
- }
265
- };
266
- exportableChildren.push(connectorJson);
267
- }
268
- });
269
- } else if (isManualSegment) {
246
+ if (isManualSegment) {
270
247
  // For manual segments, export their connector children
271
248
  threeObject.children.forEach(function (child) {
272
- var _child$userData2;
273
- if (((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment-connector') {
249
+ var _child$userData;
250
+ if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'segment-connector') {
274
251
  // Call the class method using bound reference
275
252
  var connectorJson = convertSegmentConnectorToJson(child, threeObject);
276
253
  if (connectorJson) {
@@ -289,38 +266,18 @@ var SceneExportManager = /*#__PURE__*/function () {
289
266
  // Bind the convertSegmentConnectorToJson method for use in nested function
290
267
  var convertSegmentConnectorToJson = this.convertSegmentConnectorToJson.bind(this);
291
268
 
292
- // Extract main scene objects (components and standalone connectors/gateways)
269
+ // Extract main scene objects (components and standalone connectors)
293
270
  var sceneChildren = [];
294
-
295
- // Helper to recursively find manual segments within polylines
296
- var findManualSegments = function findManualSegments(object) {
297
- var manualSegments = [];
298
- object.traverse(function (child) {
299
- var _child$userData3;
300
- // Check if this is a manual segment
301
- if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'segment') {
302
- manualSegments.push(child);
303
- }
304
- });
305
- return manualSegments;
306
- };
307
271
  this.sceneViewer.scene.children.forEach(function (child) {
308
- var _child$name;
309
- // Check if this is a polyline - if so, extract manual segments from it
310
- if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) {
311
- var manualSegments = findManualSegments(child);
312
- manualSegments.forEach(function (segment) {
313
- var jsonSegment = convertObjectToJson(segment);
314
- if (jsonSegment) {
315
- sceneChildren.push(jsonSegment);
316
- }
317
- });
318
- } else {
319
- // Regular scene object export
320
- var jsonChild = convertObjectToJson(child);
321
- if (jsonChild) {
322
- sceneChildren.push(jsonChild);
323
- }
272
+ var _child$userData2;
273
+ // Only export components and connectors; skip segments, gateways, polylines, etc.
274
+ var objectType = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType;
275
+ if (objectType !== 'component' && objectType !== 'connector') {
276
+ return;
277
+ }
278
+ var jsonChild = convertObjectToJson(child);
279
+ if (jsonChild) {
280
+ sceneChildren.push(jsonChild);
324
281
  }
325
282
  });
326
283
 
@@ -498,14 +455,14 @@ var SceneExportManager = /*#__PURE__*/function () {
498
455
  BufferGeometryUtils = BufferGeometryUtilsModule.BufferGeometryUtils || BufferGeometryUtilsModule.default || BufferGeometryUtilsModule; // Create a new scene for export instead of cloning
499
456
  exportScene = new _THREE.Scene(); // Helper function to check if an object should be exported
500
457
  shouldExport = function shouldExport(child) {
501
- var _child$name2, _child$userData4, _child$userData5, _child$userData6, _child$userData7;
502
- if ((_child$name2 = child.name) !== null && _child$name2 !== void 0 && _child$name2.includes('Polyline')) return false; // Will handle separately
458
+ var _child$name, _child$userData3, _child$userData4, _child$userData5, _child$userData6;
459
+ if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) return false; // Will handle separately
503
460
  if (child.name === 'fogPlane') return false; // Skip fog plane
504
- if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBrickWall) return false; // Skip environment
505
- if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGround) return false; // Skip environment
506
- if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isBaseGrid) return false; // Skip environment
461
+ if ((_child$userData3 = child.userData) !== null && _child$userData3 !== void 0 && _child$userData3.isBrickWall) return false; // Skip environment
462
+ if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBaseGround) return false; // Skip environment
463
+ if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGrid) return false; // Skip environment
507
464
  if (child.isLight) return false; // Skip lights
508
- if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.isTransformControls) return false; // Skip transform controls
465
+ if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isTransformControls) return false; // Skip transform controls
509
466
  if (child.isTransformControls) return false; // Skip transform controls
510
467
  if (child.type && child.type.includes('TransformControls')) return false;
511
468
  if (child.type && child.type.includes('Helper')) return false; // Skip helpers
@@ -514,8 +471,8 @@ var SceneExportManager = /*#__PURE__*/function () {
514
471
  pipeGeometries = [];
515
472
  pipeMaterial = null;
516
473
  this.sceneViewer.scene.children.forEach(function (child) {
517
- var _child$name3;
518
- if ((_child$name3 = child.name) !== null && _child$name3 !== void 0 && _child$name3.includes('Polyline')) {
474
+ var _child$name2;
475
+ if ((_child$name2 = child.name) !== null && _child$name2 !== void 0 && _child$name2.includes('Polyline')) {
519
476
  // Traverse the polyline to collect all mesh geometries
520
477
  child.traverse(function (obj) {
521
478
  if (obj.isMesh && obj.geometry) {
@@ -4,6 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
6
6
  var baseDisposable = require('../../core/baseDisposable.js');
7
+ var THREE = require('three');
8
+ var boundingBoxUtils = require('../../utils/boundingBoxUtils.js');
7
9
 
8
10
  function _interopNamespace(e) {
9
11
  if (e && e.__esModule) return e;
@@ -23,6 +25,8 @@ function _interopNamespace(e) {
23
25
  return Object.freeze(n);
24
26
  }
25
27
 
28
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
29
+
26
30
  /**
27
31
  * Viewport2DInstance
28
32
  * Represents a single 2D viewport with its own Konva stage and configuration
@@ -550,32 +554,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
550
554
  }, {
551
555
  key: "renderComponent",
552
556
  value: function renderComponent(viewport, component, centerX, centerY, scale) {
553
- var _component$position$x, _component$position, _component$position$y, _component$position2, _component$position$z, _component$position3, _component$rotation$x, _component$rotation, _component$rotation$y, _component$rotation2, _component$rotation$z, _component$rotation3;
554
- // Get component position and rotation
555
- var pos3D = {
556
- x: (_component$position$x = (_component$position = component.position) === null || _component$position === void 0 ? void 0 : _component$position.x) !== null && _component$position$x !== void 0 ? _component$position$x : 0,
557
- y: (_component$position$y = (_component$position2 = component.position) === null || _component$position2 === void 0 ? void 0 : _component$position2.y) !== null && _component$position$y !== void 0 ? _component$position$y : 0,
558
- z: (_component$position$z = (_component$position3 = component.position) === null || _component$position3 === void 0 ? void 0 : _component$position3.z) !== null && _component$position$z !== void 0 ? _component$position$z : 0
559
- };
560
- var rot3D = {
561
- x: (_component$rotation$x = (_component$rotation = component.rotation) === null || _component$rotation === void 0 ? void 0 : _component$rotation.x) !== null && _component$rotation$x !== void 0 ? _component$rotation$x : 0,
562
- y: (_component$rotation$y = (_component$rotation2 = component.rotation) === null || _component$rotation2 === void 0 ? void 0 : _component$rotation2.y) !== null && _component$rotation$y !== void 0 ? _component$rotation$y : 0,
563
- z: (_component$rotation$z = (_component$rotation3 = component.rotation) === null || _component$rotation3 === void 0 ? void 0 : _component$rotation3.z) !== null && _component$rotation$z !== void 0 ? _component$rotation$z : 0
564
- };
565
-
566
- // Get bounding box dimensions
557
+ // Get world-space bounding box dimensions and center
567
558
  var _this$getComponentDim = this.getComponentDimensions(component),
568
559
  worldWidth = _this$getComponentDim.worldWidth,
569
560
  worldDepth = _this$getComponentDim.worldDepth,
570
- worldHeight = _this$getComponentDim.worldHeight;
561
+ worldHeight = _this$getComponentDim.worldHeight,
562
+ bboxCenter = _this$getComponentDim.bboxCenter;
563
+ console.log("[2D] ".concat(component.name, " | w=").concat(worldWidth.toFixed(3), " d=").concat(worldDepth.toFixed(3), " h=").concat(worldHeight.toFixed(3), " | center=(").concat(bboxCenter.x.toFixed(2), ",").concat(bboxCenter.y.toFixed(2), ",").concat(bboxCenter.z.toFixed(2), ")"));
571
564
 
572
- // Project 3D coordinates to 2D based on view type
573
- var _this$project3DTo2D = this.project3DTo2D(viewport, pos3D, rot3D, worldWidth, worldDepth, worldHeight),
565
+ // Project 3D bbox center to 2D based on view type
566
+ var _this$project3DTo2D = this.project3DTo2D(viewport, bboxCenter, worldWidth, worldDepth, worldHeight),
574
567
  posX = _this$project3DTo2D.posX,
575
568
  posY = _this$project3DTo2D.posY,
576
569
  rectWidth = _this$project3DTo2D.rectWidth,
577
570
  rectHeight = _this$project3DTo2D.rectHeight;
578
- _this$project3DTo2D.rotationDegrees;
579
571
  var screenX = centerX + posX * scale;
580
572
  var screenY = centerY - posY * scale; // Flip Y for screen coords
581
573
 
@@ -619,105 +611,131 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
619
611
  });
620
612
 
621
613
  // Add mouse event handlers
622
- this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight);
614
+ this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter);
623
615
  componentGroup.add(rect);
624
616
  componentGroup.add(label);
625
617
  viewport.componentLayer.add(componentGroup);
626
618
  }
627
619
 
628
620
  /**
629
- * Get component dimensions from various sources
621
+ * Compute worldBoundingBox for a live Three.js Object3D using the same
622
+ * vertex-accurate approach as computeFilteredBoundingBox (explicitly calls
623
+ * geometry.computeBoundingBox() on each mesh before measuring).
624
+ * @param {THREE.Object3D} object
625
+ * @returns {{min: number[], max: number[]}}
626
+ */
627
+ }, {
628
+ key: "_getOrComputeWorldBoundingBox",
629
+ value: function _getOrComputeWorldBoundingBox(object) {
630
+ // computeFilteredBoundingBox with no exclusions = full object bbox
631
+ var box = boundingBoxUtils.computeFilteredBoundingBox(object, []);
632
+ if (box.isEmpty()) {
633
+ // Object has no geometry; fall back to a point at world position
634
+ var wp = new THREE__namespace.Vector3();
635
+ object.getWorldPosition(wp);
636
+ return {
637
+ min: [wp.x, wp.y, wp.z],
638
+ max: [wp.x, wp.y, wp.z]
639
+ };
640
+ }
641
+ return {
642
+ min: [box.min.x, box.min.y, box.min.z],
643
+ max: [box.max.x, box.max.y, box.max.z]
644
+ };
645
+ }
646
+
647
+ /**
648
+ * Get component dimensions and world-space center from worldBoundingBox
630
649
  */
631
650
  }, {
632
651
  key: "getComponentDimensions",
633
652
  value: function getComponentDimensions(component) {
634
- var _component$userData, _component$userData2, _component$geometry;
635
- var worldWidth = 1,
636
- worldHeight = 1,
637
- worldDepth = 1;
638
-
639
- // Try adaptedBoundingBox first
640
- if ((_component$userData = component.userData) !== null && _component$userData !== void 0 && _component$userData.adaptedBoundingBox) {
641
- var bbox = component.userData.adaptedBoundingBox;
642
- if (bbox.max && bbox.min) {
643
- worldWidth = Math.abs(bbox.max.x - bbox.min.x);
644
- worldDepth = Math.abs(bbox.max.y - bbox.min.y);
645
- worldHeight = Math.abs(bbox.max.z - bbox.min.z);
653
+ var _component$userData$w, _component$userData, _component$getWorldPo;
654
+ // Prefer worldBoundingBox already stored on the object — set after GLB loading
655
+ // by modelManager.replaceWithGLBModels using computeFilteredBoundingBox.
656
+ // Fall back to computing live if not yet available (e.g. first render before GLB load).
657
+ var wbb = (_component$userData$w = (_component$userData = component.userData) === null || _component$userData === void 0 ? void 0 : _component$userData.worldBoundingBox) !== null && _component$userData$w !== void 0 ? _component$userData$w : this._getOrComputeWorldBoundingBox(component);
658
+ if (wbb !== null && wbb !== void 0 && wbb.min && wbb !== null && wbb !== void 0 && wbb.max) {
659
+ var _wbb$min = _rollupPluginBabelHelpers.slicedToArray(wbb.min, 3),
660
+ minX = _wbb$min[0],
661
+ minY = _wbb$min[1],
662
+ minZ = _wbb$min[2];
663
+ var _wbb$max = _rollupPluginBabelHelpers.slicedToArray(wbb.max, 3),
664
+ maxX = _wbb$max[0],
665
+ maxY = _wbb$max[1],
666
+ maxZ = _wbb$max[2];
667
+ var cx = (minX + maxX) / 2;
668
+ var cy = (minY + maxY) / 2;
669
+ var cz = (minZ + maxZ) / 2;
670
+ // Guard against Infinity/NaN from empty or degenerate boxes
671
+ if (isFinite(cx) && isFinite(cy) && isFinite(cz)) {
672
+ return {
673
+ worldWidth: Math.max(maxX - minX, 0.01),
674
+ worldDepth: Math.max(maxY - minY, 0.01),
675
+ worldHeight: Math.max(maxZ - minZ, 0.01),
676
+ bboxCenter: {
677
+ x: cx,
678
+ y: cy,
679
+ z: cz
680
+ }
681
+ };
646
682
  }
647
683
  }
648
- // Fallback to dimensions from userData
649
- else if ((_component$userData2 = component.userData) !== null && _component$userData2 !== void 0 && _component$userData2.dimensions) {
650
- var dims = component.userData.dimensions;
651
- worldWidth = Math.abs(dims.x);
652
- worldDepth = Math.abs(dims.y);
653
- worldHeight = Math.abs(dims.z);
654
- }
655
- // Last resort: geometry bounding box
656
- else if ((_component$geometry = component.geometry) !== null && _component$geometry !== void 0 && _component$geometry.boundingBox) {
657
- var _bbox = component.geometry.boundingBox;
658
- worldWidth = Math.abs(_bbox.max.x - _bbox.min.x);
659
- worldDepth = Math.abs(_bbox.max.y - _bbox.min.y);
660
- worldHeight = Math.abs(_bbox.max.z - _bbox.min.z);
661
- }
684
+ // Fallback: world position of the object, unit dimensions
685
+ var wp = new THREE__namespace.Vector3();
686
+ (_component$getWorldPo = component.getWorldPosition) === null || _component$getWorldPo === void 0 || _component$getWorldPo.call(component, wp);
662
687
  return {
663
- worldWidth: worldWidth,
664
- worldDepth: worldDepth,
665
- worldHeight: worldHeight
688
+ worldWidth: 1,
689
+ worldDepth: 1,
690
+ worldHeight: 1,
691
+ bboxCenter: {
692
+ x: wp.x,
693
+ y: wp.y,
694
+ z: wp.z
695
+ }
666
696
  };
667
697
  }
668
698
 
669
699
  /**
670
- * Project 3D coordinates to 2D based on view type
700
+ * Project world-space bbox center to 2D based on view type.
701
+ * worldBoundingBox is an AABB so rotation is already encoded in the extents —
702
+ * no separate rotation correction is needed.
671
703
  * @param {Viewport2DInstance} viewport - The viewport instance
704
+ * @param {Object} bboxCenter - World-space center {x, y, z}
705
+ * @param {number} worldWidth - X extent (max[0] - min[0])
706
+ * @param {number} worldDepth - Y extent (max[1] - min[1])
707
+ * @param {number} worldHeight - Z extent (max[2] - min[2])
672
708
  */
673
709
  }, {
674
710
  key: "project3DTo2D",
675
- value: function project3DTo2D(viewport, pos3D, rot3D, worldWidth, worldDepth, worldHeight) {
676
- var posX, posY, rectWidth, rectHeight;
677
- var rotationAngle = rot3D.z;
678
- var rotationDegrees = rotationAngle * 180 / Math.PI;
711
+ value: function project3DTo2D(viewport, bboxCenter, worldWidth, worldDepth, worldHeight) {
679
712
  var scale = viewport.PIXELS_PER_UNIT;
713
+ var posX, posY, rectWidth, rectHeight;
680
714
  switch (viewport.viewType) {
681
715
  case 'top':
682
- // Top view: Looking down Z-axis, X-Y plane
683
- posX = pos3D.x;
684
- posY = pos3D.y;
685
-
686
- // Swap width and depth when rotated 90° or 270°
687
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
688
- rectWidth = worldDepth * scale;
689
- rectHeight = worldWidth * scale;
690
- } else {
691
- rectWidth = worldWidth * scale;
692
- rectHeight = worldDepth * scale;
693
- }
716
+ // Looking down Z-axis X/Y plane
717
+ posX = bboxCenter.x;
718
+ posY = bboxCenter.y;
719
+ rectWidth = worldWidth * scale;
720
+ rectHeight = worldDepth * scale;
694
721
  break;
695
722
  case 'front':
696
- // Front view: Looking along Y-axis, X-Z plane
697
- posX = pos3D.x;
698
- posY = pos3D.z + worldHeight / 2; // Offset Z by half height
699
-
700
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
701
- rectWidth = worldDepth * scale;
702
- } else {
703
- rectWidth = worldWidth * scale;
704
- }
723
+ // Looking along Y-axis X/Z plane
724
+ posX = bboxCenter.x;
725
+ posY = bboxCenter.z;
726
+ rectWidth = worldWidth * scale;
705
727
  rectHeight = worldHeight * scale;
706
728
  break;
707
729
  case 'side':
708
- // Side view: Looking along X-axis, Y-Z plane (flipped)
709
- posX = -pos3D.y; // Flipped
710
- posY = pos3D.z + worldHeight / 2;
711
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
712
- rectWidth = worldWidth * scale;
713
- } else {
714
- rectWidth = worldDepth * scale;
715
- }
730
+ // Looking along X-axis Y/Z plane (Y negated for left-right orientation)
731
+ posX = -bboxCenter.y;
732
+ posY = bboxCenter.z;
733
+ rectWidth = worldDepth * scale;
716
734
  rectHeight = worldHeight * scale;
717
735
  break;
718
736
  default:
719
- posX = pos3D.x;
720
- posY = pos3D.y;
737
+ posX = bboxCenter.x;
738
+ posY = bboxCenter.y;
721
739
  rectWidth = worldWidth * scale;
722
740
  rectHeight = worldDepth * scale;
723
741
  }
@@ -725,8 +743,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
725
743
  posX: posX,
726
744
  posY: posY,
727
745
  rectWidth: rectWidth,
728
- rectHeight: rectHeight,
729
- rotationDegrees: rotationDegrees
746
+ rectHeight: rectHeight
730
747
  };
731
748
  }
732
749
 
@@ -736,7 +753,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
736
753
  */
737
754
  }, {
738
755
  key: "addComponentInteractions",
739
- value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight) {
756
+ value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter) {
740
757
  var _this6 = this;
741
758
  if (!this.Konva) return;
742
759
  var colors = this.generateComponentColor(component.id);
@@ -819,9 +836,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
819
836
  // Convert screen to world coordinates
820
837
  var worldCoords = _this6.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
821
838
 
822
- // Calculate new position
839
+ // Calculate new position: delta from old bbox center to new bbox center
823
840
  var currentPos = component.position;
824
- var newPosition = _this6.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, worldWidth, worldDepth, worldHeight);
841
+ var newPosition = _this6.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
825
842
 
826
843
  // Apply translation via centralPlant API
827
844
  var deltaX = newPosition.x - currentPos.x;
@@ -924,37 +941,45 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
924
941
  }
925
942
 
926
943
  /**
927
- * Convert world coordinates to 3D object position
944
+ * Convert dragged 2D world coordinates to a new 3D object position.
945
+ * coord1/coord2 represent where the bbox CENTER should be in the view's 2D plane;
946
+ * we compute the delta from the old bbox center and apply it to the pivot position.
928
947
  * @param {Viewport2DInstance} viewport - The viewport instance
948
+ * @param {{coord1, coord2}} worldCoords - Projected world coordinates from screen
949
+ * @param {{x,y,z}} currentPosition - Current Three.js object position (pivot)
950
+ * @param {{x,y,z}} bboxCenter - Old world-space bbox center
929
951
  */
930
952
  }, {
931
953
  key: "worldCoordsToObjectPosition",
932
- value: function worldCoordsToObjectPosition(viewport, worldCoords, currentPosition, worldWidth, worldDepth, worldHeight) {
954
+ value: function worldCoordsToObjectPosition(viewport, worldCoords, currentPosition, bboxCenter) {
933
955
  var coord1 = worldCoords.coord1,
934
956
  coord2 = worldCoords.coord2;
935
957
  switch (viewport.viewType) {
936
958
  case 'top':
959
+ // coord1=X, coord2=Y in world space
937
960
  return {
938
- x: coord1,
939
- y: coord2,
961
+ x: currentPosition.x + (coord1 - bboxCenter.x),
962
+ y: currentPosition.y + (coord2 - bboxCenter.y),
940
963
  z: currentPosition.z
941
964
  };
942
965
  case 'front':
966
+ // coord1=X, coord2=Z in world space
943
967
  return {
944
- x: coord1,
968
+ x: currentPosition.x + (coord1 - bboxCenter.x),
945
969
  y: currentPosition.y,
946
- z: coord2 - worldHeight / 2
970
+ z: currentPosition.z + (coord2 - bboxCenter.z)
947
971
  };
948
972
  case 'side':
973
+ // coord1=-Y (negated), coord2=Z in world space
949
974
  return {
950
975
  x: currentPosition.x,
951
- y: -coord1,
952
- z: coord2 - worldHeight / 2
976
+ y: currentPosition.y + (-coord1 - bboxCenter.y),
977
+ z: currentPosition.z + (coord2 - bboxCenter.z)
953
978
  };
954
979
  default:
955
980
  return {
956
- x: coord1,
957
- y: coord2,
981
+ x: currentPosition.x + (coord1 - bboxCenter.x),
982
+ y: currentPosition.y + (coord2 - bboxCenter.y),
958
983
  z: currentPosition.z
959
984
  };
960
985
  }
@@ -972,8 +997,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
972
997
  var components = [];
973
998
  this.sceneViewer.scene.traverse(function (object) {
974
999
  var _object$userData, _object$userData2;
975
- var objectType = ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.objectType) || ((_object$userData2 = object.userData) === null || _object$userData2 === void 0 ? void 0 : _object$userData2.objectType);
976
- if (object.userData && objectType === 'component') {
1000
+ // Only match the ROOT component object must have both objectType:'component'
1001
+ // AND libraryId (inner GLB mesh nodes don't have libraryId)
1002
+ 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) {
977
1003
  components.push(object);
978
1004
  }
979
1005
  });
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
31
31
  * Initialize the CentralPlant manager
32
32
  *
33
33
  * @constructor
34
- * @version 0.3.2
34
+ * @version 0.3.5
35
35
  * @updated 2025-10-22
36
36
  *
37
37
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -2,6 +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
6
 
6
7
  var ModelManager = /*#__PURE__*/function () {
7
8
  function ModelManager(sceneViewer) {
@@ -286,6 +287,11 @@ var ModelManager = /*#__PURE__*/function () {
286
287
  if (!libraryModel.userData) libraryModel.userData = {};
287
288
  Object.assign(libraryModel.userData, jsonEntry.userData);
288
289
 
290
+ // Preserve the original hardcoded UUID so getHardcodedUuid() can find it during export.
291
+ // jsonEntry is the raw JSON object (no originalUuid), so we must set it explicitly from
292
+ // originalProps.uuid (which equals the uuid string from the scene JSON, e.g. "PUMP-1").
293
+ libraryModel.userData.originalUuid = originalProps.uuid;
294
+
289
295
  // Apply bounding box configurations
290
296
  if (componentData.boundingBox) {
291
297
  libraryModel.userData.boundingBox = componentData.boundingBox;
@@ -550,15 +556,21 @@ var ModelManager = /*#__PURE__*/function () {
550
556
  _context5.n = 2;
551
557
  return Promise.all(glbLoadingPromises);
552
558
  case 2:
553
- // Update world bounding boxes for loaded models
559
+ // Update world bounding boxes for loaded models and propagate to the live Three.js objects
554
560
  libraryObjectsToReplace.forEach(function (_ref2) {
555
- var _jsonData$userData2;
556
561
  var jsonData = _ref2.jsonData,
557
562
  glbModel = _ref2.glbModel;
558
- if (glbModel && (_jsonData$userData2 = jsonData.userData) !== null && _jsonData$userData2 !== void 0 && _jsonData$userData2.worldBoundingBox) {
559
- var worldBoundingBox = _this3._calculateWorldBoundingBox(glbModel);
560
- jsonData.userData.worldBoundingBox = worldBoundingBox;
561
- }
563
+ if (!glbModel) return;
564
+ // Use filtered bbox (excludes connectors + io-devices) so it matches
565
+ // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
566
+ var filteredBox = computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
567
+ var worldBoundingBox = filteredBox.isEmpty() ? _this3._calculateWorldBoundingBox(glbModel) : {
568
+ min: [filteredBox.min.x, filteredBox.min.y, filteredBox.min.z],
569
+ max: [filteredBox.max.x, filteredBox.max.y, filteredBox.max.z]
570
+ };
571
+ // Update both the JSON data object AND the live scene object
572
+ jsonData.userData.worldBoundingBox = worldBoundingBox;
573
+ glbModel.userData.worldBoundingBox = worldBoundingBox;
562
574
  });
563
575
 
564
576
  // Dispatch completion event