force-3d-graph 1.0.0

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.
@@ -0,0 +1,336 @@
1
+ (function(C,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],d):(C=typeof globalThis<"u"?globalThis:C||self,d(C.ForceGraph3D={},C.THREE))})(this,function(C,d){"use strict";var Mt=Object.defineProperty;var wt=(C,d,I)=>d in C?Mt(C,d,{enumerable:!0,configurable:!0,writable:!0,value:I}):C[d]=I;var r=(C,d,I)=>wt(C,typeof d!="symbol"?d+"":d,I);function I(c){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(c){for(const o in c)if(o!=="default"){const t=Object.getOwnPropertyDescriptor(c,o);Object.defineProperty(e,o,t.get?t:{enumerable:!0,get:()=>c[o]})}}return e.default=c,Object.freeze(e)}const g=I(d),P={backgroundColor:657930,cameraPosition:{x:0,y:0,z:80},cameraFov:75,repulsionStrength:100,attractionStrength:.01,damping:.9,useBarnesHut:!1,barnesHutTheta:.5,defaultNodeColor:4886754,nodeRadius:2,nodeSegments:32,enableLOD:!0,lodDistances:[50,100,200],lodSegments:[32,16,8],edgeColor:8947848,edgeOpacity:.4,enableEdgeCulling:!0,showPanel:!0,targetFPS:60,maxVisibleNodes:1e4};var j=(c=>(c[c.HIGH=0]="HIGH",c[c.MEDIUM=1]="MEDIUM",c[c.LOW=2]="LOW",c))(j||{});function ke(){const c=document.createElement("div");return c.id="force-graph-3d-container",c.style.cssText=`
2
+ width: 100%;
3
+ height: 100%;
4
+ position: absolute;
5
+ top: 0;
6
+ left: 0;
7
+ overflow: hidden;
8
+ `,document.body.appendChild(c),c}function De(c){return c&&c instanceof HTMLElement?c:(console.warn("[ForceGraph3D] No container provided, creating one automatically"),ke())}function le(c){const e=c.getBoundingClientRect();return{width:e.width||window.innerWidth,height:e.height||window.innerHeight}}function Q(c){if(!c||typeof c!="object")return console.warn("[ForceGraph3D] Invalid node: must be an object"),!1;const e=c;return typeof e.id!="string"||e.id.trim()===""?(console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"),!1):typeof e.label!="string"?(console.warn("[ForceGraph3D] Invalid node: label must be a string"),!1):e.color!==void 0&&typeof e.color!="number"?(console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"),!1):e.position!==void 0&&!je(e.position)?(console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"),!1):!0}function J(c){if(!c||typeof c!="object")return console.warn("[ForceGraph3D] Invalid edge: must be an object"),!1;const e=c;return typeof e.source!="string"||e.source.trim()===""?(console.warn("[ForceGraph3D] Invalid edge: source must be a non-empty string"),!1):typeof e.target!="string"||e.target.trim()===""?(console.warn("[ForceGraph3D] Invalid edge: target must be a non-empty string"),!1):!0}function Ie(c){return typeof c!="string"||c.trim()===""?(console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"),!1):!0}function je(c){if(!c||typeof c!="object")return!1;const e=c;return typeof e.x=="number"&&typeof e.y=="number"&&typeof e.z=="number"}function F(c,e){return c===e?`${c}-${e}`:c<e?`${c}-${e}`:`${e}-${c}`}const ce={type:"change"},ee={type:"start"},he={type:"end"},U=new d.Ray,de=new d.Plane,Ae=Math.cos(70*d.MathUtils.DEG2RAD);class He extends d.EventDispatcher{constructor(e,o){super(),this.object=e,this.domElement=o,this.domElement.style.touchAction="none",this.enabled=!0,this.target=new d.Vector3,this.cursor=new d.Vector3,this.minDistance=0,this.maxDistance=1/0,this.minZoom=0,this.maxZoom=1/0,this.minTargetRadius=0,this.maxTargetRadius=1/0,this.minPolarAngle=0,this.maxPolarAngle=Math.PI,this.minAzimuthAngle=-1/0,this.maxAzimuthAngle=1/0,this.enableDamping=!1,this.dampingFactor=.05,this.enableZoom=!0,this.zoomSpeed=1,this.enableRotate=!0,this.rotateSpeed=1,this.enablePan=!0,this.panSpeed=1,this.screenSpacePanning=!0,this.keyPanSpeed=7,this.zoomToCursor=!1,this.autoRotate=!1,this.autoRotateSpeed=2,this.keys={LEFT:"ArrowLeft",UP:"ArrowUp",RIGHT:"ArrowRight",BOTTOM:"ArrowDown"},this.mouseButtons={LEFT:d.MOUSE.ROTATE,MIDDLE:d.MOUSE.DOLLY,RIGHT:d.MOUSE.PAN},this.touches={ONE:d.TOUCH.ROTATE,TWO:d.TOUCH.DOLLY_PAN},this.target0=this.target.clone(),this.position0=this.object.position.clone(),this.zoom0=this.object.zoom,this._domElementKeyEvents=null,this.getPolarAngle=function(){return l.phi},this.getAzimuthalAngle=function(){return l.theta},this.getDistance=function(){return this.object.position.distanceTo(this.target)},this.listenToKeyEvents=function(s){s.addEventListener("keydown",ae),this._domElementKeyEvents=s},this.stopListenToKeyEvents=function(){this._domElementKeyEvents.removeEventListener("keydown",ae),this._domElementKeyEvents=null},this.saveState=function(){t.target0.copy(t.target),t.position0.copy(t.object.position),t.zoom0=t.object.zoom},this.reset=function(){t.target.copy(t.target0),t.object.position.copy(t.position0),t.object.zoom=t.zoom0,t.object.updateProjectionMatrix(),t.dispatchEvent(ce),t.update(),i=n.NONE},this.update=function(){const s=new d.Vector3,h=new d.Quaternion().setFromUnitVectors(e.up,new d.Vector3(0,1,0)),f=h.clone().invert(),v=new d.Vector3,E=new d.Quaternion,k=new d.Vector3,z=2*Math.PI;return function(vt=null){const Te=t.object.position;s.copy(Te).sub(t.target),s.applyQuaternion(h),l.setFromVector3(s),t.autoRotate&&i===n.NONE&&G(tt(vt)),t.enableDamping?(l.theta+=p.theta*t.dampingFactor,l.phi+=p.phi*t.dampingFactor):(l.theta+=p.theta,l.phi+=p.phi);let O=t.minAzimuthAngle,L=t.maxAzimuthAngle;isFinite(O)&&isFinite(L)&&(O<-Math.PI?O+=z:O>Math.PI&&(O-=z),L<-Math.PI?L+=z:L>Math.PI&&(L-=z),O<=L?l.theta=Math.max(O,Math.min(L,l.theta)):l.theta=l.theta>(O+L)/2?Math.max(O,l.theta):Math.min(L,l.theta)),l.phi=Math.max(t.minPolarAngle,Math.min(t.maxPolarAngle,l.phi)),l.makeSafe(),t.enableDamping===!0?t.target.addScaledVector(m,t.dampingFactor):t.target.add(m),t.target.sub(t.cursor),t.target.clampLength(t.minTargetRadius,t.maxTargetRadius),t.target.add(t.cursor),t.zoomToCursor&&_||t.object.isOrthographicCamera?l.radius=ie(l.radius):l.radius=ie(l.radius*u),s.setFromSpherical(l),s.applyQuaternion(f),Te.copy(t.target).add(s),t.object.lookAt(t.target),t.enableDamping===!0?(p.theta*=1-t.dampingFactor,p.phi*=1-t.dampingFactor,m.multiplyScalar(1-t.dampingFactor)):(p.set(0,0,0),m.set(0,0,0));let re=!1;if(t.zoomToCursor&&_){let Y=null;if(t.object.isPerspectiveCamera){const V=s.length();Y=ie(V*u);const Z=V-Y;t.object.position.addScaledVector(me,Z),t.object.updateMatrixWorld()}else if(t.object.isOrthographicCamera){const V=new d.Vector3(T.x,T.y,0);V.unproject(t.object),t.object.zoom=Math.max(t.minZoom,Math.min(t.maxZoom,t.object.zoom/u)),t.object.updateProjectionMatrix(),re=!0;const Z=new d.Vector3(T.x,T.y,0);Z.unproject(t.object),t.object.position.sub(Z).add(V),t.object.updateMatrixWorld(),Y=s.length()}else console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."),t.zoomToCursor=!1;Y!==null&&(this.screenSpacePanning?t.target.set(0,0,-1).transformDirection(t.object.matrix).multiplyScalar(Y).add(t.object.position):(U.origin.copy(t.object.position),U.direction.set(0,0,-1).transformDirection(t.object.matrix),Math.abs(t.object.up.dot(U.direction))<Ae?e.lookAt(t.target):(de.setFromNormalAndCoplanarPoint(t.object.up,t.target),U.intersectPlane(de,t.target))))}else t.object.isOrthographicCamera&&(t.object.zoom=Math.max(t.minZoom,Math.min(t.maxZoom,t.object.zoom/u)),t.object.updateProjectionMatrix(),re=!0);return u=1,_=!1,re||v.distanceToSquared(t.object.position)>a||8*(1-E.dot(t.object.quaternion))>a||k.distanceToSquared(t.target)>0?(t.dispatchEvent(ce),v.copy(t.object.position),E.copy(t.object.quaternion),k.copy(t.target),!0):!1}}(),this.dispose=function(){t.domElement.removeEventListener("contextmenu",Le),t.domElement.removeEventListener("pointerdown",ze),t.domElement.removeEventListener("pointercancel",$),t.domElement.removeEventListener("wheel",Pe),t.domElement.removeEventListener("pointermove",se),t.domElement.removeEventListener("pointerup",$),t._domElementKeyEvents!==null&&(t._domElementKeyEvents.removeEventListener("keydown",ae),t._domElementKeyEvents=null)};const t=this,n={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_PAN:4,TOUCH_DOLLY_PAN:5,TOUCH_DOLLY_ROTATE:6};let i=n.NONE;const a=1e-6,l=new d.Spherical,p=new d.Spherical;let u=1;const m=new d.Vector3,x=new d.Vector2,M=new d.Vector2,b=new d.Vector2,y=new d.Vector2,w=new d.Vector2,N=new d.Vector2,A=new d.Vector2,H=new d.Vector2,D=new d.Vector2,me=new d.Vector3,T=new d.Vector2;let _=!1;const S=[],X={};let te=!1;function tt(s){return s!==null?2*Math.PI/60*t.autoRotateSpeed*s:2*Math.PI/60/60*t.autoRotateSpeed}function q(s){const h=Math.abs(s*.01);return Math.pow(.95,t.zoomSpeed*h)}function G(s){p.theta-=s}function W(s){p.phi-=s}const fe=function(){const s=new d.Vector3;return function(f,v){s.setFromMatrixColumn(v,0),s.multiplyScalar(-f),m.add(s)}}(),ye=function(){const s=new d.Vector3;return function(f,v){t.screenSpacePanning===!0?s.setFromMatrixColumn(v,1):(s.setFromMatrixColumn(v,0),s.crossVectors(t.object.up,s)),s.multiplyScalar(f),m.add(s)}}(),R=function(){const s=new d.Vector3;return function(f,v){const E=t.domElement;if(t.object.isPerspectiveCamera){const k=t.object.position;s.copy(k).sub(t.target);let z=s.length();z*=Math.tan(t.object.fov/2*Math.PI/180),fe(2*f*z/E.clientHeight,t.object.matrix),ye(2*v*z/E.clientHeight,t.object.matrix)}else t.object.isOrthographicCamera?(fe(f*(t.object.right-t.object.left)/t.object.zoom/E.clientWidth,t.object.matrix),ye(v*(t.object.top-t.object.bottom)/t.object.zoom/E.clientHeight,t.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),t.enablePan=!1)}}();function oe(s){t.object.isPerspectiveCamera||t.object.isOrthographicCamera?u/=s:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),t.enableZoom=!1)}function be(s){t.object.isPerspectiveCamera||t.object.isOrthographicCamera?u*=s:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),t.enableZoom=!1)}function ne(s,h){if(!t.zoomToCursor)return;_=!0;const f=t.domElement.getBoundingClientRect(),v=s-f.left,E=h-f.top,k=f.width,z=f.height;T.x=v/k*2-1,T.y=-(E/z)*2+1,me.set(T.x,T.y,1).unproject(t.object).sub(t.object.position).normalize()}function ie(s){return Math.max(t.minDistance,Math.min(t.maxDistance,s))}function xe(s){x.set(s.clientX,s.clientY)}function ot(s){ne(s.clientX,s.clientX),A.set(s.clientX,s.clientY)}function ve(s){y.set(s.clientX,s.clientY)}function nt(s){M.set(s.clientX,s.clientY),b.subVectors(M,x).multiplyScalar(t.rotateSpeed);const h=t.domElement;G(2*Math.PI*b.x/h.clientHeight),W(2*Math.PI*b.y/h.clientHeight),x.copy(M),t.update()}function it(s){H.set(s.clientX,s.clientY),D.subVectors(H,A),D.y>0?oe(q(D.y)):D.y<0&&be(q(D.y)),A.copy(H),t.update()}function st(s){w.set(s.clientX,s.clientY),N.subVectors(w,y).multiplyScalar(t.panSpeed),R(N.x,N.y),y.copy(w),t.update()}function at(s){ne(s.clientX,s.clientY),s.deltaY<0?be(q(s.deltaY)):s.deltaY>0&&oe(q(s.deltaY)),t.update()}function rt(s){let h=!1;switch(s.code){case t.keys.UP:s.ctrlKey||s.metaKey||s.shiftKey?W(2*Math.PI*t.rotateSpeed/t.domElement.clientHeight):R(0,t.keyPanSpeed),h=!0;break;case t.keys.BOTTOM:s.ctrlKey||s.metaKey||s.shiftKey?W(-2*Math.PI*t.rotateSpeed/t.domElement.clientHeight):R(0,-t.keyPanSpeed),h=!0;break;case t.keys.LEFT:s.ctrlKey||s.metaKey||s.shiftKey?G(2*Math.PI*t.rotateSpeed/t.domElement.clientHeight):R(t.keyPanSpeed,0),h=!0;break;case t.keys.RIGHT:s.ctrlKey||s.metaKey||s.shiftKey?G(-2*Math.PI*t.rotateSpeed/t.domElement.clientHeight):R(-t.keyPanSpeed,0),h=!0;break}h&&(s.preventDefault(),t.update())}function Me(s){if(S.length===1)x.set(s.pageX,s.pageY);else{const h=K(s),f=.5*(s.pageX+h.x),v=.5*(s.pageY+h.y);x.set(f,v)}}function we(s){if(S.length===1)y.set(s.pageX,s.pageY);else{const h=K(s),f=.5*(s.pageX+h.x),v=.5*(s.pageY+h.y);y.set(f,v)}}function Ce(s){const h=K(s),f=s.pageX-h.x,v=s.pageY-h.y,E=Math.sqrt(f*f+v*v);A.set(0,E)}function lt(s){t.enableZoom&&Ce(s),t.enablePan&&we(s)}function ct(s){t.enableZoom&&Ce(s),t.enableRotate&&Me(s)}function Ee(s){if(S.length==1)M.set(s.pageX,s.pageY);else{const f=K(s),v=.5*(s.pageX+f.x),E=.5*(s.pageY+f.y);M.set(v,E)}b.subVectors(M,x).multiplyScalar(t.rotateSpeed);const h=t.domElement;G(2*Math.PI*b.x/h.clientHeight),W(2*Math.PI*b.y/h.clientHeight),x.copy(M)}function Se(s){if(S.length===1)w.set(s.pageX,s.pageY);else{const h=K(s),f=.5*(s.pageX+h.x),v=.5*(s.pageY+h.y);w.set(f,v)}N.subVectors(w,y).multiplyScalar(t.panSpeed),R(N.x,N.y),y.copy(w)}function Ne(s){const h=K(s),f=s.pageX-h.x,v=s.pageY-h.y,E=Math.sqrt(f*f+v*v);H.set(0,E),D.set(0,Math.pow(H.y/A.y,t.zoomSpeed)),oe(D.y),A.copy(H);const k=(s.pageX+h.x)*.5,z=(s.pageY+h.y)*.5;ne(k,z)}function ht(s){t.enableZoom&&Ne(s),t.enablePan&&Se(s)}function dt(s){t.enableZoom&&Ne(s),t.enableRotate&&Ee(s)}function ze(s){t.enabled!==!1&&(S.length===0&&(t.domElement.setPointerCapture(s.pointerId),t.domElement.addEventListener("pointermove",se),t.domElement.addEventListener("pointerup",$)),bt(s),s.pointerType==="touch"?ft(s):pt(s))}function se(s){t.enabled!==!1&&(s.pointerType==="touch"?yt(s):gt(s))}function $(s){xt(s),S.length===0&&(t.domElement.releasePointerCapture(s.pointerId),t.domElement.removeEventListener("pointermove",se),t.domElement.removeEventListener("pointerup",$)),t.dispatchEvent(he),i=n.NONE}function pt(s){let h;switch(s.button){case 0:h=t.mouseButtons.LEFT;break;case 1:h=t.mouseButtons.MIDDLE;break;case 2:h=t.mouseButtons.RIGHT;break;default:h=-1}switch(h){case d.MOUSE.DOLLY:if(t.enableZoom===!1)return;ot(s),i=n.DOLLY;break;case d.MOUSE.ROTATE:if(s.ctrlKey||s.metaKey||s.shiftKey){if(t.enablePan===!1)return;ve(s),i=n.PAN}else{if(t.enableRotate===!1)return;xe(s),i=n.ROTATE}break;case d.MOUSE.PAN:if(s.ctrlKey||s.metaKey||s.shiftKey){if(t.enableRotate===!1)return;xe(s),i=n.ROTATE}else{if(t.enablePan===!1)return;ve(s),i=n.PAN}break;default:i=n.NONE}i!==n.NONE&&t.dispatchEvent(ee)}function gt(s){switch(i){case n.ROTATE:if(t.enableRotate===!1)return;nt(s);break;case n.DOLLY:if(t.enableZoom===!1)return;it(s);break;case n.PAN:if(t.enablePan===!1)return;st(s);break}}function Pe(s){t.enabled===!1||t.enableZoom===!1||i!==n.NONE||(s.preventDefault(),t.dispatchEvent(ee),at(ut(s)),t.dispatchEvent(he))}function ut(s){const h=s.deltaMode,f={clientX:s.clientX,clientY:s.clientY,deltaY:s.deltaY};switch(h){case 1:f.deltaY*=16;break;case 2:f.deltaY*=100;break}return s.ctrlKey&&!te&&(f.deltaY*=10),f}function mt(s){s.key==="Control"&&(te=!0,document.addEventListener("keyup",Oe,{passive:!0,capture:!0}))}function Oe(s){s.key==="Control"&&(te=!1,document.removeEventListener("keyup",Oe,{passive:!0,capture:!0}))}function ae(s){t.enabled===!1||t.enablePan===!1||rt(s)}function ft(s){switch(Fe(s),S.length){case 1:switch(t.touches.ONE){case d.TOUCH.ROTATE:if(t.enableRotate===!1)return;Me(s),i=n.TOUCH_ROTATE;break;case d.TOUCH.PAN:if(t.enablePan===!1)return;we(s),i=n.TOUCH_PAN;break;default:i=n.NONE}break;case 2:switch(t.touches.TWO){case d.TOUCH.DOLLY_PAN:if(t.enableZoom===!1&&t.enablePan===!1)return;lt(s),i=n.TOUCH_DOLLY_PAN;break;case d.TOUCH.DOLLY_ROTATE:if(t.enableZoom===!1&&t.enableRotate===!1)return;ct(s),i=n.TOUCH_DOLLY_ROTATE;break;default:i=n.NONE}break;default:i=n.NONE}i!==n.NONE&&t.dispatchEvent(ee)}function yt(s){switch(Fe(s),i){case n.TOUCH_ROTATE:if(t.enableRotate===!1)return;Ee(s),t.update();break;case n.TOUCH_PAN:if(t.enablePan===!1)return;Se(s),t.update();break;case n.TOUCH_DOLLY_PAN:if(t.enableZoom===!1&&t.enablePan===!1)return;ht(s),t.update();break;case n.TOUCH_DOLLY_ROTATE:if(t.enableZoom===!1&&t.enableRotate===!1)return;dt(s),t.update();break;default:i=n.NONE}}function Le(s){t.enabled!==!1&&s.preventDefault()}function bt(s){S.push(s.pointerId)}function xt(s){delete X[s.pointerId];for(let h=0;h<S.length;h++)if(S[h]==s.pointerId){S.splice(h,1);return}}function Fe(s){let h=X[s.pointerId];h===void 0&&(h=new d.Vector2,X[s.pointerId]=h),h.set(s.pageX,s.pageY)}function K(s){const h=s.pointerId===S[0]?S[1]:S[0];return X[h]}t.domElement.addEventListener("contextmenu",Le),t.domElement.addEventListener("pointerdown",ze),t.domElement.addEventListener("pointercancel",$),t.domElement.addEventListener("wheel",Pe,{passive:!1}),document.addEventListener("keydown",mt,{passive:!0,capture:!0}),this.update()}}class Re{constructor(e,o){r(this,"scene");r(this,"camera");r(this,"renderer");r(this,"controls");r(this,"container");r(this,"resizeHandler");this.container=e,this.scene=new g.Scene,this.scene.background=new g.Color(o.backgroundColor??657930);const{width:t,height:n}=le(e),i=o.cameraFov??75;this.camera=new g.PerspectiveCamera(i,t/n,.1,2e3);const a=o.cameraPosition??{x:0,y:0,z:80};this.camera.position.set(a.x,a.y,a.z),this.renderer=new g.WebGLRenderer({antialias:!0,alpha:!0,powerPreference:"high-performance"}),this.renderer.setSize(t,n),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.toneMapping=g.ACESFilmicToneMapping,this.renderer.toneMappingExposure=1,this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=g.PCFSoftShadowMap,e.appendChild(this.renderer.domElement),this.controls=new He(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.05,this.controls.rotateSpeed=.8,this.controls.zoomSpeed=1.2,this.controls.minDistance=10,this.controls.maxDistance=500,this.setupLighting(),this.resizeHandler=this.onWindowResize.bind(this),window.addEventListener("resize",this.resizeHandler)}setupLighting(){const e=new g.AmbientLight(16777215,.4);this.scene.add(e);const o=new g.DirectionalLight(16777215,.9);o.position.set(50,60,40),o.castShadow=!0,o.shadow.mapSize.width=1024,o.shadow.mapSize.height=1024,this.scene.add(o);const t=new g.DirectionalLight(16773344,.4);t.position.set(-50,30,-40),this.scene.add(t);const n=new g.DirectionalLight(16777215,.3);n.position.set(0,-30,-50),this.scene.add(n);const i=new g.PointLight(16750950,.5,150);i.position.set(40,20,40),this.scene.add(i);const a=new g.PointLight(16764057,.4,150);a.position.set(-40,-20,40),this.scene.add(a);const l=new g.PointLight(6724095,.2,100);l.position.set(0,40,-40),this.scene.add(l)}onWindowResize(){const{width:e,height:o}=le(this.container);this.camera.aspect=e/o,this.camera.updateProjectionMatrix(),this.renderer.setSize(e,o)}add(e){this.scene.add(e)}remove(e){this.scene.remove(e)}render(){this.controls.update(),this.renderer.render(this.scene,this.camera)}getCameraPosition(){return{x:this.camera.position.x,y:this.camera.position.y,z:this.camera.position.z}}getCameraDirection(){const e=new g.Vector3;return this.camera.getWorldDirection(e),e}dispose(){for(window.removeEventListener("resize",this.resizeHandler),this.controls.dispose(),this.renderer.dispose(),this.renderer.domElement.parentNode&&this.renderer.domElement.parentNode.removeChild(this.renderer.domElement);this.scene.children.length>0;){const e=this.scene.children[0];this.scene.remove(e)}}}class Ke{constructor(e,o){r(this,"sceneManager");r(this,"nodeFactory");r(this,"nodes",new Map);r(this,"nodeObjects",new Map);this.sceneManager=e,this.nodeFactory=o}hasNode(e){return this.nodes.has(e)}addNode(e,o=0){if(!Q(e))return!1;if(this.nodes.has(e.id))return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`),!1;const t=e.position??{x:(Math.random()-.5)*50,y:(Math.random()-.5)*50,z:(Math.random()-.5)*50},n={...e,position:t,velocity:{x:0,y:0,z:0},mass:1},i=this.nodeFactory.createNode({...e,position:t},o);return this.sceneManager.add(i.group),this.nodes.set(e.id,n),this.nodeObjects.set(e.id,i),!0}removeNode(e){const o=this.nodes.get(e),t=this.nodeObjects.get(e);return!o||!t?!1:(this.sceneManager.remove(t.group),this.nodeFactory.disposeNode(t),this.nodes.delete(e),this.nodeObjects.delete(e),!0)}updateNode(e,o){const t=this.nodes.get(e),n=this.nodeObjects.get(e);return!t||!n?(console.warn(`[ForceGraph3D] Node "${e}" not found`),!1):(o.label!==void 0&&(t.label=o.label,this.nodeFactory.updateNodeLabel(n,o.label)),o.color!==void 0&&(t.color=o.color,this.nodeFactory.updateNodeColor(n,o.color)),Object.keys(o).forEach(i=>{i!=="id"&&i!=="label"&&i!=="color"&&i!=="position"&&(t[i]=o[i])}),!0)}updateNodePosition(e,o){const t=this.nodes.get(e),n=this.nodeObjects.get(e);t&&n&&(t.position=o,n.group.position.set(o.x,o.y,o.z))}updateNodeLOD(e,o){const t=this.nodeObjects.get(e);t&&this.nodeFactory.updateNodeLOD(t,o)}getNode(e){return this.nodes.get(e)}getNodeObject(e){return this.nodeObjects.get(e)}getAllNodes(){return this.nodes}getAllNodeObjects(){const e=[];return this.nodeObjects.forEach(o=>{e.push(o.group)}),e}getAllSpheres(){const e=[];return this.nodeObjects.forEach(o=>{e.push(o.sphere)}),e}getNodeCount(){return this.nodes.size}clear(){this.nodes.forEach((e,o)=>{this.removeNode(o)})}dispose(){this.clear()}}class Ge{constructor(e,o,t){r(this,"sceneManager");r(this,"nodeManager");r(this,"edgeFactory");r(this,"edges",[]);r(this,"edgeObjects",[]);r(this,"edgeKeySet",new Set);r(this,"highlightedEdgeKey",null);this.sceneManager=e,this.nodeManager=o,this.edgeFactory=t}hasEdge(e,o){const t=F(e,o);return this.edgeKeySet.has(t)}addEdge(e){if(!J(e))return!1;if(!this.nodeManager.hasNode(e.source))return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`),!1;if(!this.nodeManager.hasNode(e.target))return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`),!1;const o=F(e.source,e.target);if(this.edgeKeySet.has(o))return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`),!1;const t=this.nodeManager.getNode(e.source),n=this.nodeManager.getNode(e.target),i=this.edgeFactory.createEdge(e,t,n,t.position,n.position);return this.sceneManager.add(i.line),this.edges.push(e),this.edgeObjects.push(i),this.edgeKeySet.add(o),!0}removeEdge(e,o){const t=F(e,o);if(!this.edgeKeySet.has(t))return!1;const n=this.edges.findIndex(a=>F(a.source,a.target)===t);if(n===-1)return!1;const i=this.edgeObjects[n];return this.sceneManager.remove(i.line),this.edgeFactory.disposeEdge(i),this.edges.splice(n,1),this.edgeObjects.splice(n,1),this.edgeKeySet.delete(t),this.highlightedEdgeKey===t&&(this.highlightedEdgeKey=null),!0}highlightEdge(e,o){const t=F(e,o);this.highlightedEdgeKey&&this.highlightedEdgeKey!==t&&this.unhighlightCurrentEdge();const n=this.edges.findIndex(i=>F(i.source,i.target)===t);n!==-1&&(this.edgeFactory.highlightEdge(this.edgeObjects[n]),this.highlightedEdgeKey=t)}unhighlightCurrentEdge(){if(!this.highlightedEdgeKey)return;const e=this.edges.findIndex(o=>F(o.source,o.target)===this.highlightedEdgeKey);e!==-1&&this.edgeFactory.unhighlightEdge(this.edgeObjects[e]),this.highlightedEdgeKey=null}removeEdgesForNode(e){const o=[];this.edges.forEach(t=>{(t.source===e||t.target===e)&&o.push({source:t.source,target:t.target})}),o.forEach(t=>{this.removeEdge(t.source,t.target)})}getEdgesForNode(e){return this.edges.filter(o=>o.source===e||o.target===e)}getNeighborIds(e){const o=[];return this.edges.forEach(t=>{t.source===e?o.push(t.target):t.target===e&&o.push(t.source)}),[...new Set(o)]}updateEdgePositions(){this.edgeObjects.forEach((e,o)=>{const t=this.edges[o],n=this.nodeManager.getNode(t.source),i=this.nodeManager.getNode(t.target);n&&i&&this.edgeFactory.updateEdgePositions(e,n.position,i.position)})}getAllEdges(){return this.edges}getAllEdgeLines(){return this.edgeObjects.map(e=>e.line)}getEdgeCount(){return this.edges.length}clear(){this.edgeObjects.forEach(e=>{this.sceneManager.remove(e.line),this.edgeFactory.disposeEdge(e)}),this.edges=[],this.edgeObjects=[],this.edgeKeySet.clear(),this.highlightedEdgeKey=null}dispose(){this.clear()}}class pe{constructor(e,o,t={}){r(this,"nodes");r(this,"edges");r(this,"repulsionStrength");r(this,"attractionStrength");r(this,"damping");r(this,"useBarnesHut");r(this,"barnesHutTheta");r(this,"alpha",1);r(this,"alphaDecay",.0228);r(this,"alphaMin",.001);r(this,"alphaTarget",0);this.nodes=e,this.edges=o,this.repulsionStrength=t.repulsionStrength??100,this.attractionStrength=t.attractionStrength??.01,this.damping=t.damping??.9,this.useBarnesHut=t.useBarnesHut??!1,this.barnesHutTheta=t.barnesHutTheta??.5}simulate(){if(this.alpha<this.alphaMin)return;const e=this.nodes.size;(this.useBarnesHut||e>=1e3)&&e>=100?this.calculateRepulsionBarnesHut():this.calculateRepulsionBruteForce(),this.calculateAttraction(),this.applyForces(),this.alpha+=(this.alphaTarget-this.alpha)*this.alphaDecay}calculateRepulsionBruteForce(){const e=Array.from(this.nodes.values()),o=e.length;for(let t=0;t<o;t++){const n=e[t];for(let i=t+1;i<o;i++){const a=e[i],l=a.position.x-n.position.x,p=a.position.y-n.position.y,u=a.position.z-n.position.z;let m=l*l+p*p+u*u;m<.01&&(m=.01);const x=Math.sqrt(m),M=this.repulsionStrength*this.alpha/m,b=l/x*M,y=p/x*M,w=u/x*M;n.velocity.x-=b/n.mass,n.velocity.y-=y/n.mass,n.velocity.z-=w/n.mass,a.velocity.x+=b/a.mass,a.velocity.y+=y/a.mass,a.velocity.z+=w/a.mass}}}calculateRepulsionBarnesHut(){const e=Array.from(this.nodes.values()),o=new $e(e);for(const t of e)this.calculateForceFromOctree(t,o.root)}calculateForceFromOctree(e,o){if(o.isLeaf){o.node&&o.node.id!==e.id&&this.applyRepulsionBetween(e,o.node);return}if(o.mass===0)return;const t=o.centerOfMass.x-e.position.x,n=o.centerOfMass.y-e.position.y,i=o.centerOfMass.z-e.position.z,a=Math.sqrt(t*t+n*n+i*i);if(o.size/a<this.barnesHutTheta){const l=Math.max(a*a,.01),p=this.repulsionStrength*this.alpha*o.mass/l;e.velocity.x-=t/a*p/e.mass,e.velocity.y-=n/a*p/e.mass,e.velocity.z-=i/a*p/e.mass}else for(const l of o.children)l&&this.calculateForceFromOctree(e,l)}applyRepulsionBetween(e,o){const t=o.position.x-e.position.x,n=o.position.y-e.position.y,i=o.position.z-e.position.z;let a=t*t+n*n+i*i;a<.01&&(a=.01);const l=Math.sqrt(a),p=this.repulsionStrength*this.alpha/a;e.velocity.x-=t/l*p/e.mass,e.velocity.y-=n/l*p/e.mass,e.velocity.z-=i/l*p/e.mass}calculateAttraction(){for(const e of this.edges){const o=this.nodes.get(e.source),t=this.nodes.get(e.target);if(!o||!t)continue;const n=t.position.x-o.position.x,i=t.position.y-o.position.y,a=t.position.z-o.position.z,l=Math.sqrt(n*n+i*i+a*a);if(l<.01)continue;const u=(l-15)*this.attractionStrength*this.alpha,m=n/l*u,x=i/l*u,M=a/l*u;o.velocity.x+=m/o.mass,o.velocity.y+=x/o.mass,o.velocity.z+=M/o.mass,t.velocity.x-=m/t.mass,t.velocity.y-=x/t.mass,t.velocity.z-=M/t.mass}}applyForces(){for(const e of this.nodes.values())e.velocity.x*=this.damping,e.velocity.y*=this.damping,e.velocity.z*=this.damping,e.position.x+=e.velocity.x,e.position.y+=e.velocity.y,e.position.z+=e.velocity.z}setEdges(e){this.edges=e}restart(){this.alpha=1}getAlpha(){return this.alpha}setPhysicsParams(e){e.repulsionStrength!==void 0&&(this.repulsionStrength=e.repulsionStrength),e.attractionStrength!==void 0&&(this.attractionStrength=e.attractionStrength),e.damping!==void 0&&(this.damping=e.damping)}}class $e{constructor(e){r(this,"root");const o=this.calculateBounds(e);this.root=this.buildTree(e,o)}calculateBounds(e){if(e.length===0)return{min:{x:-100,y:-100,z:-100},max:{x:100,y:100,z:100}};const o={x:1/0,y:1/0,z:1/0},t={x:-1/0,y:-1/0,z:-1/0};for(const i of e)o.x=Math.min(o.x,i.position.x),o.y=Math.min(o.y,i.position.y),o.z=Math.min(o.z,i.position.z),t.x=Math.max(t.x,i.position.x),t.y=Math.max(t.y,i.position.y),t.z=Math.max(t.z,i.position.z);const n=10;return o.x-=n,o.y-=n,o.z-=n,t.x+=n,t.y+=n,t.z+=n,{min:o,max:t}}buildTree(e,o,t=0){const n=Math.max(o.max.x-o.min.x,o.max.y-o.min.y,o.max.z-o.min.z);if(e.length===0)return{bounds:o,size:n,centerOfMass:{x:0,y:0,z:0},mass:0,isLeaf:!0,node:null,children:[]};if(e.length===1||t>20){let b=0;const y={x:0,y:0,z:0};for(const w of e)b+=w.mass,y.x+=w.position.x*w.mass,y.y+=w.position.y*w.mass,y.z+=w.position.z*w.mass;return b>0&&(y.x/=b,y.y/=b,y.z/=b),{bounds:o,size:n,centerOfMass:y,mass:b,isLeaf:!0,node:e[0],children:[]}}const i=(o.min.x+o.max.x)/2,a=(o.min.y+o.max.y)/2,l=(o.min.z+o.max.z)/2,p=[[],[],[],[],[],[],[],[]];for(const b of e){const y=(b.position.x>=i?1:0)+(b.position.y>=a?2:0)+(b.position.z>=l?4:0);p[y].push(b)}const u=[{min:{x:o.min.x,y:o.min.y,z:o.min.z},max:{x:i,y:a,z:l}},{min:{x:i,y:o.min.y,z:o.min.z},max:{x:o.max.x,y:a,z:l}},{min:{x:o.min.x,y:a,z:o.min.z},max:{x:i,y:o.max.y,z:l}},{min:{x:i,y:a,z:o.min.z},max:{x:o.max.x,y:o.max.y,z:l}},{min:{x:o.min.x,y:o.min.y,z:l},max:{x:i,y:a,z:o.max.z}},{min:{x:i,y:o.min.y,z:l},max:{x:o.max.x,y:a,z:o.max.z}},{min:{x:o.min.x,y:a,z:l},max:{x:i,y:o.max.y,z:o.max.z}},{min:{x:i,y:a,z:l},max:{x:o.max.x,y:o.max.y,z:o.max.z}}],m=[];let x=0;const M={x:0,y:0,z:0};for(let b=0;b<8;b++)if(p[b].length>0){const y=this.buildTree(p[b],u[b],t+1);m.push(y),x+=y.mass,M.x+=y.centerOfMass.x*y.mass,M.y+=y.centerOfMass.y*y.mass,M.z+=y.centerOfMass.z*y.mass}else m.push(null);return x>0&&(M.x/=x,M.y/=x,M.z/=x),{bounds:o,size:n,centerOfMass:M,mass:x,isLeaf:!1,node:null,children:m}}}class Ye{constructor(e,o,t,n=60){r(this,"sceneManager");r(this,"animationId",null);r(this,"isRunning",!1);r(this,"frameInterval");r(this,"lastFrameTime",0);r(this,"onSimulate");r(this,"onRender");r(this,"frameCount",0);r(this,"fpsStartTime",0);r(this,"currentFPS",60);r(this,"animate",()=>{if(!this.isRunning)return;this.animationId=requestAnimationFrame(this.animate);const e=performance.now(),o=e-this.lastFrameTime;if(o<this.frameInterval)return;this.lastFrameTime=e-o%this.frameInterval,this.frameCount++;const t=e-this.fpsStartTime;t>=1e3&&(this.currentFPS=this.frameCount/(t/1e3),this.frameCount=0,this.fpsStartTime=e),this.onSimulate(),this.onRender(),this.sceneManager.render()});this.sceneManager=e,this.onSimulate=o,this.onRender=t,this.frameInterval=1e3/n}start(){this.isRunning||(this.isRunning=!0,this.lastFrameTime=performance.now(),this.fpsStartTime=performance.now(),this.frameCount=0,this.animate())}stop(){this.isRunning=!1,this.animationId!==null&&(cancelAnimationFrame(this.animationId),this.animationId=null)}getFPS(){return Math.round(this.currentFPS)}setTargetFPS(e){this.frameInterval=1e3/e}isAnimating(){return this.isRunning}dispose(){this.stop()}}class Ve{constructor(){r(this,"envMap",null);r(this,"materialCache",new Map);r(this,"COLORS",[16777215,16750950,16764057,16772829,16746564]);this.createEnvironmentMap()}createEnvironmentMap(){const o=[],t=[{colors:["#1a1a2e","#16213e","#0f3460"]},{colors:["#2d1f1f","#1a1a1a","#0f0f0f"]},{colors:["#1f2d2d","#1a1a2e","#101020"]},{colors:["#0a0a0a","#050505","#000000"]},{colors:["#1a1a2e","#0f1a2a","#0a0a15"]},{colors:["#2d1a1a","#1a0a0a","#0f0505"]}];for(const n of t){const i=document.createElement("canvas");i.width=256,i.height=256;const a=i.getContext("2d"),l=a.createRadialGradient(256/2,256/2,0,256/2,256/2,256*.8);l.addColorStop(0,n.colors[0]),l.addColorStop(.5,n.colors[1]),l.addColorStop(1,n.colors[2]),a.fillStyle=l,a.fillRect(0,0,256,256);const p=a.getImageData(0,0,256,256);for(let u=0;u<p.data.length;u+=4){const m=(Math.random()-.5)*5;p.data[u]=Math.min(255,Math.max(0,p.data[u]+m)),p.data[u+1]=Math.min(255,Math.max(0,p.data[u+1]+m)),p.data[u+2]=Math.min(255,Math.max(0,p.data[u+2]+m))}a.putImageData(p,0,0),o.push(i)}this.envMap=new g.CubeTexture(o.map(n=>{const i=new Image;return i.src=n.toDataURL(),i})),this.envMap.needsUpdate=!0}getRandomColor(){return this.COLORS[Math.floor(Math.random()*this.COLORS.length)]}createGlassMaterial(e){const t="glass-single";if(this.materialCache.has(t))return this.materialCache.get(t).clone();const n=new g.Color(16750950),i=new g.ShaderMaterial({uniforms:{uColor:{value:n},uEnvMap:{value:this.envMap},uGlowColor:{value:new g.Color(16777215)},uGlowIntensity:{value:.8},uReflectivity:{value:.4},uFresnelPower:{value:2.5}},vertexShader:`
9
+ varying vec3 vNormal;
10
+ varying vec3 vViewPosition;
11
+ varying vec3 vWorldPosition;
12
+ varying vec2 vUv;
13
+
14
+ void main() {
15
+ vNormal = normalize(normalMatrix * normal);
16
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
17
+ vViewPosition = -mvPosition.xyz;
18
+ vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
19
+ vUv = uv;
20
+ gl_Position = projectionMatrix * mvPosition;
21
+ }
22
+ `,fragmentShader:`
23
+ uniform vec3 uColor;
24
+ uniform vec3 uGlowColor;
25
+ uniform samplerCube uEnvMap;
26
+ uniform float uGlowIntensity;
27
+ uniform float uReflectivity;
28
+ uniform float uFresnelPower;
29
+
30
+ varying vec3 vNormal;
31
+ varying vec3 vViewPosition;
32
+ varying vec3 vWorldPosition;
33
+ varying vec2 vUv;
34
+
35
+ void main() {
36
+ vec3 viewDir = normalize(vViewPosition);
37
+ vec3 normal = normalize(vNormal);
38
+
39
+ // Fresnel effect - edges are more visible
40
+ float fresnel = pow(1.0 - abs(dot(viewDir, normal)), uFresnelPower);
41
+
42
+ // Center glow: bright white in center, fading to base color at edges
43
+ // Center is where normal faces camera directly (low fresnel)
44
+ float centerGlow = (1.0 - fresnel) * uGlowIntensity;
45
+
46
+ // Environment reflection on edges
47
+ vec3 reflectDir = reflect(-viewDir, normal);
48
+ vec3 envColor = textureCube(uEnvMap, reflectDir).rgb;
49
+
50
+ // Base color with reflection at edges
51
+ vec3 baseColor = uColor;
52
+ vec3 edgeColor = mix(baseColor, envColor, uReflectivity * fresnel);
53
+
54
+ // Blend: white glow in center, colored edges
55
+ vec3 finalColor = mix(edgeColor, uGlowColor, centerGlow);
56
+
57
+ // Add rim highlight for glass effect
58
+ float rimLight = pow(fresnel, 4.0) * 0.4;
59
+ finalColor += vec3(rimLight) * uColor;
60
+
61
+ // Opacity: fully opaque with glow effect visible
62
+ float opacity = 0.85 + fresnel * 0.15;
63
+
64
+ gl_FragColor = vec4(finalColor, opacity);
65
+ }
66
+ `,transparent:!0,side:g.FrontSide,depthWrite:!0,blending:g.NormalBlending});return this.materialCache.set(t,i),i.clone()}createEdgeMaterial(e=6710886,o=.4){return new g.LineBasicMaterial({color:e,transparent:!0,opacity:o,linewidth:1})}createHighlightedEdgeMaterial(){return new g.LineBasicMaterial({color:16750950,transparent:!1,opacity:1,linewidth:2})}createLabelMaterial(e,o=24){const t=document.createElement("canvas"),n=t.getContext("2d");n.font=`600 ${o}px Inter, -apple-system, sans-serif`;const a=n.measureText(e).width;t.width=Math.max(128,a+24),t.height=o+20,n.clearRect(0,0,t.width,t.height),n.font=`600 ${o}px Inter, -apple-system, sans-serif`,n.textAlign="center",n.textBaseline="middle",n.shadowColor="rgba(0, 0, 0, 0.8)",n.shadowBlur=4,n.shadowOffsetX=1,n.shadowOffsetY=1,n.fillStyle="rgba(255, 255, 255, 0.95)",n.fillText(e,t.width/2,t.height/2);const l=new g.CanvasTexture(t);return l.needsUpdate=!0,new g.SpriteMaterial({map:l,transparent:!0,depthTest:!1,depthWrite:!1})}getEnvMap(){return this.envMap}dispose(){this.materialCache.forEach(e=>e.dispose()),this.materialCache.clear(),this.envMap&&this.envMap.dispose()}}class Ue{constructor(e,o=2,t=[32,16,8]){r(this,"materialFactory");r(this,"geometryCache",new Map);r(this,"nodeRadius");r(this,"lodSegments");this.materialFactory=e,this.nodeRadius=o,this.lodSegments=t,this.initGeometryCache()}initGeometryCache(){this.lodSegments.forEach((e,o)=>{const t=`lod-${o}`;this.geometryCache.set(t,new g.SphereGeometry(this.nodeRadius,e,e))})}getGeometry(e){const o=`lod-${e}`;return this.geometryCache.has(o)?this.geometryCache.get(o):this.geometryCache.get("lod-0")}createNode(e,o=0){const t=new g.Group;t.name=`node-${e.id}`,t.userData={nodeId:e.id,nodeData:e};const n=this.getGeometry(o),i=this.materialFactory.createGlassMaterial(e.color??4886754),a=new g.Mesh(n,i);a.castShadow=!0,a.receiveShadow=!0,t.add(a);const l=this.materialFactory.createLabelMaterial(e.label),p=new g.Sprite(l);return p.position.y=this.nodeRadius+1.5,p.scale.set(4,1,1),t.add(p),e.position&&t.position.set(e.position.x,e.position.y,e.position.z),{group:t,sphere:a,label:p,lodLevel:o}}updateNodeLOD(e,o){if(e.lodLevel===o)return;const t=this.getGeometry(o);e.sphere.geometry=t,e.lodLevel=o}updateNodeColor(e,o){e.sphere.material instanceof g.Material&&e.sphere.material.dispose(),e.sphere.material=this.materialFactory.createGlassMaterial(o)}updateNodeLabel(e,o){e.label.material instanceof g.SpriteMaterial&&(e.label.material.map&&e.label.material.map.dispose(),e.label.material.dispose()),e.label.material=this.materialFactory.createLabelMaterial(o)}disposeNode(e){e.sphere.material instanceof g.Material&&e.sphere.material.dispose(),e.label.material instanceof g.SpriteMaterial&&(e.label.material.map&&e.label.material.map.dispose(),e.label.material.dispose())}dispose(){this.geometryCache.forEach(e=>e.dispose()),this.geometryCache.clear()}}class Be{constructor(e,o=10066329,t=.5){r(this,"materialFactory");r(this,"edgeColor");r(this,"edgeOpacity");r(this,"defaultMaterial",null);r(this,"highlightMaterial",null);this.materialFactory=e,this.edgeColor=o,this.edgeOpacity=t}getDefaultMaterial(){return this.defaultMaterial||(this.defaultMaterial=this.materialFactory.createEdgeMaterial(this.edgeColor,this.edgeOpacity)),this.defaultMaterial}getHighlightMaterial(){return this.highlightMaterial||(this.highlightMaterial=this.materialFactory.createHighlightedEdgeMaterial()),this.highlightMaterial}createEdge(e,o,t,n,i){const a=new g.BufferGeometry,l=new Float32Array([n.x,n.y,n.z,i.x,i.y,i.z]);a.setAttribute("position",new g.BufferAttribute(l,3));const p=this.getDefaultMaterial().clone(),u=new g.Line(a,p);return u.name=`edge-${e.source}-${e.target}`,u.userData={source:e.source,target:e.target,edge:e,sourceNode:o,targetNode:t},u.frustumCulled=!0,{line:u,source:e.source,target:e.target}}highlightEdge(e){e.line.material instanceof g.Material&&e.line.material.dispose(),e.line.material=this.getHighlightMaterial().clone()}unhighlightEdge(e){e.line.material instanceof g.Material&&e.line.material.dispose(),e.line.material=this.getDefaultMaterial().clone()}updateEdgePositions(e,o,t){const n=e.line.geometry.attributes.position,i=n.array;i[0]=o.x,i[1]=o.y,i[2]=o.z,i[3]=t.x,i[4]=t.y,i[5]=t.z,n.needsUpdate=!0,e.line.geometry.computeBoundingSphere()}disposeEdge(e){e.line.geometry.dispose(),e.line.material instanceof g.Material&&e.line.material.dispose()}dispose(){this.defaultMaterial&&this.defaultMaterial.dispose(),this.highlightMaterial&&this.highlightMaterial.dispose()}}class _e{constructor(e,o=[50,100,200],t=!0){r(this,"camera");r(this,"lodDistances");r(this,"enabled");this.camera=e,this.lodDistances=o,this.enabled=t}getLODLevel(e){if(!this.enabled)return j.HIGH;const o=e.x-this.camera.position.x,t=e.y-this.camera.position.y,n=e.z-this.camera.position.z,i=Math.sqrt(o*o+t*t+n*n);return i<this.lodDistances[0]?j.HIGH:i<this.lodDistances[1]?j.MEDIUM:j.LOW}shouldRenderNode(e,o=500){const t=e.x-this.camera.position.x,n=e.y-this.camera.position.y,i=e.z-this.camera.position.z;return Math.sqrt(t*t+n*n+i*i)<o}setLODDistances(e){this.lodDistances=e}setEnabled(e){this.enabled=e}}class Xe{constructor(e,o=!0){r(this,"camera");r(this,"frustum");r(this,"projScreenMatrix");r(this,"enabled");this.camera=e,this.frustum=new g.Frustum,this.projScreenMatrix=new g.Matrix4,this.enabled=o}update(){this.projScreenMatrix.multiplyMatrices(this.camera.projectionMatrix,this.camera.matrixWorldInverse),this.frustum.setFromProjectionMatrix(this.projScreenMatrix)}isPointVisible(e){if(!this.enabled)return!0;const o=new g.Vector3(e.x,e.y,e.z);return this.frustum.containsPoint(o)}isSphereVisible(e,o){if(!this.enabled)return!0;const t=new g.Sphere(new g.Vector3(e.x,e.y,e.z),o);return this.frustum.intersectsSphere(t)}isLineVisible(e,o){if(!this.enabled)return!0;const t=new g.Vector3(e.x,e.y,e.z),n=new g.Vector3(o.x,o.y,o.z);if(this.frustum.containsPoint(t)||this.frustum.containsPoint(n))return!0;const i=new g.Vector3((e.x+o.x)/2,(e.y+o.y)/2,(e.z+o.z)/2),a=i.distanceTo(t),l=new g.Sphere(i,a);return this.frustum.intersectsSphere(l)}setEnabled(e){this.enabled=e}}class qe{constructor(e,o){r(this,"sceneManager");r(this,"raycaster");r(this,"mouse");r(this,"container");r(this,"onNodeClick",null);r(this,"onNodeHover",null);r(this,"onEdgeHover",null);r(this,"hoveredNodeId",null);r(this,"hoveredEdgeKey",null);r(this,"nodeObjects",[]);r(this,"edgeObjects",[]);this.sceneManager=e,this.container=o,this.raycaster=new g.Raycaster,this.raycaster.params.Line={threshold:.5},this.mouse=new g.Vector2,this.handleClick=this.handleClick.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),o.addEventListener("click",this.handleClick),o.addEventListener("mousemove",this.handleMouseMove)}setNodeObjects(e){this.nodeObjects=e}setEdgeObjects(e){this.edgeObjects=e}setClickCallback(e){this.onNodeClick=e}setHoverCallback(e){this.onNodeHover=e}setEdgeHoverCallback(e){this.onEdgeHover=e}handleClick(e){const o=this.getIntersectedNode(e);o&&this.onNodeClick&&this.onNodeClick(o)}handleMouseMove(e){const o=this.getIntersectedNode(e),t=(o==null?void 0:o.id)??null;if(t!==this.hoveredNodeId&&(this.hoveredNodeId=t,this.onNodeHover&&this.onNodeHover(o)),o){this.hoveredEdgeKey!==null&&this.onEdgeHover&&(this.hoveredEdgeKey=null,this.onEdgeHover(null)),this.container.style.cursor="pointer";return}const n=this.getIntersectedEdge(e),i=n?`${n.edge.source}-${n.edge.target}`:null;i!==this.hoveredEdgeKey&&(this.hoveredEdgeKey=i,this.onEdgeHover&&this.onEdgeHover(n)),this.container.style.cursor=n?"pointer":"default"}getIntersectedNode(e){var n;const o=this.container.getBoundingClientRect();this.mouse.x=(e.clientX-o.left)/o.width*2-1,this.mouse.y=-((e.clientY-o.top)/o.height)*2+1,this.raycaster.setFromCamera(this.mouse,this.sceneManager.camera);const t=this.raycaster.intersectObjects(this.nodeObjects,!0);if(t.length>0){let i=t[0].object;for(;i;){if((n=i.userData)!=null&&n.nodeData)return i.userData.nodeData;i=i.parent}}return null}getIntersectedEdge(e){const o=this.container.getBoundingClientRect();this.mouse.x=(e.clientX-o.left)/o.width*2-1,this.mouse.y=-((e.clientY-o.top)/o.height)*2+1,this.raycaster.setFromCamera(this.mouse,this.sceneManager.camera);const t=this.raycaster.intersectObjects(this.edgeObjects,!1);if(t.length>0){const n=t[0].object,i=n.userData;if(i!=null&&i.edge&&(i!=null&&i.sourceNode)&&(i!=null&&i.targetNode))return{edge:i.edge,sourceNode:i.sourceNode,targetNode:i.targetNode,edgeLine:n}}return null}getIntersectedNodeId(e,o){var i;const t=this.container.getBoundingClientRect();this.mouse.x=(e-t.left)/t.width*2-1,this.mouse.y=-((o-t.top)/t.height)*2+1,this.raycaster.setFromCamera(this.mouse,this.sceneManager.camera);const n=this.raycaster.intersectObjects(this.nodeObjects,!0);if(n.length>0){let a=n[0].object;for(;a;){if((i=a.userData)!=null&&i.nodeId)return a.userData.nodeId;a=a.parent}}return null}dispose(){this.container.removeEventListener("click",this.handleClick),this.container.removeEventListener("mousemove",this.handleMouseMove)}}class We{constructor(e){r(this,"container");r(this,"panel",null);r(this,"currentNodeId",null);r(this,"visible",!1);r(this,"panelTemplate",null);r(this,"panelStyles",{});r(this,"onExpand",null);this.container=e,this.createPanel()}createPanel(){this.panel=document.createElement("div"),this.panel.className="force-graph-panel",this.panel.style.cssText=`
67
+ position: absolute;
68
+ right: 20px;
69
+ top: 50%;
70
+ transform: translateY(-50%);
71
+ width: 280px;
72
+ max-height: 80vh;
73
+ overflow-y: auto;
74
+ background: rgba(15, 15, 25, 0.85);
75
+ backdrop-filter: blur(20px);
76
+ -webkit-backdrop-filter: blur(20px);
77
+ border: 1px solid rgba(255, 255, 255, 0.1);
78
+ border-radius: 16px;
79
+ padding: 24px;
80
+ color: white;
81
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
82
+ box-shadow:
83
+ 0 8px 32px rgba(0, 0, 0, 0.4),
84
+ inset 0 0 0 1px rgba(255, 255, 255, 0.05);
85
+ z-index: 1000;
86
+ opacity: 0;
87
+ pointer-events: none;
88
+ transition: opacity 0.3s ease, transform 0.3s ease;
89
+ `,Object.entries(this.panelStyles).forEach(([e,o])=>{this.panel.style.setProperty(e,o)}),this.container.appendChild(this.panel)}setPanelTemplate(e){this.panelTemplate=e}setPanelStyles(e){this.panelStyles=e,this.panel&&Object.entries(e).forEach(([o,t])=>{this.panel.style.setProperty(o,t)})}setExpandCallback(e){this.onExpand=e}show(e,o){if(!this.panel)return;this.currentNodeId=e.id;let t;this.panelTemplate?t=this.panelTemplate(e,o):t=this.generateDefaultContent(e,o),this.panel.innerHTML=t;const n=this.panel.querySelector('[data-action="expand"]'),i=this.panel.querySelector("[data-depth-select]");n&&this.onExpand&&n.addEventListener("click",()=>{if(this.currentNodeId){const l=i?parseInt(i.value,10):1;this.onExpand(this.currentNodeId,l)}});const a=this.panel.querySelector('[data-action="close"]');a&&a.addEventListener("click",()=>{this.hide()}),this.panel.style.opacity="1",this.panel.style.pointerEvents="auto",this.panel.style.transform="translateY(-50%) translateX(0)",this.visible=!0}generateDefaultContent(e,o){const t=e.color?`#${e.color.toString(16).padStart(6,"0")}`:"#4A90E2";return`
90
+ <style>
91
+ .force-graph-panel h2 {
92
+ margin: 0 0 16px 0;
93
+ font-size: 20px;
94
+ font-weight: 600;
95
+ letter-spacing: -0.5px;
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 10px;
99
+ }
100
+ .force-graph-panel .color-dot {
101
+ width: 12px;
102
+ height: 12px;
103
+ border-radius: 50%;
104
+ background: ${t};
105
+ box-shadow: 0 0 10px ${t}80;
106
+ }
107
+ .force-graph-panel .info-row {
108
+ display: flex;
109
+ justify-content: space-between;
110
+ padding: 8px 0;
111
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
112
+ font-size: 13px;
113
+ }
114
+ .force-graph-panel .info-row:last-child {
115
+ border-bottom: none;
116
+ }
117
+ .force-graph-panel .info-label {
118
+ color: rgba(255, 255, 255, 0.6);
119
+ }
120
+ .force-graph-panel .info-value {
121
+ color: rgba(255, 255, 255, 0.95);
122
+ font-weight: 500;
123
+ }
124
+ .force-graph-panel .neighbors-section {
125
+ margin-top: 16px;
126
+ }
127
+ .force-graph-panel .neighbors-title {
128
+ font-size: 12px;
129
+ text-transform: uppercase;
130
+ letter-spacing: 1px;
131
+ color: rgba(255, 255, 255, 0.5);
132
+ margin-bottom: 8px;
133
+ }
134
+ .force-graph-panel .neighbor-chip {
135
+ display: inline-block;
136
+ padding: 4px 10px;
137
+ margin: 2px;
138
+ background: rgba(255, 255, 255, 0.1);
139
+ border-radius: 12px;
140
+ font-size: 12px;
141
+ }
142
+ .force-graph-panel .depth-selector {
143
+ margin-top: 16px;
144
+ margin-bottom: 8px;
145
+ }
146
+ .force-graph-panel .depth-label {
147
+ font-size: 12px;
148
+ color: rgba(255, 255, 255, 0.6);
149
+ margin-bottom: 6px;
150
+ text-transform: uppercase;
151
+ letter-spacing: 0.5px;
152
+ }
153
+ .force-graph-panel select {
154
+ width: 100%;
155
+ padding: 8px 12px;
156
+ background: rgba(255, 255, 255, 0.1);
157
+ border: 1px solid rgba(255, 255, 255, 0.15);
158
+ border-radius: 6px;
159
+ color: white;
160
+ font-size: 13px;
161
+ cursor: pointer;
162
+ outline: none;
163
+ transition: all 0.2s ease;
164
+ }
165
+ .force-graph-panel select:hover {
166
+ background: rgba(255, 255, 255, 0.15);
167
+ }
168
+ .force-graph-panel select:focus {
169
+ border-color: rgba(96, 165, 250, 0.5);
170
+ }
171
+ .force-graph-panel .btn-row {
172
+ display: flex;
173
+ gap: 8px;
174
+ margin-top: 20px;
175
+ }
176
+ .force-graph-panel button {
177
+ flex: 1;
178
+ padding: 10px 16px;
179
+ border: none;
180
+ border-radius: 8px;
181
+ font-size: 13px;
182
+ font-weight: 500;
183
+ cursor: pointer;
184
+ transition: all 0.2s ease;
185
+ }
186
+ .force-graph-panel .btn-expand {
187
+ background: linear-gradient(135deg, #60a5fa, #a78bfa);
188
+ color: white;
189
+ }
190
+ .force-graph-panel .btn-expand:hover {
191
+ transform: translateY(-1px);
192
+ box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
193
+ }
194
+ .force-graph-panel .btn-close {
195
+ background: rgba(255, 255, 255, 0.1);
196
+ color: rgba(255, 255, 255, 0.8);
197
+ }
198
+ .force-graph-panel .btn-close:hover {
199
+ background: rgba(255, 255, 255, 0.15);
200
+ }
201
+ </style>
202
+
203
+ <h2>
204
+ <span class="color-dot"></span>
205
+ ${this.escapeHtml(e.label)}
206
+ </h2>
207
+
208
+ <div class="info-row">
209
+ <span class="info-label">ID</span>
210
+ <span class="info-value">${this.escapeHtml(e.id)}</span>
211
+ </div>
212
+
213
+ <div class="info-row">
214
+ <span class="info-label">Connections</span>
215
+ <span class="info-value">${o.length}</span>
216
+ </div>
217
+
218
+ ${o.length>0?`
219
+ <div class="neighbors-section">
220
+ <div class="neighbors-title">Connected To</div>
221
+ ${o.slice(0,5).map(n=>`<span class="neighbor-chip">${this.escapeHtml(n.label)}</span>`).join("")}
222
+ ${o.length>5?`<span class="neighbor-chip">+${o.length-5} more</span>`:""}
223
+ </div>
224
+ `:""}
225
+
226
+ <div class="depth-selector">
227
+ <div class="depth-label">Expansion Depth</div>
228
+ <select data-depth-select>
229
+ <option value="1">1 Level</option>
230
+ <option value="2">2 Levels</option>
231
+ <option value="3" selected>3 Levels</option>
232
+ </select>
233
+ </div>
234
+
235
+
236
+ <div class="btn-row">
237
+ <button class="btn-expand" data-action="expand">Expand</button>
238
+ <button class="btn-close" data-action="close">Close</button>
239
+ </div>
240
+ `}escapeHtml(e){const o=document.createElement("div");return o.textContent=e,o.innerHTML}hide(){this.panel&&(this.panel.style.opacity="0",this.panel.style.pointerEvents="none",this.panel.style.transform="translateY(-50%) translateX(20px)",this.visible=!1,this.currentNodeId=null)}isVisible(){return this.visible}getCurrentNodeId(){return this.currentNodeId}dispose(){this.panel&&this.panel.parentNode&&this.panel.parentNode.removeChild(this.panel),this.panel=null}}class Ze{constructor(){r(this,"tooltip",null);r(this,"visible",!1);this.createTooltip(),this.setupMouseTracking()}createTooltip(){this.tooltip=document.createElement("div"),this.tooltip.className="force-graph-edge-tooltip",this.tooltip.style.cssText=`
241
+ position: fixed;
242
+ padding: 10px 14px;
243
+ background: rgba(30, 30, 30, 0.95);
244
+ backdrop-filter: blur(10px);
245
+ -webkit-backdrop-filter: blur(10px);
246
+ border: 1px solid rgba(255, 255, 255, 0.15);
247
+ border-radius: 8px;
248
+ color: white;
249
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
250
+ font-size: 13px;
251
+ pointer-events: none;
252
+ z-index: 10000;
253
+ opacity: 0;
254
+ transform: translateY(5px);
255
+ transition: opacity 0.15s ease, transform 0.15s ease;
256
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
257
+ max-width: 400px;
258
+ white-space: normal;
259
+ word-wrap: break-word;
260
+ overflow-wrap: break-word;
261
+ `,document.body.appendChild(this.tooltip)}setupMouseTracking(){document.addEventListener("mousemove",e=>{this.visible&&this.tooltip&&this.positionTooltip(e.clientX,e.clientY)})}positionTooltip(e,o){if(!this.tooltip)return;const t=this.tooltip.getBoundingClientRect(),n=window.innerWidth,i=window.innerHeight;let a=e+15,l=o+15;a+t.width>n-10&&(a=e-t.width-15),l+t.height>i-10&&(l=o-t.height-15),a<10&&(a=10),l<10&&(l=10),this.tooltip.style.left=`${a}px`,this.tooltip.style.top=`${l}px`}show(e,o,t,n,i){if(!this.tooltip)return;const a=e.relationship||"connected to";this.tooltip.innerHTML=`
262
+ <div style="display: flex; flex-direction: column; gap: 4px;">
263
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
264
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(o.label)}</span>
265
+ </div>
266
+ <div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
267
+ ↳ ${this.escapeHtml(a)}
268
+ </div>
269
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
270
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
271
+ </div>
272
+ </div>
273
+ `,this.positionTooltip(n,i),this.tooltip.style.opacity="1",this.tooltip.style.transform="translateY(0)",this.visible=!0}updatePosition(e,o){this.visible&&this.positionTooltip(e,o)}hide(){this.tooltip&&(this.tooltip.style.opacity="0",this.tooltip.style.transform="translateY(5px)",this.visible=!1)}isVisible(){return this.visible}escapeHtml(e){const o=document.createElement("div");return o.textContent=e,o.innerHTML}dispose(){this.tooltip&&this.tooltip.parentNode&&this.tooltip.parentNode.removeChild(this.tooltip),this.tooltip=null}}class Qe{constructor(e,o={}){r(this,"options");r(this,"container");r(this,"sceneManager");r(this,"nodeManager");r(this,"edgeManager");r(this,"graphEngine");r(this,"rendererManager");r(this,"materialFactory");r(this,"nodeFactory");r(this,"edgeFactory");r(this,"lodManager");r(this,"frustumCuller");r(this,"raycasterManager");r(this,"panelManager");r(this,"edgeTooltipManager");r(this,"eventCallbacks",new Map);r(this,"initialized",!1);r(this,"devControls",null);this.options={...P,...o},this.container=De(e),this.materialFactory=new Ve,this.nodeFactory=new Ue(this.materialFactory,this.options.nodeRadius??P.nodeRadius,this.options.lodSegments??P.lodSegments),this.edgeFactory=new Be(this.materialFactory,this.options.edgeColor??P.edgeColor,this.options.edgeOpacity??P.edgeOpacity),this.sceneManager=new Re(this.container,this.options),this.lodManager=new _e(this.sceneManager.camera,this.options.lodDistances??P.lodDistances,this.options.enableLOD??P.enableLOD),this.frustumCuller=new Xe(this.sceneManager.camera,this.options.enableEdgeCulling??P.enableEdgeCulling),this.nodeManager=new Ke(this.sceneManager,this.nodeFactory),this.edgeManager=new Ge(this.sceneManager,this.nodeManager,this.edgeFactory),this.graphEngine=new pe(this.nodeManager.getAllNodes(),this.edgeManager.getAllEdges(),{repulsionStrength:this.options.repulsionStrength,attractionStrength:this.options.attractionStrength,damping:this.options.damping,useBarnesHut:this.options.useBarnesHut,barnesHutTheta:this.options.barnesHutTheta}),this.rendererManager=new Ye(this.sceneManager,()=>this.onSimulate(),()=>this.onRender(),this.options.targetFPS??P.targetFPS),this.raycasterManager=new qe(this.sceneManager,this.container),this.panelManager=new We(this.container),this.edgeTooltipManager=new Ze,this.setupCallbacks(),this.rendererManager.start(),this.initialized=!0,this.emit("ready")}setupCallbacks(){this.raycasterManager.setClickCallback(e=>{this.onNodeClick(e)}),this.options.onNodeHover&&this.raycasterManager.setHoverCallback(this.options.onNodeHover),this.panelManager.setExpandCallback((e,o)=>{this.expandNode(e,o)}),this.options.panelTemplate&&this.panelManager.setPanelTemplate(this.options.panelTemplate),this.options.panelStyles&&this.panelManager.setPanelStyles(this.options.panelStyles),this.raycasterManager.setEdgeHoverCallback(e=>{this.onEdgeHover(e)})}onEdgeHover(e){if(e){this.edgeManager.highlightEdge(e.edge.source,e.edge.target);const o=this.container.getBoundingClientRect(),t=o.left+o.width/2,n=o.top+o.height/2;this.edgeTooltipManager.show(e.edge,e.sourceNode,e.targetNode,t,n),this.options.onEdgeHover&&this.options.onEdgeHover(e.edge,e.sourceNode,e.targetNode),this.emit("edgeHover",e.edge,e.sourceNode,e.targetNode)}else this.edgeManager.unhighlightCurrentEdge(),this.edgeTooltipManager.hide(),this.options.onEdgeHover&&this.options.onEdgeHover(null,null,null),this.emit("edgeHover",null,null,null)}onNodeClick(e){const t=this.edgeManager.getNeighborIds(e.id).map(n=>this.nodeManager.getNode(n)).filter(n=>n!==void 0);this.options.showPanel!==!1&&this.panelManager.show(e,t),this.options.onNodeClick&&this.options.onNodeClick(e),this.emit("nodeClick",e)}onSimulate(){this.graphEngine.simulate();for(const[e,o]of this.nodeManager.getAllNodes())if(this.nodeManager.updateNodePosition(e,o.position),this.options.enableLOD){const t=this.lodManager.getLODLevel(o.position);this.nodeManager.updateNodeLOD(e,t)}this.edgeManager.updateEdgePositions()}onRender(){this.frustumCuller.update(),this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()),this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines())}setData(e){if(this.edgeManager.clear(),this.nodeManager.clear(),e.nodes&&Array.isArray(e.nodes))for(const o of e.nodes)this.addNode(o);if(e.edges&&Array.isArray(e.edges))for(const o of e.edges)this.addEdge(o);this.graphEngine=new pe(this.nodeManager.getAllNodes(),this.edgeManager.getAllEdges(),{repulsionStrength:this.options.repulsionStrength,attractionStrength:this.options.attractionStrength,damping:this.options.damping,useBarnesHut:this.options.useBarnesHut,barnesHutTheta:this.options.barnesHutTheta}),this.graphEngine.restart()}addNode(e){if(!Q(e))return!1;const o=this.nodeManager.addNode(e);return o&&(this.graphEngine.restart(),this.options.onNodeAdd&&this.options.onNodeAdd(e),this.emit("nodeAdd",e)),o}removeNode(e){if(!Ie(e))return!1;this.edgeManager.removeEdgesForNode(e);const o=this.nodeManager.removeNode(e);return o&&(this.graphEngine.restart(),this.options.onNodeRemove&&this.options.onNodeRemove(e),this.emit("nodeRemove",e),this.panelManager.getCurrentNodeId()===e&&this.panelManager.hide()),o}updateNode(e,o){return this.nodeManager.updateNode(e,o)}addEdge(e){if(!J(e))return!1;const o=this.edgeManager.addEdge(e);return o&&(this.graphEngine.setEdges(this.edgeManager.getAllEdges()),this.graphEngine.restart(),this.options.onEdgeAdd&&this.options.onEdgeAdd(e),this.emit("edgeAdd",e)),o}removeEdge(e,o){const t=this.edgeManager.removeEdge(e,o);return t&&(this.graphEngine.setEdges(this.edgeManager.getAllEdges()),this.options.onEdgeRemove&&this.options.onEdgeRemove({source:e,target:o}),this.emit("edgeRemove",{source:e,target:o})),t}async expandNode(e,o=1,t){const n=t??this.options.onExpand;if(!n)return console.warn("[ForceGraph3D] No expand callback provided"),!1;try{const i=await n(e,o);if(i.nodes&&Array.isArray(i.nodes))for(const a of i.nodes)this.addNode(a);if(i.edges&&Array.isArray(i.edges))for(const a of i.edges)this.addEdge(a);return this.panelManager.hide(),this.emit("expand",e,i),!0}catch(i){return console.error("[ForceGraph3D] Error expanding node:",i),!1}}getNode(e){return this.nodeManager.getNode(e)}getNeighbors(e){return this.edgeManager.getNeighborIds(e).map(t=>this.nodeManager.getNode(t)).filter(t=>t!==void 0)}getNodeCount(){return this.nodeManager.getNodeCount()}getEdgeCount(){return this.edgeManager.getEdgeCount()}setExpandCallback(e){this.options.onExpand=e}focusOnNode(e,o=30){const t=this.nodeManager.getNode(e);if(!t){console.warn(`[ForceGraph3D] Node "${e}" not found`);return}const n=t.position,i=this.sceneManager.camera,a=this.sceneManager.controls,l=i.position.clone().sub(a.target).normalize(),p={x:n.x+l.x*o,y:n.y+l.y*o,z:n.z+l.z*o},u={x:i.position.x,y:i.position.y,z:i.position.z},m={x:a.target.x,y:a.target.y,z:a.target.z},x=800,M=performance.now(),b=()=>{const y=performance.now()-M,w=Math.min(y/x,1),N=1-Math.pow(1-w,3);i.position.x=u.x+(p.x-u.x)*N,i.position.y=u.y+(p.y-u.y)*N,i.position.z=u.z+(p.z-u.z)*N,a.target.x=m.x+(n.x-m.x)*N,a.target.y=m.y+(n.y-m.y)*N,a.target.z=m.z+(n.z-m.z)*N,a.update(),w<1&&requestAnimationFrame(b)};b()}showNodePanel(e){const o=this.nodeManager.getNode(e);if(!o){console.warn(`[ForceGraph3D] Node "${e}" not found`);return}const t=this.getNeighbors(e);this.panelManager.show(o,t)}searchNodes(e){if(!e||e.trim()==="")return[];const o=e.toLowerCase().trim(),t=this.nodeManager.getAllNodes(),n=[];return t.forEach(i=>{var u,m,x;const a=(u=i.label)==null?void 0:u.toLowerCase().includes(o),l=(m=i.id)==null?void 0:m.toLowerCase().includes(o),p=(x=i.type)==null?void 0:x.toLowerCase().includes(o);(a||l||p)&&n.push(i)}),n}searchEdges(e){var i;if(!e||e.trim()==="")return[];const o=e.toLowerCase().trim(),t=this.edgeManager.getAllEdges(),n=[];for(const a of t)if((i=a.relationship)==null?void 0:i.toLowerCase().includes(o)){const p=this.nodeManager.getNode(a.source),u=this.nodeManager.getNode(a.target);p&&u&&n.push({edge:a,sourceNode:p,targetNode:u})}return n}getAllNodes(){const e=this.nodeManager.getAllNodes();return Array.from(e.values())}getAllEdges(){return this.edgeManager.getAllEdges()}isInitialized(){return this.initialized}on(e,o){this.eventCallbacks.has(e)||this.eventCallbacks.set(e,[]),this.eventCallbacks.get(e).push(o)}emit(e,...o){const t=this.eventCallbacks.get(e);t&&t.forEach(n=>n(...o))}setPhysicsParams(e){this.graphEngine.setPhysicsParams(e),this.graphEngine.restart()}createDevControls(){this.devControls=document.createElement("div"),this.devControls.className="force-graph-dev-controls",this.devControls.innerHTML=`
274
+ <style>
275
+ .force-graph-dev-controls {
276
+ position: absolute;
277
+ top: 20px;
278
+ right: 20px;
279
+ background: rgba(0, 0, 0, 0.8);
280
+ backdrop-filter: blur(10px);
281
+ border: 1px solid rgba(255, 255, 255, 0.1);
282
+ border-radius: 12px;
283
+ padding: 16px;
284
+ color: white;
285
+ font-family: 'Inter', -apple-system, sans-serif;
286
+ font-size: 12px;
287
+ z-index: 1001;
288
+ min-width: 220px;
289
+ }
290
+ .force-graph-dev-controls h3 {
291
+ margin: 0 0 12px 0;
292
+ font-size: 14px;
293
+ color: #60a5fa;
294
+ }
295
+ .force-graph-dev-controls .control-group {
296
+ margin-bottom: 12px;
297
+ }
298
+ .force-graph-dev-controls label {
299
+ display: block;
300
+ margin-bottom: 4px;
301
+ color: rgba(255, 255, 255, 0.7);
302
+ }
303
+ .force-graph-dev-controls input[type="range"] {
304
+ width: 100%;
305
+ margin-top: 4px;
306
+ }
307
+ .force-graph-dev-controls .value {
308
+ color: #60a5fa;
309
+ font-weight: 600;
310
+ }
311
+ .force-graph-dev-controls .stats {
312
+ margin-top: 12px;
313
+ padding-top: 12px;
314
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
315
+ }
316
+ </style>
317
+ <h3>⚙️ Dev Controls</h3>
318
+ <div class="control-group">
319
+ <label>Repulsion: <span class="value" id="dev-repulsion-val">${this.options.repulsionStrength}</span></label>
320
+ <input type="range" id="dev-repulsion" min="10" max="500" value="${this.options.repulsionStrength}">
321
+ </div>
322
+ <div class="control-group">
323
+ <label>Attraction: <span class="value" id="dev-attraction-val">${(this.options.attractionStrength??.01).toFixed(3)}</span></label>
324
+ <input type="range" id="dev-attraction" min="1" max="100" value="${(this.options.attractionStrength??.01)*1e3}">
325
+ </div>
326
+ <div class="control-group">
327
+ <label>Damping: <span class="value" id="dev-damping-val">${this.options.damping}</span></label>
328
+ <input type="range" id="dev-damping" min="50" max="99" value="${(this.options.damping??.9)*100}">
329
+ </div>
330
+ <div class="stats">
331
+ <div>Nodes: <span class="value" id="dev-node-count">0</span></div>
332
+ <div>Edges: <span class="value" id="dev-edge-count">0</span></div>
333
+ <div>FPS: <span class="value" id="dev-fps">60</span></div>
334
+ </div>
335
+ `,this.container.appendChild(this.devControls);const e=this.devControls.querySelector("#dev-repulsion"),o=this.devControls.querySelector("#dev-attraction"),t=this.devControls.querySelector("#dev-damping");e==null||e.addEventListener("input",()=>{const n=parseFloat(e.value);this.setPhysicsParams({repulsionStrength:n}),this.devControls.querySelector("#dev-repulsion-val").textContent=n.toString()}),o==null||o.addEventListener("input",()=>{const n=parseFloat(o.value)/1e3;this.setPhysicsParams({attractionStrength:n}),this.devControls.querySelector("#dev-attraction-val").textContent=n.toFixed(3)}),t==null||t.addEventListener("input",()=>{const n=parseFloat(t.value)/100;this.setPhysicsParams({damping:n}),this.devControls.querySelector("#dev-damping-val").textContent=n.toFixed(2)}),setInterval(()=>{const n=this.devControls.querySelector("#dev-node-count"),i=this.devControls.querySelector("#dev-edge-count"),a=this.devControls.querySelector("#dev-fps");n&&(n.textContent=this.getNodeCount().toString()),i&&(i.textContent=this.getEdgeCount().toString()),a&&(a.textContent=this.rendererManager.getFPS().toString())},500)}destroy(){this.rendererManager.dispose(),this.panelManager.dispose(),this.raycasterManager.dispose(),this.edgeTooltipManager.dispose(),this.edgeManager.dispose(),this.nodeManager.dispose(),this.nodeFactory.dispose(),this.materialFactory.dispose(),this.sceneManager.dispose(),this.devControls&&this.devControls.parentNode&&this.devControls.parentNode.removeChild(this.devControls),this.eventCallbacks.clear(),this.initialized=!1}}const ge=["Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega","Core","Hub","Node","Link","Point","Vertex"],B=["connects to","links with","relates to","depends on","references","extends","includes","partners with","collaborates with","supports"],ue=[16777215,16750950,16764057,16772829,16746564];function Je(c=30){const e=[],o=[];for(let n=0;n<c;n++){const i=n<ge.length?ge[n]:`Node ${n+1}`;e.push({id:`node-${n}`,label:i,color:ue[n%ue.length],position:{x:(Math.random()-.5)*60,y:(Math.random()-.5)*60,z:(Math.random()-.5)*60}})}for(let n=1;n<c;n++){const i=Math.floor(Math.random()*n);o.push({source:`node-${n}`,target:`node-${i}`,relationship:B[Math.floor(Math.random()*B.length)]})}const t=Math.floor(c*.5);for(let n=0;n<t;n++){const i=Math.floor(Math.random()*c);let a=Math.floor(Math.random()*c);i===a&&(a=(a+1)%c);const l=`node-${i}`,p=`node-${a}`;o.some(m=>m.source===l&&m.target===p||m.source===p&&m.target===l)||o.push({source:l,target:p,relationship:B[Math.floor(Math.random()*B.length)]})}return{nodes:e,edges:o}}function et(c=1e3){const e=[],o=[],t=Math.ceil(c/50),n=[];for(let i=0;i<t;i++)n.push({x:(Math.random()-.5)*200,y:(Math.random()-.5)*200,z:(Math.random()-.5)*200});for(let i=0;i<c;i++){const a=n[i%t];e.push({id:`node-${i}`,label:`N${i}`,position:{x:a.x+(Math.random()-.5)*40,y:a.y+(Math.random()-.5)*40,z:a.z+(Math.random()-.5)*40}})}for(let i=1;i<c;i++){const a=Math.floor(i/50)*50,l=a===0?Math.floor(Math.random()*i):a+Math.floor(Math.random()*Math.min(i-a,50));o.push({source:`node-${i}`,target:`node-${Math.min(l,i-1)}`,relationship:"links to"})}for(let i=1;i<t;i++){const a=i*50,l=(i-1)*50+Math.floor(Math.random()*50);o.push({source:`node-${a}`,target:`node-${l}`,relationship:"bridges to"})}return{nodes:e,edges:o}}C.DEFAULT_OPTIONS=P,C.ForceGraph3D=Qe,C.LODLevel=j,C.createEdgeKey=F,C.generateLargeSampleData=et,C.generateSampleData=Je,C.validateEdgeData=J,C.validateNodeData=Q,Object.defineProperty(C,Symbol.toStringTag,{value:"Module"})});
336
+ //# sourceMappingURL=force-3d-graph.umd.cjs.map