@2112-lab/central-plant 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/bundle/index.js +33146 -1
  2. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +432 -1
  3. package/dist/cjs/node_modules/@2112-lab/pathfinder/dist/index.esm.js +1448 -1
  4. package/dist/cjs/node_modules/three/examples/jsm/controls/OrbitControls.js +1853 -1
  5. package/dist/cjs/node_modules/three/examples/jsm/exporters/GLTFExporter.js +3537 -1
  6. package/dist/cjs/node_modules/three/examples/jsm/exporters/OBJExporter.js +305 -1
  7. package/dist/cjs/node_modules/three/examples/jsm/exporters/PLYExporter.js +542 -1
  8. package/dist/cjs/node_modules/three/examples/jsm/exporters/STLExporter.js +218 -1
  9. package/dist/cjs/node_modules/three/examples/jsm/loaders/DRACOLoader.js +683 -1
  10. package/dist/cjs/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4811 -1
  11. package/dist/cjs/node_modules/three/examples/jsm/loaders/RGBELoader.js +480 -1
  12. package/dist/cjs/node_modules/three/examples/jsm/renderers/CSS2DRenderer.js +309 -1
  13. package/dist/cjs/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +120 -1
  14. package/dist/cjs/src/analysis/analysis.js +560 -1
  15. package/dist/cjs/src/analysis/testing.js +958 -1
  16. package/dist/cjs/src/core/centralPlant.js +1149 -1
  17. package/dist/cjs/src/core/debugLogger.js +175 -1
  18. package/dist/cjs/src/core/mathUtils.js +574 -1
  19. package/dist/cjs/src/core/nameUtils.js +93 -1
  20. package/dist/cjs/src/data/export.js +716 -1
  21. package/dist/cjs/src/data/import.js +380 -1
  22. package/dist/cjs/src/data/numerics.js +522 -1
  23. package/dist/cjs/src/helpers/sceneHelper.js +572 -1
  24. package/dist/cjs/src/index.js +69 -1
  25. package/dist/cjs/src/managers/components/animationManager.js +123 -1
  26. package/dist/cjs/src/managers/components/componentManager.js +332 -1
  27. package/dist/cjs/src/managers/components/pathfindingManager.js +1441 -1
  28. package/dist/cjs/src/managers/controls/TransformControls.js +1063 -1
  29. package/dist/cjs/src/managers/controls/cameraControlsManager.js +79 -1
  30. package/dist/cjs/src/managers/controls/dragDropManager.js +1026 -1
  31. package/dist/cjs/src/managers/controls/keyboardControlsManager.js +395 -1
  32. package/dist/cjs/src/managers/controls/transformControlsManager.js +1807 -1
  33. package/dist/cjs/src/managers/environment/environmentManager.js +714 -1
  34. package/dist/cjs/src/managers/environment/textureConfig.js +229 -1
  35. package/dist/cjs/src/managers/scene/sceneExportManager.js +264 -1
  36. package/dist/cjs/src/managers/scene/sceneInitializationManager.js +346 -1
  37. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +1509 -1
  38. package/dist/cjs/src/managers/scene/sceneTooltipsManager.js +661 -1
  39. package/dist/cjs/src/managers/system/disposalManager.js +444 -1
  40. package/dist/cjs/src/managers/system/hotReloadManager.js +291 -1
  41. package/dist/cjs/src/managers/system/performanceMonitor.js +863 -1
  42. package/dist/cjs/src/rendering/modelPreloader.js +369 -1
  43. package/dist/cjs/src/rendering/rendering2D.js +631 -1
  44. package/dist/cjs/src/rendering/rendering3D.js +685 -1
  45. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +396 -1
  46. package/dist/esm/node_modules/@2112-lab/pathfinder/dist/index.esm.js +1444 -1
  47. package/dist/esm/node_modules/three/examples/jsm/controls/OrbitControls.js +1849 -1
  48. package/dist/esm/node_modules/three/examples/jsm/exporters/GLTFExporter.js +3533 -1
  49. package/dist/esm/node_modules/three/examples/jsm/exporters/OBJExporter.js +301 -1
  50. package/dist/esm/node_modules/three/examples/jsm/exporters/PLYExporter.js +538 -1
  51. package/dist/esm/node_modules/three/examples/jsm/exporters/STLExporter.js +214 -1
  52. package/dist/esm/node_modules/three/examples/jsm/loaders/DRACOLoader.js +679 -1
  53. package/dist/esm/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4807 -1
  54. package/dist/esm/node_modules/three/examples/jsm/loaders/RGBELoader.js +476 -1
  55. package/dist/esm/node_modules/three/examples/jsm/renderers/CSS2DRenderer.js +304 -1
  56. package/dist/esm/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +116 -1
  57. package/dist/esm/src/analysis/analysis.js +536 -1
  58. package/dist/esm/src/analysis/testing.js +954 -1
  59. package/dist/esm/src/core/centralPlant.js +1144 -1
  60. package/dist/esm/src/core/debugLogger.js +167 -1
  61. package/dist/esm/src/core/mathUtils.js +570 -1
  62. package/dist/esm/src/core/nameUtils.js +87 -1
  63. package/dist/esm/src/data/export.js +712 -1
  64. package/dist/esm/src/data/import.js +356 -1
  65. package/dist/esm/src/data/numerics.js +518 -1
  66. package/dist/esm/src/helpers/sceneHelper.js +547 -1
  67. package/dist/esm/src/index.js +35 -1
  68. package/dist/esm/src/managers/components/animationManager.js +119 -1
  69. package/dist/esm/src/managers/components/componentManager.js +328 -1
  70. package/dist/esm/src/managers/components/pathfindingManager.js +1417 -1
  71. package/dist/esm/src/managers/controls/TransformControls.js +1057 -1
  72. package/dist/esm/src/managers/controls/cameraControlsManager.js +75 -1
  73. package/dist/esm/src/managers/controls/dragDropManager.js +1002 -1
  74. package/dist/esm/src/managers/controls/keyboardControlsManager.js +371 -1
  75. package/dist/esm/src/managers/controls/transformControlsManager.js +1782 -1
  76. package/dist/esm/src/managers/environment/environmentManager.js +690 -1
  77. package/dist/esm/src/managers/environment/textureConfig.js +202 -1
  78. package/dist/esm/src/managers/scene/sceneExportManager.js +260 -1
  79. package/dist/esm/src/managers/scene/sceneInitializationManager.js +322 -1
  80. package/dist/esm/src/managers/scene/sceneOperationsManager.js +1485 -1
  81. package/dist/esm/src/managers/scene/sceneTooltipsManager.js +637 -1
  82. package/dist/esm/src/managers/system/disposalManager.js +440 -1
  83. package/dist/esm/src/managers/system/hotReloadManager.js +287 -1
  84. package/dist/esm/src/managers/system/performanceMonitor.js +858 -1
  85. package/dist/esm/src/rendering/modelPreloader.js +364 -1
  86. package/dist/esm/src/rendering/rendering2D.js +627 -1
  87. package/dist/esm/src/rendering/rendering3D.js +661 -1
  88. package/package.json +1 -1
