@2112-lab/central-plant 0.1.4 → 0.1.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.
- package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +432 -1
- package/dist/cjs/node_modules/@2112-lab/pathfinder/dist/index.esm.js +1448 -1
- package/dist/cjs/node_modules/three/examples/jsm/controls/OrbitControls.js +1853 -1
- package/dist/cjs/node_modules/three/examples/jsm/exporters/GLTFExporter.js +3537 -1
- package/dist/cjs/node_modules/three/examples/jsm/exporters/OBJExporter.js +305 -1
- package/dist/cjs/node_modules/three/examples/jsm/exporters/PLYExporter.js +542 -1
- package/dist/cjs/node_modules/three/examples/jsm/exporters/STLExporter.js +218 -1
- package/dist/cjs/node_modules/three/examples/jsm/loaders/DRACOLoader.js +683 -1
- package/dist/cjs/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4811 -1
- package/dist/cjs/node_modules/three/examples/jsm/loaders/RGBELoader.js +480 -1
- package/dist/cjs/node_modules/three/examples/jsm/renderers/CSS2DRenderer.js +309 -1
- package/dist/cjs/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +120 -1
- package/dist/cjs/src/analysis/analysis.js +560 -1
- package/dist/cjs/src/analysis/testing.js +958 -1
- package/dist/cjs/src/core/centralPlant.js +1149 -1
- package/dist/cjs/src/core/debugLogger.js +175 -1
- package/dist/cjs/src/core/mathUtils.js +574 -1
- package/dist/cjs/src/core/nameUtils.js +93 -1
- package/dist/cjs/src/data/export.js +716 -1
- package/dist/cjs/src/data/import.js +380 -1
- package/dist/cjs/src/data/numerics.js +522 -1
- package/dist/cjs/src/helpers/sceneHelper.js +572 -1
- package/dist/cjs/src/index.js +69 -1
- package/dist/cjs/src/managers/components/animationManager.js +123 -1
- package/dist/cjs/src/managers/components/componentManager.js +332 -1
- package/dist/cjs/src/managers/components/pathfindingManager.js +1441 -1
- package/dist/cjs/src/managers/controls/TransformControls.js +1063 -1
- package/dist/cjs/src/managers/controls/cameraControlsManager.js +79 -1
- package/dist/cjs/src/managers/controls/dragDropManager.js +1026 -1
- package/dist/cjs/src/managers/controls/keyboardControlsManager.js +395 -1
- package/dist/cjs/src/managers/controls/transformControlsManager.js +1807 -1
- package/dist/cjs/src/managers/environment/environmentManager.js +714 -1
- package/dist/cjs/src/managers/environment/textureConfig.js +229 -1
- package/dist/cjs/src/managers/scene/sceneExportManager.js +264 -1
- package/dist/cjs/src/managers/scene/sceneInitializationManager.js +346 -1
- package/dist/cjs/src/managers/scene/sceneOperationsManager.js +1509 -1
- package/dist/cjs/src/managers/scene/sceneTooltipsManager.js +661 -1
- package/dist/cjs/src/managers/system/disposalManager.js +444 -1
- package/dist/cjs/src/managers/system/hotReloadManager.js +291 -1
- package/dist/cjs/src/managers/system/performanceMonitor.js +863 -1
- package/dist/cjs/src/rendering/modelPreloader.js +369 -1
- package/dist/cjs/src/rendering/rendering2D.js +631 -1
- package/dist/cjs/src/rendering/rendering3D.js +685 -1
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +396 -1
- package/dist/esm/node_modules/@2112-lab/pathfinder/dist/index.esm.js +1444 -1
- package/dist/esm/node_modules/three/examples/jsm/controls/OrbitControls.js +1849 -1
- package/dist/esm/node_modules/three/examples/jsm/exporters/GLTFExporter.js +3533 -1
- package/dist/esm/node_modules/three/examples/jsm/exporters/OBJExporter.js +301 -1
- package/dist/esm/node_modules/three/examples/jsm/exporters/PLYExporter.js +538 -1
- package/dist/esm/node_modules/three/examples/jsm/exporters/STLExporter.js +214 -1
- package/dist/esm/node_modules/three/examples/jsm/loaders/DRACOLoader.js +679 -1
- package/dist/esm/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4807 -1
- package/dist/esm/node_modules/three/examples/jsm/loaders/RGBELoader.js +476 -1
- package/dist/esm/node_modules/three/examples/jsm/renderers/CSS2DRenderer.js +304 -1
- package/dist/esm/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +116 -1
- package/dist/esm/src/analysis/analysis.js +536 -1
- package/dist/esm/src/analysis/testing.js +954 -1
- package/dist/esm/src/core/centralPlant.js +1144 -1
- package/dist/esm/src/core/debugLogger.js +167 -1
- package/dist/esm/src/core/mathUtils.js +570 -1
- package/dist/esm/src/core/nameUtils.js +87 -1
- package/dist/esm/src/data/export.js +712 -1
- package/dist/esm/src/data/import.js +356 -1
- package/dist/esm/src/data/numerics.js +518 -1
- package/dist/esm/src/helpers/sceneHelper.js +547 -1
- package/dist/esm/src/index.js +35 -1
- package/dist/esm/src/managers/components/animationManager.js +119 -1
- package/dist/esm/src/managers/components/componentManager.js +328 -1
- package/dist/esm/src/managers/components/pathfindingManager.js +1417 -1
- package/dist/esm/src/managers/controls/TransformControls.js +1057 -1
- package/dist/esm/src/managers/controls/cameraControlsManager.js +75 -1
- package/dist/esm/src/managers/controls/dragDropManager.js +1002 -1
- package/dist/esm/src/managers/controls/keyboardControlsManager.js +371 -1
- package/dist/esm/src/managers/controls/transformControlsManager.js +1782 -1
- package/dist/esm/src/managers/environment/environmentManager.js +690 -1
- package/dist/esm/src/managers/environment/textureConfig.js +202 -1
- package/dist/esm/src/managers/scene/sceneExportManager.js +260 -1
- package/dist/esm/src/managers/scene/sceneInitializationManager.js +322 -1
- package/dist/esm/src/managers/scene/sceneOperationsManager.js +1485 -1
- package/dist/esm/src/managers/scene/sceneTooltipsManager.js +637 -1
- package/dist/esm/src/managers/system/disposalManager.js +440 -1
- package/dist/esm/src/managers/system/hotReloadManager.js +287 -1
- package/dist/esm/src/managers/system/performanceMonitor.js +858 -1
- package/dist/esm/src/rendering/modelPreloader.js +364 -1
- package/dist/esm/src/rendering/rendering2D.js +627 -1
- package/dist/esm/src/rendering/rendering3D.js +661 -1
- 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 };
|