@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.
@@ -215,40 +215,17 @@ var SceneExportManager = /*#__PURE__*/function () {
215
215
  };
216
216
  }
217
217
 
218
- // For components: only export connector/gateway children (not mesh geometry)
219
- // For manual segments: export the segment's connector children
220
- // For connectors/gateways: no children (they're leaf nodes)
218
+ // For components: no children exported — connector positions are defined in the component
219
+ // dictionary GLB and will be re-injected at import time via _injectConnectorChildrenFromDictionary.
220
+ // Exporting them here would prevent that injection (it skips if children already exist)
221
+ // and would leave the pathfinder with connectors in incompatible local-position format.
221
222
  if (threeObject.children && threeObject.children.length > 0) {
222
223
  var exportableChildren = [];
223
- if (threeObject.userData.objectType === 'component') {
224
- // Export connector children (skip mesh geometry - it lives in the component dictionary GLB)
225
- threeObject.children.forEach(function (child) {
226
- var _child$userData;
227
- if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'connector') {
228
- var connectorJson = {
229
- // Use the raw Three.js UUID — connections in currentSceneData reference this exact value
230
- uuid: child.uuid,
231
- userData: _objectSpread2(_objectSpread2({
232
- objectType: 'connector'
233
- }, child.userData.direction ? {
234
- direction: child.userData.direction
235
- } : {}), child.userData.group ? {
236
- group: child.userData.group
237
- } : {}),
238
- position: {
239
- x: roundIfClose(child.position.x),
240
- y: roundIfClose(child.position.y),
241
- z: roundIfClose(child.position.z)
242
- }
243
- };
244
- exportableChildren.push(connectorJson);
245
- }
246
- });
247
- } else if (isManualSegment) {
224
+ if (isManualSegment) {
248
225
  // For manual segments, export their connector children
249
226
  threeObject.children.forEach(function (child) {
250
- var _child$userData2;
251
- if (((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment-connector') {
227
+ var _child$userData;
228
+ if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'segment-connector') {
252
229
  // Call the class method using bound reference
253
230
  var connectorJson = convertSegmentConnectorToJson(child, threeObject);
254
231
  if (connectorJson) {
@@ -267,38 +244,18 @@ var SceneExportManager = /*#__PURE__*/function () {
267
244
  // Bind the convertSegmentConnectorToJson method for use in nested function
268
245
  var convertSegmentConnectorToJson = this.convertSegmentConnectorToJson.bind(this);
269
246
 
270
- // Extract main scene objects (components and standalone connectors/gateways)
247
+ // Extract main scene objects (components and standalone connectors)
271
248
  var sceneChildren = [];
272
-
273
- // Helper to recursively find manual segments within polylines
274
- var findManualSegments = function findManualSegments(object) {
275
- var manualSegments = [];
276
- object.traverse(function (child) {
277
- var _child$userData3;
278
- // Check if this is a manual segment
279
- if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'segment') {
280
- manualSegments.push(child);
281
- }
282
- });
283
- return manualSegments;
284
- };
285
249
  this.sceneViewer.scene.children.forEach(function (child) {
286
- var _child$name;
287
- // Check if this is a polyline - if so, extract manual segments from it
288
- if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) {
289
- var manualSegments = findManualSegments(child);
290
- manualSegments.forEach(function (segment) {
291
- var jsonSegment = convertObjectToJson(segment);
292
- if (jsonSegment) {
293
- sceneChildren.push(jsonSegment);
294
- }
295
- });
296
- } else {
297
- // Regular scene object export
298
- var jsonChild = convertObjectToJson(child);
299
- if (jsonChild) {
300
- sceneChildren.push(jsonChild);
301
- }
250
+ var _child$userData2;
251
+ // Only export components and connectors; skip segments, gateways, polylines, etc.
252
+ var objectType = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType;
253
+ if (objectType !== 'component' && objectType !== 'connector') {
254
+ return;
255
+ }
256
+ var jsonChild = convertObjectToJson(child);
257
+ if (jsonChild) {
258
+ sceneChildren.push(jsonChild);
302
259
  }
303
260
  });
304
261
 
@@ -476,14 +433,14 @@ var SceneExportManager = /*#__PURE__*/function () {
476
433
  BufferGeometryUtils = BufferGeometryUtilsModule.BufferGeometryUtils || BufferGeometryUtilsModule.default || BufferGeometryUtilsModule; // Create a new scene for export instead of cloning
477
434
  exportScene = new _THREE.Scene(); // Helper function to check if an object should be exported
478
435
  shouldExport = function shouldExport(child) {
479
- var _child$name2, _child$userData4, _child$userData5, _child$userData6, _child$userData7;
480
- if ((_child$name2 = child.name) !== null && _child$name2 !== void 0 && _child$name2.includes('Polyline')) return false; // Will handle separately
436
+ var _child$name, _child$userData3, _child$userData4, _child$userData5, _child$userData6;
437
+ if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) return false; // Will handle separately
481
438
  if (child.name === 'fogPlane') return false; // Skip fog plane
482
- if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBrickWall) return false; // Skip environment
483
- if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGround) return false; // Skip environment
484
- if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isBaseGrid) return false; // Skip environment
439
+ if ((_child$userData3 = child.userData) !== null && _child$userData3 !== void 0 && _child$userData3.isBrickWall) return false; // Skip environment
440
+ if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBaseGround) return false; // Skip environment
441
+ if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGrid) return false; // Skip environment
485
442
  if (child.isLight) return false; // Skip lights
486
- if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.isTransformControls) return false; // Skip transform controls
443
+ if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isTransformControls) return false; // Skip transform controls
487
444
  if (child.isTransformControls) return false; // Skip transform controls
488
445
  if (child.type && child.type.includes('TransformControls')) return false;
489
446
  if (child.type && child.type.includes('Helper')) return false; // Skip helpers
@@ -492,8 +449,8 @@ var SceneExportManager = /*#__PURE__*/function () {
492
449
  pipeGeometries = [];
493
450
  pipeMaterial = null;
494
451
  this.sceneViewer.scene.children.forEach(function (child) {
495
- var _child$name3;
496
- if ((_child$name3 = child.name) !== null && _child$name3 !== void 0 && _child$name3.includes('Polyline')) {
452
+ var _child$name2;
453
+ if ((_child$name2 = child.name) !== null && _child$name2 !== void 0 && _child$name2.includes('Polyline')) {
497
454
  // Traverse the polyline to collect all mesh geometries
498
455
  child.traverse(function (obj) {
499
456
  if (obj.isMesh && obj.geometry) {
@@ -1,5 +1,7 @@
1
- import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, slicedToArray as _slicedToArray, createForOfIteratorHelper as _createForOfIteratorHelper, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import { BaseDisposable } from '../../core/baseDisposable.js';
3
+ import * as THREE from 'three';
4
+ import { computeFilteredBoundingBox } from '../../utils/boundingBoxUtils.js';
3
5
 
4
6
  /**
5
7
  * Viewport2DInstance
@@ -528,32 +530,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
528
530
  }, {
529
531
  key: "renderComponent",
530
532
  value: function renderComponent(viewport, component, centerX, centerY, scale) {
531
- 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;
532
- // Get component position and rotation
533
- var pos3D = {
534
- 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,
535
- 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,
536
- 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
537
- };
538
- var rot3D = {
539
- 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,
540
- 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,
541
- 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
542
- };
543
-
544
- // Get bounding box dimensions
533
+ // Get world-space bounding box dimensions and center
545
534
  var _this$getComponentDim = this.getComponentDimensions(component),
546
535
  worldWidth = _this$getComponentDim.worldWidth,
547
536
  worldDepth = _this$getComponentDim.worldDepth,
548
- worldHeight = _this$getComponentDim.worldHeight;
537
+ worldHeight = _this$getComponentDim.worldHeight,
538
+ bboxCenter = _this$getComponentDim.bboxCenter;
539
+ 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), ")"));
549
540
 
550
- // Project 3D coordinates to 2D based on view type
551
- var _this$project3DTo2D = this.project3DTo2D(viewport, pos3D, rot3D, worldWidth, worldDepth, worldHeight),
541
+ // Project 3D bbox center to 2D based on view type
542
+ var _this$project3DTo2D = this.project3DTo2D(viewport, bboxCenter, worldWidth, worldDepth, worldHeight),
552
543
  posX = _this$project3DTo2D.posX,
553
544
  posY = _this$project3DTo2D.posY,
554
545
  rectWidth = _this$project3DTo2D.rectWidth,
555
546
  rectHeight = _this$project3DTo2D.rectHeight;
556
- _this$project3DTo2D.rotationDegrees;
557
547
  var screenX = centerX + posX * scale;
558
548
  var screenY = centerY - posY * scale; // Flip Y for screen coords
559
549
 
@@ -597,105 +587,131 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
597
587
  });
598
588
 
599
589
  // Add mouse event handlers
600
- this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight);
590
+ this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter);
601
591
  componentGroup.add(rect);
602
592
  componentGroup.add(label);
603
593
  viewport.componentLayer.add(componentGroup);
604
594
  }
605
595
 
606
596
  /**
607
- * Get component dimensions from various sources
597
+ * Compute worldBoundingBox for a live Three.js Object3D using the same
598
+ * vertex-accurate approach as computeFilteredBoundingBox (explicitly calls
599
+ * geometry.computeBoundingBox() on each mesh before measuring).
600
+ * @param {THREE.Object3D} object
601
+ * @returns {{min: number[], max: number[]}}
602
+ */
603
+ }, {
604
+ key: "_getOrComputeWorldBoundingBox",
605
+ value: function _getOrComputeWorldBoundingBox(object) {
606
+ // computeFilteredBoundingBox with no exclusions = full object bbox
607
+ var box = computeFilteredBoundingBox(object, []);
608
+ if (box.isEmpty()) {
609
+ // Object has no geometry; fall back to a point at world position
610
+ var wp = new THREE.Vector3();
611
+ object.getWorldPosition(wp);
612
+ return {
613
+ min: [wp.x, wp.y, wp.z],
614
+ max: [wp.x, wp.y, wp.z]
615
+ };
616
+ }
617
+ return {
618
+ min: [box.min.x, box.min.y, box.min.z],
619
+ max: [box.max.x, box.max.y, box.max.z]
620
+ };
621
+ }
622
+
623
+ /**
624
+ * Get component dimensions and world-space center from worldBoundingBox
608
625
  */
609
626
  }, {
610
627
  key: "getComponentDimensions",
611
628
  value: function getComponentDimensions(component) {
612
- var _component$userData, _component$userData2, _component$geometry;
613
- var worldWidth = 1,
614
- worldHeight = 1,
615
- worldDepth = 1;
616
-
617
- // Try adaptedBoundingBox first
618
- if ((_component$userData = component.userData) !== null && _component$userData !== void 0 && _component$userData.adaptedBoundingBox) {
619
- var bbox = component.userData.adaptedBoundingBox;
620
- if (bbox.max && bbox.min) {
621
- worldWidth = Math.abs(bbox.max.x - bbox.min.x);
622
- worldDepth = Math.abs(bbox.max.y - bbox.min.y);
623
- worldHeight = Math.abs(bbox.max.z - bbox.min.z);
629
+ var _component$userData$w, _component$userData, _component$getWorldPo;
630
+ // Prefer worldBoundingBox already stored on the object — set after GLB loading
631
+ // by modelManager.replaceWithGLBModels using computeFilteredBoundingBox.
632
+ // Fall back to computing live if not yet available (e.g. first render before GLB load).
633
+ 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);
634
+ if (wbb !== null && wbb !== void 0 && wbb.min && wbb !== null && wbb !== void 0 && wbb.max) {
635
+ var _wbb$min = _slicedToArray(wbb.min, 3),
636
+ minX = _wbb$min[0],
637
+ minY = _wbb$min[1],
638
+ minZ = _wbb$min[2];
639
+ var _wbb$max = _slicedToArray(wbb.max, 3),
640
+ maxX = _wbb$max[0],
641
+ maxY = _wbb$max[1],
642
+ maxZ = _wbb$max[2];
643
+ var cx = (minX + maxX) / 2;
644
+ var cy = (minY + maxY) / 2;
645
+ var cz = (minZ + maxZ) / 2;
646
+ // Guard against Infinity/NaN from empty or degenerate boxes
647
+ if (isFinite(cx) && isFinite(cy) && isFinite(cz)) {
648
+ return {
649
+ worldWidth: Math.max(maxX - minX, 0.01),
650
+ worldDepth: Math.max(maxY - minY, 0.01),
651
+ worldHeight: Math.max(maxZ - minZ, 0.01),
652
+ bboxCenter: {
653
+ x: cx,
654
+ y: cy,
655
+ z: cz
656
+ }
657
+ };
624
658
  }
625
659
  }
626
- // Fallback to dimensions from userData
627
- else if ((_component$userData2 = component.userData) !== null && _component$userData2 !== void 0 && _component$userData2.dimensions) {
628
- var dims = component.userData.dimensions;
629
- worldWidth = Math.abs(dims.x);
630
- worldDepth = Math.abs(dims.y);
631
- worldHeight = Math.abs(dims.z);
632
- }
633
- // Last resort: geometry bounding box
634
- else if ((_component$geometry = component.geometry) !== null && _component$geometry !== void 0 && _component$geometry.boundingBox) {
635
- var _bbox = component.geometry.boundingBox;
636
- worldWidth = Math.abs(_bbox.max.x - _bbox.min.x);
637
- worldDepth = Math.abs(_bbox.max.y - _bbox.min.y);
638
- worldHeight = Math.abs(_bbox.max.z - _bbox.min.z);
639
- }
660
+ // Fallback: world position of the object, unit dimensions
661
+ var wp = new THREE.Vector3();
662
+ (_component$getWorldPo = component.getWorldPosition) === null || _component$getWorldPo === void 0 || _component$getWorldPo.call(component, wp);
640
663
  return {
641
- worldWidth: worldWidth,
642
- worldDepth: worldDepth,
643
- worldHeight: worldHeight
664
+ worldWidth: 1,
665
+ worldDepth: 1,
666
+ worldHeight: 1,
667
+ bboxCenter: {
668
+ x: wp.x,
669
+ y: wp.y,
670
+ z: wp.z
671
+ }
644
672
  };
645
673
  }
646
674
 
647
675
  /**
648
- * Project 3D coordinates to 2D based on view type
676
+ * Project world-space bbox center to 2D based on view type.
677
+ * worldBoundingBox is an AABB so rotation is already encoded in the extents —
678
+ * no separate rotation correction is needed.
649
679
  * @param {Viewport2DInstance} viewport - The viewport instance
680
+ * @param {Object} bboxCenter - World-space center {x, y, z}
681
+ * @param {number} worldWidth - X extent (max[0] - min[0])
682
+ * @param {number} worldDepth - Y extent (max[1] - min[1])
683
+ * @param {number} worldHeight - Z extent (max[2] - min[2])
650
684
  */
651
685
  }, {
652
686
  key: "project3DTo2D",
653
- value: function project3DTo2D(viewport, pos3D, rot3D, worldWidth, worldDepth, worldHeight) {
654
- var posX, posY, rectWidth, rectHeight;
655
- var rotationAngle = rot3D.z;
656
- var rotationDegrees = rotationAngle * 180 / Math.PI;
687
+ value: function project3DTo2D(viewport, bboxCenter, worldWidth, worldDepth, worldHeight) {
657
688
  var scale = viewport.PIXELS_PER_UNIT;
689
+ var posX, posY, rectWidth, rectHeight;
658
690
  switch (viewport.viewType) {
659
691
  case 'top':
660
- // Top view: Looking down Z-axis, X-Y plane
661
- posX = pos3D.x;
662
- posY = pos3D.y;
663
-
664
- // Swap width and depth when rotated 90° or 270°
665
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
666
- rectWidth = worldDepth * scale;
667
- rectHeight = worldWidth * scale;
668
- } else {
669
- rectWidth = worldWidth * scale;
670
- rectHeight = worldDepth * scale;
671
- }
692
+ // Looking down Z-axis X/Y plane
693
+ posX = bboxCenter.x;
694
+ posY = bboxCenter.y;
695
+ rectWidth = worldWidth * scale;
696
+ rectHeight = worldDepth * scale;
672
697
  break;
673
698
  case 'front':
674
- // Front view: Looking along Y-axis, X-Z plane
675
- posX = pos3D.x;
676
- posY = pos3D.z + worldHeight / 2; // Offset Z by half height
677
-
678
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
679
- rectWidth = worldDepth * scale;
680
- } else {
681
- rectWidth = worldWidth * scale;
682
- }
699
+ // Looking along Y-axis X/Z plane
700
+ posX = bboxCenter.x;
701
+ posY = bboxCenter.z;
702
+ rectWidth = worldWidth * scale;
683
703
  rectHeight = worldHeight * scale;
684
704
  break;
685
705
  case 'side':
686
- // Side view: Looking along X-axis, Y-Z plane (flipped)
687
- posX = -pos3D.y; // Flipped
688
- posY = pos3D.z + worldHeight / 2;
689
- if (Math.abs(rotationDegrees) === 90 || Math.abs(rotationDegrees) === 270) {
690
- rectWidth = worldWidth * scale;
691
- } else {
692
- rectWidth = worldDepth * scale;
693
- }
706
+ // Looking along X-axis Y/Z plane (Y negated for left-right orientation)
707
+ posX = -bboxCenter.y;
708
+ posY = bboxCenter.z;
709
+ rectWidth = worldDepth * scale;
694
710
  rectHeight = worldHeight * scale;
695
711
  break;
696
712
  default:
697
- posX = pos3D.x;
698
- posY = pos3D.y;
713
+ posX = bboxCenter.x;
714
+ posY = bboxCenter.y;
699
715
  rectWidth = worldWidth * scale;
700
716
  rectHeight = worldDepth * scale;
701
717
  }
@@ -703,8 +719,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
703
719
  posX: posX,
704
720
  posY: posY,
705
721
  rectWidth: rectWidth,
706
- rectHeight: rectHeight,
707
- rotationDegrees: rotationDegrees
722
+ rectHeight: rectHeight
708
723
  };
709
724
  }
710
725
 
@@ -714,7 +729,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
714
729
  */
715
730
  }, {
716
731
  key: "addComponentInteractions",
717
- value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight) {
732
+ value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter) {
718
733
  var _this6 = this;
719
734
  if (!this.Konva) return;
720
735
  var colors = this.generateComponentColor(component.id);
@@ -797,9 +812,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
797
812
  // Convert screen to world coordinates
798
813
  var worldCoords = _this6.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
799
814
 
800
- // Calculate new position
815
+ // Calculate new position: delta from old bbox center to new bbox center
801
816
  var currentPos = component.position;
802
- var newPosition = _this6.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, worldWidth, worldDepth, worldHeight);
817
+ var newPosition = _this6.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
803
818
 
804
819
  // Apply translation via centralPlant API
805
820
  var deltaX = newPosition.x - currentPos.x;
@@ -902,37 +917,45 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
902
917
  }
903
918
 
904
919
  /**
905
- * Convert world coordinates to 3D object position
920
+ * Convert dragged 2D world coordinates to a new 3D object position.
921
+ * coord1/coord2 represent where the bbox CENTER should be in the view's 2D plane;
922
+ * we compute the delta from the old bbox center and apply it to the pivot position.
906
923
  * @param {Viewport2DInstance} viewport - The viewport instance
924
+ * @param {{coord1, coord2}} worldCoords - Projected world coordinates from screen
925
+ * @param {{x,y,z}} currentPosition - Current Three.js object position (pivot)
926
+ * @param {{x,y,z}} bboxCenter - Old world-space bbox center
907
927
  */
908
928
  }, {
909
929
  key: "worldCoordsToObjectPosition",
910
- value: function worldCoordsToObjectPosition(viewport, worldCoords, currentPosition, worldWidth, worldDepth, worldHeight) {
930
+ value: function worldCoordsToObjectPosition(viewport, worldCoords, currentPosition, bboxCenter) {
911
931
  var coord1 = worldCoords.coord1,
912
932
  coord2 = worldCoords.coord2;
913
933
  switch (viewport.viewType) {
914
934
  case 'top':
935
+ // coord1=X, coord2=Y in world space
915
936
  return {
916
- x: coord1,
917
- y: coord2,
937
+ x: currentPosition.x + (coord1 - bboxCenter.x),
938
+ y: currentPosition.y + (coord2 - bboxCenter.y),
918
939
  z: currentPosition.z
919
940
  };
920
941
  case 'front':
942
+ // coord1=X, coord2=Z in world space
921
943
  return {
922
- x: coord1,
944
+ x: currentPosition.x + (coord1 - bboxCenter.x),
923
945
  y: currentPosition.y,
924
- z: coord2 - worldHeight / 2
946
+ z: currentPosition.z + (coord2 - bboxCenter.z)
925
947
  };
926
948
  case 'side':
949
+ // coord1=-Y (negated), coord2=Z in world space
927
950
  return {
928
951
  x: currentPosition.x,
929
- y: -coord1,
930
- z: coord2 - worldHeight / 2
952
+ y: currentPosition.y + (-coord1 - bboxCenter.y),
953
+ z: currentPosition.z + (coord2 - bboxCenter.z)
931
954
  };
932
955
  default:
933
956
  return {
934
- x: coord1,
935
- y: coord2,
957
+ x: currentPosition.x + (coord1 - bboxCenter.x),
958
+ y: currentPosition.y + (coord2 - bboxCenter.y),
936
959
  z: currentPosition.z
937
960
  };
938
961
  }
@@ -950,8 +973,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
950
973
  var components = [];
951
974
  this.sceneViewer.scene.traverse(function (object) {
952
975
  var _object$userData, _object$userData2;
953
- 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);
954
- if (object.userData && objectType === 'component') {
976
+ // Only match the ROOT component object must have both objectType:'component'
977
+ // AND libraryId (inner GLB mesh nodes don't have libraryId)
978
+ 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) {
955
979
  components.push(object);
956
980
  }
957
981
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/src/index.js",