force-3d-graph 1.3.7 → 1.3.9

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.
@@ -1,9 +1,9 @@
1
1
  var it = Object.defineProperty;
2
- var ot = (h, e, s) => e in h ? it(h, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : h[e] = s;
3
- var l = (h, e, s) => ot(h, typeof e != "symbol" ? e + "" : e, s);
4
- import * as x from "three";
5
- import { EventDispatcher as nt, Vector3 as S, MOUSE as j, TOUCH as $, Spherical as Se, Quaternion as ze, Vector2 as k, Ray as at, Plane as rt, MathUtils as lt } from "three";
6
- const P = {
2
+ var nt = (p, e, s) => e in p ? it(p, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : p[e] = s;
3
+ var l = (p, e, s) => nt(p, typeof e != "symbol" ? e + "" : e, s);
4
+ import * as y from "three";
5
+ import { EventDispatcher as ot, Vector3 as k, MOUSE as j, TOUCH as B, Spherical as ze, Quaternion as Pe, Vector2 as P, Ray as at, Plane as rt, MathUtils as lt } from "three";
6
+ const C = {
7
7
  backgroundColor: 657930,
8
8
  cameraPosition: { x: 0, y: 0, z: 80 },
9
9
  cameraFov: 75,
@@ -30,98 +30,108 @@ const P = {
30
30
  showViewToggle: !0,
31
31
  showLegend: !0,
32
32
  targetFPS: 60,
33
- maxVisibleNodes: 1e4
33
+ maxVisibleNodes: 1e4,
34
+ performanceMode: "balanced",
35
+ enableAdaptivePerformance: !0,
36
+ minPixelRatio: 0.6,
37
+ maxPixelRatio: 2,
38
+ labelRenderMode: "adaptive",
39
+ labelDistance: 140,
40
+ disableEdgeInteractionsInLowMode: !0,
41
+ edgeBatchThreshold: 1200,
42
+ enableWorkerPhysics: !0,
43
+ workerPhysicsNodeThreshold: 1500
34
44
  };
35
- var _ = /* @__PURE__ */ ((h) => (h[h.HIGH = 0] = "HIGH", h[h.MEDIUM = 1] = "MEDIUM", h[h.LOW = 2] = "LOW", h))(_ || {});
45
+ var K = /* @__PURE__ */ ((p) => (p[p.HIGH = 0] = "HIGH", p[p.MEDIUM = 1] = "MEDIUM", p[p.LOW = 2] = "LOW", p))(K || {});
36
46
  function ct() {
37
- const h = document.createElement("div");
38
- return h.id = "force-graph-3d-container", h.style.cssText = `
47
+ const p = document.createElement("div");
48
+ return p.id = "force-graph-3d-container", p.style.cssText = `
39
49
  width: 100%;
40
50
  height: 100%;
41
51
  position: absolute;
42
52
  top: 0;
43
53
  left: 0;
44
54
  overflow: hidden;
45
- `, document.body.appendChild(h), h;
55
+ `, document.body.appendChild(p), p;
46
56
  }
47
- function dt(h) {
48
- return h && h instanceof HTMLElement ? h : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ct());
57
+ function ht(p) {
58
+ return p && p instanceof HTMLElement ? p : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ct());
49
59
  }
50
- function ke(h) {
51
- const e = h.getBoundingClientRect();
60
+ function Re(p) {
61
+ const e = p.getBoundingClientRect();
52
62
  return {
53
63
  width: e.width || window.innerWidth,
54
64
  height: e.height || window.innerHeight
55
65
  };
56
66
  }
57
- function Fe(h) {
58
- if (!h || typeof h != "object")
67
+ function ce(p) {
68
+ if (!p || typeof p != "object")
59
69
  return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
60
- const e = h;
70
+ const e = p;
61
71
  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 && !pt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
62
72
  }
63
- function De(h) {
64
- if (!h || typeof h != "object")
73
+ function he(p) {
74
+ if (!p || typeof p != "object")
65
75
  return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
66
- const e = h;
76
+ const e = p;
67
77
  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;
68
78
  }
69
- function ht(h) {
70
- return typeof h != "string" || h.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
79
+ function dt(p) {
80
+ return typeof p != "string" || p.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
71
81
  }
72
- function pt(h) {
73
- if (!h || typeof h != "object") return !1;
74
- const e = h;
82
+ function pt(p) {
83
+ if (!p || typeof p != "object") return !1;
84
+ const e = p;
75
85
  return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
76
86
  }
77
- function Dt(h, e) {
78
- return h === e ? `${h}-${e}` : h < e ? `${h}-${e}` : `${e}-${h}`;
87
+ function At(p, e) {
88
+ return p === e ? `${p}-${e}` : p < e ? `${p}-${e}` : `${e}-${p}`;
79
89
  }
80
- const Te = { type: "change" }, le = { type: "start" }, Pe = { type: "end" }, Q = new at(), Le = new rt(), gt = Math.cos(70 * lt.DEG2RAD);
81
- class ut extends nt {
90
+ const Le = { type: "change" }, le = { type: "start" }, Te = { type: "end" }, Q = new at(), Ie = new rt(), gt = Math.cos(70 * lt.DEG2RAD);
91
+ class ut extends ot {
82
92
  constructor(e, s) {
83
- super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), 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 = 0.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: j.ROTATE, MIDDLE: j.DOLLY, RIGHT: j.PAN }, this.touches = { ONE: $.ROTATE, TWO: $.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
84
- return r.phi;
93
+ super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new k(), this.cursor = new k(), 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 = 0.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: j.ROTATE, MIDDLE: j.DOLLY, RIGHT: j.PAN }, this.touches = { ONE: B.ROTATE, TWO: B.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
94
+ return a.phi;
85
95
  }, this.getAzimuthalAngle = function() {
86
- return r.theta;
96
+ return a.theta;
87
97
  }, this.getDistance = function() {
88
98
  return this.object.position.distanceTo(this.target);
89
- }, this.listenToKeyEvents = function(a) {
90
- a.addEventListener("keydown", ae), this._domElementKeyEvents = a;
99
+ }, this.listenToKeyEvents = function(r) {
100
+ r.addEventListener("keydown", ae), this._domElementKeyEvents = r;
91
101
  }, this.stopListenToKeyEvents = function() {
92
102
  this._domElementKeyEvents.removeEventListener("keydown", ae), this._domElementKeyEvents = null;
93
103
  }, this.saveState = function() {
94
104
  t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
95
105
  }, this.reset = function() {
96
- t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Te), t.update(), o = i.NONE;
106
+ t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Le), t.update(), n = i.NONE;
97
107
  }, this.update = function() {
98
- const a = new S(), g = new ze().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), M = new S(), C = new ze(), F = new S(), z = 2 * Math.PI;
108
+ const r = new k(), g = new Pe().setFromUnitVectors(e.up, new k(0, 1, 0)), v = g.clone().invert(), w = new k(), S = new Pe(), D = new k(), z = 2 * Math.PI;
99
109
  return function(st = null) {
100
- const Ne = t.object.position;
101
- a.copy(Ne).sub(t.target), a.applyQuaternion(g), r.setFromVector3(a), t.autoRotate && o === i.NONE && Y(He(st)), t.enableDamping ? (r.theta += d.theta * t.dampingFactor, r.phi += d.phi * t.dampingFactor) : (r.theta += d.theta, r.phi += d.phi);
102
- let R = t.minAzimuthAngle, I = t.maxAzimuthAngle;
103
- isFinite(R) && isFinite(I) && (R < -Math.PI ? R += z : R > Math.PI && (R -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), R <= I ? r.theta = Math.max(R, Math.min(I, r.theta)) : r.theta = r.theta > (R + I) / 2 ? Math.max(R, r.theta) : Math.min(I, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && D || t.object.isOrthographicCamera ? r.radius = oe(r.radius) : r.radius = oe(r.radius * c), a.setFromSpherical(r), a.applyQuaternion(v), Ne.copy(t.target).add(a), t.object.lookAt(t.target), t.enableDamping === !0 ? (d.theta *= 1 - t.dampingFactor, d.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (d.set(0, 0, 0), p.set(0, 0, 0));
110
+ const ke = t.object.position;
111
+ r.copy(ke).sub(t.target), r.applyQuaternion(g), a.setFromVector3(r), t.autoRotate && n === i.NONE && $(Ae(st)), t.enableDamping ? (a.theta += c.theta * t.dampingFactor, a.phi += c.phi * t.dampingFactor) : (a.theta += c.theta, a.phi += c.phi);
112
+ let T = t.minAzimuthAngle, I = t.maxAzimuthAngle;
113
+ isFinite(T) && isFinite(I) && (T < -Math.PI ? T += z : T > Math.PI && (T -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), T <= I ? a.theta = Math.max(T, Math.min(I, a.theta)) : a.theta = a.theta > (T + I) / 2 ? Math.max(T, a.theta) : Math.min(I, a.theta)), a.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, a.phi)), a.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(d, t.dampingFactor) : t.target.add(d), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && F || t.object.isOrthographicCamera ? a.radius = ne(a.radius) : a.radius = ne(a.radius * h), r.setFromSpherical(a), r.applyQuaternion(v), ke.copy(t.target).add(r), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, d.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), d.set(0, 0, 0));
104
114
  let re = !1;
105
- if (t.zoomToCursor && D) {
106
- let K = null;
115
+ if (t.zoomToCursor && F) {
116
+ let W = null;
107
117
  if (t.object.isPerspectiveCamera) {
108
- const U = a.length();
109
- K = oe(U * c);
110
- const Z = U - K;
118
+ const U = r.length();
119
+ W = ne(U * h);
120
+ const Z = U - W;
111
121
  t.object.position.addScaledVector(G, Z), t.object.updateMatrixWorld();
112
122
  } else if (t.object.isOrthographicCamera) {
113
- const U = new S(T.x, T.y, 0);
114
- U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0;
115
- const Z = new S(T.x, T.y, 0);
116
- Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(), K = a.length();
123
+ const U = new k(R.x, R.y, 0);
124
+ U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), re = !0;
125
+ const Z = new k(R.x, R.y, 0);
126
+ Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(), W = r.length();
117
127
  } else
118
128
  console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
119
- K !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(K).add(t.object.position) : (Q.origin.copy(t.object.position), Q.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Q.direction)) < gt ? e.lookAt(t.target) : (Le.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Le, t.target))));
120
- } else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0);
121
- return c = 1, D = !1, re || M.distanceToSquared(t.object.position) > n || 8 * (1 - C.dot(t.object.quaternion)) > n || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Te), M.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
129
+ W !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(W).add(t.object.position) : (Q.origin.copy(t.object.position), Q.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Q.direction)) < gt ? e.lookAt(t.target) : (Ie.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Ie, t.target))));
130
+ } else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), re = !0);
131
+ return h = 1, F = !1, re || w.distanceToSquared(t.object.position) > o || 8 * (1 - S.dot(t.object.quaternion)) > o || D.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Le), w.copy(t.object.position), S.copy(t.object.quaternion), D.copy(t.target), !0) : !1;
122
132
  };
123
133
  }(), this.dispose = function() {
124
- t.domElement.removeEventListener("contextmenu", Ee), t.domElement.removeEventListener("pointerdown", ve), t.domElement.removeEventListener("pointercancel", B), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ae), t._domElementKeyEvents = null);
134
+ t.domElement.removeEventListener("contextmenu", Se), t.domElement.removeEventListener("pointerdown", we), t.domElement.removeEventListener("pointercancel", Y), t.domElement.removeEventListener("wheel", Ee), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", Y), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ae), t._domElementKeyEvents = null);
125
135
  };