@@ -1 +1,637 @@
1
- import{createClass as t,classCallCheck as i}from"../../../_virtual/_rollupPluginBabelHelpers.js";import{CSS2DRenderer as n,CSS2DObject as e}from"../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js";import*as o from"three";var s=function(t){for(var i,n=arguments.length,e=new Array(n>1?n-1:0),o=1;o<n;o++)e[o-1]=arguments[o];return(i=console).log.apply(i,["🔍 [Tooltips] ".concat(t)].concat(e))},r=function(t){for(var i,n=arguments.length,e=new Array(n>1?n-1:0),o=1;o<n;o++)e[o-1]=arguments[o];return(i=console).log.apply(i,["ℹ️ [Tooltips] ".concat(t)].concat(e))},a=function(t){for(var i,n=arguments.length,e=new Array(n>1?n-1:0),o=1;o<n;o++)e[o-1]=arguments[o];return(i=console).warn.apply(i,["⚠️ [Tooltips] ".concat(t)].concat(e))},l=function(t){for(var i,n=arguments.length,e=new Array(n>1?n-1:0),o=1;o<n;o++)e[o-1]=arguments[o];return(i=console).error.apply(i,["❌ [Tooltips] ".concat(t)].concat(e))},h=function(){return t(function t(n,e,o){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;i(this,t),this.container=n,this.camera=e,this.scene=o,this.transformControlsManager=s,this.tooltipTemplate='\n<div class="component-tooltip" style="\n background-color: rgba(255, 255, 255, 0.95); \n color: #333333; \n border-radius: 8px; \n padding: 12px; \n width: 250px; \n font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n border: 1px solid rgba(0, 0, 0, 0.1);\n user-select: none;\n pointer-events: auto;\n font-size: 13px;\n line-height: 1.4;\n transform: translate(-50%, -100%);\n margin-top: 0px;\n margin-left: 0px;\n position: relative;\n z-index: 1000;\n">\n <div class="tooltip-header" style="\n font-weight: 600; \n padding-bottom: 8px; \n border-bottom: 1px solid rgba(0, 0, 0, 0.1); \n margin-bottom: 8px;\n font-size: 14px;\n color: #2c3e50;\n ">Component Name</div>\n <div class="tooltip-body">\n <div class="tooltip-type" style="\n font-size: 12px;\n color: #666;\n margin-bottom: 8px;\n font-style: italic;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n "></div>\n <div class="tooltip-attributes">\n \x3c!-- Dynamic attributes will be inserted here --\x3e\n </div>\n <div class="tooltip-row tooltip-actions" style="display: flex; align-items: center; justify-content: space-between; margin-top: 12px; padding-top: 8px; border-top: 1px solid rgba(0, 0, 0, 0.1);">\n <div class="tooltip-key" style="flex: 1; color: #555555; font-size: 12px;">Add:</div>\n <div class="tooltip-dropdown-container" style="flex: 1; text-align: center;">\n <select class="tooltip-dropdown" style="\n background-color: #f8f9fa;\n color: #333333;\n border: 1px solid #ddd;\n padding: 4px 8px;\n border-radius: 4px;\n width: 100%;\n cursor: pointer;\n pointer-events: auto;\n font-size: 12px;\n ">\n <option value="" disabled selected>Select...</option>\n <option value="gateway">Gateway</option>\n <option value="connector">Connector</option>\n </select>\n </div>\n </div>\n </div>\n</div>\n',this.tooltipRenderer=null,this.selectedMesh=null,this.tooltipParent=null,this.tooltipObject=null,this.init()},[{key:"init",value:function(){this.tooltipRenderer=new n,this.tooltipRenderer.setSize(this.container.clientWidth,this.container.clientHeight),this.tooltipRenderer.domElement.style.position="absolute",this.tooltipRenderer.domElement.style.top="0",this.tooltipRenderer.domElement.style.left="0",this.tooltipRenderer.domElement.style.overflow="visible",this.tooltipRenderer.domElement.style.pointerEvents="none",this.tooltipRenderer.domElement.style.zIndex="100",this.container.appendChild(this.tooltipRenderer.domElement)}},{key:"resize",value:function(){this.tooltipRenderer&&this.tooltipRenderer.setSize(this.container.clientWidth,this.container.clientHeight)}},{key:"setTransformControlsManager",value:function(t){this.transformControlsManager=t,r("Transform controls manager set for tooltips"),this.transformControlsManager&&this.setupTransformControlsEvents(),this.tooltipObject&&this.updateTooltipForTransform()}},{key:"setupTransformControlsEvents",value:function(){var t=this;this.transformControlsManager&&this.transformControlsManager.on({onTransform:function(i){i===t.selectedMesh&&t.tooltipObject&&t.updateTooltipForTransform()},onTransformEnd:function(i){i===t.selectedMesh&&t.tooltipObject&&t.updateTooltipForTransform()}})}},{key:"setSelectedMesh",value:function(){var t,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.selectedMesh!==i&&(r("Setting selected mesh:",i?i.uuid:"none"),this.clearTooltip(),this.selectedMesh=i,i&&null!==(t=i.userData)&&void 0!==t&&null!==(t=t.component)&&void 0!==t&&t.attributes?(r("Mesh has component attributes, showing tooltip"),this.showTooltip()):i&&(a("Mesh does not have required component data for tooltip:",i),i.userData&&s("Available userData:",i.userData)))}},{key:"render",value:function(){if(this.tooltipRenderer&&this.camera&&this.scene)try{this.tooltipRenderer.render(this.scene,this.camera)}catch(t){l("Error rendering tooltips:",t)}}},{key:"dispose",value:function(){this.clearTooltip(),this.transformControlsManager&&(this.transformControlsManager=null),this.tooltipRenderer&&this.tooltipRenderer.domElement&&this.tooltipRenderer.domElement.parentNode&&this.tooltipRenderer.domElement.parentNode.removeChild(this.tooltipRenderer.domElement),this.tooltipRenderer=null,this.selectedMesh=null,this.container=null,this.camera=null,this.scene=null}},{key:"showTooltip",value:function(){try{if(!this.selectedMesh||!this.selectedMesh.userData||!this.selectedMesh.userData.component)return void a("Cannot show tooltip, missing data in selectedMesh");r("Showing tooltip for:",this.selectedMesh.uuid);var t=this.selectedMesh.userData.component;if(!t.attributes)return void a("Component has no attributes:",t);var i=t.attributes,n=document.createElement("div");n.innerHTML=this.tooltipTemplate.trim();var o=n.firstElementChild||n.firstChild;s("showTooltip this.selectedMesh:",this.selectedMesh),s("showTooltip meshComponentData:",t);var h=o.querySelector(".tooltip-header");h&&(this.selectedMesh.name&&""!==this.selectedMesh.name?h.textContent=this.selectedMesh.name:h.textContent=this.selectedMesh.userData.originalUuid||this.selectedMesh.userData.uuid||"Unknown Component");var c=o.querySelector(".tooltip-type");c&&t.type&&(c.textContent=t.type);var u=o.querySelector(".tooltip-attributes");if(u&&i&&Object.keys(i).length>0){u.innerHTML="",Object.keys(i).forEach(function(t){var n=i[t],e=document.createElement("div");e.className="tooltip-row",e.style.display="flex",e.style.alignItems="center",e.style.justifyContent="space-between",e.style.marginBottom="5px";var o=document.createElement("div");o.className="tooltip-key",o.style.flex="1",o.style.color="#555555",o.textContent="".concat(n.key||t,":");var s=document.createElement("div");s.className="tooltip-value",s.style.flex="1",s.style.textAlign="center",s.style.color="#000000";var r=n.value||"0";n.unit&&(r+=" ".concat(n.unit)),s.textContent=r,e.appendChild(o),e.appendChild(s),u.appendChild(e)});var p=o.querySelector(".tooltip-dropdown");p&&p.addEventListener("change",function(t){t.stopPropagation();var i=t.target.value;r("Add option selected: ".concat(i)),"gateway"===i?r("Adding Gateway"):"connector"===i&&r("Adding Connector"),t.target.selectedIndex=0})}var d=new e(o);this.tooltipParent=this.selectedMesh,this.tooltipObject=d;var v=this.calculateTooltipPosition();d.position.set(v.x,v.y,v.z),d.userData={isTooltip:!0},this.selectedMesh.add(d),r("Tooltip positioned at:",v)}catch(t){l("Error showing tooltip:",t)}}},{key:"updateTooltip",value:function(){if(this.tooltipParent&&this.tooltipObject){var t=this.calculateTooltipPosition();this.tooltipObject.position.set(t.x,t.y,t.z)}}},{key:"updateTooltipForTransform",value:function(){if(this.tooltipObject&&this.selectedMesh){var t=this.calculateTooltipPosition();this.tooltipObject.position.set(t.x,t.y,t.z),s("Tooltip position updated for transform")}}},{key:"getObjectBoundingBox",value:function(t){return(new o.Box3).setFromObject(t)}},{key:"calculateOptimalTooltipOffset",value:function(){if(!this.camera||!this.selectedMesh)return 1.5;var t=new o.Vector3;this.selectedMesh.getWorldPosition(t);var i=new o.Vector3;this.camera.getWorldPosition(i);return 1}},{key:"calculateTooltipPosition",value:function(){var t=this.getObjectBoundingBox(this.selectedMesh),i=this.findClosestBoundingBoxCorner(t),n=new o.Vector3;this.selectedMesh.getWorldPosition(n);var e=i.clone().sub(n),s=this.calculatePixelBasedOffset(e,n),l={x:e.x+s.x,y:e.y+s.y,z:e.z+s.z};if(this.transformControlsManager&&this.transformControlsManager.selectedObject===this.selectedMesh)try{var h=this.transformControlsManager.getWorldTransformData();if(h&&h.position)return r("Using transform controls position for tooltip at closest corner"),{x:e.x+s.x,y:e.y+s.y,z:e.z+s.z}}catch(t){a("Error getting transform controls position:",t)}return r("Positioning tooltip at closest corner to origin:",l),l}},{key:"calculatePixelBasedOffset",value:function(t,i){if(!this.camera)return{x:0,y:0,z:0};var n=250;if(this.tooltipObject&&this.tooltipObject.element){var e=this.tooltipObject.element;if(e.offsetWidth>0)n=e.offsetWidth;else{var r=document.createElement("div");r.style.position="absolute",r.style.visibility="hidden",r.style.top="-9999px",document.body.appendChild(r),r.appendChild(e),n=e.offsetWidth||250,document.body.removeChild(r)}}var a=n/2,l=i.clone().add(t),h=new o.Vector3;this.camera.getWorldPosition(h);var c=new o.Vector3;this.camera.getWorldDirection(c);var u=this.isPositionOnRightSideOfCameraMidline(l,h,c),p=l.distanceTo(h),d=0;if(this.camera.isPerspectiveCamera){var v=this.camera.fov*Math.PI/180,f=Math.tan(v/2)*p*this.camera.aspect;d=a/this.container.clientWidth*(2*f)}else if(this.camera.isOrthographicCamera){var m=(this.camera.right-this.camera.left)/2;d=a/this.container.clientWidth*(2*m)}var y=new o.Vector3;this.camera.getWorldDirection(y),y.cross(this.camera.up).normalize();var w=u?-1:1,g=y.multiplyScalar(d*w);return s("Tooltip pixel offset calculated:",{tooltipWidth:n,halfTooltipWidth:a,distanceToCamera:p,screenSpaceOffset:d,isOnRightSide:u,offsetDirection:w,worldOffset:g}),{x:g.x,y:0,z:g.z}}},{key:"findClosestBoundingBoxCorner",value:function(t){var i=t.min,n=t.max,e=[new o.Vector3(i.x,i.y,i.z),new o.Vector3(n.x,i.y,i.z),new o.Vector3(i.x,n.y,i.z),new o.Vector3(n.x,n.y,i.z),new o.Vector3(i.x,i.y,n.z),new o.Vector3(n.x,i.y,n.z),new o.Vector3(i.x,n.y,n.z),new o.Vector3(n.x,n.y,n.z)];return this.findCornerClosestToCameraVerticalMidline(e)}},{key:"findCornerClosestToCameraVerticalMidline",value:function(t){if(!this.camera)return a("No camera available, using first corner"),t[0];var i=new o.Vector3;this.camera.getWorldPosition(i);var n=new o.Vector3;this.camera.getWorldDirection(n);for(var e=t[0],r=this.getDistanceToVerticalMidline(t[0],i,n),l=1;l<t.length;l++){var h=this.getDistanceToVerticalMidline(t[l],i,n);h<r&&(r=h,e=t[l])}return s("Closest corner to camera vertical midline:",e,"Distance:",r),e}},{key:"getDistanceToVerticalMidline",value:function(t,i,n){var e=t.clone().sub(i),o=e.dot(n),s=n.clone().multiplyScalar(o);return e.clone().sub(s).length()}},{key:"clearTooltip",value:function(){this.tooltipParent&&this.tooltipObject&&(this.tooltipParent.remove(this.tooltipObject),this.tooltipParent=null,this.tooltipObject=null)}},{key:"handleSceneClick",value:function(t,i){this.clearTooltip(),this.selectedMesh=null}},{key:"isSelectableComponent",value:function(t){var i,n;return!!(t&&t.userData&&t.userData.component)||!(!t||!(null!==(i=t.userData)&&void 0!==i&&i.isPipeSegment||null!==(n=t.userData)&&void 0!==n&&n.isPipeJunction))}},{key:"isPositionOnRightSideOfCameraMidline",value:function(t,i,n){var e=new o.Vector3;return this.camera.getWorldDirection(e),e.cross(this.camera.up).normalize(),t.clone().sub(i).dot(e)>0}}])}();export{h as SceneTooltipsManager};
1
+ import { createClass as _createClass, classCallCheck as _classCallCheck } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { CSS2DRenderer, CSS2DObject } from '../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js';
3
+ import * as THREE from 'three';
4
+
5
+ // Add a simple debug logger for tooltip events
6
+ var tooltipLogger = {
7
+ debug: function debug(message) {
8
+ var _console;
9
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
10
+ args[_key - 1] = arguments[_key];
11
+ }
12
+ return (_console = console).log.apply(_console, ["\uD83D\uDD0D [Tooltips] ".concat(message)].concat(args));
13
+ },
14
+ info: function info(message) {
15
+ var _console2;
16
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
17
+ args[_key2 - 1] = arguments[_key2];
18
+ }
19
+ return (_console2 = console).log.apply(_console2, ["\u2139\uFE0F [Tooltips] ".concat(message)].concat(args));
20
+ },
21
+ warn: function warn(message) {
22
+ var _console3;
23
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
24
+ args[_key3 - 1] = arguments[_key3];
25
+ }
26
+ return (_console3 = console).warn.apply(_console3, ["\u26A0\uFE0F [Tooltips] ".concat(message)].concat(args));
27
+ },
28
+ error: function error(message) {
29
+ var _console4;
30
+ for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
31
+ args[_key4 - 1] = arguments[_key4];
32
+ }
33
+ return (_console4 = console).error.apply(_console4, ["\u274C [Tooltips] ".concat(message)].concat(args));
34
+ }
35
+ };
36
+
37
+ // Inline tooltip template instead of importing from a file
38
+ var tooltipTemplate = "\n<div class=\"component-tooltip\" style=\"\n background-color: rgba(255, 255, 255, 0.95); \n color: #333333; \n border-radius: 8px; \n padding: 12px; \n width: 250px; \n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n border: 1px solid rgba(0, 0, 0, 0.1);\n user-select: none;\n pointer-events: auto;\n font-size: 13px;\n line-height: 1.4;\n transform: translate(-50%, -100%);\n margin-top: 0px;\n margin-left: 0px;\n position: relative;\n z-index: 1000;\n\">\n <div class=\"tooltip-header\" style=\"\n font-weight: 600; \n padding-bottom: 8px; \n border-bottom: 1px solid rgba(0, 0, 0, 0.1); \n margin-bottom: 8px;\n font-size: 14px;\n color: #2c3e50;\n \">Component Name</div>\n <div class=\"tooltip-body\">\n <div class=\"tooltip-type\" style=\"\n font-size: 12px;\n color: #666;\n margin-bottom: 8px;\n font-style: italic;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n \"></div>\n <div class=\"tooltip-attributes\">\n <!-- Dynamic attributes will be inserted here -->\n </div>\n <div class=\"tooltip-row tooltip-actions\" style=\"display: flex; align-items: center; justify-content: space-between; margin-top: 12px; padding-top: 8px; border-top: 1px solid rgba(0, 0, 0, 0.1);\">\n <div class=\"tooltip-key\" style=\"flex: 1; color: #555555; font-size: 12px;\">Add:</div>\n <div class=\"tooltip-dropdown-container\" style=\"flex: 1; text-align: center;\">\n <select class=\"tooltip-dropdown\" style=\"\n background-color: #f8f9fa;\n color: #333333;\n border: 1px solid #ddd;\n padding: 4px 8px;\n border-radius: 4px;\n width: 100%;\n cursor: pointer;\n pointer-events: auto;\n font-size: 12px;\n \">\n <option value=\"\" disabled selected>Select...</option>\n <option value=\"gateway\">Gateway</option>\n <option value=\"connector\">Connector</option>\n </select>\n </div>\n </div>\n </div>\n</div>\n";
39
+ var SceneTooltipsManager = /*#__PURE__*/function () {
40
+ function SceneTooltipsManager(container, camera, scene) {
41
+ var transformControlsManager = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
42
+ _classCallCheck(this, SceneTooltipsManager);
43
+ this.container = container;
44
+ this.camera = camera;
45
+ this.scene = scene;
46
+ this.transformControlsManager = transformControlsManager;
47
+ this.tooltipTemplate = tooltipTemplate;
48
+ this.tooltipRenderer = null;
49
+ this.selectedMesh = null;
50
+ this.tooltipParent = null;
51
+ this.tooltipObject = null;
52
+ this.init();
53
+ }
54
+ return _createClass(SceneTooltipsManager, [{
55
+ key: "init",
56
+ value: function init() {
57
+ // Create the CSS2D renderer for the tooltips
58
+ this.tooltipRenderer = new CSS2DRenderer();
59
+ this.tooltipRenderer.setSize(this.container.clientWidth, this.container.clientHeight);
60
+ this.tooltipRenderer.domElement.style.position = 'absolute';
61
+ this.tooltipRenderer.domElement.style.top = '0';
62
+ this.tooltipRenderer.domElement.style.left = '0';
63
+ this.tooltipRenderer.domElement.style.overflow = 'visible';
64
+ // Use 'none' for the renderer to allow camera controls to work
65
+ this.tooltipRenderer.domElement.style.pointerEvents = 'none';
66
+ this.tooltipRenderer.domElement.style.zIndex = '100'; // Ensure tooltips appear on top
67
+ this.container.appendChild(this.tooltipRenderer.domElement);
68
+ }
69
+ }, {
70
+ key: "resize",
71
+ value: function resize() {
72
+ if (this.tooltipRenderer) {
73
+ this.tooltipRenderer.setSize(this.container.clientWidth, this.container.clientHeight);
74
+ }
75
+ }
76
+ }, {
77
+ key: "setTransformControlsManager",
78
+ value: function setTransformControlsManager(transformControlsManager) {
79
+ this.transformControlsManager = transformControlsManager;
80
+ tooltipLogger.info('Transform controls manager set for tooltips');
81
+
82
+ // Set up event listeners for transform controls
83
+ if (this.transformControlsManager) {
84
+ this.setupTransformControlsEvents();
85
+ }
86
+
87
+ // Update existing tooltip position if one is shown
88
+ if (this.tooltipObject) {
89
+ this.updateTooltipForTransform();
90
+ }
91
+ }
92
+ }, {
93
+ key: "setupTransformControlsEvents",
94
+ value: function setupTransformControlsEvents() {
95
+ var _this = this;
96
+ if (!this.transformControlsManager) return;
97
+
98
+ // Listen for transform events to update tooltip position
99
+ this.transformControlsManager.on({
100
+ onTransform: function onTransform(object) {
101
+ // Update tooltip position during transformation
102
+ if (object === _this.selectedMesh && _this.tooltipObject) {
103
+ _this.updateTooltipForTransform();
104
+ }
105
+ },
106
+ onTransformEnd: function onTransformEnd(object) {
107
+ // Final position update after transformation
108
+ if (object === _this.selectedMesh && _this.tooltipObject) {
109
+ _this.updateTooltipForTransform();
110
+ }
111
+ }
112
+ });
113
+ }
114
+ }, {
115
+ key: "setSelectedMesh",
116
+ value: function setSelectedMesh() {
117
+ var _mesh$userData;
118
+ var mesh = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
119
+ if (this.selectedMesh === mesh) return;
120
+ tooltipLogger.info('Setting selected mesh:', mesh ? mesh.uuid : 'none');
121
+ this.clearTooltip();
122
+ this.selectedMesh = mesh;
123
+ if (mesh && (_mesh$userData = mesh.userData) !== null && _mesh$userData !== void 0 && (_mesh$userData = _mesh$userData.component) !== null && _mesh$userData !== void 0 && _mesh$userData.attributes) {
124
+ tooltipLogger.info('Mesh has component attributes, showing tooltip');
125
+ this.showTooltip();
126
+ } else if (mesh) {
127
+ tooltipLogger.warn('Mesh does not have required component data for tooltip:', mesh);
128
+ if (mesh.userData) {
129
+ tooltipLogger.debug('Available userData:', mesh.userData);
130
+ }
131
+ }
132
+ }
133
+ }, {
134
+ key: "render",
135
+ value: function render() {
136
+ if (this.tooltipRenderer && this.camera && this.scene) {
137
+ try {
138
+ this.tooltipRenderer.render(this.scene, this.camera);
139
+ } catch (error) {
140
+ tooltipLogger.error('Error rendering tooltips:', error);
141
+ }
142
+ }
143
+ }
144
+
145
+ // This method should be called when disposing the manager
146
+ }, {
147
+ key: "dispose",
148
+ value: function dispose() {
149
+ this.clearTooltip();
150
+
151
+ // Clean up transform controls event listeners
152
+ if (this.transformControlsManager) {
153
+ // Note: The transform controls manager doesn't have a removeEventListener method
154
+ // so we'll just remove our reference
155
+ this.transformControlsManager = null;
156
+ }
157
+ if (this.tooltipRenderer && this.tooltipRenderer.domElement && this.tooltipRenderer.domElement.parentNode) {
158
+ this.tooltipRenderer.domElement.parentNode.removeChild(this.tooltipRenderer.domElement);
159
+ }
160
+ this.tooltipRenderer = null;
161
+ this.selectedMesh = null;
162
+ this.container = null;
163
+ this.camera = null;
164
+ this.scene = null;
165
+ }
166
+ }, {
167
+ key: "showTooltip",
168
+ value: function showTooltip() {
169
+ try {
170
+ if (!this.selectedMesh || !this.selectedMesh.userData || !this.selectedMesh.userData.component) {
171
+ tooltipLogger.warn('Cannot show tooltip, missing data in selectedMesh');
172
+ return;
173
+ }
174
+ console.log("showTooltip this.selectedMesh:", this.selectedMesh);
175
+ tooltipLogger.info('Showing tooltip for:', this.selectedMesh.uuid);
176
+ var meshComponentData = this.selectedMesh.userData.component;
177
+ if (!meshComponentData.attributes) {
178
+ tooltipLogger.warn('Component has no attributes:', meshComponentData);
179
+ return;
180
+ }
181
+ var meshAttributes = meshComponentData.attributes;
182
+
183
+ // Clone the loaded template
184
+ var tooltipDiv = document.createElement('div');
185
+ tooltipDiv.innerHTML = this.tooltipTemplate.trim(); // Use the template string
186
+ var tooltipElement = tooltipDiv.firstElementChild || tooltipDiv.firstChild;
187
+ tooltipLogger.debug("showTooltip this.selectedMesh:", this.selectedMesh);
188
+ tooltipLogger.debug("showTooltip meshComponentData:", meshComponentData);
189
+
190
+ // Update the tooltip header with object name or ID
191
+ var headerElement = tooltipElement.querySelector('.tooltip-header');
192
+ if (headerElement) {
193
+ // Use a more user-friendly display name
194
+ if (this.selectedMesh.name && this.selectedMesh.name !== "") {
195
+ headerElement.textContent = this.selectedMesh.name;
196
+ } else {
197
+ headerElement.textContent = this.selectedMesh.userData.originalUuid || this.selectedMesh.userData.uuid || "Unknown Component";
198
+ }
199
+ }
200
+
201
+ // Add component type if available
202
+ var typeElement = tooltipElement.querySelector('.tooltip-type');
203
+ if (typeElement && meshComponentData.type) {
204
+ typeElement.textContent = meshComponentData.type;
205
+ }
206
+
207
+ // Populate all attributes
208
+ var attributesContainer = tooltipElement.querySelector('.tooltip-attributes');
209
+ if (attributesContainer && meshAttributes && Object.keys(meshAttributes).length > 0) {
210
+ // First clear any existing content
211
+ attributesContainer.innerHTML = '';
212
+
213
+ // Add each attribute as a row
214
+ var attrKeys = Object.keys(meshAttributes);
215
+ attrKeys.forEach(function (key) {
216
+ var attr = meshAttributes[key];
217
+
218
+ // Create attribute row
219
+ var rowDiv = document.createElement('div');
220
+ rowDiv.className = 'tooltip-row';
221
+ rowDiv.style.display = 'flex';
222
+ rowDiv.style.alignItems = 'center';
223
+ rowDiv.style.justifyContent = 'space-between';
224
+ rowDiv.style.marginBottom = '5px';
225
+
226
+ // Create key div
227
+ var keyDiv = document.createElement('div');
228
+ keyDiv.className = 'tooltip-key';
229
+ keyDiv.style.flex = '1';
230
+ keyDiv.style.color = '#555555';
231
+ keyDiv.textContent = "".concat(attr.key || key, ":");
232
+
233
+ // Create value div
234
+ var valueDiv = document.createElement('div');
235
+ valueDiv.className = 'tooltip-value';
236
+ valueDiv.style.flex = '1';
237
+ valueDiv.style.textAlign = 'center';
238
+ valueDiv.style.color = '#000000';
239
+
240
+ // Format the value with unit if available
241
+ var displayValue = attr.value || '0';
242
+ if (attr.unit) {
243
+ displayValue += " ".concat(attr.unit);
244
+ }
245
+ valueDiv.textContent = displayValue;
246
+
247
+ // Append to row
248
+ rowDiv.appendChild(keyDiv);
249
+ rowDiv.appendChild(valueDiv);
250
+
251
+ // Append row to container
252
+ attributesContainer.appendChild(rowDiv);
253
+ });
254
+
255
+ // Set up dropdown event handler
256
+ var dropdown = tooltipElement.querySelector('.tooltip-dropdown');
257
+ if (dropdown) {
258
+ dropdown.addEventListener('change', function (event) {
259
+ event.stopPropagation(); // Prevent event from bubbling up to the scene
260
+ var selectedValue = event.target.value;
261
+ tooltipLogger.info("Add option selected: ".concat(selectedValue));
262
+
263
+ // Handle the dropdown selection (add Gateway or Connector)
264
+ if (selectedValue === 'gateway') {
265
+ tooltipLogger.info('Adding Gateway');
266
+ // Add your gateway creation logic here
267
+ } else if (selectedValue === 'connector') {
268
+ tooltipLogger.info('Adding Connector');
269
+ // Add your connector creation logic here
270
+ }
271
+
272
+ // Reset dropdown to default "Select..." option after use
273
+ event.target.selectedIndex = 0;
274
+ });
275
+ }
276
+ }
277
+
278
+ // Add the tooltip as a CSS2DObject with proper positioning
279
+ var label = new CSS2DObject(tooltipElement);
280
+
281
+ // Store references first so calculatePixelBasedOffset can access them
282
+ this.tooltipParent = this.selectedMesh;
283
+ this.tooltipObject = label;
284
+
285
+ // Calculate tooltip position with pixel-based offset (now that tooltip object exists)
286
+ var tooltipPosition = this.calculateTooltipPosition();
287
+
288
+ // Set the position
289
+ label.position.set(tooltipPosition.x, tooltipPosition.y, tooltipPosition.z);
290
+
291
+ // Ensure the CSS2DObject renders properly
292
+ label.userData = {
293
+ isTooltip: true
294
+ };
295
+
296
+ // For CSS2DRenderer, we want the tooltip to be positioned correctly
297
+ // The CSS transform in the template will center it horizontally and position it above the anchor point
298
+
299
+ // Add the label to the mesh (this makes it follow the mesh's transformations)
300
+ this.selectedMesh.add(label);
301
+ tooltipLogger.info('Tooltip positioned at:', tooltipPosition);
302
+ } catch (error) {
303
+ tooltipLogger.error('Error showing tooltip:', error);
304
+ }
305
+ }
306
+ }, {
307
+ key: "updateTooltip",
308
+ value: function updateTooltip() {
309
+ if (this.tooltipParent && this.tooltipObject) {
310
+ // Update position when object is transformed
311
+ var newPosition = this.calculateTooltipPosition();
312
+ this.tooltipObject.position.set(newPosition.x, newPosition.y, newPosition.z);
313
+ }
314
+ }
315
+ }, {
316
+ key: "updateTooltipForTransform",
317
+ value: function updateTooltipForTransform() {
318
+ // This method is called when transform controls move the object
319
+ if (this.tooltipObject && this.selectedMesh) {
320
+ var newPosition = this.calculateTooltipPosition();
321
+ this.tooltipObject.position.set(newPosition.x, newPosition.y, newPosition.z);
322
+ tooltipLogger.debug('Tooltip position updated for transform');
323
+ }
324
+ }
325
+ }, {
326
+ key: "getObjectBoundingBox",
327
+ value: function getObjectBoundingBox(object) {
328
+ // Calculate bounding box for the object
329
+ var box = new THREE.Box3().setFromObject(object);
330
+ return box;
331
+ }
332
+ }, {
333
+ key: "calculateOptimalTooltipOffset",
334
+ value: function calculateOptimalTooltipOffset() {
335
+ // Calculate offset based on camera distance for consistent visual appearance
336
+ if (!this.camera || !this.selectedMesh) {
337
+ return 1.5; // Default offset
338
+ }
339
+ var meshWorldPosition = new THREE.Vector3();
340
+ this.selectedMesh.getWorldPosition(meshWorldPosition);
341
+ var cameraWorldPosition = new THREE.Vector3();
342
+ this.camera.getWorldPosition(cameraWorldPosition);
343
+ var baseOffset = 1.0;
344
+ return baseOffset;
345
+ }
346
+
347
+ /**
348
+ * Calculates the optimal position for tooltip placement
349
+ *
350
+ * This method determines tooltip positioning through several steps:
351
+ * 1. Finds the closest corner of the mesh's bounding box to the camera's vertical midline
352
+ * 2. Converts this corner from world space to local space relative to the mesh
353
+ * 3. Applies a pixel-based horizontal offset to prevent tooltip from overlapping the mesh
354
+ * 4. The offset direction depends on which side of camera midline the tooltip is on
355
+ * 5. If transform controls are active, considers their position for more accurate placement
356
+ *
357
+ * The positioning algorithm aims to:
358
+ * - Keep tooltips visible and readable
359
+ * - Prevent obstruction of the mesh being described
360
+ * - Maintain consistent visual appearance across different camera distances
361
+ * - Adapt to camera perspective and orientation changes
362
+ *
363
+ * Known Issues:
364
+ * - Sometimes tooltips still obscure the mesh despite positioning logic
365
+ * - The root cause of this obstruction is currently unknown and needs investigation (this issue on hold)
366
+ * - May be related to CSS transform calculations or screen-space projection accuracy
367
+ *
368
+ * @returns {Object} Position coordinates {x, y, z} in local space relative to the mesh
369
+ */
370
+ }, {
371
+ key: "calculateTooltipPosition",
372
+ value: function calculateTooltipPosition() {
373
+ // Get bounding box of the selected mesh for better positioning
374
+ var boundingBox = this.getObjectBoundingBox(this.selectedMesh);
375
+
376
+ // Find the closest corner of the bounding box to world origin (0, 0, 0)
377
+ var closestCorner = this.findClosestBoundingBoxCorner(boundingBox);
378
+
379
+ // Get mesh world position to convert from world space to local space
380
+ var meshWorldPosition = new THREE.Vector3();
381
+ this.selectedMesh.getWorldPosition(meshWorldPosition);
382
+
383
+ // Convert closest corner from world space to local space relative to mesh
384
+ var localCornerPosition = closestCorner.clone().sub(meshWorldPosition);
385
+
386
+ // Calculate 2D pixel offset for tooltip positioning
387
+ var pixelOffset = this.calculatePixelBasedOffset(localCornerPosition, meshWorldPosition);
388
+
389
+ // Add a small offset above the corner for better visibility, plus pixel-based offset
390
+ var tooltipPosition = {
391
+ x: localCornerPosition.x + pixelOffset.x,
392
+ y: localCornerPosition.y + pixelOffset.y,
393
+ z: localCornerPosition.z + pixelOffset.z
394
+ };
395
+
396
+ // If transform controls are available and have a selected object
397
+ if (this.transformControlsManager && this.transformControlsManager.selectedObject === this.selectedMesh) {
398
+ try {
399
+ // Get world transform data from transform controls
400
+ var worldTransform = this.transformControlsManager.getWorldTransformData();
401
+ if (worldTransform && worldTransform.position) {
402
+ tooltipLogger.info('Using transform controls position for tooltip at closest corner');
403
+ return {
404
+ x: localCornerPosition.x + pixelOffset.x,
405
+ y: localCornerPosition.y + pixelOffset.y,
406
+ z: localCornerPosition.z + pixelOffset.z
407
+ };
408
+ }
409
+ } catch (error) {
410
+ tooltipLogger.warn('Error getting transform controls position:', error);
411
+ }
412
+ }
413
+ tooltipLogger.info('Positioning tooltip at closest corner to origin:', tooltipPosition);
414
+ return tooltipPosition;
415
+ }
416
+ }, {
417
+ key: "calculatePixelBasedOffset",
418
+ value: function calculatePixelBasedOffset(localCornerPosition, meshWorldPosition) {
419
+ if (!this.camera) {
420
+ return {
421
+ x: 0,
422
+ y: 0,
423
+ z: 0
424
+ };
425
+ }
426
+
427
+ // Get tooltip width - use the template width or measure if tooltip exists
428
+ var tooltipWidth = 250; // Default from template
429
+
430
+ if (this.tooltipObject && this.tooltipObject.element) {
431
+ // Force a layout update to get accurate measurements
432
+ var element = this.tooltipObject.element;
433
+ if (element.offsetWidth > 0) {
434
+ tooltipWidth = element.offsetWidth;
435
+ } else {
436
+ // If not yet measured, temporarily add to DOM to measure
437
+ var tempParent = document.createElement('div');
438
+ tempParent.style.position = 'absolute';
439
+ tempParent.style.visibility = 'hidden';
440
+ tempParent.style.top = '-9999px';
441
+ document.body.appendChild(tempParent);
442
+ tempParent.appendChild(element);
443
+ tooltipWidth = element.offsetWidth || 250;
444
+
445
+ // Remove from temp parent
446
+ document.body.removeChild(tempParent);
447
+ }
448
+ }
449
+ var halfTooltipWidth = tooltipWidth / 2;
450
+
451
+ // Calculate world position for the tooltip anchor point
452
+ var worldAnchorPosition = meshWorldPosition.clone().add(localCornerPosition);
453
+
454
+ // Get camera position and direction
455
+ var cameraPosition = new THREE.Vector3();
456
+ this.camera.getWorldPosition(cameraPosition);
457
+ var cameraDirection = new THREE.Vector3();
458
+ this.camera.getWorldDirection(cameraDirection);
459
+
460
+ // Determine which side of the camera midline the tooltip is on
461
+ var isOnRightSide = this.isPositionOnRightSideOfCameraMidline(worldAnchorPosition, cameraPosition, cameraDirection);
462
+
463
+ // Calculate distance to camera for scaling the offset
464
+ var distanceToCamera = worldAnchorPosition.distanceTo(cameraPosition);
465
+
466
+ // Calculate screen-space offset magnitude using camera projection
467
+ var screenSpaceOffset = 0;
468
+ if (this.camera.isPerspectiveCamera) {
469
+ // Convert pixel offset to world space based on camera distance and FOV
470
+ var fov = this.camera.fov * Math.PI / 180; // Convert to radians
471
+ var halfHeight = Math.tan(fov / 2) * distanceToCamera;
472
+ var halfWidth = halfHeight * this.camera.aspect;
473
+
474
+ // Convert pixel offset to world space
475
+ screenSpaceOffset = halfTooltipWidth / this.container.clientWidth * (halfWidth * 2);
476
+ } else if (this.camera.isOrthographicCamera) {
477
+ // For orthographic cameras, use the zoom factor
478
+ var _halfWidth = (this.camera.right - this.camera.left) / 2;
479
+ screenSpaceOffset = halfTooltipWidth / this.container.clientWidth * (_halfWidth * 2);
480
+ }
481
+
482
+ // Get camera's right vector for horizontal offset
483
+ var cameraRight = new THREE.Vector3();
484
+ this.camera.getWorldDirection(cameraRight);
485
+ cameraRight.cross(this.camera.up).normalize();
486
+
487
+ // Apply offset direction: left if on right side, right if on left side
488
+ var offsetDirection = isOnRightSide ? -1 : 1;
489
+ var worldOffset = cameraRight.multiplyScalar(screenSpaceOffset * offsetDirection);
490
+ tooltipLogger.debug('Tooltip pixel offset calculated:', {
491
+ tooltipWidth: tooltipWidth,
492
+ halfTooltipWidth: halfTooltipWidth,
493
+ distanceToCamera: distanceToCamera,
494
+ screenSpaceOffset: screenSpaceOffset,
495
+ isOnRightSide: isOnRightSide,
496
+ offsetDirection: offsetDirection,
497
+ worldOffset: worldOffset
498
+ });
499
+ return {
500
+ x: worldOffset.x,
501
+ y: 0,
502
+ // No vertical offset - only move horizontally
503
+ z: worldOffset.z
504
+ };
505
+ }
506
+ }, {
507
+ key: "findClosestBoundingBoxCorner",
508
+ value: function findClosestBoundingBoxCorner(boundingBox) {
509
+ // Get the 8 corners of the bounding box in world space
510
+ var min = boundingBox.min;
511
+ var max = boundingBox.max;
512
+
513
+ // Define all 8 corners of the bounding box
514
+ var corners = [new THREE.Vector3(min.x, min.y, min.z),
515
+ // Bottom-front-left
516
+ new THREE.Vector3(max.x, min.y, min.z),
517
+ // Bottom-front-right
518
+ new THREE.Vector3(min.x, max.y, min.z),
519
+ // Top-front-left
520
+ new THREE.Vector3(max.x, max.y, min.z),
521
+ // Top-front-right
522
+ new THREE.Vector3(min.x, min.y, max.z),
523
+ // Bottom-back-left
524
+ new THREE.Vector3(max.x, min.y, max.z),
525
+ // Bottom-back-right
526
+ new THREE.Vector3(min.x, max.y, max.z),
527
+ // Top-back-left
528
+ new THREE.Vector3(max.x, max.y, max.z) // Top-back-right
529
+ ];
530
+
531
+ // Find the corner closest to the camera's vertical midline
532
+ return this.findCornerClosestToCameraVerticalMidline(corners);
533
+ }
534
+ }, {
535
+ key: "findCornerClosestToCameraVerticalMidline",
536
+ value: function findCornerClosestToCameraVerticalMidline(corners) {
537
+ if (!this.camera) {
538
+ tooltipLogger.warn('No camera available, using first corner');
539
+ return corners[0];
540
+ }
541
+
542
+ // Get camera position and direction
543
+ var cameraPosition = new THREE.Vector3();
544
+ this.camera.getWorldPosition(cameraPosition);
545
+ var cameraDirection = new THREE.Vector3();
546
+ this.camera.getWorldDirection(cameraDirection);
547
+
548
+ // Calculate the camera's vertical midline plane
549
+ // The midline is a line extending from the camera in the forward direction
550
+ // We need to find which corner has the smallest perpendicular distance to this line
551
+
552
+ var closestCorner = corners[0];
553
+ var smallestDistance = this.getDistanceToVerticalMidline(corners[0], cameraPosition, cameraDirection);
554
+ for (var i = 1; i < corners.length; i++) {
555
+ var distance = this.getDistanceToVerticalMidline(corners[i], cameraPosition, cameraDirection);
556
+ if (distance < smallestDistance) {
557
+ smallestDistance = distance;
558
+ closestCorner = corners[i];
559
+ }
560
+ }
561
+ tooltipLogger.debug('Closest corner to camera vertical midline:', closestCorner, 'Distance:', smallestDistance);
562
+ return closestCorner;
563
+ }
564
+ }, {
565
+ key: "getDistanceToVerticalMidline",
566
+ value: function getDistanceToVerticalMidline(point, cameraPosition, cameraDirection) {
567
+ // Calculate the perpendicular distance from a point to the camera's vertical midline
568
+ // The vertical midline is the line extending from the camera in the forward direction
569
+
570
+ // Vector from camera to the point
571
+ var cameraToPoint = point.clone().sub(cameraPosition);
572
+
573
+ // Project this vector onto the camera direction to get the component along the midline
574
+ var projectionLength = cameraToPoint.dot(cameraDirection);
575
+ var projectionVector = cameraDirection.clone().multiplyScalar(projectionLength);
576
+
577
+ // The perpendicular component is the remainder
578
+ var perpendicularVector = cameraToPoint.clone().sub(projectionVector);
579
+
580
+ // Return the magnitude of the perpendicular component
581
+ return perpendicularVector.length();
582
+ }
583
+ }, {
584
+ key: "clearTooltip",
585
+ value: function clearTooltip() {
586
+ if (this.tooltipParent && this.tooltipObject) {
587
+ this.tooltipParent.remove(this.tooltipObject);
588
+ this.tooltipParent = null;
589
+ this.tooltipObject = null;
590
+ }
591
+ }
592
+
593
+ // Method to handle scene interaction when clicking away from objects (single clicks now clear tooltips)
594
+ }, {
595
+ key: "handleSceneClick",
596
+ value: function handleSceneClick(raycaster, camera) {
597
+ // If no intersection found, clear tooltip
598
+ this.clearTooltip();
599
+ this.selectedMesh = null;
600
+ }
601
+
602
+ // Method to check if an object is a selectable component
603
+ }, {
604
+ key: "isSelectableComponent",
605
+ value: function isSelectableComponent(object) {
606
+ var _object$userData, _object$userData2;
607
+ // Check if the object has component data
608
+ if (object && object.userData && object.userData.component) {
609
+ return true;
610
+ }
611
+
612
+ // Check if the object is a pipe segment or junction
613
+ if (object && ((_object$userData = object.userData) !== null && _object$userData !== void 0 && _object$userData.isPipeSegment || (_object$userData2 = object.userData) !== null && _object$userData2 !== void 0 && _object$userData2.isPipeJunction)) {
614
+ return true;
615
+ }
616
+ return false;
617
+ }
618
+ }, {
619
+ key: "isPositionOnRightSideOfCameraMidline",
620
+ value: function isPositionOnRightSideOfCameraMidline(worldPosition, cameraPosition, cameraDirection) {
621
+ // Get camera's right vector (camera's local X-axis)
622
+ var cameraRight = new THREE.Vector3();
623
+ this.camera.getWorldDirection(cameraRight);
624
+ cameraRight.cross(this.camera.up).normalize();
625
+
626
+ // Vector from camera to the position
627
+ var cameraToPosition = worldPosition.clone().sub(cameraPosition);
628
+
629
+ // Project this vector onto the camera's right vector
630
+ // Positive dot product means it's on the right side, negative means left side
631
+ var rightComponent = cameraToPosition.dot(cameraRight);
632
+ return rightComponent > 0;
633
+ }
634
+ }]);
635
+ }();
636
+
637
+ export { SceneTooltipsManager };