126
136
  const t = this, i = {
127
137
  NONE: -1,
@@ -133,175 +143,175 @@ class ut extends nt {
133
143
  TOUCH_DOLLY_PAN: 5,
134
144
  TOUCH_DOLLY_ROTATE: 6
135
145
  };
136
- let o = i.NONE;
137
- const n = 1e-6, r = new Se(), d = new Se();
138
- let c = 1;
139
- const p = new S(), f = new k(), m = new k(), u = new k(), y = new k(), b = new k(), w = new k(), N = new k(), O = new k(), L = new k(), G = new S(), T = new k();
140
- let D = !1;
141
- const E = [], V = {};
146
+ let n = i.NONE;
147
+ const o = 1e-6, a = new ze(), c = new ze();
148
+ let h = 1;
149
+ const d = new k(), f = new P(), m = new P(), u = new P(), b = new P(), x = new P(), M = new P(), N = new P(), O = new P(), L = new P(), G = new k(), R = new P();
150
+ let F = !1;
151
+ const E = [], _ = {};
142
152
  let te = !1;
143
- function He(a) {
144
- return a !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * a : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
153
+ function Ae(r) {
154
+ return r !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * r : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
145
155
  }
146
- function q(a) {
147
- const g = Math.abs(a * 0.01);
156
+ function V(r) {
157
+ const g = Math.abs(r * 0.01);
148
158
  return Math.pow(0.95, t.zoomSpeed * g);
149
159
  }
150
- function Y(a) {
151
- d.theta -= a;
160
+ function $(r) {
161
+ c.theta -= r;
152
162
  }
153
- function W(a) {
154
- d.phi -= a;
163
+ function X(r) {
164
+ c.phi -= r;
155
165
  }
156
- const ce = function() {
157
- const a = new S();
158
- return function(v, M) {
159
- a.setFromMatrixColumn(M, 0), a.multiplyScalar(-v), p.add(a);
166
+ const de = function() {
167
+ const r = new k();
168
+ return function(v, w) {
169
+ r.setFromMatrixColumn(w, 0), r.multiplyScalar(-v), d.add(r);
160
170
  };
161
- }(), de = function() {
162
- const a = new S();
163
- return function(v, M) {
164
- t.screenSpacePanning === !0 ? a.setFromMatrixColumn(M, 1) : (a.setFromMatrixColumn(M, 0), a.crossVectors(t.object.up, a)), a.multiplyScalar(v), p.add(a);
171
+ }(), pe = function() {
172
+ const r = new k();
173
+ return function(v, w) {
174
+ t.screenSpacePanning === !0 ? r.setFromMatrixColumn(w, 1) : (r.setFromMatrixColumn(w, 0), r.crossVectors(t.object.up, r)), r.multiplyScalar(v), d.add(r);
165
175
  };
166
- }(), H = function() {
167
- const a = new S();
168
- return function(v, M) {
169
- const C = t.domElement;
176
+ }(), A = function() {
177
+ const r = new k();
178
+ return function(v, w) {
179
+ const S = t.domElement;
170
180
  if (t.object.isPerspectiveCamera) {
171
- const F = t.object.position;
172
- a.copy(F).sub(t.target);
173
- let z = a.length();
174
- z *= Math.tan(t.object.fov / 2 * Math.PI / 180), ce(2 * v * z / C.clientHeight, t.object.matrix), de(2 * M * z / C.clientHeight, t.object.matrix);
175
- } else t.object.isOrthographicCamera ? (ce(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), de(M * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
181
+ const D = t.object.position;
182
+ r.copy(D).sub(t.target);
183
+ let z = r.length();
184
+ z *= Math.tan(t.object.fov / 2 * Math.PI / 180), de(2 * v * z / S.clientHeight, t.object.matrix), pe(2 * w * z / S.clientHeight, t.object.matrix);
185
+ } else t.object.isOrthographicCamera ? (de(v * (t.object.right - t.object.left) / t.object.zoom / S.clientWidth, t.object.matrix), pe(w * (t.object.top - t.object.bottom) / t.object.zoom / S.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
176
186
  };
177
187
  }();
178
- function se(a) {
179
- t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c /= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
188
+ function se(r) {
189
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h /= r : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
180
190
  }
181
- function he(a) {
182
- t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c *= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
191
+ function ge(r) {
192
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h *= r : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
183
193
  }
184
- function ie(a, g) {
194
+ function ie(r, g) {
185
195
  if (!t.zoomToCursor)
186
196
  return;
187
- D = !0;
188
- const v = t.domElement.getBoundingClientRect(), M = a - v.left, C = g - v.top, F = v.width, z = v.height;
189
- T.x = M / F * 2 - 1, T.y = -(C / z) * 2 + 1, G.set(T.x, T.y, 1).unproject(t.object).sub(t.object.position).normalize();
197
+ F = !0;
198
+ const v = t.domElement.getBoundingClientRect(), w = r - v.left, S = g - v.top, D = v.width, z = v.height;
199
+ R.x = w / D * 2 - 1, R.y = -(S / z) * 2 + 1, G.set(R.x, R.y, 1).unproject(t.object).sub(t.object.position).normalize();
190
200
  }
191
- function oe(a) {
192
- return Math.max(t.minDistance, Math.min(t.maxDistance, a));
201
+ function ne(r) {
202
+ return Math.max(t.minDistance, Math.min(t.maxDistance, r));
193
203
  }
194
- function pe(a) {
195
- f.set(a.clientX, a.clientY);
204
+ function ue(r) {
205
+ f.set(r.clientX, r.clientY);
196
206
  }
197
- function Ae(a) {
198
- ie(a.clientX, a.clientX), N.set(a.clientX, a.clientY);
207
+ function He(r) {
208
+ ie(r.clientX, r.clientX), N.set(r.clientX, r.clientY);
199
209
  }
200
- function ge(a) {
201
- y.set(a.clientX, a.clientY);
210
+ function fe(r) {
211
+ b.set(r.clientX, r.clientY);
202
212
  }
203
- function je(a) {
204
- m.set(a.clientX, a.clientY), u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
213
+ function je(r) {
214
+ m.set(r.clientX, r.clientY), u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
205
215
  const g = t.domElement;
206
- Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m), t.update();
216
+ $(2 * Math.PI * u.x / g.clientHeight), X(2 * Math.PI * u.y / g.clientHeight), f.copy(m), t.update();
207
217
  }
208
- function $e(a) {
209
- O.set(a.clientX, a.clientY), L.subVectors(O, N), L.y > 0 ? se(q(L.y)) : L.y < 0 && he(q(L.y)), N.copy(O), t.update();
218
+ function Be(r) {
219
+ O.set(r.clientX, r.clientY), L.subVectors(O, N), L.y > 0 ? se(V(L.y)) : L.y < 0 && ge(V(L.y)), N.copy(O), t.update();
210
220
  }
211
- function Ge(a) {
212
- b.set(a.clientX, a.clientY), w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b), t.update();
221
+ function Ge(r) {
222
+ x.set(r.clientX, r.clientY), M.subVectors(x, b).multiplyScalar(t.panSpeed), A(M.x, M.y), b.copy(x), t.update();
213
223
  }
214
- function Ye(a) {
215
- ie(a.clientX, a.clientY), a.deltaY < 0 ? he(q(a.deltaY)) : a.deltaY > 0 && se(q(a.deltaY)), t.update();
224
+ function $e(r) {
225
+ ie(r.clientX, r.clientY), r.deltaY < 0 ? ge(V(r.deltaY)) : r.deltaY > 0 && se(V(r.deltaY)), t.update();
216
226
  }
217
- function Be(a) {
227
+ function Ye(r) {
218
228
  let g = !1;
219
- switch (a.code) {
229
+ switch (r.code) {
220
230
  case t.keys.UP:
221
- a.ctrlKey || a.metaKey || a.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, t.keyPanSpeed), g = !0;
231
+ r.ctrlKey || r.metaKey || r.shiftKey ? X(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), g = !0;
222
232
  break;
223
233
  case t.keys.BOTTOM:
224
- a.ctrlKey || a.metaKey || a.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, -t.keyPanSpeed), g = !0;
234
+ r.ctrlKey || r.metaKey || r.shiftKey ? X(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), g = !0;
225
235
  break;
226
236
  case t.keys.LEFT:
227
- a.ctrlKey || a.metaKey || a.shiftKey ? Y(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(t.keyPanSpeed, 0), g = !0;
237
+ r.ctrlKey || r.metaKey || r.shiftKey ? $(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), g = !0;
228
238
  break;
229
239
  case t.keys.RIGHT:
230
- a.ctrlKey || a.metaKey || a.shiftKey ? Y(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(-t.keyPanSpeed, 0), g = !0;
240
+ r.ctrlKey || r.metaKey || r.shiftKey ? $(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(-t.keyPanSpeed, 0), g = !0;
231
241
  break;
232
242
  }
233
- g && (a.preventDefault(), t.update());
243
+ g && (r.preventDefault(), t.update());
234
244
  }
235
- function ue(a) {
245
+ function me(r) {
236
246
  if (E.length === 1)
237
- f.set(a.pageX, a.pageY);
247
+ f.set(r.pageX, r.pageY);
238
248
  else {
239
- const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
240
- f.set(v, M);
249
+ const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
250
+ f.set(v, w);
241
251
  }
242
252
  }
243
- function fe(a) {
253
+ function ye(r) {
244
254
  if (E.length === 1)
245
- y.set(a.pageX, a.pageY);
255
+ b.set(r.pageX, r.pageY);
246
256
  else {
247
- const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
248
- y.set(v, M);
257
+ const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
258
+ b.set(v, w);
249
259
  }
250
260
  }
251
- function me(a) {
252
- const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
253
- N.set(0, C);
261
+ function be(r) {
262
+ const g = H(r), v = r.pageX - g.x, w = r.pageY - g.y, S = Math.sqrt(v * v + w * w);
263
+ N.set(0, S);
254
264
  }
255
- function Ke(a) {
256
- t.enableZoom && me(a), t.enablePan && fe(a);
265
+ function We(r) {
266
+ t.enableZoom && be(r), t.enablePan && ye(r);
257
267
  }
258
- function Ue(a) {
259
- t.enableZoom && me(a), t.enableRotate && ue(a);
268
+ function Ue(r) {
269
+ t.enableZoom && be(r), t.enableRotate && me(r);
260
270
  }
261
- function ye(a) {
271
+ function xe(r) {
262
272
  if (E.length == 1)
263
- m.set(a.pageX, a.pageY);
273
+ m.set(r.pageX, r.pageY);
264
274
  else {
265
- const v = A(a), M = 0.5 * (a.pageX + v.x), C = 0.5 * (a.pageY + v.y);
266
- m.set(M, C);
275
+ const v = H(r), w = 0.5 * (r.pageX + v.x), S = 0.5 * (r.pageY + v.y);
276
+ m.set(w, S);
267
277
  }
268
278
  u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
269
279
  const g = t.domElement;
270
- Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m);
280
+ $(2 * Math.PI * u.x / g.clientHeight), X(2 * Math.PI * u.y / g.clientHeight), f.copy(m);
271
281
  }
272
- function xe(a) {
282
+ function ve(r) {
273
283
  if (E.length === 1)
274
- b.set(a.pageX, a.pageY);
284
+ x.set(r.pageX, r.pageY);
275
285
  else {
276
- const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
277
- b.set(v, M);
286
+ const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
287
+ x.set(v, w);
278
288
  }
279
- w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b);
289
+ M.subVectors(x, b).multiplyScalar(t.panSpeed), A(M.x, M.y), b.copy(x);
280
290
  }
281
- function be(a) {
282
- const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
283
- O.set(0, C), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), se(L.y), N.copy(O);
284
- const F = (a.pageX + g.x) * 0.5, z = (a.pageY + g.y) * 0.5;
285
- ie(F, z);
291
+ function Me(r) {
292
+ const g = H(r), v = r.pageX - g.x, w = r.pageY - g.y, S = Math.sqrt(v * v + w * w);
293
+ O.set(0, S), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), se(L.y), N.copy(O);
294
+ const D = (r.pageX + g.x) * 0.5, z = (r.pageY + g.y) * 0.5;
295
+ ie(D, z);
286
296
  }
287
- function _e(a) {
288
- t.enableZoom && be(a), t.enablePan && xe(a);
297
+ function Ke(r) {
298
+ t.enableZoom && Me(r), t.enablePan && ve(r);
289
299
  }
290
- function Xe(a) {
291
- t.enableZoom && be(a), t.enableRotate && ye(a);
300
+ function qe(r) {
301
+ t.enableZoom && Me(r), t.enableRotate && xe(r);
292
302
  }
293
- function ve(a) {
294
- t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(a.pointerId), t.domElement.addEventListener("pointermove", ne), t.domElement.addEventListener("pointerup", B)), et(a), a.pointerType === "touch" ? Qe(a) : Ve(a));
303
+ function we(r) {
304
+ t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(r.pointerId), t.domElement.addEventListener("pointermove", oe), t.domElement.addEventListener("pointerup", Y)), et(r), r.pointerType === "touch" ? Qe(r) : _e(r));
295
305
  }
296
- function ne(a) {
297
- t.enabled !== !1 && (a.pointerType === "touch" ? Je(a) : qe(a));
306
+ function oe(r) {
307
+ t.enabled !== !1 && (r.pointerType === "touch" ? Je(r) : Ve(r));
298
308
  }
299
- function B(a) {
300
- tt(a), E.length === 0 && (t.domElement.releasePointerCapture(a.pointerId), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B)), t.dispatchEvent(Pe), o = i.NONE;
309
+ function Y(r) {
310
+ tt(r), E.length === 0 && (t.domElement.releasePointerCapture(r.pointerId), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", Y)), t.dispatchEvent(Te), n = i.NONE;
301
311
  }
302
- function Ve(a) {
312
+ function _e(r) {
303
313
  let g;
304
- switch (a.button) {
314
+ switch (r.button) {
305
315
  case 0:
306
316
  g = t.mouseButtons.LEFT;
307
317
  break;
@@ -317,55 +327,55 @@ class ut extends nt {
317
327
  switch (g) {
318
328
  case j.DOLLY:
319
329
  if (t.enableZoom === !1) return;
320
- Ae(a), o = i.DOLLY;
330
+ He(r), n = i.DOLLY;
321
331
  break;
322
332
  case j.ROTATE:
323
- if (a.ctrlKey || a.metaKey || a.shiftKey) {
333
+ if (r.ctrlKey || r.metaKey || r.shiftKey) {
324
334
  if (t.enablePan === !1) return;
325
- ge(a), o = i.PAN;
335
+ fe(r), n = i.PAN;
326
336
  } else {
327
337
  if (t.enableRotate === !1) return;
328
- pe(a), o = i.ROTATE;
338
+ ue(r), n = i.ROTATE;
329
339
  }
330
340
  break;
331
341
  case j.PAN:
332
- if (a.ctrlKey || a.metaKey || a.shiftKey) {
342
+ if (r.ctrlKey || r.metaKey || r.shiftKey) {
333
343
  if (t.enableRotate === !1) return;
334
- pe(a), o = i.ROTATE;
344
+ ue(r), n = i.ROTATE;
335
345
  } else {
336
346
  if (t.enablePan === !1) return;
337
- ge(a), o = i.PAN;
347
+ fe(r), n = i.PAN;
338
348
  }
339
349
  break;
340
350
  default:
341
- o = i.NONE;
351
+ n = i.NONE;
342
352
  }
343
- o !== i.NONE && t.dispatchEvent(le);
353
+ n !== i.NONE && t.dispatchEvent(le);
344
354
  }
345
- function qe(a) {
346
- switch (o) {
355
+ function Ve(r) {
356
+ switch (n) {
347
357
  case i.ROTATE:
348
358
  if (t.enableRotate === !1) return;
349
- je(a);
359
+ je(r);
350
360
  break;
351
361
  case i.DOLLY:
352
362
  if (t.enableZoom === !1) return;
353
- $e(a);
363
+ Be(r);
354
364
  break;
355
365
  case i.PAN:
356
366
  if (t.enablePan === !1) return;
357
- Ge(a);
367
+ Ge(r);
358
368
  break;
359
369
  }
360
370
  }
361
- function we(a) {
362
- t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (a.preventDefault(), t.dispatchEvent(le), Ye(We(a)), t.dispatchEvent(Pe));
371
+ function Ee(r) {
372
+ t.enabled === !1 || t.enableZoom === !1 || n !== i.NONE || (r.preventDefault(), t.dispatchEvent(le), $e(Xe(r)), t.dispatchEvent(Te));
363
373
  }
364
- function We(a) {
365
- const g = a.deltaMode, v = {
366
- clientX: a.clientX,
367
- clientY: a.clientY,
368
- deltaY: a.deltaY
374
+ function Xe(r) {
375
+ const g = r.deltaMode, v = {
376
+ clientX: r.clientX,
377
+ clientY: r.clientY,
378
+ deltaY: r.deltaY
369
379
  };
370
380
  switch (g) {
371
381
  case 1:
@@ -375,97 +385,97 @@ class ut extends nt {
375
385
  v.deltaY *= 100;
376
386
  break;
377
387
  }
378
- return a.ctrlKey && !te && (v.deltaY *= 10), v;
388
+ return r.ctrlKey && !te && (v.deltaY *= 10), v;
379
389
  }
380
- function Ze(a) {
381
- a.key === "Control" && (te = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
390
+ function Ze(r) {
391
+ r.key === "Control" && (te = !0, document.addEventListener("keyup", Ce, { passive: !0, capture: !0 }));
382
392
  }
383
- function Me(a) {
384
- a.key === "Control" && (te = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
393
+ function Ce(r) {
394
+ r.key === "Control" && (te = !1, document.removeEventListener("keyup", Ce, { passive: !0, capture: !0 }));
385
395
  }
386
- function ae(a) {
387
- t.enabled === !1 || t.enablePan === !1 || Be(a);
396
+ function ae(r) {
397
+ t.enabled === !1 || t.enablePan === !1 || Ye(r);
388
398
  }
389
- function Qe(a) {
390
- switch (Ce(a), E.length) {
399
+ function Qe(r) {
400
+ switch (Ne(r), E.length) {
391
401
  case 1:
392
402
  switch (t.touches.ONE) {
393
- case $.ROTATE:
403
+ case B.ROTATE:
394
404
  if (t.enableRotate === !1) return;
395
- ue(a), o = i.TOUCH_ROTATE;
405
+ me(r), n = i.TOUCH_ROTATE;
396
406
  break;
397
- case $.PAN:
407
+ case B.PAN:
398
408
  if (t.enablePan === !1) return;
399
- fe(a), o = i.TOUCH_PAN;
409
+ ye(r), n = i.TOUCH_PAN;
400
410
  break;
401
411
  default:
402
- o = i.NONE;
412
+ n = i.NONE;
403
413
  }
404
414
  break;
405
415
  case 2:
406
416
  switch (t.touches.TWO) {
407
- case $.DOLLY_PAN:
417
+ case B.DOLLY_PAN:
408
418
  if (t.enableZoom === !1 && t.enablePan === !1) return;
409
- Ke(a), o = i.TOUCH_DOLLY_PAN;
419
+ We(r), n = i.TOUCH_DOLLY_PAN;
410
420
  break;
411
- case $.DOLLY_ROTATE:
421
+ case B.DOLLY_ROTATE:
412
422
  if (t.enableZoom === !1 && t.enableRotate === !1) return;
413
- Ue(a), o = i.TOUCH_DOLLY_ROTATE;
423
+ Ue(r), n = i.TOUCH_DOLLY_ROTATE;
414
424
  break;
415
425
  default:
416
- o = i.NONE;
426
+ n = i.NONE;
417
427
  }
418
428
  break;
419
429
  default:
420
- o = i.NONE;
430
+ n = i.NONE;
421
431
  }
422
- o !== i.NONE && t.dispatchEvent(le);
432
+ n !== i.NONE && t.dispatchEvent(le);
423
433
  }
424
- function Je(a) {
425
- switch (Ce(a), o) {
434
+ function Je(r) {
435
+ switch (Ne(r), n) {
426
436
  case i.TOUCH_ROTATE:
427
437
  if (t.enableRotate === !1) return;
428
- ye(a), t.update();
438
+ xe(r), t.update();
429
439
  break;
430
440
  case i.TOUCH_PAN:
431
441
  if (t.enablePan === !1) return;
432
- xe(a), t.update();
442
+ ve(r), t.update();
433
443
  break;
434
444
  case i.TOUCH_DOLLY_PAN:
435
445
  if (t.enableZoom === !1 && t.enablePan === !1) return;
436
- _e(a), t.update();
446
+ Ke(r), t.update();
437
447
  break;
438
448
  case i.TOUCH_DOLLY_ROTATE:
439
449
  if (t.enableZoom === !1 && t.enableRotate === !1) return;
440
- Xe(a), t.update();
450
+ qe(r), t.update();
441
451
  break;
442
452
  default:
443
- o = i.NONE;
453
+ n = i.NONE;
444
454
  }
445
455
  }
446
- function Ee(a) {
447
- t.enabled !== !1 && a.preventDefault();
456
+ function Se(r) {
457
+ t.enabled !== !1 && r.preventDefault();
448
458
  }
449
- function et(a) {
450
- E.push(a.pointerId);
459
+ function et(r) {
460
+ E.push(r.pointerId);
451
461
  }
452
- function tt(a) {
453
- delete V[a.pointerId];
462
+ function tt(r) {
463
+ delete _[r.pointerId];
454
464
  for (let g = 0; g < E.length; g++)
455
- if (E[g] == a.pointerId) {
465
+ if (E[g] == r.pointerId) {
456
466
  E.splice(g, 1);
457
467
  return;
458
468
  }
459
469
  }
460
- function Ce(a) {
461
- let g = V[a.pointerId];
462
- g === void 0 && (g = new k(), V[a.pointerId] = g), g.set(a.pageX, a.pageY);
470
+ function Ne(r) {
471
+ let g = _[r.pointerId];
472
+ g === void 0 && (g = new P(), _[r.pointerId] = g), g.set(r.pageX, r.pageY);
463
473
  }
464
- function A(a) {
465
- const g = a.pointerId === E[0] ? E[1] : E[0];
466
- return V[g];
474
+ function H(r) {
475
+ const g = r.pointerId === E[0] ? E[1] : E[0];
476
+ return _[g];
467
477
  }
468
- t.domElement.addEventListener("contextmenu", Ee), t.domElement.addEventListener("pointerdown", ve), t.domElement.addEventListener("pointercancel", B), t.domElement.addEventListener("wheel", we, { passive: !1 }), document.addEventListener("keydown", Ze, { passive: !0, capture: !0 }), this.update();
478
+ t.domElement.addEventListener("contextmenu", Se), t.domElement.addEventListener("pointerdown", we), t.domElement.addEventListener("pointercancel", Y), t.domElement.addEventListener("wheel", Ee, { passive: !1 }), document.addEventListener("keydown", Ze, { passive: !0, capture: !0 }), this.update();
469
479
  }
470
480
  }
471
481
  class ft {
@@ -476,42 +486,43 @@ class ft {
476
486
  l(this, "controls");
477
487
  l(this, "container");
478
488
  l(this, "resizeHandler");
479
- this.container = e, this.scene = new x.Scene(), this.scene.background = new x.Color(
489
+ l(this, "maxPixelRatio");
490
+ this.container = e, this.maxPixelRatio = s.maxPixelRatio ?? 2, this.scene = new y.Scene(), this.scene.background = new y.Color(
480
491
  s.backgroundColor ?? 657930
481
492
  );
482
- const { width: t, height: i } = ke(e), o = s.cameraFov ?? 75;
483
- this.camera = new x.PerspectiveCamera(o, t / i, 0.1, 2e3);
484
- const n = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
485
- this.camera.position.set(n.x, n.y, n.z), this.renderer = new x.WebGLRenderer({
493
+ const { width: t, height: i } = Re(e), n = s.cameraFov ?? 75;
494
+ this.camera = new y.PerspectiveCamera(n, t / i, 0.1, 2e3);
495
+ const o = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
496
+ this.camera.position.set(o.x, o.y, o.z), this.renderer = new y.WebGLRenderer({
486
497
  antialias: !0,
487
498
  alpha: !0,
488
499
  powerPreference: "high-performance"
489
- }), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = x.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = x.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
500
+ }), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, this.maxPixelRatio)), this.renderer.toneMapping = y.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = y.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
490
501
  }
491
502
  /**
492
503
  * Sets up scene lighting for gradient glass on dark background
493
504
  */
494
505
  setupLighting() {
495
- const e = new x.AmbientLight(16777215, 0.4);
506
+ const e = new y.AmbientLight(16777215, 0.4);
496
507
  this.scene.add(e);
497
- const s = new x.DirectionalLight(16777215, 0.9);
508
+ const s = new y.DirectionalLight(16777215, 0.9);
498
509
  s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
499
- const t = new x.DirectionalLight(16773344, 0.4);
510
+ const t = new y.DirectionalLight(16773344, 0.4);
500
511
  t.position.set(-50, 30, -40), this.scene.add(t);
501
- const i = new x.DirectionalLight(16777215, 0.3);
512
+ const i = new y.DirectionalLight(16777215, 0.3);
502
513
  i.position.set(0, -30, -50), this.scene.add(i);
503
- const o = new x.PointLight(16750950, 0.5, 150);
504
- o.position.set(40, 20, 40), this.scene.add(o);
505
- const n = new x.PointLight(16764057, 0.4, 150);
506
- n.position.set(-40, -20, 40), this.scene.add(n);
507
- const r = new x.PointLight(6724095, 0.2, 100);
508
- r.position.set(0, 40, -40), this.scene.add(r);
514
+ const n = new y.PointLight(16750950, 0.5, 150);
515
+ n.position.set(40, 20, 40), this.scene.add(n);
516
+ const o = new y.PointLight(16764057, 0.4, 150);
517
+ o.position.set(-40, -20, 40), this.scene.add(o);
518
+ const a = new y.PointLight(6724095, 0.2, 100);
519
+ a.position.set(0, 40, -40), this.scene.add(a);
509
520
  }
510
521
  /**
511
522
  * Handle window resize
512
523
  */
513
524
  onWindowResize() {
514
- const { width: e, height: s } = ke(this.container);
525
+ const { width: e, height: s } = Re(this.container);
515
526
  this.camera.aspect = e / s, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, s);
516
527
  }
517
528
  /**
@@ -532,6 +543,25 @@ class ft {
532
543
  render() {
533
544
  this.controls.update(), this.renderer.render(this.scene, this.camera);
534
545
  }
546
+ /**
547
+ * Updates renderer pixel ratio with bounds checking
548
+ */
549
+ setPixelRatio(e) {
550
+ const s = Math.max(0.3, Math.min(e, this.maxPixelRatio));
551
+ this.renderer.setPixelRatio(s);
552
+ }
553
+ /**
554
+ * Gets current renderer pixel ratio
555
+ */
556
+ getPixelRatio() {
557
+ return this.renderer.getPixelRatio();
558
+ }
559
+ /**
560
+ * Enables/disables expensive renderer features
561
+ */
562
+ setHighQualityRendering(e) {
563
+ this.renderer.shadowMap.enabled = e, this.renderer.toneMapping = e ? y.ACESFilmicToneMapping : y.NoToneMapping, this.renderer.toneMappingExposure = 1;
564
+ }
535
565
  /**
536
566
  * Gets the camera position
537
567
  */
@@ -546,7 +576,7 @@ class ft {
546
576
  * Gets the camera's forward direction
547
577
  */
548
578
  getCameraDirection() {
549
- const e = new x.Vector3();
579
+ const e = new y.Vector3();
550
580
  return this.camera.getWorldDirection(e), e;
551
581
  }
552
582
  /**
@@ -559,12 +589,16 @@ class ft {
559
589
  }
560
590
  }
561
591
  }
562
- const X = class X {
592
+ const q = class q {
563
593
  constructor(e, s) {
564
594
  l(this, "sceneManager");
565
595
  l(this, "nodeFactory");
566
596
  l(this, "nodes", /* @__PURE__ */ new Map());
567
597
  l(this, "nodeObjects", /* @__PURE__ */ new Map());
598
+ l(this, "nodeObjectCache", []);
599
+ l(this, "nodeObjectCacheDirty", !0);
600
+ l(this, "labelRenderMode", "adaptive");
601
+ l(this, "labelDistance", 140);
568
602
  this.sceneManager = e, this.nodeFactory = s;
569
603
  }
570
604
  /**
@@ -578,31 +612,31 @@ const X = class X {
578
612
  * Call this before adding nodes in bulk.
579
613
  */
580
614
  static setExpectedNodeCount(e) {
581
- X.expectedNodeCount = e;
615
+ q.expectedNodeCount = e;
582
616
  }
583
617
  /**
584
618
  * Adds a node to the graph
585
619
  * @returns true if added, false if node already exists or invalid
586
620
  */
587
621
  addNode(e, s = 0) {
588
- if (!Fe(e))
622
+ if (!ce(e))
589
623
  return !1;
590
624
  if (this.nodes.has(e.id))
591
625
  return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
592
- const t = Math.max(50, Math.cbrt(X.expectedNodeCount) * 10), i = e.position ?? {
626
+ const t = Math.max(50, Math.cbrt(q.expectedNodeCount) * 10), i = e.position ?? {
593
627
  x: (Math.random() - 0.5) * t,
594
628
  y: (Math.random() - 0.5) * t,
595
629
  z: (Math.random() - 0.5) * t
596
- }, o = {
630
+ }, n = {
597
631
  ...e,
598
632
  position: i,
599
633
  velocity: { x: 0, y: 0, z: 0 },
600
634
  mass: 1
601
- }, n = this.nodeFactory.createNode(
635
+ }, o = this.nodeFactory.createNode(
602
636
  { ...e, position: i },
603
637
  s
604
638
  );
605
- return this.sceneManager.add(n.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, n), !0;
639
+ return this.sceneManager.add(o.group), this.nodes.set(e.id, n), this.nodeObjects.set(e.id, o), this.nodeObjectCacheDirty = !0, !0;
606
640
  }
607
641
  /**
608
642
  * Removes a node from the graph
@@ -610,15 +644,15 @@ const X = class X {
610
644
  */
611
645
  removeNode(e) {
612
646
  const s = this.nodes.get(e), t = this.nodeObjects.get(e);
613
- return !s || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), !0);
647
+ return !s || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), this.nodeObjectCacheDirty = !0, !0);
614
648
  }
615
649
  /**
616
650
  * Updates a node's properties
617
651
  */
618
652
  updateNode(e, s) {
619
653
  const t = this.nodes.get(e), i = this.nodeObjects.get(e);
620
- return !t || !i ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(i, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(i, s.color)), Object.keys(s).forEach((o) => {
621
- o !== "id" && o !== "label" && o !== "color" && o !== "position" && (t[o] = s[o]);
654
+ return !t || !i ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(i, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(i, s.color)), Object.keys(s).forEach((n) => {
655
+ n !== "id" && n !== "label" && n !== "color" && n !== "position" && (t[n] = s[n]);
622
656
  }), !0);
623
657
  }
624
658
  /**
@@ -657,10 +691,12 @@ const X = class X {
657
691
  * Gets all node objects (for raycasting)
658
692
  */
659
693
  getAllNodeObjects() {
694
+ if (!this.nodeObjectCacheDirty)
695
+ return this.nodeObjectCache;
660
696
  const e = [];
661
697
  return this.nodeObjects.forEach((s) => {
662
698
  e.push(s.group);
663
- }), e;
699
+ }), this.nodeObjectCache = e, this.nodeObjectCacheDirty = !1, this.nodeObjectCache;
664
700
  }
665
701
  /**
666
702
  * Gets all spheres (for raycasting)
@@ -683,6 +719,41 @@ const X = class X {
683
719
  clear() {
684
720
  this.nodes.forEach((e, s) => {
685
721
  this.removeNode(s);
722
+ }), this.nodeObjectCacheDirty = !0;
723
+ }
724
+ /**
725
+ * Controls label rendering strategy for performance
726
+ */
727
+ setLabelRenderMode(e) {
728
+ this.labelRenderMode = e;
729
+ }
730
+ /**
731
+ * Sets camera distance threshold used in adaptive label mode
732
+ */
733
+ setLabelDistance(e) {
734
+ this.labelDistance = Math.max(20, e);
735
+ }
736
+ /**
737
+ * Updates label visibility for all nodes
738
+ */
739
+ updateLabelVisibility(e, s = null, t = null) {
740
+ this.nodeObjects.forEach((i, n) => {
741
+ const o = n === s || n === t;
742
+ if (this.labelRenderMode === "none") {
743
+ i.label.visible = o;
744
+ return;
745
+ }
746
+ if (this.labelRenderMode === "all") {
747
+ i.label.visible = !0;
748
+ return;
749
+ }
750
+ const a = this.nodes.get(n);
751
+ if (!a) {
752
+ i.label.visible = !1;
753
+ return;
754
+ }
755
+ const c = a.position.x - e.x, h = a.position.y - e.y, d = a.position.z - e.z, f = c * c + h * h + d * d, m = o || f <= this.labelDistance * this.labelDistance;
756
+ i.label.visible = m;
686
757
  });
687
758
  }
688
759
  /**
@@ -693,8 +764,8 @@ const X = class X {
693
764
  }
694
765
  };
695
766
  // Scale spawn volume with expected graph size
696
- l(X, "expectedNodeCount", 100);
697
- let ee = X;
767
+ l(q, "expectedNodeCount", 100);
768
+ let ee = q;
698
769
  class mt {
699
770
  constructor(e, s, t) {
700
771
  l(this, "sceneManager");
@@ -704,6 +775,8 @@ class mt {
704
775
  l(this, "edges", []);
705
776
  // One visual line per directed pair (source -> target)
706
777
  l(this, "edgeObjects", []);
778
+ l(this, "edgeLineCache", []);
779
+ l(this, "edgeLineCacheDirty", !0);
707
780
  // Tracks which directed pairs currently have at least one relationship
708
781
  l(this, "edgeKeySet", /* @__PURE__ */ new Set());
709
782
  // Maps directed pair key to the relationship list for that pair
@@ -711,6 +784,9 @@ class mt {
711
784
  // Maps directed pair key to the corresponding visual line
712
785
  l(this, "edgeObjectMap", /* @__PURE__ */ new Map());
713
786
  l(this, "highlightedEdgeKey", null);
787
+ l(this, "batchRenderingEnabled", !1);
788
+ l(this, "batchLineSegments", null);
789
+ l(this, "batchGeometryDirty", !1);
714
790
  this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
715
791
  }
716
792
  /**
@@ -725,7 +801,7 @@ class mt {
725
801
  * @returns true if added, false if invalid or nodes don't exist
726
802
  */
727
803
  addEdge(e) {
728
- if (!De(e))
804
+ if (!he(e))
729
805
  return !1;
730
806
  if (!this.nodeManager.hasNode(e.source))
731
807
  return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
@@ -733,20 +809,20 @@ class mt {
733
809
  return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
734
810
  const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target), i = this.createDirectedEdgeKey(e.source, e.target);
735
811
  this.edges.push(e);
736
- const o = this.edgeGroups.get(i);
737
- if (o) {
738
- o.push(e);
739
- const r = this.edgeObjectMap.get(i);
740
- return r && this.updateEdgeUserData(r, o, s, t), !0;
812
+ const n = this.edgeGroups.get(i);
813
+ if (n) {
814
+ n.push(e);
815
+ const a = this.edgeObjectMap.get(i);
816
+ return a && this.updateEdgeUserData(a, n, s, t), !0;
741
817
  }
742
- const n = this.edgeFactory.createEdge(
818
+ const o = this.edgeFactory.createEdge(
743
819
  e,
744
820
  s,
745
821
  t,
746
822
  s.position,
747
823
  t.position
748
824
  );
749
- return this.sceneManager.add(n.line), this.edgeGroups.set(i, [e]), this.edgeObjectMap.set(i, n), this.edgeObjects.push(n), this.edgeKeySet.add(i), this.updateEdgeUserData(n, [e], s, t), !0;
825
+ return this.batchRenderingEnabled || this.sceneManager.add(o.line), this.edgeGroups.set(i, [e]), this.edgeObjectMap.set(i, o), this.edgeObjects.push(o), this.edgeLineCacheDirty = !0, this.edgeKeySet.add(i), this.updateEdgeUserData(o, [e], s, t), this.batchRenderingEnabled && (this.batchGeometryDirty = !0), !0;
750
826
  }
751
827
  /**
752
828
  * Removes an edge from the graph
@@ -758,32 +834,32 @@ class mt {
758
834
  if (!this.edgeKeySet.has(t))
759
835
  return !1;
760
836
  const i = this.edges.findIndex(
761
- (c) => c.source === e && c.target === s
837
+ (h) => h.source === e && h.target === s
762
838
  );
763
839
  if (i === -1)
764
840
  return !1;
765
- const o = this.edges[i];
841
+ const n = this.edges[i];
766
842
  this.edges.splice(i, 1);
767
- const n = this.edgeGroups.get(t);
768
- if (!n)
843
+ const o = this.edgeGroups.get(t);
844
+ if (!o)
769
845
  return !0;
770
- const r = n.findIndex((c) => c === o);
771
- if (r !== -1)
772
- n.splice(r, 1);
846
+ const a = o.findIndex((h) => h === n);
847
+ if (a !== -1)
848
+ o.splice(a, 1);
773
849
  else {
774
- const c = n.findIndex((p) => p.source === e && p.target === s);
775
- c !== -1 && n.splice(c, 1);
850
+ const h = o.findIndex((d) => d.source === e && d.target === s);
851
+ h !== -1 && o.splice(h, 1);
776
852
  }
777
- const d = this.edgeObjectMap.get(t);
778
- if (!d)
853
+ const c = this.edgeObjectMap.get(t);
854
+ if (!c)
779
855
  return !0;
780
- if (n.length === 0) {
781
- this.sceneManager.remove(d.line), this.edgeFactory.disposeEdge(d);
782
- const c = this.edgeObjects.findIndex((p) => p === d);
783
- c !== -1 && this.edgeObjects.splice(c, 1), this.edgeGroups.delete(t), this.edgeObjectMap.delete(t), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null);
856
+ if (o.length === 0) {
857
+ this.sceneManager.remove(c.line), this.edgeFactory.disposeEdge(c);
858
+ const h = this.edgeObjects.findIndex((d) => d === c);
859
+ h !== -1 && (this.edgeObjects.splice(h, 1), this.edgeLineCacheDirty = !0), this.edgeGroups.delete(t), this.edgeObjectMap.delete(t), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), this.batchRenderingEnabled && (this.batchGeometryDirty = !0);
784
860
  } else {
785
- const c = this.nodeManager.getNode(e), p = this.nodeManager.getNode(s);
786
- c && p && this.updateEdgeUserData(d, n, c, p);
861
+ const h = this.nodeManager.getNode(e), d = this.nodeManager.getNode(s);
862
+ h && d && this.updateEdgeUserData(c, o, h, d), this.batchRenderingEnabled && (this.batchGeometryDirty = !0);
787
863
  }
788
864
  return !0;
789
865
  }
@@ -834,6 +910,14 @@ class mt {
834
910
  * Updates all edge positions based on current node positions
835
911
  */
836
912
  updateEdgePositions() {
913
+ if (this.batchRenderingEnabled && this.batchGeometryDirty) {
914
+ this.rebuildBatchGeometry(), this.batchGeometryDirty = !1;
915
+ return;
916
+ }
917
+ if (this.batchRenderingEnabled && this.batchLineSegments) {
918
+ this.updateBatchEdgePositions();
919
+ return;
920
+ }
837
921
  this.edgeObjects.forEach((e) => {
838
922
  const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target);
839
923
  s && t && this.edgeFactory.updateEdgePositions(
@@ -853,7 +937,7 @@ class mt {
853
937
  * Gets all edge line objects (for raycasting)
854
938
  */
855
939
  getAllEdgeLines() {
856
- return this.edgeObjects.map((e) => e.line);
940
+ return this.edgeLineCacheDirty ? (this.edgeLineCache = this.edgeObjects.map((e) => e.line), this.edgeLineCacheDirty = !1, this.edgeLineCache) : this.edgeLineCache;
857
941
  }
858
942
  /**
859
943
  * Gets edge count
@@ -867,7 +951,23 @@ class mt {
867
951
  clear() {
868
952
  this.edgeObjects.forEach((e) => {
869
953
  this.sceneManager.remove(e.line), this.edgeFactory.disposeEdge(e);
870
- }), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null;
954
+ }), this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edges = [], this.edgeObjects = [], this.edgeLineCache = [], this.edgeLineCacheDirty = !0, this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null, this.batchGeometryDirty = !1;
955
+ }
956
+ /**
957
+ * Enables batched edge rendering (single LineSegments draw call)
958
+ */
959
+ setBatchRenderingEnabled(e) {
960
+ this.batchRenderingEnabled !== e && (this.batchRenderingEnabled = e, e ? (this.edgeObjects.forEach((s) => {
961
+ this.sceneManager.remove(s.line);
962
+ }), this.batchGeometryDirty = !0, this.rebuildBatchGeometry()) : (this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edgeObjects.forEach((s) => {
963
+ this.sceneManager.add(s.line);
964
+ }), this.batchGeometryDirty = !1));
965
+ }
966
+ /**
967
+ * Checks whether batch rendering is active
968
+ */
969
+ isBatchRenderingEnabled() {
970
+ return this.batchRenderingEnabled;
871
971
  }
872
972
  /**
873
973
  * Creates a directed key (source -> target)
@@ -879,18 +979,46 @@ class mt {
879
979
  * Syncs relationship metadata to the line for hover/click UI
880
980
  */
881
981
  updateEdgeUserData(e, s, t, i) {
882
- const o = s[0];
982
+ const n = s[0];
883
983
  e.line.userData = {
884
984
  ...e.line.userData,
885
985
  source: e.source,
886
986
  target: e.target,
887
- edge: o,
987
+ edge: n,
888
988
  relationships: [...s],
889
989
  relationshipCount: s.length,
890
990
  sourceNode: t,
891
991
  targetNode: i
892
992
  };
893
993
  }
994
+ /**
995
+ * Rebuilds merged edge geometry used for low-end rendering mode
996
+ */
997
+ rebuildBatchGeometry() {
998
+ if (!this.batchRenderingEnabled || (this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edgeObjects.length === 0)) return;
999
+ const e = new Float32Array(this.edgeObjects.length * 6), s = new y.BufferGeometry();
1000
+ s.setAttribute("position", new y.BufferAttribute(e, 3)), this.batchLineSegments = new y.LineSegments(
1001
+ s,
1002
+ this.edgeFactory.getSharedEdgeMaterial()
1003
+ ), this.batchLineSegments.name = "f3d-edge-batch", this.sceneManager.add(this.batchLineSegments), this.updateBatchEdgePositions();
1004
+ }
1005
+ /**
1006
+ * Updates merged edge geometry positions from node coordinates
1007
+ */
1008
+ updateBatchEdgePositions() {
1009
+ if (!this.batchLineSegments) return;
1010
+ const e = this.batchLineSegments.geometry.getAttribute("position"), s = e.array;
1011
+ let t = 0;
1012
+ for (const i of this.edgeObjects) {
1013
+ const n = this.nodeManager.getNode(i.source), o = this.nodeManager.getNode(i.target);
1014
+ if (!n || !o) {
1015
+ t += 6;
1016
+ continue;
1017
+ }
1018
+ s[t++] = n.position.x, s[t++] = n.position.y, s[t++] = n.position.z, s[t++] = o.position.x, s[t++] = o.position.y, s[t++] = o.position.z;
1019
+ }
1020
+ e.needsUpdate = !0, this.batchLineSegments.geometry.computeBoundingSphere();
1021
+ }
894
1022
  /**
895
1023
  * Dispose resources
896
1024
  */
@@ -898,7 +1026,7 @@ class mt {
898
1026
  this.clear();
899
1027
  }
900
1028
  }
901
- class Re {
1029
+ class Oe {
902
1030
  constructor(e, s, t = {}) {
903
1031
  l(this, "nodes");
904
1032
  l(this, "edges");
@@ -936,10 +1064,10 @@ class Re {
936
1064
  e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
937
1065
  console.log(`[ForceGraph3D] initializeNodeMassAndPin: ${this.edges.length} edges, ${this.nodes.size} nodes, ${e.size} unique endpoints`);
938
1066
  let s = 0, t = null;
939
- for (const [i, o] of e) {
940
- o > s && (s = o, t = i);
941
- const n = this.nodes.get(i);
942
- n && (n.mass = 1 + Math.log2(1 + o));
1067
+ for (const [i, n] of e) {
1068
+ n > s && (s = n, t = i);
1069
+ const o = this.nodes.get(i);
1070
+ o && (o.mass = 1 + Math.log2(1 + n));
943
1071
  }
944
1072
  if (console.log(`[ForceGraph3D] Hub detected: id="${t}", degree=${s}, threshold=10`), t && s > 10) {
945
1073
  this.pinnedNodeId = t;
@@ -970,14 +1098,14 @@ class Re {
970
1098
  calculateRepulsionBruteForce() {
971
1099
  const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
972
1100
  for (let i = 0; i < s; i++) {
973
- const o = e[i];
974
- for (let n = i + 1; n < s; n++) {
975
- const r = e[n], d = r.position.x - o.position.x, c = r.position.y - o.position.y, p = r.position.z - o.position.z;
976
- let f = d * d + c * c + p * p;
1101
+ const n = e[i];
1102
+ for (let o = i + 1; o < s; o++) {
1103
+ const a = e[o], c = a.position.x - n.position.x, h = a.position.y - n.position.y, d = a.position.z - n.position.z;
1104
+ let f = c * c + h * h + d * d;
977
1105
  if (f > this.REPULSION_CUTOFF_SQ) continue;
978
1106
  f < 0.01 && (f = 0.01);
979
- const m = Math.sqrt(f), u = t * this.alpha / f, y = d / m * u, b = c / m * u, w = p / m * u;
980
- o.velocity.x -= y / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= w / o.mass, r.velocity.x += y / r.mass, r.velocity.y += b / r.mass, r.velocity.z += w / r.mass;
1107
+ const m = Math.sqrt(f), u = t * this.alpha / f, b = c / m * u, x = h / m * u, M = d / m * u;
1108
+ n.velocity.x -= b / n.mass, n.velocity.y -= x / n.mass, n.velocity.z -= M / n.mass, a.velocity.x += b / a.mass, a.velocity.y += x / a.mass, a.velocity.z += M / a.mass;
981
1109
  }
982
1110
  }
983
1111
  }
@@ -998,26 +1126,26 @@ class Re {
998
1126
  return;
999
1127
  }
1000
1128
  if (s.mass === 0) return;
1001
- const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z, n = t * t + i * i + o * o;
1002
- if (n > this.REPULSION_CUTOFF_SQ) return;
1003
- const r = Math.sqrt(n), d = this.getEffectiveRepulsion();
1004
- if (r > 0 && s.size / r < this.barnesHutTheta) {
1005
- const c = Math.max(n, 0.01), p = d * this.alpha * s.mass / c;
1006
- e.velocity.x -= t / r * p / e.mass, e.velocity.y -= i / r * p / e.mass, e.velocity.z -= o / r * p / e.mass;
1129
+ const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, n = s.centerOfMass.z - e.position.z, o = t * t + i * i + n * n;
1130
+ if (o > this.REPULSION_CUTOFF_SQ) return;
1131
+ const a = Math.sqrt(o), c = this.getEffectiveRepulsion();
1132
+ if (a > 0 && s.size / a < this.barnesHutTheta) {
1133
+ const h = Math.max(o, 0.01), d = c * this.alpha * s.mass / h;
1134
+ e.velocity.x -= t / a * d / e.mass, e.velocity.y -= i / a * d / e.mass, e.velocity.z -= n / a * d / e.mass;
1007
1135
  } else
1008
- for (const c of s.children)
1009
- c && this.calculateForceFromOctree(e, c);
1136
+ for (const h of s.children)
1137
+ h && this.calculateForceFromOctree(e, h);
1010
1138
  }
1011
1139
  /**
1012
1140
  * Apply repulsion between two nodes (with cutoff)
1013
1141
  */
1014
1142
  applyRepulsionBetween(e, s) {
1015
- const t = s.position.x - e.position.x, i = s.position.y - e.position.y, o = s.position.z - e.position.z;
1016
- let n = t * t + i * i + o * o;
1017
- if (n > this.REPULSION_CUTOFF_SQ) return;
1018
- n < 0.01 && (n = 0.01);
1019
- const r = Math.sqrt(n), c = this.getEffectiveRepulsion() * this.alpha / n;
1020
- e.velocity.x -= t / r * c / e.mass, e.velocity.y -= i / r * c / e.mass, e.velocity.z -= o / r * c / e.mass;
1143
+ const t = s.position.x - e.position.x, i = s.position.y - e.position.y, n = s.position.z - e.position.z;
1144
+ let o = t * t + i * i + n * n;
1145
+ if (o > this.REPULSION_CUTOFF_SQ) return;
1146
+ o < 0.01 && (o = 0.01);
1147
+ const a = Math.sqrt(o), h = this.getEffectiveRepulsion() * this.alpha / o;
1148
+ e.velocity.x -= t / a * h / e.mass, e.velocity.y -= i / a * h / e.mass, e.velocity.z -= n / a * h / e.mass;
1021
1149
  }
1022
1150
  /**
1023
1151
  * Calculate attraction forces along edges
@@ -1025,12 +1153,12 @@ class Re {
1025
1153
  calculateAttraction() {
1026
1154
  const e = this.nodes.size, s = e > 500 ? 1 + Math.log10(e / 500) : 1;
1027
1155
  for (const t of this.edges) {
1028
- const i = this.nodes.get(t.source), o = this.nodes.get(t.target);
1029
- if (!i || !o) continue;
1030
- const n = o.position.x - i.position.x, r = o.position.y - i.position.y, d = o.position.z - i.position.z, c = Math.sqrt(n * n + r * r + d * d);
1031
- if (c < 0.01) continue;
1032
- const f = (c - 15) * this.attractionStrength * s * this.alpha, m = n / c * f, u = r / c * f, y = d / c * f;
1033
- i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += y / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= y / o.mass;
1156
+ const i = this.nodes.get(t.source), n = this.nodes.get(t.target);
1157
+ if (!i || !n) continue;
1158
+ const o = n.position.x - i.position.x, a = n.position.y - i.position.y, c = n.position.z - i.position.z, h = Math.sqrt(o * o + a * a + c * c);
1159
+ if (h < 0.01) continue;
1160
+ const f = (h - 15) * this.attractionStrength * s * this.alpha, m = o / h * f, u = a / h * f, b = c / h * f;
1161
+ i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += b / i.mass, n.velocity.x -= m / n.mass, n.velocity.y -= u / n.mass, n.velocity.z -= b / n.mass;
1034
1162
  }
1035
1163
  }
1036
1164
  /**
@@ -1100,8 +1228,8 @@ class yt {
1100
1228
  max: { x: 100, y: 100, z: 100 }
1101
1229
  };
1102
1230
  const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
1103
- for (const o of e)
1104
- s.x = Math.min(s.x, o.position.x), s.y = Math.min(s.y, o.position.y), s.z = Math.min(s.z, o.position.z), t.x = Math.max(t.x, o.position.x), t.y = Math.max(t.y, o.position.y), t.z = Math.max(t.z, o.position.z);
1231
+ for (const n of e)
1232
+ s.x = Math.min(s.x, n.position.x), s.y = Math.min(s.y, n.position.y), s.z = Math.min(s.z, n.position.z), t.x = Math.max(t.x, n.position.x), t.y = Math.max(t.y, n.position.y), t.z = Math.max(t.z, n.position.z);
1105
1233
  const i = 10;
1106
1234
  return s.x -= i, s.y -= i, s.z -= i, t.x += i, t.y += i, t.z += i, { min: s, max: t };
1107
1235
  }
@@ -1123,42 +1251,42 @@ class yt {
1123
1251
  };
1124
1252
  if (e.length === 1 || t > 20) {
1125
1253
  let u = 0;
1126
- const y = { x: 0, y: 0, z: 0 };
1127
- for (const b of e)
1128
- u += b.mass, y.x += b.position.x * b.mass, y.y += b.position.y * b.mass, y.z += b.position.z * b.mass;
1129
- return u > 0 && (y.x /= u, y.y /= u, y.z /= u), {
1254
+ const b = { x: 0, y: 0, z: 0 };
1255
+ for (const x of e)
1256
+ u += x.mass, b.x += x.position.x * x.mass, b.y += x.position.y * x.mass, b.z += x.position.z * x.mass;
1257
+ return u > 0 && (b.x /= u, b.y /= u, b.z /= u), {
1130
1258
  bounds: s,
1131
1259
  size: i,
1132
- centerOfMass: y,
1260
+ centerOfMass: b,
1133
1261
  mass: u,
1134
1262
  isLeaf: !0,
1135
1263
  node: e[0],
1136
1264
  children: []
1137
1265
  };
1138
1266
  }
1139
- const o = (s.min.x + s.max.x) / 2, n = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, d = [[], [], [], [], [], [], [], []];
1267
+ const n = (s.min.x + s.max.x) / 2, o = (s.min.y + s.max.y) / 2, a = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
1140
1268
  for (const u of e) {
1141
- const y = (u.position.x >= o ? 1 : 0) + (u.position.y >= n ? 2 : 0) + (u.position.z >= r ? 4 : 0);
1142
- d[y].push(u);
1143
- }
1144
- const c = [
1145
- { min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: n, z: r } },
1146
- { min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: n, z: r } },
1147
- { min: { x: s.min.x, y: n, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
1148
- { min: { x: o, y: n, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
1149
- { min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y: n, z: s.max.z } },
1150
- { min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y: n, z: s.max.z } },
1151
- { min: { x: s.min.x, y: n, z: r }, max: { x: o, y: s.max.y, z: s.max.z } },
1152
- { min: { x: o, y: n, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
1153
- ], p = [];
1269
+ const b = (u.position.x >= n ? 1 : 0) + (u.position.y >= o ? 2 : 0) + (u.position.z >= a ? 4 : 0);
1270
+ c[b].push(u);
1271
+ }
1272
+ const h = [
1273
+ { min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: n, y: o, z: a } },
1274
+ { min: { x: n, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: o, z: a } },
1275
+ { min: { x: s.min.x, y: o, z: s.min.z }, max: { x: n, y: s.max.y, z: a } },
1276
+ { min: { x: n, y: o, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: a } },
1277
+ { min: { x: s.min.x, y: s.min.y, z: a }, max: { x: n, y: o, z: s.max.z } },
1278
+ { min: { x: n, y: s.min.y, z: a }, max: { x: s.max.x, y: o, z: s.max.z } },
1279
+ { min: { x: s.min.x, y: o, z: a }, max: { x: n, y: s.max.y, z: s.max.z } },
1280
+ { min: { x: n, y: o, z: a }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
1281
+ ], d = [];
1154
1282
  let f = 0;
1155
1283
  const m = { x: 0, y: 0, z: 0 };
1156
1284
  for (let u = 0; u < 8; u++)
1157
- if (d[u].length > 0) {
1158
- const y = this.buildTree(d[u], c[u], t + 1);
1159
- p.push(y), f += y.mass, m.x += y.centerOfMass.x * y.mass, m.y += y.centerOfMass.y * y.mass, m.z += y.centerOfMass.z * y.mass;
1285
+ if (c[u].length > 0) {
1286
+ const b = this.buildTree(c[u], h[u], t + 1);
1287
+ d.push(b), f += b.mass, m.x += b.centerOfMass.x * b.mass, m.y += b.centerOfMass.y * b.mass, m.z += b.centerOfMass.z * b.mass;
1160
1288
  } else
1161
- p.push(null);
1289
+ d.push(null);
1162
1290
  return f > 0 && (m.x /= f, m.y /= f, m.z /= f), {
1163
1291
  bounds: s,
1164
1292
  size: i,
@@ -1166,11 +1294,11 @@ class yt {
1166
1294
  mass: f,
1167
1295
  isLeaf: !1,
1168
1296
  node: null,
1169
- children: p
1297
+ children: d
1170
1298
  };
1171
1299
  }
1172
1300
  }
1173
- class xt {
1301
+ class bt {
1174
1302
  constructor(e, s, t, i = 60) {
1175
1303
  l(this, "sceneManager");
1176
1304
  l(this, "animationId", null);
@@ -1235,7 +1363,222 @@ class xt {
1235
1363
  this.stop();
1236
1364
  }
1237
1365
  }
1238
- class bt {
1366
+ class xt {
1367
+ constructor() {
1368
+ l(this, "worker", null);
1369
+ l(this, "nodeIds", []);
1370
+ l(this, "pendingStep", !1);
1371
+ l(this, "latestFrame", null);
1372
+ typeof Worker > "u" || typeof URL > "u" || typeof Blob > "u" || (this.worker = this.createWorker(), this.worker.onmessage = (e) => {
1373
+ const s = e.data;
1374
+ s.type === "positions" && s.positions && (this.latestFrame = {
1375
+ ids: this.nodeIds,
1376
+ positions: s.positions
1377
+ }, this.pendingStep = !1);
1378
+ });
1379
+ }
1380
+ isAvailable() {
1381
+ return this.worker !== null;
1382
+ }
1383
+ init(e, s, t) {
1384
+ if (!this.worker) return;
1385
+ this.nodeIds = Array.from(e.keys());
1386
+ const i = /* @__PURE__ */ new Map(), n = [];
1387
+ let o = 0;
1388
+ e.forEach((c, h) => {
1389
+ i.set(h, o++), n.push({
1390
+ id: h,
1391
+ x: c.position.x,
1392
+ y: c.position.y,
1393
+ z: c.position.z,
1394
+ mass: c.mass
1395
+ });
1396
+ });
1397
+ const a = [];
1398
+ s.forEach((c) => {
1399
+ const h = i.get(c.source), d = i.get(c.target);
1400
+ h !== void 0 && d !== void 0 && a.push([h, d]);
1401
+ }), this.latestFrame = null, this.pendingStep = !1, this.worker.postMessage({
1402
+ type: "init",
1403
+ nodes: n,
1404
+ edges: a,
1405
+ params: t
1406
+ });
1407
+ }
1408
+ setPhysicsParams(e) {
1409
+ this.worker && this.worker.postMessage({ type: "params", params: e });
1410
+ }
1411
+ requestStep() {
1412
+ !this.worker || this.pendingStep || (this.pendingStep = !0, this.worker.postMessage({ type: "step" }));
1413
+ }
1414
+ consumeLatestFrame() {
1415
+ if (!this.latestFrame) return null;
1416
+ const e = this.latestFrame;
1417
+ return this.latestFrame = null, e;
1418
+ }
1419
+ dispose() {
1420
+ this.worker && (this.worker.terminate(), this.worker = null), this.latestFrame = null, this.pendingStep = !1, this.nodeIds = [];
1421
+ }
1422
+ createWorker() {
1423
+ const e = `
1424
+ let nodes = [];
1425
+ let edges = [];
1426
+ let alpha = 1;
1427
+ let repulsion = 180;
1428
+ let attraction = 0.008;
1429
+ let damping = 0.95;
1430
+ let randState = 123456789;
1431
+
1432
+ function random() {
1433
+ randState = (1664525 * randState + 1013904223) >>> 0;
1434
+ return randState / 4294967296;
1435
+ }
1436
+
1437
+ function applySampledRepulsion() {
1438
+ const n = nodes.length;
1439
+ if (n < 2) return;
1440
+ const sampleCount = n > 2500 ? 18 : n > 1200 ? 24 : 40;
1441
+ const nScale = n > 0 ? Math.sqrt(n / 500) : 1;
1442
+
1443
+ for (let i = 0; i < n; i++) {
1444
+ const nodeA = nodes[i];
1445
+ for (let s = 0; s < sampleCount; s++) {
1446
+ const j = (i + 1 + Math.floor(random() * (n - 1))) % n;
1447
+ const nodeB = nodes[j];
1448
+ const dx = nodeB.x - nodeA.x;
1449
+ const dy = nodeB.y - nodeA.y;
1450
+ const dz = nodeB.z - nodeA.z;
1451
+ let distSq = dx * dx + dy * dy + dz * dz;
1452
+ if (distSq > 40000) continue;
1453
+ if (distSq < 0.01) distSq = 0.01;
1454
+ const dist = Math.sqrt(distSq);
1455
+ const force = (repulsion * alpha) / (distSq * nScale);
1456
+ const fx = (dx / dist) * force;
1457
+ const fy = (dy / dist) * force;
1458
+ const fz = (dz / dist) * force;
1459
+ nodeA.vx -= fx / nodeA.mass;
1460
+ nodeA.vy -= fy / nodeA.mass;
1461
+ nodeA.vz -= fz / nodeA.mass;
1462
+ }
1463
+ }
1464
+ }
1465
+
1466
+ function applyAttraction() {
1467
+ const targetDist = 15;
1468
+ const attrMul = nodes.length > 500 ? 1 + Math.log10(nodes.length / 500) : 1;
1469
+ for (let i = 0; i < edges.length; i++) {
1470
+ const e = edges[i];
1471
+ const source = nodes[e[0]];
1472
+ const target = nodes[e[1]];
1473
+ if (!source || !target) continue;
1474
+ const dx = target.x - source.x;
1475
+ const dy = target.y - source.y;
1476
+ const dz = target.z - source.z;
1477
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
1478
+ if (dist < 0.01) continue;
1479
+ const force = (dist - targetDist) * attraction * attrMul * alpha;
1480
+ const fx = (dx / dist) * force;
1481
+ const fy = (dy / dist) * force;
1482
+ const fz = (dz / dist) * force;
1483
+ source.vx += fx / source.mass;
1484
+ source.vy += fy / source.mass;
1485
+ source.vz += fz / source.mass;
1486
+ target.vx -= fx / target.mass;
1487
+ target.vy -= fy / target.mass;
1488
+ target.vz -= fz / target.mass;
1489
+ }
1490
+ }
1491
+
1492
+ function integrate() {
1493
+ const gravity = 0.05 * alpha;
1494
+ const maxVelocity = 5;
1495
+
1496
+ for (let i = 0; i < nodes.length; i++) {
1497
+ const node = nodes[i];
1498
+ node.vx -= node.x * gravity;
1499
+ node.vy -= node.y * gravity;
1500
+ node.vz -= node.z * gravity;
1501
+
1502
+ node.vx *= damping;
1503
+ node.vy *= damping;
1504
+ node.vz *= damping;
1505
+
1506
+ const speed = Math.sqrt(node.vx * node.vx + node.vy * node.vy + node.vz * node.vz);
1507
+ if (speed > maxVelocity) {
1508
+ const scale = maxVelocity / speed;
1509
+ node.vx *= scale;
1510
+ node.vy *= scale;
1511
+ node.vz *= scale;
1512
+ }
1513
+
1514
+ node.x += node.vx;
1515
+ node.y += node.vy;
1516
+ node.z += node.vz;
1517
+ }
1518
+
1519
+ alpha += (0 - alpha) * 0.0228;
1520
+ if (alpha < 0.001) {
1521
+ alpha = 0.001;
1522
+ }
1523
+ }
1524
+
1525
+ function step() {
1526
+ if (nodes.length === 0) return;
1527
+ applySampledRepulsion();
1528
+ applyAttraction();
1529
+ integrate();
1530
+
1531
+ const positions = new Float32Array(nodes.length * 3);
1532
+ for (let i = 0; i < nodes.length; i++) {
1533
+ const node = nodes[i];
1534
+ const base = i * 3;
1535
+ positions[base] = node.x;
1536
+ positions[base + 1] = node.y;
1537
+ positions[base + 2] = node.z;
1538
+ }
1539
+
1540
+ postMessage({ type: 'positions', positions }, [positions.buffer]);
1541
+ }
1542
+
1543
+ self.onmessage = (event) => {
1544
+ const msg = event.data || {};
1545
+ if (msg.type === 'init') {
1546
+ nodes = (msg.nodes || []).map((node) => ({
1547
+ id: node.id,
1548
+ x: node.x,
1549
+ y: node.y,
1550
+ z: node.z,
1551
+ vx: 0,
1552
+ vy: 0,
1553
+ vz: 0,
1554
+ mass: node.mass || 1,
1555
+ }));
1556
+ edges = msg.edges || [];
1557
+ alpha = 1;
1558
+ if (msg.params) {
1559
+ if (typeof msg.params.repulsionStrength === 'number') repulsion = msg.params.repulsionStrength;
1560
+ if (typeof msg.params.attractionStrength === 'number') attraction = msg.params.attractionStrength;
1561
+ if (typeof msg.params.damping === 'number') damping = msg.params.damping;
1562
+ }
1563
+ return;
1564
+ }
1565
+
1566
+ if (msg.type === 'params') {
1567
+ if (typeof msg.params?.repulsionStrength === 'number') repulsion = msg.params.repulsionStrength;
1568
+ if (typeof msg.params?.attractionStrength === 'number') attraction = msg.params.attractionStrength;
1569
+ if (typeof msg.params?.damping === 'number') damping = msg.params.damping;
1570
+ return;
1571
+ }
1572
+
1573
+ if (msg.type === 'step') {
1574
+ step();
1575
+ }
1576
+ };
1577
+ `, s = new Blob([e], { type: "application/javascript" }), t = URL.createObjectURL(s), i = new Worker(t);
1578
+ return URL.revokeObjectURL(t), i;
1579
+ }
1580
+ }
1581
+ class vt {
1239
1582
  constructor() {
1240
1583
  l(this, "envMap", null);
1241
1584
  l(this, "materialCache", /* @__PURE__ */ new Map());
@@ -1274,9 +1617,9 @@ class bt {
1274
1617
  // -z
1275
1618
  ];
1276
1619
  for (const i of t) {
1277
- const o = document.createElement("canvas");
1278
- o.width = 256, o.height = 256;
1279
- const n = o.getContext("2d"), r = n.createRadialGradient(
1620
+ const n = document.createElement("canvas");
1621
+ n.width = 256, n.height = 256;
1622
+ const o = n.getContext("2d"), a = o.createRadialGradient(
1280
1623
  256 / 2,
1281
1624
  256 / 2,
1282
1625
  0,
@@ -1284,17 +1627,17 @@ class bt {
1284
1627
  256 / 2,
1285
1628
  256 * 0.8
1286
1629
  );
1287
- r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), n.fillStyle = r, n.fillRect(0, 0, 256, 256);
1288
- const d = n.getImageData(0, 0, 256, 256);
1289
- for (let c = 0; c < d.data.length; c += 4) {
1290
- const p = (Math.random() - 0.5) * 5;
1291
- d.data[c] = Math.min(255, Math.max(0, d.data[c] + p)), d.data[c + 1] = Math.min(255, Math.max(0, d.data[c + 1] + p)), d.data[c + 2] = Math.min(255, Math.max(0, d.data[c + 2] + p));
1630
+ a.addColorStop(0, i.colors[0]), a.addColorStop(0.5, i.colors[1]), a.addColorStop(1, i.colors[2]), o.fillStyle = a, o.fillRect(0, 0, 256, 256);
1631
+ const c = o.getImageData(0, 0, 256, 256);
1632
+ for (let h = 0; h < c.data.length; h += 4) {
1633
+ const d = (Math.random() - 0.5) * 5;
1634
+ c.data[h] = Math.min(255, Math.max(0, c.data[h] + d)), c.data[h + 1] = Math.min(255, Math.max(0, c.data[h + 1] + d)), c.data[h + 2] = Math.min(255, Math.max(0, c.data[h + 2] + d));
1292
1635
  }
1293
- n.putImageData(d, 0, 0), s.push(o);
1636
+ o.putImageData(c, 0, 0), s.push(n);
1294
1637
  }
1295
- this.envMap = new x.CubeTexture(s.map((i) => {
1296
- const o = new Image();
1297
- return o.src = i.toDataURL(), o;
1638
+ this.envMap = new y.CubeTexture(s.map((i) => {
1639
+ const n = new Image();
1640
+ return n.src = i.toDataURL(), n;
1298
1641
  })), this.envMap.needsUpdate = !0;
1299
1642
  }
1300
1643
  /**
@@ -1311,11 +1654,11 @@ class bt {
1311
1654
  const t = "glass-single";
1312
1655
  if (this.materialCache.has(t))
1313
1656
  return this.materialCache.get(t).clone();
1314
- const i = new x.Color(16750950), o = new x.ShaderMaterial({
1657
+ const i = new y.Color(16750950), n = new y.ShaderMaterial({
1315
1658
  uniforms: {
1316
1659
  uColor: { value: i },
1317
1660
  uEnvMap: { value: this.envMap },
1318
- uGlowColor: { value: new x.Color(16777215) },
1661
+ uGlowColor: { value: new y.Color(16777215) },
1319
1662
  uGlowIntensity: { value: 0.8 },
1320
1663
  uReflectivity: { value: 0.4 },
1321
1664
  uFresnelPower: { value: 2.5 }
@@ -1381,17 +1724,17 @@ class bt {
1381
1724
  }
1382
1725
  `,
1383
1726
  transparent: !0,
1384
- side: x.FrontSide,
1727
+ side: y.FrontSide,
1385
1728
  depthWrite: !0,
1386
- blending: x.NormalBlending
1729
+ blending: y.NormalBlending
1387
1730
  });
1388
- return this.materialCache.set(t, o), o.clone();
1731
+ return this.materialCache.set(t, n), n.clone();
1389
1732
  }
1390
1733
  /**
1391
1734
  * Creates material for edges (light color for dark background)
1392
1735
  */
1393
1736
  createEdgeMaterial(e = 6710886, s = 0.4) {
1394
- return new x.LineBasicMaterial({
1737
+ return new y.LineBasicMaterial({
1395
1738
  color: e,
1396
1739
  transparent: !0,
1397
1740
  opacity: s,
@@ -1402,7 +1745,7 @@ class bt {
1402
1745
  * Creates highlighted edge material
1403
1746
  */
1404
1747
  createHighlightedEdgeMaterial() {
1405
- return new x.LineBasicMaterial({
1748
+ return new y.LineBasicMaterial({
1406
1749
  color: 16750950,
1407
1750
  // Tangerine highlight
1408
1751
  transparent: !1,
@@ -1416,11 +1759,11 @@ class bt {
1416
1759
  createLabelMaterial(e, s = 24) {
1417
1760
  const t = document.createElement("canvas"), i = t.getContext("2d");
1418
1761
  i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
1419
- const n = i.measureText(e).width;
1420
- t.width = Math.max(128, n + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
1421
- const r = new x.CanvasTexture(t);
1422
- return r.needsUpdate = !0, new x.SpriteMaterial({
1423
- map: r,
1762
+ const o = i.measureText(e).width;
1763
+ t.width = Math.max(128, o + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
1764
+ const a = new y.CanvasTexture(t);
1765
+ return a.needsUpdate = !0, new y.SpriteMaterial({
1766
+ map: a,
1424
1767
  transparent: !0,
1425
1768
  depthTest: !1,
1426
1769
  depthWrite: !1
@@ -1439,7 +1782,7 @@ class bt {
1439
1782
  this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
1440
1783
  }
1441
1784
  }
1442
- class vt {
1785
+ class Mt {
1443
1786
  constructor(e, s = 2, t = [32, 16, 8], i = 16750950) {
1444
1787
  l(this, "materialFactory");
1445
1788
  l(this, "geometryCache", /* @__PURE__ */ new Map());
@@ -1456,7 +1799,7 @@ class vt {
1456
1799
  const t = `lod-${s}`;
1457
1800
  this.geometryCache.set(
1458
1801
  t,
1459
- new x.SphereGeometry(this.nodeRadius, e, e)
1802
+ new y.SphereGeometry(this.nodeRadius, e, e)
1460
1803
  );
1461
1804
  });
1462
1805
  }
@@ -1471,21 +1814,21 @@ class vt {
1471
1814
  * Creates a node visual (glass ball + label)
1472
1815
  */
1473
1816
  createNode(e, s = 0) {
1474
- const t = new x.Group();
1817
+ const t = new y.Group();
1475
1818
  t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
1476
- const i = this.getGeometry(s), o = this.materialFactory.createGlassMaterial(
1819
+ const i = this.getGeometry(s), n = this.materialFactory.createGlassMaterial(
1477
1820
  e.color ?? this.defaultNodeColor
1478
- ), n = new x.Mesh(i, o);
1479
- n.castShadow = !0, n.receiveShadow = !0, t.add(n);
1480
- const r = this.materialFactory.createLabelMaterial(e.label), d = new x.Sprite(r);
1481
- return d.position.y = this.nodeRadius + 1.5, d.scale.set(4, 1, 1), t.add(d), e.position && t.position.set(
1821
+ ), o = new y.Mesh(i, n);
1822
+ o.castShadow = !0, o.receiveShadow = !0, t.add(o);
1823
+ const a = this.materialFactory.createLabelMaterial(e.label), c = new y.Sprite(a);
1824
+ return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
1482
1825
  e.position.x,
1483
1826
  e.position.y,
1484
1827
  e.position.z
1485
1828
  ), {
1486
1829
  group: t,
1487
- sphere: n,
1488
- label: d,
1830
+ sphere: o,
1831
+ label: c,
1489
1832
  lodLevel: s
1490
1833
  };
1491
1834
  }
@@ -1501,19 +1844,19 @@ class vt {
1501
1844
  * Updates the color of a node
1502
1845
  */
1503
1846
  updateNodeColor(e, s) {
1504
- e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
1847
+ e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
1505
1848
  }
1506
1849
  /**
1507
1850
  * Updates the label of a node
1508
1851
  */
1509
1852
  updateNodeLabel(e, s) {
1510
- e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
1853
+ e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
1511
1854
  }
1512
1855
  /**
1513
1856
  * Disposes a node's resources
1514
1857
  */
1515
1858
  disposeNode(e) {
1516
- e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
1859
+ e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
1517
1860
  }
1518
1861
  /**
1519
1862
  * Dispose factory resources
@@ -1549,17 +1892,17 @@ class wt {
1549
1892
  /**
1550
1893
  * Creates an edge line between two positions
1551
1894
  */
1552
- createEdge(e, s, t, i, o) {
1553
- const n = new x.BufferGeometry(), r = new Float32Array([
1895
+ createEdge(e, s, t, i, n) {
1896
+ const o = new y.BufferGeometry(), a = new Float32Array([
1554
1897
  i.x,
1555
1898
  i.y,
1556
1899
  i.z,
1557
- o.x,
1558
- o.y,
1559
- o.z
1900
+ n.x,
1901
+ n.y,
1902
+ n.z
1560
1903
  ]);
1561
- n.setAttribute("position", new x.BufferAttribute(r, 3));
1562
- const d = this.getDefaultMaterial().clone(), c = new x.Line(n, d);
1904
+ o.setAttribute("position", new y.BufferAttribute(a, 3));
1905
+ const c = new y.Line(o, this.getDefaultMaterial());
1563
1906
  return c.name = `edge-${e.source}-${e.target}`, c.userData = {
1564
1907
  source: e.source,
1565
1908
  target: e.target,
@@ -1576,26 +1919,35 @@ class wt {
1576
1919
  * Highlights an edge
1577
1920
  */
1578
1921
  highlightEdge(e) {
1579
- e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
1922
+ e.line.material = this.getHighlightMaterial();
1580
1923
  }
1581
1924
  /**
1582
1925
  * Resets an edge to default appearance
1583
1926
  */
1584
1927
  unhighlightEdge(e) {
1585
- e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
1928
+ e.line.material = this.getDefaultMaterial();
1586
1929
  }
1587
1930
  /**
1588
1931
  * Updates an edge's positions
1589
1932
  */
1590
1933
  updateEdgePositions(e, s, t) {
1591
- const i = e.line.geometry.attributes.position, o = i.array;
1592
- o[0] = s.x, o[1] = s.y, o[2] = s.z, o[3] = t.x, o[4] = t.y, o[5] = t.z, i.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
1934
+ const i = e.line.geometry.attributes.position, n = i.array;
1935
+ n[0] = s.x, n[1] = s.y, n[2] = s.z, n[3] = t.x, n[4] = t.y, n[5] = t.z, i.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
1593
1936
  }
1594
1937
  /**
1595
1938
  * Disposes an edge's resources
1596
1939
  */
1597
1940
  disposeEdge(e) {
1598
- e.line.geometry.dispose(), e.line.material instanceof x.Material && e.line.material.dispose();
1941
+ if (e.line.geometry.dispose(), e.line.material instanceof y.Material) {
1942
+ const s = e.line.material;
1943
+ s !== this.defaultMaterial && s !== this.highlightMaterial && s.dispose();
1944
+ }
1945
+ }
1946
+ /**
1947
+ * Returns the shared edge material for batch rendering
1948
+ */
1949
+ getSharedEdgeMaterial() {
1950
+ return this.getDefaultMaterial();
1599
1951
  }
1600
1952
  /**
1601
1953
  * Dispose factory resources
@@ -1604,7 +1956,7 @@ class wt {
1604
1956
  this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
1605
1957
  }
1606
1958
  }
1607
- class Mt {
1959
+ class Et {
1608
1960
  constructor(e, s = [50, 100, 200], t = !0) {
1609
1961
  l(this, "camera");
1610
1962
  l(this, "lodDistances");
@@ -1616,16 +1968,16 @@ class Mt {
1616
1968
  */
1617
1969
  getLODLevel(e) {
1618
1970
  if (!this.enabled)
1619
- return _.HIGH;
1620
- const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, o = Math.sqrt(s * s + t * t + i * i);
1621
- return o < this.lodDistances[0] ? _.HIGH : o < this.lodDistances[1] ? _.MEDIUM : _.LOW;
1971
+ return K.HIGH;
1972
+ const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, n = Math.sqrt(s * s + t * t + i * i);
1973
+ return n < this.lodDistances[0] ? K.HIGH : n < this.lodDistances[1] ? K.MEDIUM : K.LOW;
1622
1974
  }
1623
1975
  /**
1624
1976
  * Checks if a node should be visible based on distance
1625
1977
  */
1626
1978
  shouldRenderNode(e, s = 500) {
1627
- const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y, o = e.z - this.camera.position.z;
1628
- return Math.sqrt(t * t + i * i + o * o) < s;
1979
+ const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y, n = e.z - this.camera.position.z;
1980
+ return Math.sqrt(t * t + i * i + n * n) < s;
1629
1981
  }
1630
1982
  /**
1631
1983
  * Sets the LOD distances
@@ -1640,13 +1992,13 @@ class Mt {
1640
1992
  this.enabled = e;
1641
1993
  }
1642
1994
  }
1643
- class Et {
1995
+ class Ct {
1644
1996
  constructor(e, s = !0) {
1645
1997
  l(this, "camera");
1646
1998
  l(this, "frustum");
1647
1999
  l(this, "projScreenMatrix");
1648
2000
  l(this, "enabled");
1649
- this.camera = e, this.frustum = new x.Frustum(), this.projScreenMatrix = new x.Matrix4(), this.enabled = s;
2001
+ this.camera = e, this.frustum = new y.Frustum(), this.projScreenMatrix = new y.Matrix4(), this.enabled = s;
1650
2002
  }
1651
2003
  /**
1652
2004
  * Updates the frustum from the camera
@@ -1662,7 +2014,7 @@ class Et {
1662
2014
  */
1663
2015
  isPointVisible(e) {
1664
2016
  if (!this.enabled) return !0;
1665
- const s = new x.Vector3(e.x, e.y, e.z);
2017
+ const s = new y.Vector3(e.x, e.y, e.z);
1666
2018
  return this.frustum.containsPoint(s);
1667
2019
  }
1668
2020
  /**
@@ -1670,8 +2022,8 @@ class Et {
1670
2022
  */
1671
2023
  isSphereVisible(e, s) {
1672
2024
  if (!this.enabled) return !0;
1673
- const t = new x.Sphere(
1674
- new x.Vector3(e.x, e.y, e.z),
2025
+ const t = new y.Sphere(
2026
+ new y.Vector3(e.x, e.y, e.z),
1675
2027
  s
1676
2028
  );
1677
2029
  return this.frustum.intersectsSphere(t);
@@ -1681,15 +2033,15 @@ class Et {
1681
2033
  */
1682
2034
  isLineVisible(e, s) {
1683
2035
  if (!this.enabled) return !0;
1684
- const t = new x.Vector3(e.x, e.y, e.z), i = new x.Vector3(s.x, s.y, s.z);
2036
+ const t = new y.Vector3(e.x, e.y, e.z), i = new y.Vector3(s.x, s.y, s.z);
1685
2037
  if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
1686
2038
  return !0;
1687
- const o = new x.Vector3(
2039
+ const n = new y.Vector3(
1688
2040
  (e.x + s.x) / 2,
1689
2041
  (e.y + s.y) / 2,
1690
2042
  (e.z + s.z) / 2
1691
- ), n = o.distanceTo(t), r = new x.Sphere(o, n);
1692
- return this.frustum.intersectsSphere(r);
2043
+ ), o = n.distanceTo(t), a = new y.Sphere(n, o);
2044
+ return this.frustum.intersectsSphere(a);
1693
2045
  }
1694
2046
  /**
1695
2047
  * Enables/disables frustum culling
@@ -1698,7 +2050,7 @@ class Et {
1698
2050
  this.enabled = e;
1699
2051
  }
1700
2052
  }
1701
- class Ct {
2053
+ class St {
1702
2054
  constructor(e, s) {
1703
2055
  l(this, "sceneManager");
1704
2056
  l(this, "raycaster");
@@ -1710,9 +2062,10 @@ class Ct {
1710
2062
  l(this, "onEdgeClick", null);
1711
2063
  l(this, "hoveredNodeId", null);
1712
2064
  l(this, "hoveredEdgeKey", null);
2065
+ l(this, "edgeInteractionEnabled", !0);
1713
2066
  l(this, "nodeObjects", []);
1714
2067
  l(this, "edgeObjects", []);
1715
- this.sceneManager = e, this.container = s, this.raycaster = new x.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new x.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
2068
+ this.sceneManager = e, this.container = s, this.raycaster = new y.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new y.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
1716
2069
  }
1717
2070
  /**
1718
2071
  * Updates the list of node objects to raycast against
@@ -1726,6 +2079,12 @@ class Ct {
1726
2079
  setEdgeObjects(e) {
1727
2080
  this.edgeObjects = e;
1728
2081
  }
2082
+ /**
2083
+ * Enables/disables edge hover and click raycasting
2084
+ */
2085
+ setEdgeInteractionEnabled(e) {
2086
+ this.edgeInteractionEnabled = e, e || (this.hoveredEdgeKey = null, this.onEdgeHover && this.onEdgeHover(null));
2087
+ }
1729
2088
  /**
1730
2089
  * Sets the click callback
1731
2090
  */
@@ -1759,7 +2118,7 @@ class Ct {
1759
2118
  this.onNodeClick(s);
1760
2119
  return;
1761
2120
  }
1762
- const t = this.getIntersectedEdge(e);
2121
+ const t = this.edgeInteractionEnabled ? this.getIntersectedEdge(e) : null;
1763
2122
  t && this.onEdgeClick && this.onEdgeClick(t);
1764
2123
  }
1765
2124
  /**
@@ -1771,8 +2130,14 @@ class Ct {
1771
2130
  this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
1772
2131
  return;
1773
2132
  }
1774
- const i = this.getIntersectedEdge(e), o = i ? `${i.edge.source}-${i.edge.target}` : null;
1775
- o !== this.hoveredEdgeKey && (this.hoveredEdgeKey = o, this.onEdgeHover && this.onEdgeHover(i)), this.container.style.cursor = i ? "pointer" : "default";
2133
+ const i = this.edgeInteractionEnabled ? this.getIntersectedEdge(e) : null, n = i ? `${i.edge.source}-${i.edge.target}` : null;
2134
+ n !== this.hoveredEdgeKey && (this.hoveredEdgeKey = n, this.onEdgeHover && this.onEdgeHover(i)), this.container.style.cursor = i ? "pointer" : "default";
2135
+ }
2136
+ /**
2137
+ * Gets currently hovered node ID
2138
+ */
2139
+ getHoveredNodeId() {
2140
+ return this.hoveredNodeId;
1776
2141
  }
1777
2142
  /**
1778
2143
  * Gets the intersected node from a mouse event
@@ -1783,11 +2148,11 @@ class Ct {
1783
2148
  this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1784
2149
  const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
1785
2150
  if (t.length > 0) {
1786
- let o = t[0].object;
1787
- for (; o; ) {
1788
- if ((i = o.userData) != null && i.nodeData)
1789
- return o.userData.nodeData;
1790
- o = o.parent;
2151
+ let n = t[0].object;
2152
+ for (; n; ) {
2153
+ if ((i = n.userData) != null && i.nodeData)
2154
+ return n.userData.nodeData;
2155
+ n = n.parent;
1791
2156
  }
1792
2157
  }
1793
2158
  return null;
@@ -1800,15 +2165,15 @@ class Ct {
1800
2165
  this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1801
2166
  const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
1802
2167
  if (t.length > 0) {
1803
- const i = t[0].object, o = i.userData;
1804
- if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode)) {
1805
- const n = Array.isArray(o.relationships) ? o.relationships : [o.edge];
2168
+ const i = t[0].object, n = i.userData;
2169
+ if (n != null && n.edge && (n != null && n.sourceNode) && (n != null && n.targetNode)) {
2170
+ const o = Array.isArray(n.relationships) ? n.relationships : [n.edge];
1806
2171
  return {
1807
- edge: o.edge,
1808
- relationships: n,
1809
- relationshipCount: typeof o.relationshipCount == "number" ? o.relationshipCount : n.length,
1810
- sourceNode: o.sourceNode,
1811
- targetNode: o.targetNode,
2172
+ edge: n.edge,
2173
+ relationships: o,
2174
+ relationshipCount: typeof n.relationshipCount == "number" ? n.relationshipCount : o.length,
2175
+ sourceNode: n.sourceNode,
2176
+ targetNode: n.targetNode,
1812
2177
  edgeLine: i
1813
2178
  };
1814
2179
  }
@@ -1819,16 +2184,16 @@ class Ct {
1819
2184
  * Performs a raycast and returns the intersected node ID
1820
2185
  */
1821
2186
  getIntersectedNodeId(e, s) {
1822
- var o;
2187
+ var n;
1823
2188
  const t = this.container.getBoundingClientRect();
1824
2189
  this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((s - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1825
2190
  const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
1826
2191
  if (i.length > 0) {
1827
- let n = i[0].object;
1828
- for (; n; ) {
1829
- if ((o = n.userData) != null && o.nodeId)
1830
- return n.userData.nodeId;
1831
- n = n.parent;
2192
+ let o = i[0].object;
2193
+ for (; o; ) {
2194
+ if ((n = o.userData) != null && n.nodeId)
2195
+ return o.userData.nodeId;
2196
+ o = o.parent;
1832
2197
  }
1833
2198
  }
1834
2199
  return null;
@@ -1908,15 +2273,15 @@ class Nt {
1908
2273
  this.currentNodeId = e.id;
1909
2274
  let t;
1910
2275
  this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
1911
- const i = this.panel.querySelector('[data-action="expand"]'), o = this.panel.querySelector("[data-depth-select]");
2276
+ const i = this.panel.querySelector('[data-action="expand"]'), n = this.panel.querySelector("[data-depth-select]");
1912
2277
  i && this.onExpand && i.addEventListener("click", () => {
1913
2278
  if (this.currentNodeId) {
1914
- const r = o ? parseInt(o.value, 10) : 1;
1915
- this.onExpand(this.currentNodeId, r);
2279
+ const a = n ? parseInt(n.value, 10) : 1;
2280
+ this.onExpand(this.currentNodeId, a);
1916
2281
  }
1917
2282
  });
1918
- const n = this.panel.querySelector('[data-action="close"]');
1919
- n && n.addEventListener("click", () => {
2283
+ const o = this.panel.querySelector('[data-action="close"]');
2284
+ o && o.addEventListener("click", () => {
1920
2285
  this.hide();
1921
2286
  }), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
1922
2287
  }
@@ -1995,36 +2360,6 @@ class Nt {
1995
2360
  border-radius: 12px;
1996
2361
  font-size: 12px;
1997
2362
  }
1998
- .force-graph-panel .depth-selector {
1999
- margin-top: 16px;
2000
- margin-bottom: 8px;
2001
- }
2002
- .force-graph-panel .depth-label {
2003
- font-size: 12px;
2004
- color: rgba(255, 255, 255, 0.65);
2005
- margin-bottom: 6px;
2006
- text-transform: uppercase;
2007
- letter-spacing: 0.5px;
2008
- }
2009
- .force-graph-panel select {
2010
- width: 100%;
2011
- padding: 8px 12px;
2012
- background: rgba(255, 255, 255, 0.08);
2013
- border: 1px solid rgba(255, 255, 255, 0.12);
2014
- border-radius: 8px;
2015
- color: white;
2016
- font-size: 13px;
2017
- cursor: pointer;
2018
- outline: none;
2019
- transition: all 0.2s ease;
2020
- }
2021
- .force-graph-panel select:hover {
2022
- background: rgba(255, 255, 255, 0.12);
2023
- }
2024
- .force-graph-panel select:focus {
2025
- border-color: rgba(255, 153, 102, 0.5);
2026
- box-shadow: 0 0 20px rgba(255, 153, 102, 0.15);
2027
- }
2028
2363
  .force-graph-panel .btn-row {
2029
2364
  display: flex;
2030
2365
  gap: 8px;
@@ -2040,15 +2375,6 @@ class Nt {
2040
2375
  cursor: pointer;
2041
2376
  transition: all 0.2s ease;
2042
2377
  }
2043
- .force-graph-panel .btn-expand {
2044
- background: linear-gradient(135deg, rgba(255, 153, 102, 0.3), rgba(255, 102, 153, 0.2));
2045
- border-color: rgba(255, 153, 102, 0.35);
2046
- color: white;
2047
- }
2048
- .force-graph-panel .btn-expand:hover {
2049
- transform: translateY(-1px);
2050
- box-shadow: 0 0 15px rgba(255, 153, 102, 0.2);
2051
- }
2052
2378
  .force-graph-panel .btn-close {
2053
2379
  background: rgba(255, 255, 255, 0.08);
2054
2380
  color: rgba(255, 255, 255, 0.8);
@@ -2087,18 +2413,7 @@ class Nt {
2087
2413
  </div>
2088
2414
  ` : ""}
2089
2415
 
2090
- <div class="depth-selector">
2091
- <div class="depth-label">Expansion Depth</div>
2092
- <select data-depth-select>
2093
- <option value="1">1 Level</option>
2094
- <option value="2">2 Levels</option>
2095
- <option value="3" selected>3 Levels</option>
2096
- </select>
2097
- </div>
2098
-
2099
-
2100
2416
  <div class="btn-row">
2101
- <button class="btn-expand" data-action="expand">Expand</button>
2102
2417
  <button class="btn-close" data-action="close">Close</button>
2103
2418
  </div>
2104
2419
  `;
@@ -2135,7 +2450,7 @@ class Nt {
2135
2450
  this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
2136
2451
  }
2137
2452
  }
2138
- class St {
2453
+ class kt {
2139
2454
  constructor(e) {
2140
2455
  l(this, "container");
2141
2456
  l(this, "panel", null);
@@ -2197,18 +2512,18 @@ class St {
2197
2512
  show(e, s, t, i = [e]) {
2198
2513
  if (!this.panel) return;
2199
2514
  this.currentEdgeKey = `${e.source}-${e.target}`;
2200
- let o;
2201
- this.panelTemplate ? o = this.panelTemplate(e, s, t) : o = this.generateDefaultContent(e, s, t, i), this.panel.innerHTML = o;
2202
- const n = this.panel.querySelector('[data-action="close"]');
2203
- n && n.addEventListener("click", () => {
2515
+ let n;
2516
+ this.panelTemplate ? n = this.panelTemplate(e, s, t) : n = this.generateDefaultContent(e, s, t, i), this.panel.innerHTML = n;
2517
+ const o = this.panel.querySelector('[data-action="close"]');
2518
+ o && o.addEventListener("click", () => {
2204
2519
  this.hide(), this.onClose && this.onClose();
2205
2520
  });
2206
- const r = this.panel.querySelector('[data-action="goto-source"]');
2207
- r && this.onNodeClick && r.addEventListener("click", () => {
2521
+ const a = this.panel.querySelector('[data-action="goto-source"]');
2522
+ a && this.onNodeClick && a.addEventListener("click", () => {
2208
2523
  this.onNodeClick && this.onNodeClick(e.source);
2209
2524
  });
2210
- const d = this.panel.querySelector('[data-action="goto-target"]');
2211
- d && this.onNodeClick && d.addEventListener("click", () => {
2525
+ const c = this.panel.querySelector('[data-action="goto-target"]');
2526
+ c && this.onNodeClick && c.addEventListener("click", () => {
2212
2527
  this.onNodeClick && this.onNodeClick(e.target);
2213
2528
  }), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
2214
2529
  }
@@ -2216,12 +2531,12 @@ class St {
2216
2531
  * Generates default panel content
2217
2532
  */
2218
2533
  generateDefaultContent(e, s, t, i = [e]) {
2219
- const o = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", n = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", r = this.getRelationshipLabel(e), d = i.length > 0 ? i : [e], c = this.summarizeRelationships(d), p = c.slice(0, 10).map((m) => `
2534
+ const n = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", o = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = this.getRelationshipLabel(e), c = i.length > 0 ? i : [e], h = this.summarizeRelationships(c), d = h.slice(0, 10).map((m) => `
2220
2535
  <div class="relationship-item">
2221
2536
  <span class="relationship-item-label">${this.escapeHtml(m.label)}</span>
2222
2537
  <span class="relationship-item-count">${m.count}</span>
2223
2538
  </div>
2224
- `).join(""), f = c.length - Math.min(c.length, 10);
2539
+ `).join(""), f = h.length - Math.min(h.length, 10);
2225
2540
  return `
2226
2541
  <style>
2227
2542
  .force-graph-edge-panel .panel-header {
@@ -2374,19 +2689,19 @@ class St {
2374
2689
  </div>
2375
2690
 
2376
2691
  <div class="relationship-section">
2377
- <span class="relationship-label">${this.escapeHtml(r)}</span>
2378
- <div class="relationship-count">${d.length} relationships</div>
2692
+ <span class="relationship-label">${this.escapeHtml(a)}</span>
2693
+ <div class="relationship-count">${c.length} relationships</div>
2379
2694
  </div>
2380
2695
 
2381
2696
  <div class="relationship-list">
2382
- ${p}
2697
+ ${d}
2383
2698
  ${f > 0 ? `<div class="relationship-more">+ ${f} more</div>` : ""}
2384
2699
  </div>
2385
2700
 
2386
2701
  <div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
2387
2702
  <div class="node-type">Source</div>
2388
2703
  <div class="node-card-header">
2389
- <span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
2704
+ <span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
2390
2705
  <span class="node-label">${this.escapeHtml(s.label)}</span>
2391
2706
  </div>
2392
2707
  </div>
@@ -2396,7 +2711,7 @@ class St {
2396
2711
  <div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
2397
2712
  <div class="node-type">Target</div>
2398
2713
  <div class="node-card-header">
2399
- <span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
2714
+ <span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
2400
2715
  <span class="node-label">${this.escapeHtml(t.label)}</span>
2401
2716
  </div>
2402
2717
  </div>
@@ -2495,25 +2810,25 @@ class zt {
2495
2810
  */
2496
2811
  positionTooltip(e, s) {
2497
2812
  if (!this.tooltip) return;
2498
- const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, o = window.innerHeight;
2499
- let n = e + 15, r = s + 15;
2500
- n + t.width > i - 10 && (n = e - t.width - 15), r + t.height > o - 10 && (r = s - t.height - 15), n < 10 && (n = 10), r < 10 && (r = 10), this.tooltip.style.left = `${n}px`, this.tooltip.style.top = `${r}px`;
2813
+ const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, n = window.innerHeight;
2814
+ let o = e + 15, a = s + 15;
2815
+ o + t.width > i - 10 && (o = e - t.width - 15), a + t.height > n - 10 && (a = s - t.height - 15), o < 10 && (o = 10), a < 10 && (a = 10), this.tooltip.style.left = `${o}px`, this.tooltip.style.top = `${a}px`;
2501
2816
  }
2502
2817
  /**
2503
2818
  * Shows the tooltip with edge info
2504
2819
  */
2505
- show(e, s, t, i, o, n = [e]) {
2820
+ show(e, s, t, i, n, o = [e]) {
2506
2821
  if (!this.tooltip) return;
2507
- const r = n.length > 0 ? n : [e], d = this.summarizeRelationships(r), c = r.length;
2508
- if (c <= 1) {
2509
- const p = this.getRelationshipLabel(r[0]);
2822
+ const a = o.length > 0 ? o : [e], c = this.summarizeRelationships(a), h = a.length;
2823
+ if (h <= 1) {
2824
+ const d = this.getRelationshipLabel(a[0]);
2510
2825
  this.tooltip.innerHTML = `
2511
2826
  <div style="display: flex; flex-direction: column; gap: 4px;">
2512
2827
  <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2513
2828
  <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
2514
2829
  </div>
2515
2830
  <div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
2516
- ↳ ${this.escapeHtml(p)}
2831
+ ↳ ${this.escapeHtml(d)}
2517
2832
  </div>
2518
2833
  <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2519
2834
  <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
@@ -2521,7 +2836,7 @@ class zt {
2521
2836
  </div>
2522
2837
  `;
2523
2838
  } else {
2524
- const p = d.slice(0, 4), f = d.length - p.length, m = p.map((u) => `<div style="display:flex; justify-content:space-between; gap:10px; font-size:12px; color: rgba(255,255,255,0.8);">
2839
+ const d = c.slice(0, 4), f = c.length - d.length, m = d.map((u) => `<div style="display:flex; justify-content:space-between; gap:10px; font-size:12px; color: rgba(255,255,255,0.8);">
2525
2840
  <span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${this.escapeHtml(u.label)}</span>
2526
2841
  <span style="color: rgba(255,153,102,0.9); font-weight:600;">${u.count}</span>
2527
2842
  </div>`).join("");
@@ -2531,7 +2846,7 @@ class zt {
2531
2846
  ${this.escapeHtml(s.label)} → ${this.escapeHtml(t.label)}
2532
2847
  </div>
2533
2848
  <div style="font-size:11px; letter-spacing:0.4px; text-transform:uppercase; color: rgba(255,255,255,0.6);">
2534
- ${c} relationships
2849
+ ${h} relationships
2535
2850
  </div>
2536
2851
  <div style="display:flex; flex-direction:column; gap:4px;">
2537
2852
  ${m}
@@ -2540,7 +2855,7 @@ class zt {
2540
2855
  </div>
2541
2856
  `;
2542
2857
  }
2543
- this.positionTooltip(i, o), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
2858
+ this.positionTooltip(i, n), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
2544
2859
  }
2545
2860
  /**
2546
2861
  * Updates tooltip position (called externally on mouse move)
@@ -2585,7 +2900,7 @@ class zt {
2585
2900
  this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
2586
2901
  }
2587
2902
  }
2588
- class kt {
2903
+ class Pt {
2589
2904
  constructor(e, s) {
2590
2905
  l(this, "container");
2591
2906
  l(this, "searchContainer", null);
@@ -2729,25 +3044,25 @@ class kt {
2729
3044
  return;
2730
3045
  }
2731
3046
  let i = "";
2732
- s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((o) => {
2733
- const n = o.type || "Node";
3047
+ s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((n) => {
3048
+ const o = n.type || "Node";
2734
3049
  i += `
2735
- <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.id)}">
2736
- <div class="f3d-result-label">${this.escapeHtml(o.label)}</div>
2737
- <div class="f3d-result-type">${this.escapeHtml(n)}</div>
3050
+ <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.id)}">
3051
+ <div class="f3d-result-label">${this.escapeHtml(n.label)}</div>
3052
+ <div class="f3d-result-type">${this.escapeHtml(o)}</div>
2738
3053
  </div>
2739
3054
  `;
2740
- }), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: o, sourceNode: n, targetNode: r }) => {
3055
+ }), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: n, sourceNode: o, targetNode: a }) => {
2741
3056
  i += `
2742
- <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.source)}">
2743
- <div class="f3d-result-label">${this.escapeHtml(n.label)} → ${this.escapeHtml(r.label)}</div>
2744
- <div class="f3d-result-relationship">${this.escapeHtml(o.relationship || "connected")}</div>
3057
+ <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.source)}">
3058
+ <div class="f3d-result-label">${this.escapeHtml(o.label)} → ${this.escapeHtml(a.label)}</div>
3059
+ <div class="f3d-result-relationship">${this.escapeHtml(n.relationship || "connected")}</div>
2745
3060
  </div>
2746
3061
  `;
2747
- }), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((o) => {
2748
- o.addEventListener("click", () => {
2749
- const n = o.dataset.nodeId;
2750
- n && (this.onResultClick(n), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
3062
+ }), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((n) => {
3063
+ n.addEventListener("click", () => {
3064
+ const o = n.dataset.nodeId;
3065
+ o && (this.onResultClick(o), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
2751
3066
  });
2752
3067
  });
2753
3068
  }
@@ -2759,7 +3074,7 @@ class kt {
2759
3074
  this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
2760
3075
  }
2761
3076
  }
2762
- class Tt {
3077
+ class Rt {
2763
3078
  constructor(e, s) {
2764
3079
  l(this, "container");
2765
3080
  l(this, "toggleContainer", null);
@@ -2851,7 +3166,7 @@ class Tt {
2851
3166
  this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
2852
3167
  }
2853
3168
  }
2854
- class Pt {
3169
+ class Lt {
2855
3170
  constructor(e) {
2856
3171
  l(this, "container");
2857
3172
  l(this, "legendContainer", null);
@@ -2934,7 +3249,7 @@ class Pt {
2934
3249
  this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
2935
3250
  }
2936
3251
  }
2937
- const Lt = {
3252
+ const Tt = {
2938
3253
  backgroundColor: "#0a0a0a",
2939
3254
  gridColor: "rgba(255, 255, 255, 0.03)",
2940
3255
  nodeRadius: 24,
@@ -2947,7 +3262,7 @@ const Lt = {
2947
3262
  damping: 0.85
2948
3263
  // Fast energy dissipation
2949
3264
  };
2950
- class Rt {
3265
+ class It {
2951
3266
  constructor(e, s = {}) {
2952
3267
  l(this, "container");
2953
3268
  l(this, "canvas");
@@ -2974,7 +3289,7 @@ class Rt {
2974
3289
  l(this, "isSimulating", !0);
2975
3290
  // Resize handler
2976
3291
  l(this, "resizeHandler");
2977
- this.container = e, this.options = { ...Lt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
3292
+ this.container = e, this.options = { ...Tt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
2978
3293
  const t = this.canvas.getContext("2d");
2979
3294
  if (!t)
2980
3295
  throw new Error("Failed to get 2D context");
@@ -2987,23 +3302,23 @@ class Rt {
2987
3302
  setupInteractions() {
2988
3303
  this.canvas.addEventListener("wheel", (e) => {
2989
3304
  e.preventDefault();
2990
- const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1, n = Math.max(0.1, Math.min(5, this.transform.scale * o)), r = n / this.transform.scale;
2991
- this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale = n;
3305
+ const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = e.deltaY > 0 ? 0.9 : 1.1, o = Math.max(0.1, Math.min(5, this.transform.scale * n)), a = o / this.transform.scale;
3306
+ this.transform.x = t - (t - this.transform.x) * a, this.transform.y = i - (i - this.transform.y) * a, this.transform.scale = o;
2992
3307
  }), this.canvas.addEventListener("mousedown", (e) => {
2993
- const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i), n = this.findNodeAt(o.x, o.y);
2994
- this.dragStartPos = { x: e.clientX, y: e.clientY }, n ? (this.isDragging = !0, this.draggedNode = n, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
3308
+ const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = this.screenToWorld(t, i), o = this.findNodeAt(n.x, n.y);
3309
+ this.dragStartPos = { x: e.clientX, y: e.clientY }, o ? (this.isDragging = !0, this.draggedNode = o, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
2995
3310
  }), this.canvas.addEventListener("mousemove", (e) => {
2996
- const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i);
3311
+ const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = this.screenToWorld(t, i);
2997
3312
  if (this.isDragging && this.draggedNode)
2998
- this.draggedNode.x = o.x, this.draggedNode.y = o.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
3313
+ this.draggedNode.x = n.x, this.draggedNode.y = n.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
2999
3314
  else if (this.isPanning) {
3000
- const n = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
3001
- this.transform.x += n, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
3315
+ const o = e.clientX - this.lastMousePos.x, a = e.clientY - this.lastMousePos.y;
3316
+ this.transform.x += o, this.transform.y += a, this.lastMousePos = { x: e.clientX, y: e.clientY };
3002
3317
  } else {
3003
- const n = this.findNodeAt(o.x, o.y);
3004
- if (n !== this.hoveredNode && (this.hoveredNode = n, n ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(n.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !n) {
3005
- const r = this.findEdgeAt(o.x, o.y);
3006
- r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
3318
+ const o = this.findNodeAt(n.x, n.y);
3319
+ if (o !== this.hoveredNode && (this.hoveredNode = o, o ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(o.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !o) {
3320
+ const a = this.findEdgeAt(n.x, n.y);
3321
+ a !== this.hoveredEdge && (this.hoveredEdge = a, this.canvas.style.cursor = a ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(a ? a.data : null, e));
3007
3322
  }
3008
3323
  }
3009
3324
  }), this.canvas.addEventListener("mouseup", (e) => {
@@ -3011,8 +3326,8 @@ class Rt {
3011
3326
  if (this.isDragging && this.draggedNode)
3012
3327
  i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
3013
3328
  else if (i) {
3014
- const o = this.canvas.getBoundingClientRect(), n = this.screenToWorld(e.clientX - o.left, e.clientY - o.top), r = this.findEdgeAt(n.x, n.y);
3015
- r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
3329
+ const n = this.canvas.getBoundingClientRect(), o = this.screenToWorld(e.clientX - n.left, e.clientY - n.top), a = this.findEdgeAt(o.x, o.y);
3330
+ a && this.options.onEdgeClick && this.options.onEdgeClick(a.data);
3016
3331
  }
3017
3332
  this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
3018
3333
  }), this.canvas.addEventListener("mouseleave", () => {
@@ -3027,20 +3342,20 @@ class Rt {
3027
3342
  }
3028
3343
  findNodeAt(e, s) {
3029
3344
  for (const t of this.nodes.values()) {
3030
- const i = t.x - e, o = t.y - s;
3031
- if (Math.sqrt(i * i + o * o) < t.radius)
3345
+ const i = t.x - e, n = t.y - s;
3346
+ if (Math.sqrt(i * i + n * n) < t.radius)
3032
3347
  return t;
3033
3348
  }
3034
3349
  return null;
3035
3350
  }
3036
3351
  findEdgeAt(e, s) {
3037
3352
  for (const i of this.edges) {
3038
- const o = this.nodes.get(i.source), n = this.nodes.get(i.target);
3039
- if (!o || !n) continue;
3040
- const r = n.x - o.x, d = n.y - o.y, c = r * r + d * d;
3041
- if (c === 0) continue;
3042
- const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * d) / c)), f = o.x + p * r, m = o.y + p * d, u = e - f, y = s - m;
3043
- if (Math.sqrt(u * u + y * y) < 12)
3353
+ const n = this.nodes.get(i.source), o = this.nodes.get(i.target);
3354
+ if (!n || !o) continue;
3355
+ const a = o.x - n.x, c = o.y - n.y, h = a * a + c * c;
3356
+ if (h === 0) continue;
3357
+ const d = Math.max(0, Math.min(1, ((e - n.x) * a + (s - n.y) * c) / h)), f = n.x + d * a, m = n.y + d * c, u = e - f, b = s - m;
3358
+ if (Math.sqrt(u * u + b * b) < 12)
3044
3359
  return i;
3045
3360
  }
3046
3361
  return null;
@@ -3055,79 +3370,79 @@ class Rt {
3055
3370
  const e = Array.from(this.nodes.values()), s = e.length;
3056
3371
  if (s === 0) return;
3057
3372
  const t = 60, i = 5;
3058
- let o = 0;
3059
- for (let r = 0; r < s; r++)
3060
- for (let d = r + 1; d < s; d++) {
3061
- const c = e[r], p = e[d];
3062
- let f = p.x - c.x, m = p.y - c.y, u = Math.sqrt(f * f + m * m);
3373
+ let n = 0;
3374
+ for (let a = 0; a < s; a++)
3375
+ for (let c = a + 1; c < s; c++) {
3376
+ const h = e[a], d = e[c];
3377
+ let f = d.x - h.x, m = d.y - h.y, u = Math.sqrt(f * f + m * m);
3063
3378
  if (u < t * 3) {
3064
3379
  u < 1 && (u = 1);
3065
- const y = this.options.repulsionStrength / (u * u), b = f / u * y, w = m / u * y;
3066
- c.vx -= b, c.vy -= w, p.vx += b, p.vy += w;
3380
+ const b = this.options.repulsionStrength / (u * u), x = f / u * b, M = m / u * b;
3381
+ h.vx -= x, h.vy -= M, d.vx += x, d.vy += M;
3067
3382
  }
3068
3383
  }
3069
- const n = 80;
3070
- for (const r of this.edges) {
3071
- const d = this.nodes.get(r.source), c = this.nodes.get(r.target);
3072
- if (!d || !c) continue;
3073
- let p = c.x - d.x, f = c.y - d.y, m = Math.sqrt(p * p + f * f);
3384
+ const o = 80;
3385
+ for (const a of this.edges) {
3386
+ const c = this.nodes.get(a.source), h = this.nodes.get(a.target);
3387
+ if (!c || !h) continue;
3388
+ let d = h.x - c.x, f = h.y - c.y, m = Math.sqrt(d * d + f * f);
3074
3389
  m < 1 && (m = 1);
3075
- const y = (m - n) * this.options.attractionStrength, b = p / m * y, w = f / m * y;
3076
- d.vx += b, d.vy += w, c.vx -= b, c.vy -= w;
3390
+ const b = (m - o) * this.options.attractionStrength, x = d / m * b, M = f / m * b;
3391
+ c.vx += x, c.vy += M, h.vx -= x, h.vy -= M;
3077
3392
  }
3078
- for (const r of e) {
3079
- if (this.draggedNode === r) continue;
3080
- r.vx *= this.options.damping, r.vy *= this.options.damping;
3081
- const d = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
3082
- d > i && (r.vx = r.vx / d * i, r.vy = r.vy / d * i), r.x += r.vx, r.y += r.vy, o += r.vx * r.vx + r.vy * r.vy;
3393
+ for (const a of e) {
3394
+ if (this.draggedNode === a) continue;
3395
+ a.vx *= this.options.damping, a.vy *= this.options.damping;
3396
+ const c = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
3397
+ c > i && (a.vx = a.vx / c * i, a.vy = a.vy / c * i), a.x += a.vx, a.y += a.vy, n += a.vx * a.vx + a.vy * a.vy;
3083
3398
  }
3084
- o < 0.01 && !this.draggedNode && (this.isSimulating = !1);
3399
+ n < 0.01 && !this.draggedNode && (this.isSimulating = !1);
3085
3400
  }
3086
3401
  render() {
3087
3402
  const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
3088
3403
  e.fillStyle = this.options.backgroundColor, e.fillRect(0, 0, s, t), this.renderGrid(s, t), e.save(), e.translate(this.transform.x, this.transform.y), e.scale(this.transform.scale, this.transform.scale), this.renderEdges(), this.renderNodes(), e.restore();
3089
3404
  }
3090
3405
  renderGrid(e, s) {
3091
- const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, n = this.transform.x % i, r = this.transform.y % i;
3406
+ const t = this.ctx, i = 40 * this.transform.scale, n = 1.5, o = this.transform.x % i, a = this.transform.y % i;
3092
3407
  t.fillStyle = this.options.gridColor;
3093
- for (let d = n; d < e; d += i)
3094
- for (let c = r; c < s; c += i)
3095
- t.beginPath(), t.arc(d, c, o, 0, Math.PI * 2), t.fill();
3408
+ for (let c = o; c < e; c += i)
3409
+ for (let h = a; h < s; h += i)
3410
+ t.beginPath(), t.arc(c, h, n, 0, Math.PI * 2), t.fill();
3096
3411
  }
3097
3412
  renderEdges() {
3098
3413
  const e = this.ctx;
3099
3414
  for (const s of this.edges) {
3100
3415
  const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
3101
3416
  if (!t || !i) continue;
3102
- const o = s === this.hoveredEdge, n = e.createLinearGradient(t.x, t.y, i.x, i.y);
3103
- o ? (n.addColorStop(0, "rgba(255, 153, 102, 0.8)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), n.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (n.addColorStop(0, "rgba(255, 153, 102, 0.3)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), n.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = n, e.stroke();
3417
+ const n = s === this.hoveredEdge, o = e.createLinearGradient(t.x, t.y, i.x, i.y);
3418
+ n ? (o.addColorStop(0, "rgba(255, 153, 102, 0.8)"), o.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), o.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (o.addColorStop(0, "rgba(255, 153, 102, 0.3)"), o.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), o.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = o, e.stroke();
3104
3419
  }
3105
3420
  }
3106
3421
  renderNodes() {
3107
3422
  const e = this.ctx;
3108
3423
  for (const s of this.nodes.values()) {
3109
- const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), n = s.radius * (t ? 1.1 : 1);
3110
- if (t || i || o) {
3111
- const y = e.createRadialGradient(
3424
+ const t = s === this.hoveredNode, i = s === this.selectedNode, n = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), o = s.radius * (t ? 1.1 : 1);
3425
+ if (t || i || n) {
3426
+ const b = e.createRadialGradient(
3112
3427
  s.x,
3113
3428
  s.y,
3114
- n * 0.5,
3429
+ o * 0.5,
3115
3430
  s.x,
3116
3431
  s.y,
3117
- n * 2
3118
- ), b = t || i ? 0.4 : 0.25;
3119
- y.addColorStop(0, `rgba(255, 153, 102, ${b})`), y.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = y, e.beginPath(), e.arc(s.x, s.y, n * 2, 0, Math.PI * 2), e.fill();
3432
+ o * 2
3433
+ ), x = t || i ? 0.4 : 0.25;
3434
+ b.addColorStop(0, `rgba(255, 153, 102, ${x})`), b.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = b, e.beginPath(), e.arc(s.x, s.y, o * 2, 0, Math.PI * 2), e.fill();
3120
3435
  }
3121
- const r = e.createRadialGradient(
3122
- s.x - n * 0.3,
3123
- s.y - n * 0.3,
3436
+ const a = e.createRadialGradient(
3437
+ s.x - o * 0.3,
3438
+ s.y - o * 0.3,
3124
3439
  0,
3125
3440
  s.x,
3126
3441
  s.y,
3127
- n
3128
- ), d = s.color >> 16 & 255, c = s.color >> 8 & 255, p = s.color & 255;
3129
- r.addColorStop(0, `rgba(${Math.min(255, d + 60)}, ${Math.min(255, c + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${d}, ${c}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, d - 40)}, ${Math.max(0, c - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, n, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - n * 0.25, s.y - n * 0.25, n * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
3130
- const f = n * 1.6;
3442
+ o
3443
+ ), c = s.color >> 16 & 255, h = s.color >> 8 & 255, d = s.color & 255;
3444
+ a.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255, h + 60)}, ${Math.min(255, d + 60)}, 0.95)`), a.addColorStop(0.7, `rgba(${c}, ${h}, ${d}, 0.9)`), a.addColorStop(1, `rgba(${Math.max(0, c - 40)}, ${Math.max(0, h - 40)}, ${Math.max(0, d - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, o, 0, Math.PI * 2), e.fillStyle = a, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - o * 0.25, s.y - o * 0.25, o * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
3445
+ const f = o * 1.6;
3131
3446
  let m = s.label, u = e.measureText(m).width;
3132
3447
  if (u > f) {
3133
3448
  for (; u > f && m.length > 3; )
@@ -3145,7 +3460,7 @@ class Rt {
3145
3460
  const i = s.position || {
3146
3461
  x: (Math.random() - 0.5) * 300,
3147
3462
  y: (Math.random() - 0.5) * 300
3148
- }, o = {
3463
+ }, n = {
3149
3464
  id: s.id,
3150
3465
  label: s.label,
3151
3466
  x: i.x,
@@ -3157,7 +3472,7 @@ class Rt {
3157
3472
  radius: this.options.nodeRadius,
3158
3473
  data: s
3159
3474
  };
3160
- this.nodes.set(s.id, o), this.nodeIdToIndex.set(s.id, t);
3475
+ this.nodes.set(s.id, n), this.nodeIdToIndex.set(s.id, t);
3161
3476
  }), this.edges = e.edges.map((s) => ({
3162
3477
  source: s.source,
3163
3478
  target: s.target,
@@ -3233,11 +3548,11 @@ class Rt {
3233
3548
  focusOnNode(e) {
3234
3549
  const s = this.nodes.get(e);
3235
3550
  if (!s) return;
3236
- const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, n = this.transform.y, r = 500, d = performance.now(), c = () => {
3237
- const p = performance.now() - d, f = Math.min(p / r, 1), m = 1 - Math.pow(1 - f, 3);
3238
- this.transform.x = o + (t - o) * m, this.transform.y = n + (i - n) * m, f < 1 ? requestAnimationFrame(c) : this.selectedNode = s;
3551
+ const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, n = this.transform.x, o = this.transform.y, a = 500, c = performance.now(), h = () => {
3552
+ const d = performance.now() - c, f = Math.min(d / a, 1), m = 1 - Math.pow(1 - f, 3);
3553
+ this.transform.x = n + (t - n) * m, this.transform.y = o + (i - o) * m, f < 1 ? requestAnimationFrame(h) : this.selectedNode = s;
3239
3554
  };
3240
- c();
3555
+ h();
3241
3556
  }
3242
3557
  /**
3243
3558
  * Updates node positions from 3D data
@@ -3280,7 +3595,6 @@ class Rt {
3280
3595
  }
3281
3596
  }
3282
3597
  class Ht {
3283
- // Store graph data for view switching
3284
3598
  constructor(e, s = {}) {
3285
3599
  // Options
3286
3600
  l(this, "options");
@@ -3291,6 +3605,8 @@ class Ht {
3291
3605
  l(this, "edgeManager");
3292
3606
  l(this, "graphEngine");
3293
3607
  l(this, "rendererManager");
3608
+ l(this, "physicsWorkerBridge", null);
3609
+ l(this, "useWorkerPhysics", !1);
3294
3610
  // Factories
3295
3611
  l(this, "materialFactory");
3296
3612
  l(this, "nodeFactory");
@@ -3315,23 +3631,33 @@ class Ht {
3315
3631
  l(this, "devControls", null);
3316
3632
  l(this, "viewMode", "3d");
3317
3633
  l(this, "graphData", null);
3318
- this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new bt(), this.nodeFactory = new vt(
3634
+ // Store graph data for view switching
3635
+ l(this, "simulationStride", 1);
3636
+ l(this, "simulationTick", 0);
3637
+ l(this, "labelUpdateIntervalMs", 140);
3638
+ l(this, "lastLabelUpdateAt", 0);
3639
+ l(this, "performanceCheckIntervalMs", 1e3);
3640
+ l(this, "lastPerformanceCheckAt", 0);
3641
+ l(this, "currentPixelRatio", 1);
3642
+ l(this, "edgeInteractionsEnabled", !0);
3643
+ l(this, "physicsWorkerNeedsSync", !0);
3644
+ this.options = { ...C, ...s }, this.container = ht(e), this.materialFactory = new vt(), this.nodeFactory = new Mt(
3319
3645
  this.materialFactory,
3320
- this.options.nodeRadius ?? P.nodeRadius,
3321
- this.options.lodSegments ?? P.lodSegments,
3322
- this.options.defaultNodeColor ?? P.defaultNodeColor
3646
+ this.options.nodeRadius ?? C.nodeRadius,
3647
+ this.options.lodSegments ?? C.lodSegments,
3648
+ this.options.defaultNodeColor ?? C.defaultNodeColor
3323
3649
  ), this.edgeFactory = new wt(
3324
3650
  this.materialFactory,
3325
- this.options.edgeColor ?? P.edgeColor,
3326
- this.options.edgeOpacity ?? P.edgeOpacity
3327
- ), this.sceneManager = new ft(this.container, this.options), this.lodManager = new Mt(
3651
+ this.options.edgeColor ?? C.edgeColor,
3652
+ this.options.edgeOpacity ?? C.edgeOpacity
3653
+ ), this.sceneManager = new ft(this.container, this.options), this.currentPixelRatio = this.sceneManager.getPixelRatio(), this.lodManager = new Et(
3328
3654
  this.sceneManager.camera,
3329
- this.options.lodDistances ?? P.lodDistances,
3330
- this.options.enableLOD ?? P.enableLOD
3331
- ), this.frustumCuller = new Et(
3655
+ this.options.lodDistances ?? C.lodDistances,
3656
+ this.options.enableLOD ?? C.enableLOD
3657
+ ), this.frustumCuller = new Ct(
3332
3658
  this.sceneManager.camera,
3333
- this.options.enableEdgeCulling ?? P.enableEdgeCulling
3334
- ), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Re(
3659
+ this.options.enableEdgeCulling ?? C.enableEdgeCulling
3660
+ ), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Oe(
3335
3661
  this.nodeManager.getAllNodes(),
3336
3662
  this.edgeManager.getAllEdges(),
3337
3663
  {
@@ -3341,16 +3667,16 @@ class Ht {
3341
3667
  useBarnesHut: this.options.useBarnesHut,
3342
3668
  barnesHutTheta: this.options.barnesHutTheta
3343
3669
  }
3344
- ), this.rendererManager = new xt(
3670
+ ), this.rendererManager = new bt(
3345
3671
  this.sceneManager,
3346
3672
  () => this.onSimulate(),
3347
3673
  () => this.onRender(),
3348
- this.options.targetFPS ?? P.targetFPS
3349
- ), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgePanelManager = new St(this.container), this.edgeTooltipManager = new zt(), this.edgePanelManager.setNodeClickCallback((t) => {
3674
+ this.options.targetFPS ?? C.targetFPS
3675
+ ), this.raycasterManager = new St(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgePanelManager = new kt(this.container), this.edgeTooltipManager = new zt(), this.edgePanelManager.setNodeClickCallback((t) => {
3350
3676
  this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
3351
3677
  this.showNodePanel(t);
3352
3678
  }, 400);
3353
- }), this.options.showSearch !== !1 && (this.searchManager = new kt(this.container, {
3679
+ }), this.options.showSearch !== !1 && (this.searchManager = new Pt(this.container, {
3354
3680
  placeholder: this.options.searchPlaceholder,
3355
3681
  onSearch: (t) => ({
3356
3682
  nodeResults: this.searchNodes(t),
@@ -3361,12 +3687,12 @@ class Ht {
3361
3687
  this.showNodePanel(t);
3362
3688
  }, 400);
3363
3689
  }
3364
- })), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Tt(this.container, {
3690
+ })), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Rt(this.container, {
3365
3691
  initialMode: this.viewMode,
3366
3692
  onViewChange: (t) => {
3367
3693
  this.switchView(t);
3368
3694
  }
3369
- })), this.options.showLegend !== !1 && (this.legendManager = new Pt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
3695
+ })), this.options.showLegend !== !1 && (this.legendManager = new Lt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.applyPerformanceModeSettings(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
3370
3696
  }
3371
3697
  /**
3372
3698
  * Sets up internal callbacks
@@ -3418,19 +3744,50 @@ class Ht {
3418
3744
  * Called every simulation step
3419
3745
  */
3420
3746
  onSimulate() {
3421
- this.graphEngine.simulate();
3422
- for (const [e, s] of this.nodeManager.getAllNodes())
3423
- if (this.nodeManager.updateNodePosition(e, s.position), this.options.enableLOD) {
3424
- const t = this.lodManager.getLODLevel(s.position);
3425
- this.nodeManager.updateNodeLOD(e, t);
3747
+ if (this.useWorkerPhysics && this.physicsWorkerBridge) {
3748
+ const e = this.physicsWorkerBridge.consumeLatestFrame();
3749
+ if (e) {
3750
+ const { ids: s, positions: t } = e;
3751
+ for (let i = 0; i < s.length; i++) {
3752
+ const n = i * 3, o = s[i];
3753
+ if (this.nodeManager.updateNodePosition(o, {
3754
+ x: t[n],
3755
+ y: t[n + 1],
3756
+ z: t[n + 2]
3757
+ }), this.options.enableLOD) {
3758
+ const a = this.nodeManager.getNode(o);
3759
+ if (a) {
3760
+ const c = this.lodManager.getLODLevel(a.position);
3761
+ this.nodeManager.updateNodeLOD(o, c);
3762
+ }
3763
+ }
3764
+ }
3765
+ this.edgeManager.updateEdgePositions();
3426
3766
  }
3427
- this.edgeManager.updateEdgePositions();
3767
+ this.physicsWorkerBridge.requestStep();
3768
+ return;
3769
+ }
3770
+ if (this.simulationTick = (this.simulationTick + 1) % this.simulationStride, this.simulationTick === 0) {
3771
+ this.graphEngine.simulate();
3772
+ for (const [e, s] of this.nodeManager.getAllNodes())
3773
+ if (this.nodeManager.updateNodePosition(e, s.position), this.options.enableLOD) {
3774
+ const t = this.lodManager.getLODLevel(s.position);
3775
+ this.nodeManager.updateNodeLOD(e, t);
3776
+ }
3777
+ this.edgeManager.updateEdgePositions();
3778
+ }
3428
3779
  }
3429
3780
  /**
3430
3781
  * Called every render frame
3431
3782
  */
3432
3783
  onRender() {
3433
- this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
3784
+ this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeInteractionEnabled(this.edgeInteractionsEnabled), this.edgeInteractionsEnabled ? this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines()) : this.raycasterManager.setEdgeObjects([]);
3785
+ const e = performance.now();
3786
+ e - this.lastLabelUpdateAt >= this.labelUpdateIntervalMs && (this.nodeManager.updateLabelVisibility(
3787
+ this.sceneManager.getCameraPosition(),
3788
+ this.panelManager.getCurrentNodeId(),
3789
+ this.raycasterManager.getHoveredNodeId()
3790
+ ), this.lastLabelUpdateAt = e), (this.options.enableAdaptivePerformance ?? !0) && e - this.lastPerformanceCheckAt >= this.performanceCheckIntervalMs && (this.tunePerformanceForCurrentFPS(), this.lastPerformanceCheckAt = e);
3434
3791
  }
3435
3792
  /**
3436
3793
  * Updates bottom-right legend counts
@@ -3438,6 +3795,55 @@ class Ht {
3438
3795
  updateLegendCounts() {
3439
3796
  this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
3440
3797
  }
3798
+ /**
3799
+ * Applies baseline performance preset from constructor options
3800
+ */
3801
+ applyPerformanceModeSettings() {
3802
+ const e = this.options.performanceMode ?? "balanced";
3803
+ this.nodeManager.setLabelDistance(this.options.labelDistance ?? C.labelDistance), e === "quality" ? (this.sceneManager.setHighQualityRendering(!0), this.rendererManager.setTargetFPS(this.options.targetFPS ?? 60), this.simulationStride = 1, this.nodeManager.setLabelRenderMode("all"), this.edgeInteractionsEnabled = !0) : e === "low" ? (this.sceneManager.setHighQualityRendering(!1), this.rendererManager.setTargetFPS(Math.min(this.options.targetFPS ?? 60, 45)), this.simulationStride = 2, this.nodeManager.setLabelRenderMode(
3804
+ this.options.labelRenderMode === "all" ? "adaptive" : this.options.labelRenderMode ?? "none"
3805
+ ), this.edgeInteractionsEnabled = !(this.options.disableEdgeInteractionsInLowMode ?? !0), this.currentPixelRatio = Math.min(this.currentPixelRatio, 1), this.sceneManager.setPixelRatio(this.currentPixelRatio)) : (this.sceneManager.setHighQualityRendering(!0), this.rendererManager.setTargetFPS(this.options.targetFPS ?? 60), this.simulationStride = 1, this.nodeManager.setLabelRenderMode(this.options.labelRenderMode ?? "adaptive"), this.edgeInteractionsEnabled = !0), this.refreshPhysicsBackend();
3806
+ }
3807
+ /**
3808
+ * Applies adaptive quality tuning based on runtime FPS
3809
+ */
3810
+ tunePerformanceForCurrentFPS() {
3811
+ const e = this.rendererManager.getFPS(), s = this.options.minPixelRatio ?? C.minPixelRatio, t = Math.min(
3812
+ this.options.maxPixelRatio ?? C.maxPixelRatio,
3813
+ window.devicePixelRatio || 1
3814
+ );
3815
+ e < 24 ? (this.simulationStride = 3, this.currentPixelRatio > s && (this.currentPixelRatio = Math.max(s, this.currentPixelRatio - 0.1), this.sceneManager.setPixelRatio(this.currentPixelRatio)), this.options.performanceMode !== "quality" && this.nodeManager.setLabelRenderMode("none")) : e < 35 ? (this.simulationStride = Math.max(this.simulationStride, 2), this.currentPixelRatio > s && (this.currentPixelRatio = Math.max(s, this.currentPixelRatio - 0.06), this.sceneManager.setPixelRatio(this.currentPixelRatio)), this.options.performanceMode === "balanced" && this.nodeManager.setLabelRenderMode("adaptive")) : e > 52 ? (this.simulationStride = 1, this.options.performanceMode !== "low" && this.nodeManager.setLabelRenderMode(this.options.labelRenderMode ?? "adaptive"), this.currentPixelRatio < t && (this.currentPixelRatio = Math.min(t, this.currentPixelRatio + 0.05), this.sceneManager.setPixelRatio(this.currentPixelRatio))) : this.simulationStride = Math.min(this.simulationStride, 2), this.refreshPerformanceRenderingMode(), this.refreshPhysicsBackend();
3816
+ }
3817
+ /**
3818
+ * Chooses edge rendering strategy based on graph size and mode
3819
+ */
3820
+ refreshPerformanceRenderingMode() {
3821
+ const e = this.getEdgeCount(), s = this.options.edgeBatchThreshold ?? C.edgeBatchThreshold, t = this.options.performanceMode === "low" && e >= s;
3822
+ this.edgeManager.setBatchRenderingEnabled(t), t && (this.options.disableEdgeInteractionsInLowMode ?? !0) ? this.edgeInteractionsEnabled = !1 : this.edgeInteractionsEnabled = !0;
3823
+ }
3824
+ /**
3825
+ * Enables worker-based physics on large graphs when requested
3826
+ */
3827
+ refreshPhysicsBackend() {
3828
+ const e = this.options.workerPhysicsNodeThreshold ?? C.workerPhysicsNodeThreshold;
3829
+ if (!((this.options.enableWorkerPhysics ?? C.enableWorkerPhysics) && this.options.performanceMode !== "quality" && this.getNodeCount() >= e)) {
3830
+ this.useWorkerPhysics = !1, this.physicsWorkerBridge && (this.physicsWorkerBridge.dispose(), this.physicsWorkerBridge = null), this.physicsWorkerNeedsSync = !0;
3831
+ return;
3832
+ }
3833
+ if (this.physicsWorkerBridge || (this.physicsWorkerBridge = new xt()), !this.physicsWorkerBridge.isAvailable()) {
3834
+ this.useWorkerPhysics = !1;
3835
+ return;
3836
+ }
3837
+ (!this.useWorkerPhysics || this.physicsWorkerNeedsSync) && (this.physicsWorkerBridge.init(
3838
+ this.nodeManager.getAllNodes(),
3839
+ this.edgeManager.getAllEdges(),
3840
+ {
3841
+ repulsionStrength: this.options.repulsionStrength,
3842
+ attractionStrength: this.options.attractionStrength,
3843
+ damping: this.options.damping
3844
+ }
3845
+ ), this.physicsWorkerBridge.requestStep(), this.physicsWorkerNeedsSync = !1), this.useWorkerPhysics = !0, this.simulationStride = 1;
3846
+ }
3441
3847
  // ==========================================================================
3442
3848
  // Public API
3443
3849
  // ==========================================================================
@@ -3445,23 +3851,23 @@ class Ht {
3445
3851
  * Sets the graph data
3446
3852
  */
3447
3853
  setData(e) {
3448
- var n;
3449
- const s = (n = e.data) != null && n.nodes ? e.data : e, i = (s.edges || s.links || []).map((r) => ({
3450
- ...r,
3451
- relationship: r.relationship || r.label || "related_to"
3452
- })), o = {
3854
+ var o;
3855
+ const s = (o = e.data) != null && o.nodes ? e.data : e, i = (s.edges || s.links || []).map((a) => ({
3856
+ ...a,
3857
+ relationship: a.relationship || a.label || "related_to"
3858
+ })), n = {
3453
3859
  nodes: s.nodes || [],
3454
3860
  edges: i
3455
3861
  };
3456
- if (this.graphData = o, this.edgeManager.clear(), this.nodeManager.clear(), o.nodes && Array.isArray(o.nodes)) {
3457
- ee.setExpectedNodeCount(o.nodes.length);
3458
- for (const r of o.nodes)
3459
- this.addNode(r);
3460
- }
3461
- if (o.edges && Array.isArray(o.edges))
3462
- for (const r of o.edges)
3463
- this.addEdge(r);
3464
- this.graphEngine = new Re(
3862
+ if (this.graphData = n, this.edgeManager.clear(), this.nodeManager.clear(), n.nodes && Array.isArray(n.nodes)) {
3863
+ ee.setExpectedNodeCount(n.nodes.length);
3864
+ for (const a of n.nodes)
3865
+ ce(a) && this.nodeManager.addNode(a);
3866
+ }
3867
+ if (n.edges && Array.isArray(n.edges))
3868
+ for (const a of n.edges)
3869
+ he(a) && this.edgeManager.addEdge(a);
3870
+ this.graphEngine = new Oe(
3465
3871
  this.nodeManager.getAllNodes(),
3466
3872
  this.edgeManager.getAllEdges(),
3467
3873
  {
@@ -3471,28 +3877,28 @@ class Ht {
3471
3877
  useBarnesHut: this.options.useBarnesHut,
3472
3878
  barnesHutTheta: this.options.barnesHutTheta
3473
3879
  }
3474
- ), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o), this.updateLegendCounts();
3880
+ ), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(n), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend();
3475
3881
  }
3476
3882
  /**
3477
3883
  * Adds a node to the graph
3478
3884
  * @returns true if added, false if node already exists or invalid
3479
3885
  */
3480
3886
  addNode(e) {
3481
- if (!Fe(e))
3887
+ if (!ce(e))
3482
3888
  return !1;
3483
3889
  const s = this.nodeManager.addNode(e);
3484
- return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
3890
+ return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
3485
3891
  }
3486
3892
  /**
3487
3893
  * Removes a node from the graph
3488
3894
  * @returns true if removed, false if not found
3489
3895
  */
3490
3896
  removeNode(e) {
3491
- if (!ht(e))
3897
+ if (!dt(e))
3492
3898
  return !1;
3493
3899
  this.edgeManager.removeEdgesForNode(e);
3494
3900
  const s = this.nodeManager.removeNode(e);
3495
- return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
3901
+ return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
3496
3902
  }
3497
3903
  /**
3498
3904
  * Updates a node's properties
@@ -3505,10 +3911,10 @@ class Ht {
3505
3911
  * @returns true if added, false if invalid or nodes don't exist
3506
3912
  */
3507
3913
  addEdge(e) {
3508
- if (!De(e))
3914
+ if (!he(e))
3509
3915
  return !1;
3510
3916
  const s = this.edgeManager.addEdge(e);
3511
- return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
3917
+ return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
3512
3918
  }
3513
3919
  /**
3514
3920
  * Removes an edge from the graph
@@ -3516,7 +3922,7 @@ class Ht {
3516
3922
  */
3517
3923
  removeEdge(e, s) {
3518
3924
  const t = this.edgeManager.removeEdge(e, s);
3519
- return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
3925
+ return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), t;
3520
3926
  }
3521
3927
  /**
3522
3928
  * Expands a node by fetching more data
@@ -3529,16 +3935,16 @@ class Ht {
3529
3935
  if (!i)
3530
3936
  return console.warn("[ForceGraph3D] No expand callback provided"), !1;
3531
3937
  try {
3532
- const o = await i(e, s);
3533
- if (o.nodes && Array.isArray(o.nodes))
3534
- for (const n of o.nodes)
3535
- this.addNode(n);
3536
- if (o.edges && Array.isArray(o.edges))
3537
- for (const n of o.edges)
3538
- this.addEdge(n);
3539
- return this.panelManager.hide(), this.emit("expand", e, o), !0;
3540
- } catch (o) {
3541
- return console.error("[ForceGraph3D] Error expanding node:", o), !1;
3938
+ const n = await i(e, s);
3939
+ if (n.nodes && Array.isArray(n.nodes))
3940
+ for (const o of n.nodes)
3941
+ this.addNode(o);
3942
+ if (n.edges && Array.isArray(n.edges))
3943
+ for (const o of n.edges)
3944
+ this.addEdge(o);
3945
+ return this.panelManager.hide(), this.emit("expand", e, n), !0;
3946
+ } catch (n) {
3947
+ return console.error("[ForceGraph3D] Error expanding node:", n), !1;
3542
3948
  }
3543
3949
  }
3544
3950
  /**
@@ -3585,13 +3991,13 @@ class Ht {
3585
3991
  console.warn(`[ForceGraph3D] Node "${e}" not found`);
3586
3992
  return;
3587
3993
  }
3588
- const i = t.position, o = this.sceneManager.camera, n = this.sceneManager.controls, r = o.position.clone().sub(n.target).normalize(), d = {
3589
- x: i.x + r.x * s,
3590
- y: i.y + r.y * s,
3591
- z: i.z + r.z * s
3592
- }, c = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: n.target.x, y: n.target.y, z: n.target.z }, f = 800, m = performance.now(), u = () => {
3593
- const y = performance.now() - m, b = Math.min(y / f, 1), w = 1 - Math.pow(1 - b, 3);
3594
- o.position.x = c.x + (d.x - c.x) * w, o.position.y = c.y + (d.y - c.y) * w, o.position.z = c.z + (d.z - c.z) * w, n.target.x = p.x + (i.x - p.x) * w, n.target.y = p.y + (i.y - p.y) * w, n.target.z = p.z + (i.z - p.z) * w, n.update(), b < 1 && requestAnimationFrame(u);
3994
+ const i = t.position, n = this.sceneManager.camera, o = this.sceneManager.controls, a = n.position.clone().sub(o.target).normalize(), c = {
3995
+ x: i.x + a.x * s,
3996
+ y: i.y + a.y * s,
3997
+ z: i.z + a.z * s
3998
+ }, h = { x: n.position.x, y: n.position.y, z: n.position.z }, d = { x: o.target.x, y: o.target.y, z: o.target.z }, f = 800, m = performance.now(), u = () => {
3999
+ const b = performance.now() - m, x = Math.min(b / f, 1), M = 1 - Math.pow(1 - x, 3);
4000
+ n.position.x = h.x + (c.x - h.x) * M, n.position.y = h.y + (c.y - h.y) * M, n.position.z = h.z + (c.z - h.z) * M, o.target.x = d.x + (i.x - d.x) * M, o.target.y = d.y + (i.y - d.y) * M, o.target.z = d.z + (i.z - d.z) * M, o.update(), x < 1 && requestAnimationFrame(u);
3595
4001
  };
3596
4002
  u();
3597
4003
  }
@@ -3600,22 +4006,22 @@ class Ht {
3600
4006
  * Camera targets the midpoint and zooms out enough to see both nodes
3601
4007
  */
3602
4008
  focusOnEdge(e, s, t = 1.5) {
3603
- const i = this.nodeManager.getNode(e), o = this.nodeManager.getNode(s);
3604
- if (!i || !o) {
4009
+ const i = this.nodeManager.getNode(e), n = this.nodeManager.getNode(s);
4010
+ if (!i || !n) {
3605
4011
  console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
3606
4012
  return;
3607
4013
  }
3608
- const n = this.sceneManager.camera, r = this.sceneManager.controls, d = {
3609
- x: (i.position.x + o.position.x) / 2,
3610
- y: (i.position.y + o.position.y) / 2,
3611
- z: (i.position.z + o.position.z) / 2
3612
- }, c = o.position.x - i.position.x, p = o.position.y - i.position.y, f = o.position.z - i.position.z, m = Math.sqrt(c * c + p * p + f * f), u = Math.max(m * t, 40), y = n.position.clone().sub(r.target).normalize(), b = {
3613
- x: d.x + y.x * u,
3614
- y: d.y + y.y * u,
3615
- z: d.z + y.z * u
3616
- }, w = { x: n.position.x, y: n.position.y, z: n.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, L = performance.now(), G = () => {
3617
- const T = performance.now() - L, D = Math.min(T / O, 1), E = 1 - Math.pow(1 - D, 3);
3618
- n.position.x = w.x + (b.x - w.x) * E, n.position.y = w.y + (b.y - w.y) * E, n.position.z = w.z + (b.z - w.z) * E, r.target.x = N.x + (d.x - N.x) * E, r.target.y = N.y + (d.y - N.y) * E, r.target.z = N.z + (d.z - N.z) * E, r.update(), D < 1 && requestAnimationFrame(G);
4014
+ const o = this.sceneManager.camera, a = this.sceneManager.controls, c = {
4015
+ x: (i.position.x + n.position.x) / 2,
4016
+ y: (i.position.y + n.position.y) / 2,
4017
+ z: (i.position.z + n.position.z) / 2
4018
+ }, h = n.position.x - i.position.x, d = n.position.y - i.position.y, f = n.position.z - i.position.z, m = Math.sqrt(h * h + d * d + f * f), u = Math.max(m * t, 40), b = o.position.clone().sub(a.target).normalize(), x = {
4019
+ x: c.x + b.x * u,
4020
+ y: c.y + b.y * u,
4021
+ z: c.z + b.z * u
4022
+ }, M = { x: o.position.x, y: o.position.y, z: o.position.z }, N = { x: a.target.x, y: a.target.y, z: a.target.z }, O = 800, L = performance.now(), G = () => {
4023
+ const R = performance.now() - L, F = Math.min(R / O, 1), E = 1 - Math.pow(1 - F, 3);
4024
+ o.position.x = M.x + (x.x - M.x) * E, o.position.y = M.y + (x.y - M.y) * E, o.position.z = M.z + (x.z - M.z) * E, a.target.x = N.x + (c.x - N.x) * E, a.target.y = N.y + (c.y - N.y) * E, a.target.z = N.z + (c.z - N.z) * E, a.update(), F < 1 && requestAnimationFrame(G);
3619
4025
  };
3620
4026
  G();
3621
4027
  }
@@ -3639,10 +4045,10 @@ class Ht {
3639
4045
  if (!e || e.trim() === "")
3640
4046
  return [];
3641
4047
  const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
3642
- return t.forEach((o) => {
3643
- var c, p, f;
3644
- const n = (c = o.label) == null ? void 0 : c.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), d = (f = o.type) == null ? void 0 : f.toLowerCase().includes(s);
3645
- (n || r || d) && i.push(o);
4048
+ return t.forEach((n) => {
4049
+ var h, d, f;
4050
+ const o = (h = n.label) == null ? void 0 : h.toLowerCase().includes(s), a = (d = n.id) == null ? void 0 : d.toLowerCase().includes(s), c = (f = n.type) == null ? void 0 : f.toLowerCase().includes(s);
4051
+ (o || a || c) && i.push(n);
3646
4052
  }), i;
3647
4053
  }
3648
4054
  /**
@@ -3650,14 +4056,14 @@ class Ht {
3650
4056
  * @returns Array of matching edges with source/target node info
3651
4057
  */
3652
4058
  searchEdges(e) {
3653
- var o;
4059
+ var n;
3654
4060
  if (!e || e.trim() === "")
3655
4061
  return [];
3656
4062
  const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
3657
- for (const n of t)
3658
- if ((o = n.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
3659
- const d = this.nodeManager.getNode(n.source), c = this.nodeManager.getNode(n.target);
3660
- d && c && i.push({ edge: n, sourceNode: d, targetNode: c });
4063
+ for (const o of t)
4064
+ if ((n = o.relationship) == null ? void 0 : n.toLowerCase().includes(s)) {
4065
+ const c = this.nodeManager.getNode(o.source), h = this.nodeManager.getNode(o.target);
4066
+ c && h && i.push({ edge: o, sourceNode: c, targetNode: h });
3661
4067
  }
3662
4068
  return i;
3663
4069
  }
@@ -3690,7 +4096,7 @@ class Ht {
3690
4096
  * Switches between 2D and 3D view modes
3691
4097
  */
3692
4098
  switchView(e) {
3693
- this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new Rt(this.container, {
4099
+ this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new It(this.container, {
3694
4100
  backgroundColor: "#0a0a0a",
3695
4101
  nodeRadius: 24,
3696
4102
  onNodeClick: (s) => {
@@ -3701,16 +4107,16 @@ class Ht {
3701
4107
  },
3702
4108
  onEdgeHover: (s, t) => {
3703
4109
  if (s && t) {
3704
- const i = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
3705
- if (i && o) {
3706
- const n = this.edgeManager.getAllEdges().filter((r) => r.source === s.source && r.target === s.target);
4110
+ const i = this.nodeManager.getNode(s.source), n = this.nodeManager.getNode(s.target);
4111
+ if (i && n) {
4112
+ const o = this.edgeManager.getAllEdges().filter((a) => a.source === s.source && a.target === s.target);
3707
4113
  this.edgeTooltipManager.show(
3708
4114
  s,
3709
4115
  i,
3710
- o,
4116
+ n,
3711
4117
  t.clientX,
3712
4118
  t.clientY,
3713
- n.length > 0 ? n : [s]
4119
+ o.length > 0 ? o : [s]
3714
4120
  );
3715
4121
  }
3716
4122
  } else
@@ -3719,12 +4125,12 @@ class Ht {
3719
4125
  onEdgeClick: (s) => {
3720
4126
  const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
3721
4127
  if (t && i) {
3722
- const o = this.edgeManager.getAllEdges().filter((n) => n.source === s.source && n.target === s.target);
4128
+ const n = this.edgeManager.getAllEdges().filter((o) => o.source === s.source && o.target === s.target);
3723
4129
  this.edgePanelManager.show(
3724
4130
  s,
3725
4131
  t,
3726
4132
  i,
3727
- o.length > 0 ? o : [s]
4133
+ n.length > 0 ? n : [s]
3728
4134
  );
3729
4135
  }
3730
4136
  }
@@ -3747,7 +4153,7 @@ class Ht {
3747
4153
  * Sets physics parameters for both 3D and 2D views
3748
4154
  */
3749
4155
  setPhysicsParams(e) {
3750
- this.graphEngine.setPhysicsParams(e), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setPhysicsParams(e);
4156
+ e.repulsionStrength !== void 0 && (this.options.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.options.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.options.damping = e.damping), this.graphEngine.setPhysicsParams(e), this.graphEngine.restart(), this.physicsWorkerBridge && this.physicsWorkerBridge.setPhysicsParams(e), this.forceGraph2D && this.forceGraph2D.setPhysicsParams(e);
3751
4157
  }
3752
4158
  /**
3753
4159
  * Creates dev mode controls (only in development)
@@ -3827,18 +4233,18 @@ class Ht {
3827
4233
  const i = parseFloat(t.value) / 100;
3828
4234
  this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
3829
4235
  }), setInterval(() => {
3830
- const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"), n = this.devControls.querySelector("#dev-fps");
3831
- i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()), n && (n.textContent = this.rendererManager.getFPS().toString());
4236
+ const i = this.devControls.querySelector("#dev-node-count"), n = this.devControls.querySelector("#dev-edge-count"), o = this.devControls.querySelector("#dev-fps");
4237
+ i && (i.textContent = this.getNodeCount().toString()), n && (n.textContent = this.getEdgeCount().toString()), o && (o.textContent = this.rendererManager.getFPS().toString());
3832
4238
  }, 500);
3833
4239
  }
3834
4240
  /**
3835
4241
  * Destroys the graph and releases all resources
3836
4242
  */
3837
4243
  destroy() {
3838
- this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.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;
4244
+ this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.physicsWorkerBridge && (this.physicsWorkerBridge.dispose(), this.physicsWorkerBridge = null), this.forceGraph2D && this.forceGraph2D.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;
3839
4245
  }
3840
4246
  }
3841
- const Ie = [
4247
+ const De = [
3842
4248
  "Alpha",
3843
4249
  "Beta",
3844
4250
  "Gamma",
@@ -3880,7 +4286,7 @@ const Ie = [
3880
4286
  "partners with",
3881
4287
  "collaborates with",
3882
4288
  "supports"
3883
- ], Oe = [
4289
+ ], Fe = [
3884
4290
  16777215,
3885
4291
  // White
3886
4292
  16750950,
@@ -3892,14 +4298,14 @@ const Ie = [
3892
4298
  16746564
3893
4299
  // Darker tangerine
3894
4300
  ];
3895
- function At(h = 30) {
4301
+ function jt(p = 30) {
3896
4302
  const e = [], s = [];
3897
- for (let i = 0; i < h; i++) {
3898
- const o = i < Ie.length ? Ie[i] : `Node ${i + 1}`;
4303
+ for (let i = 0; i < p; i++) {
4304
+ const n = i < De.length ? De[i] : `Node ${i + 1}`;
3899
4305
  e.push({
3900
4306
  id: `node-${i}`,
3901
- label: o,
3902
- color: Oe[i % Oe.length],
4307
+ label: n,
4308
+ color: Fe[i % Fe.length],
3903
4309
  position: {
3904
4310
  x: (Math.random() - 0.5) * 60,
3905
4311
  y: (Math.random() - 0.5) * 60,
@@ -3907,76 +4313,76 @@ function At(h = 30) {
3907
4313
  }
3908
4314
  });
3909
4315
  }
3910
- for (let i = 1; i < h; i++) {
3911
- const o = Math.floor(Math.random() * i);
4316
+ for (let i = 1; i < p; i++) {
4317
+ const n = Math.floor(Math.random() * i);
3912
4318
  s.push({
3913
4319
  source: `node-${i}`,
3914
- target: `node-${o}`,
4320
+ target: `node-${n}`,
3915
4321
  relationship: J[Math.floor(Math.random() * J.length)]
3916
4322
  });
3917
4323
  }
3918
- const t = Math.floor(h * 0.5);
4324
+ const t = Math.floor(p * 0.5);
3919
4325
  for (let i = 0; i < t; i++) {
3920
- const o = Math.floor(Math.random() * h);
3921
- let n = Math.floor(Math.random() * h);
3922
- o === n && (n = (n + 1) % h);
3923
- const r = `node-${o}`, d = `node-${n}`;
4326
+ const n = Math.floor(Math.random() * p);
4327
+ let o = Math.floor(Math.random() * p);
4328
+ n === o && (o = (o + 1) % p);
4329
+ const a = `node-${n}`, c = `node-${o}`;
3924
4330
  s.some(
3925
- (p) => p.source === r && p.target === d || p.source === d && p.target === r
4331
+ (d) => d.source === a && d.target === c || d.source === c && d.target === a
3926
4332
  ) || s.push({
3927
- source: r,
3928
- target: d,
4333
+ source: a,
4334
+ target: c,
3929
4335
  relationship: J[Math.floor(Math.random() * J.length)]
3930
4336
  });
3931
4337
  }
3932
4338
  return { nodes: e, edges: s };
3933
4339
  }
3934
- function jt(h = 1e3) {
3935
- const e = [], s = [], t = Math.ceil(h / 50), i = [];
3936
- for (let o = 0; o < t; o++)
4340
+ function Bt(p = 1e3) {
4341
+ const e = [], s = [], t = Math.ceil(p / 50), i = [];
4342
+ for (let n = 0; n < t; n++)
3937
4343
  i.push({
3938
4344
  x: (Math.random() - 0.5) * 200,
3939
4345
  y: (Math.random() - 0.5) * 200,
3940
4346
  z: (Math.random() - 0.5) * 200
3941
4347
  });
3942
- for (let o = 0; o < h; o++) {
3943
- const n = i[o % t];
4348
+ for (let n = 0; n < p; n++) {
4349
+ const o = i[n % t];
3944
4350
  e.push({
3945
- id: `node-${o}`,
3946
- label: `N${o}`,
4351
+ id: `node-${n}`,
4352
+ label: `N${n}`,
3947
4353
  position: {
3948
- x: n.x + (Math.random() - 0.5) * 40,
3949
- y: n.y + (Math.random() - 0.5) * 40,
3950
- z: n.z + (Math.random() - 0.5) * 40
4354
+ x: o.x + (Math.random() - 0.5) * 40,
4355
+ y: o.y + (Math.random() - 0.5) * 40,
4356
+ z: o.z + (Math.random() - 0.5) * 40
3951
4357
  }
3952
4358
  });
3953
4359
  }
3954
- for (let o = 1; o < h; o++) {
3955
- const n = Math.floor(o / 50) * 50, r = n === 0 ? Math.floor(Math.random() * o) : n + Math.floor(Math.random() * Math.min(o - n, 50));
4360
+ for (let n = 1; n < p; n++) {
4361
+ const o = Math.floor(n / 50) * 50, a = o === 0 ? Math.floor(Math.random() * n) : o + Math.floor(Math.random() * Math.min(n - o, 50));
3956
4362
  s.push({
3957
- source: `node-${o}`,
3958
- target: `node-${Math.min(r, o - 1)}`,
4363
+ source: `node-${n}`,
4364
+ target: `node-${Math.min(a, n - 1)}`,
3959
4365
  relationship: "links to"
3960
4366
  });
3961
4367
  }
3962
- for (let o = 1; o < t; o++) {
3963
- const n = o * 50, r = (o - 1) * 50 + Math.floor(Math.random() * 50);
4368
+ for (let n = 1; n < t; n++) {
4369
+ const o = n * 50, a = (n - 1) * 50 + Math.floor(Math.random() * 50);
3964
4370
  s.push({
3965
- source: `node-${n}`,
3966
- target: `node-${r}`,
4371
+ source: `node-${o}`,
4372
+ target: `node-${a}`,
3967
4373
  relationship: "bridges to"
3968
4374
  });
3969
4375
  }
3970
4376
  return { nodes: e, edges: s };
3971
4377
  }
3972
4378
  export {
3973
- P as DEFAULT_OPTIONS,
4379
+ C as DEFAULT_OPTIONS,
3974
4380
  Ht as ForceGraph3D,
3975
- _ as LODLevel,
3976
- Dt as createEdgeKey,
3977
- jt as generateLargeSampleData,
3978
- At as generateSampleData,
3979
- De as validateEdgeData,
3980
- Fe as validateNodeData
4381
+ K as LODLevel,
4382
+ At as createEdgeKey,
4383
+ Bt as generateLargeSampleData,
4384
+ jt as generateSampleData,
4385
+ he as validateEdgeData,
4386
+ ce as validateNodeData
3981
4387
  };
3982
4388
  //# sourceMappingURL=force-3d-graph.js.map