force-3d-graph 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2625 @@
1
+ var ot = Object.defineProperty;
2
+ var nt = (c, e, o) => e in c ? ot(c, e, { enumerable: !0, configurable: !0, writable: !0, value: o }) : c[e] = o;
3
+ var r = (c, e, o) => nt(c, typeof e != "symbol" ? e + "" : e, o);
4
+ import * as p from "three";
5
+ import { EventDispatcher as it, Vector3 as N, MOUSE as H, TOUCH as K, Spherical as Ne, Quaternion as ze, Vector2 as S, Ray as st, Plane as at, MathUtils as rt } from "three";
6
+ const O = {
7
+ backgroundColor: 657930,
8
+ cameraPosition: { x: 0, y: 0, z: 80 },
9
+ cameraFov: 75,
10
+ repulsionStrength: 100,
11
+ attractionStrength: 0.01,
12
+ damping: 0.9,
13
+ useBarnesHut: !1,
14
+ // Auto-enabled for large graphs
15
+ barnesHutTheta: 0.5,
16
+ defaultNodeColor: 4886754,
17
+ nodeRadius: 2,
18
+ nodeSegments: 32,
19
+ enableLOD: !0,
20
+ lodDistances: [50, 100, 200],
21
+ lodSegments: [32, 16, 8],
22
+ edgeColor: 8947848,
23
+ edgeOpacity: 0.4,
24
+ enableEdgeCulling: !0,
25
+ showPanel: !0,
26
+ targetFPS: 60,
27
+ maxVisibleNodes: 1e4
28
+ };
29
+ var _ = /* @__PURE__ */ ((c) => (c[c.HIGH = 0] = "HIGH", c[c.MEDIUM = 1] = "MEDIUM", c[c.LOW = 2] = "LOW", c))(_ || {});
30
+ function lt() {
31
+ const c = document.createElement("div");
32
+ return c.id = "force-graph-3d-container", c.style.cssText = `
33
+ width: 100%;
34
+ height: 100%;
35
+ position: absolute;
36
+ top: 0;
37
+ left: 0;
38
+ overflow: hidden;
39
+ `, document.body.appendChild(c), c;
40
+ }
41
+ function ct(c) {
42
+ return c && c instanceof HTMLElement ? c : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), lt());
43
+ }
44
+ function Se(c) {
45
+ const e = c.getBoundingClientRect();
46
+ return {
47
+ width: e.width || window.innerWidth,
48
+ height: e.height || window.innerHeight
49
+ };
50
+ }
51
+ function Ie(c) {
52
+ if (!c || typeof c != "object")
53
+ return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
54
+ const e = c;
55
+ 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 && !dt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
56
+ }
57
+ function Re(c) {
58
+ if (!c || typeof c != "object")
59
+ return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
60
+ const e = c;
61
+ 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;
62
+ }
63
+ function ht(c) {
64
+ return typeof c != "string" || c.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
65
+ }
66
+ function dt(c) {
67
+ if (!c || typeof c != "object") return !1;
68
+ const e = c;
69
+ return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
70
+ }
71
+ function I(c, e) {
72
+ return c === e ? `${c}-${e}` : c < e ? `${c}-${e}` : `${e}-${c}`;
73
+ }
74
+ const Pe = { type: "change" }, ae = { type: "start" }, Te = { type: "end" }, Z = new st(), Oe = new at(), pt = Math.cos(70 * rt.DEG2RAD);
75
+ class gt extends it {
76
+ constructor(e, o) {
77
+ super(), this.object = e, this.domElement = o, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new N(), this.cursor = new N(), 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: H.ROTATE, MIDDLE: H.DOLLY, RIGHT: H.PAN }, this.touches = { ONE: K.ROTATE, TWO: K.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
78
+ return l.phi;
79
+ }, this.getAzimuthalAngle = function() {
80
+ return l.theta;
81
+ }, this.getDistance = function() {
82
+ return this.object.position.distanceTo(this.target);
83
+ }, this.listenToKeyEvents = function(s) {
84
+ s.addEventListener("keydown", ie), this._domElementKeyEvents = s;
85
+ }, this.stopListenToKeyEvents = function() {
86
+ this._domElementKeyEvents.removeEventListener("keydown", ie), this._domElementKeyEvents = null;
87
+ }, this.saveState = function() {
88
+ t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
89
+ }, this.reset = function() {
90
+ t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(), i = n.NONE;
91
+ }, this.update = function() {
92
+ const s = new N(), h = new ze().setFromUnitVectors(e.up, new N(0, 1, 0)), m = h.clone().invert(), b = new N(), E = new ze(), F = new N(), z = 2 * Math.PI;
93
+ return function(tt = null) {
94
+ const Ce = t.object.position;
95
+ s.copy(Ce).sub(t.target), s.applyQuaternion(h), l.setFromVector3(s), t.autoRotate && i === n.NONE && G(Ae(tt)), t.enableDamping ? (l.theta += d.theta * t.dampingFactor, l.phi += d.phi * t.dampingFactor) : (l.theta += d.theta, l.phi += d.phi);
96
+ let P = t.minAzimuthAngle, T = t.maxAzimuthAngle;
97
+ isFinite(P) && isFinite(T) && (P < -Math.PI ? P += z : P > Math.PI && (P -= z), T < -Math.PI ? T += z : T > Math.PI && (T -= z), P <= T ? l.theta = Math.max(P, Math.min(T, l.theta)) : l.theta = l.theta > (P + T) / 2 ? Math.max(P, l.theta) : Math.min(T, l.theta)), l.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, l.phi)), l.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(u, t.dampingFactor) : t.target.add(u), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && U || t.object.isOrthographicCamera ? l.radius = oe(l.radius) : l.radius = oe(l.radius * g), s.setFromSpherical(l), s.applyQuaternion(m), Ce.copy(t.target).add(s), t.object.lookAt(t.target), t.enableDamping === !0 ? (d.theta *= 1 - t.dampingFactor, d.phi *= 1 - t.dampingFactor, u.multiplyScalar(1 - t.dampingFactor)) : (d.set(0, 0, 0), u.set(0, 0, 0));
98
+ let se = !1;
99
+ if (t.zoomToCursor && U) {
100
+ let Y = null;
101
+ if (t.object.isPerspectiveCamera) {
102
+ const B = s.length();
103
+ Y = oe(B * g);
104
+ const q = B - Y;
105
+ t.object.position.addScaledVector(re, q), t.object.updateMatrixWorld();
106
+ } else if (t.object.isOrthographicCamera) {
107
+ const B = new N(L.x, L.y, 0);
108
+ B.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / g)), t.object.updateProjectionMatrix(), se = !0;
109
+ const q = new N(L.x, L.y, 0);
110
+ q.unproject(t.object), t.object.position.sub(q).add(B), t.object.updateMatrixWorld(), Y = s.length();
111
+ } else
112
+ console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
113
+ Y !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(Y).add(t.object.position) : (Z.origin.copy(t.object.position), Z.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Z.direction)) < pt ? e.lookAt(t.target) : (Oe.setFromNormalAndCoplanarPoint(t.object.up, t.target), Z.intersectPlane(Oe, t.target))));
114
+ } else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / g)), t.object.updateProjectionMatrix(), se = !0);
115
+ return g = 1, U = !1, se || b.distanceToSquared(t.object.position) > a || 8 * (1 - E.dot(t.object.quaternion)) > a || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Pe), b.copy(t.object.position), E.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
116
+ };
117
+ }(), this.dispose = function() {
118
+ t.domElement.removeEventListener("contextmenu", Ee), t.domElement.removeEventListener("pointerdown", be), t.domElement.removeEventListener("pointercancel", $), t.domElement.removeEventListener("wheel", ve), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", $), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ie), t._domElementKeyEvents = null);
119
+ };
120
+ const t = this, n = {
121
+ NONE: -1,
122
+ ROTATE: 0,
123
+ DOLLY: 1,
124
+ PAN: 2,
125
+ TOUCH_ROTATE: 3,
126
+ TOUCH_PAN: 4,
127
+ TOUCH_DOLLY_PAN: 5,
128
+ TOUCH_DOLLY_ROTATE: 6
129
+ };
130
+ let i = n.NONE;
131
+ const a = 1e-6, l = new Ne(), d = new Ne();
132
+ let g = 1;
133
+ const u = new N(), x = new S(), v = new S(), y = new S(), f = new S(), M = new S(), C = new S(), R = new S(), A = new S(), k = new S(), re = new N(), L = new S();
134
+ let U = !1;
135
+ const w = [], V = {};
136
+ let J = !1;
137
+ function Ae(s) {
138
+ return s !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * s : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
139
+ }
140
+ function X(s) {
141
+ const h = Math.abs(s * 0.01);
142
+ return Math.pow(0.95, t.zoomSpeed * h);
143
+ }
144
+ function G(s) {
145
+ d.theta -= s;
146
+ }
147
+ function W(s) {
148
+ d.phi -= s;
149
+ }
150
+ const le = function() {
151
+ const s = new N();
152
+ return function(m, b) {
153
+ s.setFromMatrixColumn(b, 0), s.multiplyScalar(-m), u.add(s);
154
+ };
155
+ }(), ce = function() {
156
+ const s = new N();
157
+ return function(m, b) {
158
+ t.screenSpacePanning === !0 ? s.setFromMatrixColumn(b, 1) : (s.setFromMatrixColumn(b, 0), s.crossVectors(t.object.up, s)), s.multiplyScalar(m), u.add(s);
159
+ };
160
+ }(), j = function() {
161
+ const s = new N();
162
+ return function(m, b) {
163
+ const E = t.domElement;
164
+ if (t.object.isPerspectiveCamera) {
165
+ const F = t.object.position;
166
+ s.copy(F).sub(t.target);
167
+ let z = s.length();
168
+ z *= Math.tan(t.object.fov / 2 * Math.PI / 180), le(2 * m * z / E.clientHeight, t.object.matrix), ce(2 * b * z / E.clientHeight, t.object.matrix);
169
+ } else t.object.isOrthographicCamera ? (le(m * (t.object.right - t.object.left) / t.object.zoom / E.clientWidth, t.object.matrix), ce(b * (t.object.top - t.object.bottom) / t.object.zoom / E.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
170
+ };
171
+ }();
172
+ function ee(s) {
173
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? g /= s : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
174
+ }
175
+ function he(s) {
176
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? g *= s : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
177
+ }
178
+ function te(s, h) {
179
+ if (!t.zoomToCursor)
180
+ return;
181
+ U = !0;
182
+ const m = t.domElement.getBoundingClientRect(), b = s - m.left, E = h - m.top, F = m.width, z = m.height;
183
+ L.x = b / F * 2 - 1, L.y = -(E / z) * 2 + 1, re.set(L.x, L.y, 1).unproject(t.object).sub(t.object.position).normalize();
184
+ }
185
+ function oe(s) {
186
+ return Math.max(t.minDistance, Math.min(t.maxDistance, s));
187
+ }
188
+ function de(s) {
189
+ x.set(s.clientX, s.clientY);
190
+ }
191
+ function je(s) {
192
+ te(s.clientX, s.clientX), R.set(s.clientX, s.clientY);
193
+ }
194
+ function pe(s) {
195
+ f.set(s.clientX, s.clientY);
196
+ }
197
+ function De(s) {
198
+ v.set(s.clientX, s.clientY), y.subVectors(v, x).multiplyScalar(t.rotateSpeed);
199
+ const h = t.domElement;
200
+ G(2 * Math.PI * y.x / h.clientHeight), W(2 * Math.PI * y.y / h.clientHeight), x.copy(v), t.update();
201
+ }
202
+ function He(s) {
203
+ A.set(s.clientX, s.clientY), k.subVectors(A, R), k.y > 0 ? ee(X(k.y)) : k.y < 0 && he(X(k.y)), R.copy(A), t.update();
204
+ }
205
+ function Ke(s) {
206
+ M.set(s.clientX, s.clientY), C.subVectors(M, f).multiplyScalar(t.panSpeed), j(C.x, C.y), f.copy(M), t.update();
207
+ }
208
+ function Ge(s) {
209
+ te(s.clientX, s.clientY), s.deltaY < 0 ? he(X(s.deltaY)) : s.deltaY > 0 && ee(X(s.deltaY)), t.update();
210
+ }
211
+ function $e(s) {
212
+ let h = !1;
213
+ switch (s.code) {
214
+ case t.keys.UP:
215
+ s.ctrlKey || s.metaKey || s.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : j(0, t.keyPanSpeed), h = !0;
216
+ break;
217
+ case t.keys.BOTTOM:
218
+ s.ctrlKey || s.metaKey || s.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : j(0, -t.keyPanSpeed), h = !0;
219
+ break;
220
+ case t.keys.LEFT:
221
+ s.ctrlKey || s.metaKey || s.shiftKey ? G(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : j(t.keyPanSpeed, 0), h = !0;
222
+ break;
223
+ case t.keys.RIGHT:
224
+ s.ctrlKey || s.metaKey || s.shiftKey ? G(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : j(-t.keyPanSpeed, 0), h = !0;
225
+ break;
226
+ }
227
+ h && (s.preventDefault(), t.update());
228
+ }
229
+ function ge(s) {
230
+ if (w.length === 1)
231
+ x.set(s.pageX, s.pageY);
232
+ else {
233
+ const h = D(s), m = 0.5 * (s.pageX + h.x), b = 0.5 * (s.pageY + h.y);
234
+ x.set(m, b);
235
+ }
236
+ }
237
+ function ue(s) {
238
+ if (w.length === 1)
239
+ f.set(s.pageX, s.pageY);
240
+ else {
241
+ const h = D(s), m = 0.5 * (s.pageX + h.x), b = 0.5 * (s.pageY + h.y);
242
+ f.set(m, b);
243
+ }
244
+ }
245
+ function me(s) {
246
+ const h = D(s), m = s.pageX - h.x, b = s.pageY - h.y, E = Math.sqrt(m * m + b * b);
247
+ R.set(0, E);
248
+ }
249
+ function Ye(s) {
250
+ t.enableZoom && me(s), t.enablePan && ue(s);
251
+ }
252
+ function Be(s) {
253
+ t.enableZoom && me(s), t.enableRotate && ge(s);
254
+ }
255
+ function fe(s) {
256
+ if (w.length == 1)
257
+ v.set(s.pageX, s.pageY);
258
+ else {
259
+ const m = D(s), b = 0.5 * (s.pageX + m.x), E = 0.5 * (s.pageY + m.y);
260
+ v.set(b, E);
261
+ }
262
+ y.subVectors(v, x).multiplyScalar(t.rotateSpeed);
263
+ const h = t.domElement;
264
+ G(2 * Math.PI * y.x / h.clientHeight), W(2 * Math.PI * y.y / h.clientHeight), x.copy(v);
265
+ }
266
+ function ye(s) {
267
+ if (w.length === 1)
268
+ M.set(s.pageX, s.pageY);
269
+ else {
270
+ const h = D(s), m = 0.5 * (s.pageX + h.x), b = 0.5 * (s.pageY + h.y);
271
+ M.set(m, b);
272
+ }
273
+ C.subVectors(M, f).multiplyScalar(t.panSpeed), j(C.x, C.y), f.copy(M);
274
+ }
275
+ function xe(s) {
276
+ const h = D(s), m = s.pageX - h.x, b = s.pageY - h.y, E = Math.sqrt(m * m + b * b);
277
+ A.set(0, E), k.set(0, Math.pow(A.y / R.y, t.zoomSpeed)), ee(k.y), R.copy(A);
278
+ const F = (s.pageX + h.x) * 0.5, z = (s.pageY + h.y) * 0.5;
279
+ te(F, z);
280
+ }
281
+ function _e(s) {
282
+ t.enableZoom && xe(s), t.enablePan && ye(s);
283
+ }
284
+ function Ue(s) {
285
+ t.enableZoom && xe(s), t.enableRotate && fe(s);
286
+ }
287
+ function be(s) {
288
+ t.enabled !== !1 && (w.length === 0 && (t.domElement.setPointerCapture(s.pointerId), t.domElement.addEventListener("pointermove", ne), t.domElement.addEventListener("pointerup", $)), Je(s), s.pointerType === "touch" ? Ze(s) : Ve(s));
289
+ }
290
+ function ne(s) {
291
+ t.enabled !== !1 && (s.pointerType === "touch" ? Qe(s) : Xe(s));
292
+ }
293
+ function $(s) {
294
+ et(s), w.length === 0 && (t.domElement.releasePointerCapture(s.pointerId), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", $)), t.dispatchEvent(Te), i = n.NONE;
295
+ }
296
+ function Ve(s) {
297
+ let h;
298
+ switch (s.button) {
299
+ case 0:
300
+ h = t.mouseButtons.LEFT;
301
+ break;
302
+ case 1:
303
+ h = t.mouseButtons.MIDDLE;
304
+ break;
305
+ case 2:
306
+ h = t.mouseButtons.RIGHT;
307
+ break;
308
+ default:
309
+ h = -1;
310
+ }
311
+ switch (h) {
312
+ case H.DOLLY:
313
+ if (t.enableZoom === !1) return;
314
+ je(s), i = n.DOLLY;
315
+ break;
316
+ case H.ROTATE:
317
+ if (s.ctrlKey || s.metaKey || s.shiftKey) {
318
+ if (t.enablePan === !1) return;
319
+ pe(s), i = n.PAN;
320
+ } else {
321
+ if (t.enableRotate === !1) return;
322
+ de(s), i = n.ROTATE;
323
+ }
324
+ break;
325
+ case H.PAN:
326
+ if (s.ctrlKey || s.metaKey || s.shiftKey) {
327
+ if (t.enableRotate === !1) return;
328
+ de(s), i = n.ROTATE;
329
+ } else {
330
+ if (t.enablePan === !1) return;
331
+ pe(s), i = n.PAN;
332
+ }
333
+ break;
334
+ default:
335
+ i = n.NONE;
336
+ }
337
+ i !== n.NONE && t.dispatchEvent(ae);
338
+ }
339
+ function Xe(s) {
340
+ switch (i) {
341
+ case n.ROTATE:
342
+ if (t.enableRotate === !1) return;
343
+ De(s);
344
+ break;
345
+ case n.DOLLY:
346
+ if (t.enableZoom === !1) return;
347
+ He(s);
348
+ break;
349
+ case n.PAN:
350
+ if (t.enablePan === !1) return;
351
+ Ke(s);
352
+ break;
353
+ }
354
+ }
355
+ function ve(s) {
356
+ t.enabled === !1 || t.enableZoom === !1 || i !== n.NONE || (s.preventDefault(), t.dispatchEvent(ae), Ge(We(s)), t.dispatchEvent(Te));
357
+ }
358
+ function We(s) {
359
+ const h = s.deltaMode, m = {
360
+ clientX: s.clientX,
361
+ clientY: s.clientY,
362
+ deltaY: s.deltaY
363
+ };
364
+ switch (h) {
365
+ case 1:
366
+ m.deltaY *= 16;
367
+ break;
368
+ case 2:
369
+ m.deltaY *= 100;
370
+ break;
371
+ }
372
+ return s.ctrlKey && !J && (m.deltaY *= 10), m;
373
+ }
374
+ function qe(s) {
375
+ s.key === "Control" && (J = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
376
+ }
377
+ function Me(s) {
378
+ s.key === "Control" && (J = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
379
+ }
380
+ function ie(s) {
381
+ t.enabled === !1 || t.enablePan === !1 || $e(s);
382
+ }
383
+ function Ze(s) {
384
+ switch (we(s), w.length) {
385
+ case 1:
386
+ switch (t.touches.ONE) {
387
+ case K.ROTATE:
388
+ if (t.enableRotate === !1) return;
389
+ ge(s), i = n.TOUCH_ROTATE;
390
+ break;
391
+ case K.PAN:
392
+ if (t.enablePan === !1) return;
393
+ ue(s), i = n.TOUCH_PAN;
394
+ break;
395
+ default:
396
+ i = n.NONE;
397
+ }
398
+ break;
399
+ case 2:
400
+ switch (t.touches.TWO) {
401
+ case K.DOLLY_PAN:
402
+ if (t.enableZoom === !1 && t.enablePan === !1) return;
403
+ Ye(s), i = n.TOUCH_DOLLY_PAN;
404
+ break;
405
+ case K.DOLLY_ROTATE:
406
+ if (t.enableZoom === !1 && t.enableRotate === !1) return;
407
+ Be(s), i = n.TOUCH_DOLLY_ROTATE;
408
+ break;
409
+ default:
410
+ i = n.NONE;
411
+ }
412
+ break;
413
+ default:
414
+ i = n.NONE;
415
+ }
416
+ i !== n.NONE && t.dispatchEvent(ae);
417
+ }
418
+ function Qe(s) {
419
+ switch (we(s), i) {
420
+ case n.TOUCH_ROTATE:
421
+ if (t.enableRotate === !1) return;
422
+ fe(s), t.update();
423
+ break;
424
+ case n.TOUCH_PAN:
425
+ if (t.enablePan === !1) return;
426
+ ye(s), t.update();
427
+ break;
428
+ case n.TOUCH_DOLLY_PAN:
429
+ if (t.enableZoom === !1 && t.enablePan === !1) return;
430
+ _e(s), t.update();
431
+ break;
432
+ case n.TOUCH_DOLLY_ROTATE:
433
+ if (t.enableZoom === !1 && t.enableRotate === !1) return;
434
+ Ue(s), t.update();
435
+ break;
436
+ default:
437
+ i = n.NONE;
438
+ }
439
+ }
440
+ function Ee(s) {
441
+ t.enabled !== !1 && s.preventDefault();
442
+ }
443
+ function Je(s) {
444
+ w.push(s.pointerId);
445
+ }
446
+ function et(s) {
447
+ delete V[s.pointerId];
448
+ for (let h = 0; h < w.length; h++)
449
+ if (w[h] == s.pointerId) {
450
+ w.splice(h, 1);
451
+ return;
452
+ }
453
+ }
454
+ function we(s) {
455
+ let h = V[s.pointerId];
456
+ h === void 0 && (h = new S(), V[s.pointerId] = h), h.set(s.pageX, s.pageY);
457
+ }
458
+ function D(s) {
459
+ const h = s.pointerId === w[0] ? w[1] : w[0];
460
+ return V[h];
461
+ }
462
+ t.domElement.addEventListener("contextmenu", Ee), t.domElement.addEventListener("pointerdown", be), t.domElement.addEventListener("pointercancel", $), t.domElement.addEventListener("wheel", ve, { passive: !1 }), document.addEventListener("keydown", qe, { passive: !0, capture: !0 }), this.update();
463
+ }
464
+ }
465
+ class ut {
466
+ constructor(e, o) {
467
+ r(this, "scene");
468
+ r(this, "camera");
469
+ r(this, "renderer");
470
+ r(this, "controls");
471
+ r(this, "container");
472
+ r(this, "resizeHandler");
473
+ this.container = e, this.scene = new p.Scene(), this.scene.background = new p.Color(
474
+ o.backgroundColor ?? 657930
475
+ );
476
+ const { width: t, height: n } = Se(e), i = o.cameraFov ?? 75;
477
+ this.camera = new p.PerspectiveCamera(i, t / n, 0.1, 2e3);
478
+ const a = o.cameraPosition ?? { x: 0, y: 0, z: 80 };
479
+ this.camera.position.set(a.x, a.y, a.z), this.renderer = new p.WebGLRenderer({
480
+ antialias: !0,
481
+ alpha: !0,
482
+ powerPreference: "high-performance"
483
+ }), this.renderer.setSize(t, n), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = p.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = p.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new gt(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 = 500, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
484
+ }
485
+ /**
486
+ * Sets up scene lighting for gradient glass on dark background
487
+ */
488
+ setupLighting() {
489
+ const e = new p.AmbientLight(16777215, 0.4);
490
+ this.scene.add(e);
491
+ const o = new p.DirectionalLight(16777215, 0.9);
492
+ o.position.set(50, 60, 40), o.castShadow = !0, o.shadow.mapSize.width = 1024, o.shadow.mapSize.height = 1024, this.scene.add(o);
493
+ const t = new p.DirectionalLight(16773344, 0.4);
494
+ t.position.set(-50, 30, -40), this.scene.add(t);
495
+ const n = new p.DirectionalLight(16777215, 0.3);
496
+ n.position.set(0, -30, -50), this.scene.add(n);
497
+ const i = new p.PointLight(16750950, 0.5, 150);
498
+ i.position.set(40, 20, 40), this.scene.add(i);
499
+ const a = new p.PointLight(16764057, 0.4, 150);
500
+ a.position.set(-40, -20, 40), this.scene.add(a);
501
+ const l = new p.PointLight(6724095, 0.2, 100);
502
+ l.position.set(0, 40, -40), this.scene.add(l);
503
+ }
504
+ /**
505
+ * Handle window resize
506
+ */
507
+ onWindowResize() {
508
+ const { width: e, height: o } = Se(this.container);
509
+ this.camera.aspect = e / o, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, o);
510
+ }
511
+ /**
512
+ * Adds an object to the scene
513
+ */
514
+ add(e) {
515
+ this.scene.add(e);
516
+ }
517
+ /**
518
+ * Removes an object from the scene
519
+ */
520
+ remove(e) {
521
+ this.scene.remove(e);
522
+ }
523
+ /**
524
+ * Renders the scene
525
+ */
526
+ render() {
527
+ this.controls.update(), this.renderer.render(this.scene, this.camera);
528
+ }
529
+ /**
530
+ * Gets the camera position
531
+ */
532
+ getCameraPosition() {
533
+ return {
534
+ x: this.camera.position.x,
535
+ y: this.camera.position.y,
536
+ z: this.camera.position.z
537
+ };
538
+ }
539
+ /**
540
+ * Gets the camera's forward direction
541
+ */
542
+ getCameraDirection() {
543
+ const e = new p.Vector3();
544
+ return this.camera.getWorldDirection(e), e;
545
+ }
546
+ /**
547
+ * Dispose all resources
548
+ */
549
+ dispose() {
550
+ for (window.removeEventListener("resize", this.resizeHandler), this.controls.dispose(), this.renderer.dispose(), this.renderer.domElement.parentNode && this.renderer.domElement.parentNode.removeChild(this.renderer.domElement); this.scene.children.length > 0; ) {
551
+ const e = this.scene.children[0];
552
+ this.scene.remove(e);
553
+ }
554
+ }
555
+ }
556
+ class mt {
557
+ constructor(e, o) {
558
+ r(this, "sceneManager");
559
+ r(this, "nodeFactory");
560
+ r(this, "nodes", /* @__PURE__ */ new Map());
561
+ r(this, "nodeObjects", /* @__PURE__ */ new Map());
562
+ this.sceneManager = e, this.nodeFactory = o;
563
+ }
564
+ /**
565
+ * Checks if a node exists
566
+ */
567
+ hasNode(e) {
568
+ return this.nodes.has(e);
569
+ }
570
+ /**
571
+ * Adds a node to the graph
572
+ * @returns true if added, false if node already exists or invalid
573
+ */
574
+ addNode(e, o = 0) {
575
+ if (!Ie(e))
576
+ return !1;
577
+ if (this.nodes.has(e.id))
578
+ return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
579
+ const t = e.position ?? {
580
+ x: (Math.random() - 0.5) * 50,
581
+ y: (Math.random() - 0.5) * 50,
582
+ z: (Math.random() - 0.5) * 50
583
+ }, n = {
584
+ ...e,
585
+ position: t,
586
+ velocity: { x: 0, y: 0, z: 0 },
587
+ mass: 1
588
+ }, i = this.nodeFactory.createNode(
589
+ { ...e, position: t },
590
+ o
591
+ );
592
+ return this.sceneManager.add(i.group), this.nodes.set(e.id, n), this.nodeObjects.set(e.id, i), !0;
593
+ }
594
+ /**
595
+ * Removes a node from the graph
596
+ * @returns true if removed, false if not found
597
+ */
598
+ removeNode(e) {
599
+ const o = this.nodes.get(e), t = this.nodeObjects.get(e);
600
+ return !o || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), !0);
601
+ }
602
+ /**
603
+ * Updates a node's properties
604
+ */
605
+ updateNode(e, o) {
606
+ const t = this.nodes.get(e), n = this.nodeObjects.get(e);
607
+ return !t || !n ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (o.label !== void 0 && (t.label = o.label, this.nodeFactory.updateNodeLabel(n, o.label)), o.color !== void 0 && (t.color = o.color, this.nodeFactory.updateNodeColor(n, o.color)), Object.keys(o).forEach((i) => {
608
+ i !== "id" && i !== "label" && i !== "color" && i !== "position" && (t[i] = o[i]);
609
+ }), !0);
610
+ }
611
+ /**
612
+ * Updates a node's position (called by physics engine)
613
+ */
614
+ updateNodePosition(e, o) {
615
+ const t = this.nodes.get(e), n = this.nodeObjects.get(e);
616
+ t && n && (t.position = o, n.group.position.set(o.x, o.y, o.z));
617
+ }
618
+ /**
619
+ * Updates a node's LOD level
620
+ */
621
+ updateNodeLOD(e, o) {
622
+ const t = this.nodeObjects.get(e);
623
+ t && this.nodeFactory.updateNodeLOD(t, o);
624
+ }
625
+ /**
626
+ * Gets a node by ID
627
+ */
628
+ getNode(e) {
629
+ return this.nodes.get(e);
630
+ }
631
+ /**
632
+ * Gets a node's visual object
633
+ */
634
+ getNodeObject(e) {
635
+ return this.nodeObjects.get(e);
636
+ }
637
+ /**
638
+ * Gets all nodes
639
+ */
640
+ getAllNodes() {
641
+ return this.nodes;
642
+ }
643
+ /**
644
+ * Gets all node objects (for raycasting)
645
+ */
646
+ getAllNodeObjects() {
647
+ const e = [];
648
+ return this.nodeObjects.forEach((o) => {
649
+ e.push(o.group);
650
+ }), e;
651
+ }
652
+ /**
653
+ * Gets all spheres (for raycasting)
654
+ */
655
+ getAllSpheres() {
656
+ const e = [];
657
+ return this.nodeObjects.forEach((o) => {
658
+ e.push(o.sphere);
659
+ }), e;
660
+ }
661
+ /**
662
+ * Gets node count
663
+ */
664
+ getNodeCount() {
665
+ return this.nodes.size;
666
+ }
667
+ /**
668
+ * Clears all nodes
669
+ */
670
+ clear() {
671
+ this.nodes.forEach((e, o) => {
672
+ this.removeNode(o);
673
+ });
674
+ }
675
+ /**
676
+ * Dispose resources
677
+ */
678
+ dispose() {
679
+ this.clear();
680
+ }
681
+ }
682
+ class ft {
683
+ constructor(e, o, t) {
684
+ r(this, "sceneManager");
685
+ r(this, "nodeManager");
686
+ r(this, "edgeFactory");
687
+ r(this, "edges", []);
688
+ r(this, "edgeObjects", []);
689
+ r(this, "edgeKeySet", /* @__PURE__ */ new Set());
690
+ r(this, "highlightedEdgeKey", null);
691
+ this.sceneManager = e, this.nodeManager = o, this.edgeFactory = t;
692
+ }
693
+ /**
694
+ * Checks if an edge exists
695
+ */
696
+ hasEdge(e, o) {
697
+ const t = I(e, o);
698
+ return this.edgeKeySet.has(t);
699
+ }
700
+ /**
701
+ * Adds an edge to the graph
702
+ * @returns true if added, false if edge already exists or nodes don't exist
703
+ */
704
+ addEdge(e) {
705
+ if (!Re(e))
706
+ return !1;
707
+ if (!this.nodeManager.hasNode(e.source))
708
+ return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
709
+ if (!this.nodeManager.hasNode(e.target))
710
+ return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
711
+ const o = I(e.source, e.target);
712
+ if (this.edgeKeySet.has(o))
713
+ return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
714
+ const t = this.nodeManager.getNode(e.source), n = this.nodeManager.getNode(e.target), i = this.edgeFactory.createEdge(
715
+ e,
716
+ t,
717
+ n,
718
+ t.position,
719
+ n.position
720
+ );
721
+ return this.sceneManager.add(i.line), this.edges.push(e), this.edgeObjects.push(i), this.edgeKeySet.add(o), !0;
722
+ }
723
+ /**
724
+ * Removes an edge from the graph
725
+ * @returns true if removed, false if not found
726
+ */
727
+ removeEdge(e, o) {
728
+ const t = I(e, o);
729
+ if (!this.edgeKeySet.has(t))
730
+ return !1;
731
+ const n = this.edges.findIndex(
732
+ (a) => I(a.source, a.target) === t
733
+ );
734
+ if (n === -1)
735
+ return !1;
736
+ const i = this.edgeObjects[n];
737
+ return this.sceneManager.remove(i.line), this.edgeFactory.disposeEdge(i), this.edges.splice(n, 1), this.edgeObjects.splice(n, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
738
+ }
739
+ /**
740
+ * Highlights an edge
741
+ */
742
+ highlightEdge(e, o) {
743
+ const t = I(e, o);
744
+ this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
745
+ const n = this.edges.findIndex(
746
+ (i) => I(i.source, i.target) === t
747
+ );
748
+ n !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[n]), this.highlightedEdgeKey = t);
749
+ }
750
+ /**
751
+ * Unhighlights the currently highlighted edge
752
+ */
753
+ unhighlightCurrentEdge() {
754
+ if (!this.highlightedEdgeKey) return;
755
+ const e = this.edges.findIndex(
756
+ (o) => I(o.source, o.target) === this.highlightedEdgeKey
757
+ );
758
+ e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
759
+ }
760
+ /**
761
+ * Removes all edges connected to a node
762
+ */
763
+ removeEdgesForNode(e) {
764
+ const o = [];
765
+ this.edges.forEach((t) => {
766
+ (t.source === e || t.target === e) && o.push({ source: t.source, target: t.target });
767
+ }), o.forEach((t) => {
768
+ this.removeEdge(t.source, t.target);
769
+ });
770
+ }
771
+ /**
772
+ * Gets all edges for a node
773
+ */
774
+ getEdgesForNode(e) {
775
+ return this.edges.filter((o) => o.source === e || o.target === e);
776
+ }
777
+ /**
778
+ * Gets neighbor node IDs for a node
779
+ */
780
+ getNeighborIds(e) {
781
+ const o = [];
782
+ return this.edges.forEach((t) => {
783
+ t.source === e ? o.push(t.target) : t.target === e && o.push(t.source);
784
+ }), [...new Set(o)];
785
+ }
786
+ /**
787
+ * Updates all edge positions based on current node positions
788
+ */
789
+ updateEdgePositions() {
790
+ this.edgeObjects.forEach((e, o) => {
791
+ const t = this.edges[o], n = this.nodeManager.getNode(t.source), i = this.nodeManager.getNode(t.target);
792
+ n && i && this.edgeFactory.updateEdgePositions(
793
+ e,
794
+ n.position,
795
+ i.position
796
+ );
797
+ });
798
+ }
799
+ /**
800
+ * Gets all edges
801
+ */
802
+ getAllEdges() {
803
+ return this.edges;
804
+ }
805
+ /**
806
+ * Gets all edge line objects (for raycasting)
807
+ */
808
+ getAllEdgeLines() {
809
+ return this.edgeObjects.map((e) => e.line);
810
+ }
811
+ /**
812
+ * Gets edge count
813
+ */
814
+ getEdgeCount() {
815
+ return this.edges.length;
816
+ }
817
+ /**
818
+ * Clears all edges
819
+ */
820
+ clear() {
821
+ this.edgeObjects.forEach((e) => {
822
+ this.sceneManager.remove(e.line), this.edgeFactory.disposeEdge(e);
823
+ }), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.highlightedEdgeKey = null;
824
+ }
825
+ /**
826
+ * Dispose resources
827
+ */
828
+ dispose() {
829
+ this.clear();
830
+ }
831
+ }
832
+ class Le {
833
+ constructor(e, o, t = {}) {
834
+ r(this, "nodes");
835
+ r(this, "edges");
836
+ // Physics parameters
837
+ r(this, "repulsionStrength");
838
+ r(this, "attractionStrength");
839
+ r(this, "damping");
840
+ r(this, "useBarnesHut");
841
+ r(this, "barnesHutTheta");
842
+ // Simulation state
843
+ r(this, "alpha", 1);
844
+ r(this, "alphaDecay", 0.0228);
845
+ r(this, "alphaMin", 1e-3);
846
+ r(this, "alphaTarget", 0);
847
+ this.nodes = e, this.edges = o, this.repulsionStrength = t.repulsionStrength ?? 100, this.attractionStrength = t.attractionStrength ?? 0.01, this.damping = t.damping ?? 0.9, this.useBarnesHut = t.useBarnesHut ?? !1, this.barnesHutTheta = t.barnesHutTheta ?? 0.5;
848
+ }
849
+ /**
850
+ * Runs one simulation step
851
+ */
852
+ simulate() {
853
+ if (this.alpha < this.alphaMin) return;
854
+ const e = this.nodes.size;
855
+ (this.useBarnesHut || e >= 1e3) && e >= 100 ? this.calculateRepulsionBarnesHut() : this.calculateRepulsionBruteForce(), this.calculateAttraction(), this.applyForces(), this.alpha += (this.alphaTarget - this.alpha) * this.alphaDecay;
856
+ }
857
+ /**
858
+ * Brute force repulsion - O(n²)
859
+ */
860
+ calculateRepulsionBruteForce() {
861
+ const e = Array.from(this.nodes.values()), o = e.length;
862
+ for (let t = 0; t < o; t++) {
863
+ const n = e[t];
864
+ for (let i = t + 1; i < o; i++) {
865
+ const a = e[i], l = a.position.x - n.position.x, d = a.position.y - n.position.y, g = a.position.z - n.position.z;
866
+ let u = l * l + d * d + g * g;
867
+ u < 0.01 && (u = 0.01);
868
+ const x = Math.sqrt(u), v = this.repulsionStrength * this.alpha / u, y = l / x * v, f = d / x * v, M = g / x * v;
869
+ n.velocity.x -= y / n.mass, n.velocity.y -= f / n.mass, n.velocity.z -= M / n.mass, a.velocity.x += y / a.mass, a.velocity.y += f / a.mass, a.velocity.z += M / a.mass;
870
+ }
871
+ }
872
+ }
873
+ /**
874
+ * Barnes-Hut approximation - O(n log n)
875
+ */
876
+ calculateRepulsionBarnesHut() {
877
+ const e = Array.from(this.nodes.values()), o = new yt(e);
878
+ for (const t of e)
879
+ this.calculateForceFromOctree(t, o.root);
880
+ }
881
+ /**
882
+ * Recursive force calculation using octree
883
+ */
884
+ calculateForceFromOctree(e, o) {
885
+ if (o.isLeaf) {
886
+ o.node && o.node.id !== e.id && this.applyRepulsionBetween(e, o.node);
887
+ return;
888
+ }
889
+ if (o.mass === 0) return;
890
+ const t = o.centerOfMass.x - e.position.x, n = o.centerOfMass.y - e.position.y, i = o.centerOfMass.z - e.position.z, a = Math.sqrt(t * t + n * n + i * i);
891
+ if (o.size / a < this.barnesHutTheta) {
892
+ const l = Math.max(a * a, 0.01), d = this.repulsionStrength * this.alpha * o.mass / l;
893
+ e.velocity.x -= t / a * d / e.mass, e.velocity.y -= n / a * d / e.mass, e.velocity.z -= i / a * d / e.mass;
894
+ } else
895
+ for (const l of o.children)
896
+ l && this.calculateForceFromOctree(e, l);
897
+ }
898
+ /**
899
+ * Apply repulsion between two nodes
900
+ */
901
+ applyRepulsionBetween(e, o) {
902
+ const t = o.position.x - e.position.x, n = o.position.y - e.position.y, i = o.position.z - e.position.z;
903
+ let a = t * t + n * n + i * i;
904
+ a < 0.01 && (a = 0.01);
905
+ const l = Math.sqrt(a), d = this.repulsionStrength * this.alpha / a;
906
+ e.velocity.x -= t / l * d / e.mass, e.velocity.y -= n / l * d / e.mass, e.velocity.z -= i / l * d / e.mass;
907
+ }
908
+ /**
909
+ * Calculate attraction forces along edges
910
+ */
911
+ calculateAttraction() {
912
+ for (const e of this.edges) {
913
+ const o = this.nodes.get(e.source), t = this.nodes.get(e.target);
914
+ if (!o || !t) continue;
915
+ const n = t.position.x - o.position.x, i = t.position.y - o.position.y, a = t.position.z - o.position.z, l = Math.sqrt(n * n + i * i + a * a);
916
+ if (l < 0.01) continue;
917
+ const g = (l - 15) * this.attractionStrength * this.alpha, u = n / l * g, x = i / l * g, v = a / l * g;
918
+ o.velocity.x += u / o.mass, o.velocity.y += x / o.mass, o.velocity.z += v / o.mass, t.velocity.x -= u / t.mass, t.velocity.y -= x / t.mass, t.velocity.z -= v / t.mass;
919
+ }
920
+ }
921
+ /**
922
+ * Apply velocities and damping to positions
923
+ */
924
+ applyForces() {
925
+ for (const e of this.nodes.values())
926
+ e.velocity.x *= this.damping, e.velocity.y *= this.damping, e.velocity.z *= this.damping, e.position.x += e.velocity.x, e.position.y += e.velocity.y, e.position.z += e.velocity.z;
927
+ }
928
+ /**
929
+ * Updates the edges reference
930
+ */
931
+ setEdges(e) {
932
+ this.edges = e;
933
+ }
934
+ /**
935
+ * Resets the simulation (restarts with full energy)
936
+ */
937
+ restart() {
938
+ this.alpha = 1;
939
+ }
940
+ /**
941
+ * Gets the current alpha value
942
+ */
943
+ getAlpha() {
944
+ return this.alpha;
945
+ }
946
+ /**
947
+ * Sets physics parameters
948
+ */
949
+ setPhysicsParams(e) {
950
+ e.repulsionStrength !== void 0 && (this.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.damping = e.damping);
951
+ }
952
+ }
953
+ class yt {
954
+ constructor(e) {
955
+ r(this, "root");
956
+ const o = this.calculateBounds(e);
957
+ this.root = this.buildTree(e, o);
958
+ }
959
+ calculateBounds(e) {
960
+ if (e.length === 0)
961
+ return {
962
+ min: { x: -100, y: -100, z: -100 },
963
+ max: { x: 100, y: 100, z: 100 }
964
+ };
965
+ const o = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
966
+ for (const i of e)
967
+ o.x = Math.min(o.x, i.position.x), o.y = Math.min(o.y, i.position.y), o.z = Math.min(o.z, i.position.z), t.x = Math.max(t.x, i.position.x), t.y = Math.max(t.y, i.position.y), t.z = Math.max(t.z, i.position.z);
968
+ const n = 10;
969
+ return o.x -= n, o.y -= n, o.z -= n, t.x += n, t.y += n, t.z += n, { min: o, max: t };
970
+ }
971
+ buildTree(e, o, t = 0) {
972
+ const n = Math.max(
973
+ o.max.x - o.min.x,
974
+ o.max.y - o.min.y,
975
+ o.max.z - o.min.z
976
+ );
977
+ if (e.length === 0)
978
+ return {
979
+ bounds: o,
980
+ size: n,
981
+ centerOfMass: { x: 0, y: 0, z: 0 },
982
+ mass: 0,
983
+ isLeaf: !0,
984
+ node: null,
985
+ children: []
986
+ };
987
+ if (e.length === 1 || t > 20) {
988
+ let y = 0;
989
+ const f = { x: 0, y: 0, z: 0 };
990
+ for (const M of e)
991
+ y += M.mass, f.x += M.position.x * M.mass, f.y += M.position.y * M.mass, f.z += M.position.z * M.mass;
992
+ return y > 0 && (f.x /= y, f.y /= y, f.z /= y), {
993
+ bounds: o,
994
+ size: n,
995
+ centerOfMass: f,
996
+ mass: y,
997
+ isLeaf: !0,
998
+ node: e[0],
999
+ children: []
1000
+ };
1001
+ }
1002
+ const i = (o.min.x + o.max.x) / 2, a = (o.min.y + o.max.y) / 2, l = (o.min.z + o.max.z) / 2, d = [[], [], [], [], [], [], [], []];
1003
+ for (const y of e) {
1004
+ const f = (y.position.x >= i ? 1 : 0) + (y.position.y >= a ? 2 : 0) + (y.position.z >= l ? 4 : 0);
1005
+ d[f].push(y);
1006
+ }
1007
+ const g = [
1008
+ { min: { x: o.min.x, y: o.min.y, z: o.min.z }, max: { x: i, y: a, z: l } },
1009
+ { min: { x: i, y: o.min.y, z: o.min.z }, max: { x: o.max.x, y: a, z: l } },
1010
+ { min: { x: o.min.x, y: a, z: o.min.z }, max: { x: i, y: o.max.y, z: l } },
1011
+ { min: { x: i, y: a, z: o.min.z }, max: { x: o.max.x, y: o.max.y, z: l } },
1012
+ { min: { x: o.min.x, y: o.min.y, z: l }, max: { x: i, y: a, z: o.max.z } },
1013
+ { min: { x: i, y: o.min.y, z: l }, max: { x: o.max.x, y: a, z: o.max.z } },
1014
+ { min: { x: o.min.x, y: a, z: l }, max: { x: i, y: o.max.y, z: o.max.z } },
1015
+ { min: { x: i, y: a, z: l }, max: { x: o.max.x, y: o.max.y, z: o.max.z } }
1016
+ ], u = [];
1017
+ let x = 0;
1018
+ const v = { x: 0, y: 0, z: 0 };
1019
+ for (let y = 0; y < 8; y++)
1020
+ if (d[y].length > 0) {
1021
+ const f = this.buildTree(d[y], g[y], t + 1);
1022
+ u.push(f), x += f.mass, v.x += f.centerOfMass.x * f.mass, v.y += f.centerOfMass.y * f.mass, v.z += f.centerOfMass.z * f.mass;
1023
+ } else
1024
+ u.push(null);
1025
+ return x > 0 && (v.x /= x, v.y /= x, v.z /= x), {
1026
+ bounds: o,
1027
+ size: n,
1028
+ centerOfMass: v,
1029
+ mass: x,
1030
+ isLeaf: !1,
1031
+ node: null,
1032
+ children: u
1033
+ };
1034
+ }
1035
+ }
1036
+ class xt {
1037
+ constructor(e, o, t, n = 60) {
1038
+ r(this, "sceneManager");
1039
+ r(this, "animationId", null);
1040
+ r(this, "isRunning", !1);
1041
+ r(this, "frameInterval");
1042
+ r(this, "lastFrameTime", 0);
1043
+ r(this, "onSimulate");
1044
+ r(this, "onRender");
1045
+ // Performance monitoring
1046
+ r(this, "frameCount", 0);
1047
+ r(this, "fpsStartTime", 0);
1048
+ r(this, "currentFPS", 60);
1049
+ /**
1050
+ * Main animation loop
1051
+ */
1052
+ r(this, "animate", () => {
1053
+ if (!this.isRunning) return;
1054
+ this.animationId = requestAnimationFrame(this.animate);
1055
+ const e = performance.now(), o = e - this.lastFrameTime;
1056
+ if (o < this.frameInterval)
1057
+ return;
1058
+ this.lastFrameTime = e - o % this.frameInterval, this.frameCount++;
1059
+ const t = e - this.fpsStartTime;
1060
+ t >= 1e3 && (this.currentFPS = this.frameCount / (t / 1e3), this.frameCount = 0, this.fpsStartTime = e), this.onSimulate(), this.onRender(), this.sceneManager.render();
1061
+ });
1062
+ this.sceneManager = e, this.onSimulate = o, this.onRender = t, this.frameInterval = 1e3 / n;
1063
+ }
1064
+ /**
1065
+ * Starts the animation loop
1066
+ */
1067
+ start() {
1068
+ this.isRunning || (this.isRunning = !0, this.lastFrameTime = performance.now(), this.fpsStartTime = performance.now(), this.frameCount = 0, this.animate());
1069
+ }
1070
+ /**
1071
+ * Stops the animation loop
1072
+ */
1073
+ stop() {
1074
+ this.isRunning = !1, this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null);
1075
+ }
1076
+ /**
1077
+ * Gets the current FPS
1078
+ */
1079
+ getFPS() {
1080
+ return Math.round(this.currentFPS);
1081
+ }
1082
+ /**
1083
+ * Sets target FPS
1084
+ */
1085
+ setTargetFPS(e) {
1086
+ this.frameInterval = 1e3 / e;
1087
+ }
1088
+ /**
1089
+ * Checks if animation is running
1090
+ */
1091
+ isAnimating() {
1092
+ return this.isRunning;
1093
+ }
1094
+ /**
1095
+ * Dispose
1096
+ */
1097
+ dispose() {
1098
+ this.stop();
1099
+ }
1100
+ }
1101
+ class bt {
1102
+ constructor() {
1103
+ r(this, "envMap", null);
1104
+ r(this, "materialCache", /* @__PURE__ */ new Map());
1105
+ // Color palette - white and tangerine
1106
+ r(this, "COLORS", [
1107
+ 16777215,
1108
+ // White
1109
+ 16750950,
1110
+ // Tangerine/orange
1111
+ 16764057,
1112
+ // Light peach
1113
+ 16772829,
1114
+ // Cream
1115
+ 16746564
1116
+ // Darker tangerine
1117
+ ]);
1118
+ this.createEnvironmentMap();
1119
+ }
1120
+ /**
1121
+ * Creates environment map for glass reflections on dark background
1122
+ */
1123
+ createEnvironmentMap() {
1124
+ const o = [], t = [
1125
+ // Subtle colored gradients for interesting reflections
1126
+ { colors: ["#1a1a2e", "#16213e", "#0f3460"] },
1127
+ // +x
1128
+ { colors: ["#2d1f1f", "#1a1a1a", "#0f0f0f"] },
1129
+ // -x
1130
+ { colors: ["#1f2d2d", "#1a1a2e", "#101020"] },
1131
+ // +y
1132
+ { colors: ["#0a0a0a", "#050505", "#000000"] },
1133
+ // -y
1134
+ { colors: ["#1a1a2e", "#0f1a2a", "#0a0a15"] },
1135
+ // +z
1136
+ { colors: ["#2d1a1a", "#1a0a0a", "#0f0505"] }
1137
+ // -z
1138
+ ];
1139
+ for (const n of t) {
1140
+ const i = document.createElement("canvas");
1141
+ i.width = 256, i.height = 256;
1142
+ const a = i.getContext("2d"), l = a.createRadialGradient(
1143
+ 256 / 2,
1144
+ 256 / 2,
1145
+ 0,
1146
+ 256 / 2,
1147
+ 256 / 2,
1148
+ 256 * 0.8
1149
+ );
1150
+ l.addColorStop(0, n.colors[0]), l.addColorStop(0.5, n.colors[1]), l.addColorStop(1, n.colors[2]), a.fillStyle = l, a.fillRect(0, 0, 256, 256);
1151
+ const d = a.getImageData(0, 0, 256, 256);
1152
+ for (let g = 0; g < d.data.length; g += 4) {
1153
+ const u = (Math.random() - 0.5) * 5;
1154
+ d.data[g] = Math.min(255, Math.max(0, d.data[g] + u)), d.data[g + 1] = Math.min(255, Math.max(0, d.data[g + 1] + u)), d.data[g + 2] = Math.min(255, Math.max(0, d.data[g + 2] + u));
1155
+ }
1156
+ a.putImageData(d, 0, 0), o.push(i);
1157
+ }
1158
+ this.envMap = new p.CubeTexture(o.map((n) => {
1159
+ const i = new Image();
1160
+ return i.src = n.toDataURL(), i;
1161
+ })), this.envMap.needsUpdate = !0;
1162
+ }
1163
+ /**
1164
+ * Gets a random color from the palette
1165
+ */
1166
+ getRandomColor() {
1167
+ return this.COLORS[Math.floor(Math.random() * this.COLORS.length)];
1168
+ }
1169
+ /**
1170
+ * Creates a gradient glass material with white glow in center
1171
+ * All balls use the same tangerine color with white center glow
1172
+ */
1173
+ createGlassMaterial(e) {
1174
+ const t = "glass-single";
1175
+ if (this.materialCache.has(t))
1176
+ return this.materialCache.get(t).clone();
1177
+ const n = new p.Color(16750950), i = new p.ShaderMaterial({
1178
+ uniforms: {
1179
+ uColor: { value: n },
1180
+ uEnvMap: { value: this.envMap },
1181
+ uGlowColor: { value: new p.Color(16777215) },
1182
+ uGlowIntensity: { value: 0.8 },
1183
+ uReflectivity: { value: 0.4 },
1184
+ uFresnelPower: { value: 2.5 }
1185
+ },
1186
+ vertexShader: `
1187
+ varying vec3 vNormal;
1188
+ varying vec3 vViewPosition;
1189
+ varying vec3 vWorldPosition;
1190
+ varying vec2 vUv;
1191
+
1192
+ void main() {
1193
+ vNormal = normalize(normalMatrix * normal);
1194
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
1195
+ vViewPosition = -mvPosition.xyz;
1196
+ vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
1197
+ vUv = uv;
1198
+ gl_Position = projectionMatrix * mvPosition;
1199
+ }
1200
+ `,
1201
+ fragmentShader: `
1202
+ uniform vec3 uColor;
1203
+ uniform vec3 uGlowColor;
1204
+ uniform samplerCube uEnvMap;
1205
+ uniform float uGlowIntensity;
1206
+ uniform float uReflectivity;
1207
+ uniform float uFresnelPower;
1208
+
1209
+ varying vec3 vNormal;
1210
+ varying vec3 vViewPosition;
1211
+ varying vec3 vWorldPosition;
1212
+ varying vec2 vUv;
1213
+
1214
+ void main() {
1215
+ vec3 viewDir = normalize(vViewPosition);
1216
+ vec3 normal = normalize(vNormal);
1217
+
1218
+ // Fresnel effect - edges are more visible
1219
+ float fresnel = pow(1.0 - abs(dot(viewDir, normal)), uFresnelPower);
1220
+
1221
+ // Center glow: bright white in center, fading to base color at edges
1222
+ // Center is where normal faces camera directly (low fresnel)
1223
+ float centerGlow = (1.0 - fresnel) * uGlowIntensity;
1224
+
1225
+ // Environment reflection on edges
1226
+ vec3 reflectDir = reflect(-viewDir, normal);
1227
+ vec3 envColor = textureCube(uEnvMap, reflectDir).rgb;
1228
+
1229
+ // Base color with reflection at edges
1230
+ vec3 baseColor = uColor;
1231
+ vec3 edgeColor = mix(baseColor, envColor, uReflectivity * fresnel);
1232
+
1233
+ // Blend: white glow in center, colored edges
1234
+ vec3 finalColor = mix(edgeColor, uGlowColor, centerGlow);
1235
+
1236
+ // Add rim highlight for glass effect
1237
+ float rimLight = pow(fresnel, 4.0) * 0.4;
1238
+ finalColor += vec3(rimLight) * uColor;
1239
+
1240
+ // Opacity: fully opaque with glow effect visible
1241
+ float opacity = 0.85 + fresnel * 0.15;
1242
+
1243
+ gl_FragColor = vec4(finalColor, opacity);
1244
+ }
1245
+ `,
1246
+ transparent: !0,
1247
+ side: p.FrontSide,
1248
+ depthWrite: !0,
1249
+ blending: p.NormalBlending
1250
+ });
1251
+ return this.materialCache.set(t, i), i.clone();
1252
+ }
1253
+ /**
1254
+ * Creates material for edges (light color for dark background)
1255
+ */
1256
+ createEdgeMaterial(e = 6710886, o = 0.4) {
1257
+ return new p.LineBasicMaterial({
1258
+ color: e,
1259
+ transparent: !0,
1260
+ opacity: o,
1261
+ linewidth: 1
1262
+ });
1263
+ }
1264
+ /**
1265
+ * Creates highlighted edge material
1266
+ */
1267
+ createHighlightedEdgeMaterial() {
1268
+ return new p.LineBasicMaterial({
1269
+ color: 16750950,
1270
+ // Tangerine highlight
1271
+ transparent: !1,
1272
+ opacity: 1,
1273
+ linewidth: 2
1274
+ });
1275
+ }
1276
+ /**
1277
+ * Creates a sprite material for labels (light text for dark background)
1278
+ */
1279
+ createLabelMaterial(e, o = 24) {
1280
+ const t = document.createElement("canvas"), n = t.getContext("2d");
1281
+ n.font = `600 ${o}px Inter, -apple-system, sans-serif`;
1282
+ const a = n.measureText(e).width;
1283
+ t.width = Math.max(128, a + 24), t.height = o + 20, n.clearRect(0, 0, t.width, t.height), n.font = `600 ${o}px Inter, -apple-system, sans-serif`, n.textAlign = "center", n.textBaseline = "middle", n.shadowColor = "rgba(0, 0, 0, 0.8)", n.shadowBlur = 4, n.shadowOffsetX = 1, n.shadowOffsetY = 1, n.fillStyle = "rgba(255, 255, 255, 0.95)", n.fillText(e, t.width / 2, t.height / 2);
1284
+ const l = new p.CanvasTexture(t);
1285
+ return l.needsUpdate = !0, new p.SpriteMaterial({
1286
+ map: l,
1287
+ transparent: !0,
1288
+ depthTest: !1,
1289
+ depthWrite: !1
1290
+ });
1291
+ }
1292
+ /**
1293
+ * Gets environment map
1294
+ */
1295
+ getEnvMap() {
1296
+ return this.envMap;
1297
+ }
1298
+ /**
1299
+ * Dispose all cached materials
1300
+ */
1301
+ dispose() {
1302
+ this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
1303
+ }
1304
+ }
1305
+ class vt {
1306
+ constructor(e, o = 2, t = [32, 16, 8]) {
1307
+ r(this, "materialFactory");
1308
+ r(this, "geometryCache", /* @__PURE__ */ new Map());
1309
+ r(this, "nodeRadius");
1310
+ r(this, "lodSegments");
1311
+ this.materialFactory = e, this.nodeRadius = o, this.lodSegments = t, this.initGeometryCache();
1312
+ }
1313
+ /**
1314
+ * Pre-create geometries for each LOD level
1315
+ */
1316
+ initGeometryCache() {
1317
+ this.lodSegments.forEach((e, o) => {
1318
+ const t = `lod-${o}`;
1319
+ this.geometryCache.set(
1320
+ t,
1321
+ new p.SphereGeometry(this.nodeRadius, e, e)
1322
+ );
1323
+ });
1324
+ }
1325
+ /**
1326
+ * Gets geometry for the specified LOD level
1327
+ */
1328
+ getGeometry(e) {
1329
+ const o = `lod-${e}`;
1330
+ return this.geometryCache.has(o) ? this.geometryCache.get(o) : this.geometryCache.get("lod-0");
1331
+ }
1332
+ /**
1333
+ * Creates a node visual (glass ball + label)
1334
+ */
1335
+ createNode(e, o = 0) {
1336
+ const t = new p.Group();
1337
+ t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
1338
+ const n = this.getGeometry(o), i = this.materialFactory.createGlassMaterial(
1339
+ e.color ?? 4886754
1340
+ ), a = new p.Mesh(n, i);
1341
+ a.castShadow = !0, a.receiveShadow = !0, t.add(a);
1342
+ const l = this.materialFactory.createLabelMaterial(e.label), d = new p.Sprite(l);
1343
+ return d.position.y = this.nodeRadius + 1.5, d.scale.set(4, 1, 1), t.add(d), e.position && t.position.set(
1344
+ e.position.x,
1345
+ e.position.y,
1346
+ e.position.z
1347
+ ), {
1348
+ group: t,
1349
+ sphere: a,
1350
+ label: d,
1351
+ lodLevel: o
1352
+ };
1353
+ }
1354
+ /**
1355
+ * Updates the LOD level of a node
1356
+ */
1357
+ updateNodeLOD(e, o) {
1358
+ if (e.lodLevel === o) return;
1359
+ const t = this.getGeometry(o);
1360
+ e.sphere.geometry = t, e.lodLevel = o;
1361
+ }
1362
+ /**
1363
+ * Updates the color of a node
1364
+ */
1365
+ updateNodeColor(e, o) {
1366
+ e.sphere.material instanceof p.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(o);
1367
+ }
1368
+ /**
1369
+ * Updates the label of a node
1370
+ */
1371
+ updateNodeLabel(e, o) {
1372
+ e.label.material instanceof p.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(o);
1373
+ }
1374
+ /**
1375
+ * Disposes a node's resources
1376
+ */
1377
+ disposeNode(e) {
1378
+ e.sphere.material instanceof p.Material && e.sphere.material.dispose(), e.label.material instanceof p.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
1379
+ }
1380
+ /**
1381
+ * Dispose factory resources
1382
+ */
1383
+ dispose() {
1384
+ this.geometryCache.forEach((e) => e.dispose()), this.geometryCache.clear();
1385
+ }
1386
+ }
1387
+ class Mt {
1388
+ constructor(e, o = 10066329, t = 0.5) {
1389
+ r(this, "materialFactory");
1390
+ r(this, "edgeColor");
1391
+ r(this, "edgeOpacity");
1392
+ r(this, "defaultMaterial", null);
1393
+ r(this, "highlightMaterial", null);
1394
+ this.materialFactory = e, this.edgeColor = o, this.edgeOpacity = t;
1395
+ }
1396
+ /**
1397
+ * Gets or creates the default edge material
1398
+ */
1399
+ getDefaultMaterial() {
1400
+ return this.defaultMaterial || (this.defaultMaterial = this.materialFactory.createEdgeMaterial(
1401
+ this.edgeColor,
1402
+ this.edgeOpacity
1403
+ )), this.defaultMaterial;
1404
+ }
1405
+ /**
1406
+ * Gets or creates the highlight material
1407
+ */
1408
+ getHighlightMaterial() {
1409
+ return this.highlightMaterial || (this.highlightMaterial = this.materialFactory.createHighlightedEdgeMaterial()), this.highlightMaterial;
1410
+ }
1411
+ /**
1412
+ * Creates an edge line between two positions
1413
+ */
1414
+ createEdge(e, o, t, n, i) {
1415
+ const a = new p.BufferGeometry(), l = new Float32Array([
1416
+ n.x,
1417
+ n.y,
1418
+ n.z,
1419
+ i.x,
1420
+ i.y,
1421
+ i.z
1422
+ ]);
1423
+ a.setAttribute("position", new p.BufferAttribute(l, 3));
1424
+ const d = this.getDefaultMaterial().clone(), g = new p.Line(a, d);
1425
+ return g.name = `edge-${e.source}-${e.target}`, g.userData = {
1426
+ source: e.source,
1427
+ target: e.target,
1428
+ edge: e,
1429
+ sourceNode: o,
1430
+ targetNode: t
1431
+ }, g.frustumCulled = !0, {
1432
+ line: g,
1433
+ source: e.source,
1434
+ target: e.target
1435
+ };
1436
+ }
1437
+ /**
1438
+ * Highlights an edge
1439
+ */
1440
+ highlightEdge(e) {
1441
+ e.line.material instanceof p.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
1442
+ }
1443
+ /**
1444
+ * Resets an edge to default appearance
1445
+ */
1446
+ unhighlightEdge(e) {
1447
+ e.line.material instanceof p.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
1448
+ }
1449
+ /**
1450
+ * Updates an edge's positions
1451
+ */
1452
+ updateEdgePositions(e, o, t) {
1453
+ const n = e.line.geometry.attributes.position, i = n.array;
1454
+ i[0] = o.x, i[1] = o.y, i[2] = o.z, i[3] = t.x, i[4] = t.y, i[5] = t.z, n.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
1455
+ }
1456
+ /**
1457
+ * Disposes an edge's resources
1458
+ */
1459
+ disposeEdge(e) {
1460
+ e.line.geometry.dispose(), e.line.material instanceof p.Material && e.line.material.dispose();
1461
+ }
1462
+ /**
1463
+ * Dispose factory resources
1464
+ */
1465
+ dispose() {
1466
+ this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
1467
+ }
1468
+ }
1469
+ class Et {
1470
+ constructor(e, o = [50, 100, 200], t = !0) {
1471
+ r(this, "camera");
1472
+ r(this, "lodDistances");
1473
+ r(this, "enabled");
1474
+ this.camera = e, this.lodDistances = o, this.enabled = t;
1475
+ }
1476
+ /**
1477
+ * Gets the LOD level for a position based on distance from camera
1478
+ */
1479
+ getLODLevel(e) {
1480
+ if (!this.enabled)
1481
+ return _.HIGH;
1482
+ const o = e.x - this.camera.position.x, t = e.y - this.camera.position.y, n = e.z - this.camera.position.z, i = Math.sqrt(o * o + t * t + n * n);
1483
+ return i < this.lodDistances[0] ? _.HIGH : i < this.lodDistances[1] ? _.MEDIUM : _.LOW;
1484
+ }
1485
+ /**
1486
+ * Checks if a node should be visible based on distance
1487
+ */
1488
+ shouldRenderNode(e, o = 500) {
1489
+ const t = e.x - this.camera.position.x, n = e.y - this.camera.position.y, i = e.z - this.camera.position.z;
1490
+ return Math.sqrt(t * t + n * n + i * i) < o;
1491
+ }
1492
+ /**
1493
+ * Sets the LOD distances
1494
+ */
1495
+ setLODDistances(e) {
1496
+ this.lodDistances = e;
1497
+ }
1498
+ /**
1499
+ * Enables/disables LOD
1500
+ */
1501
+ setEnabled(e) {
1502
+ this.enabled = e;
1503
+ }
1504
+ }
1505
+ class wt {
1506
+ constructor(e, o = !0) {
1507
+ r(this, "camera");
1508
+ r(this, "frustum");
1509
+ r(this, "projScreenMatrix");
1510
+ r(this, "enabled");
1511
+ this.camera = e, this.frustum = new p.Frustum(), this.projScreenMatrix = new p.Matrix4(), this.enabled = o;
1512
+ }
1513
+ /**
1514
+ * Updates the frustum from the camera
1515
+ */
1516
+ update() {
1517
+ this.projScreenMatrix.multiplyMatrices(
1518
+ this.camera.projectionMatrix,
1519
+ this.camera.matrixWorldInverse
1520
+ ), this.frustum.setFromProjectionMatrix(this.projScreenMatrix);
1521
+ }
1522
+ /**
1523
+ * Checks if a point is inside the frustum
1524
+ */
1525
+ isPointVisible(e) {
1526
+ if (!this.enabled) return !0;
1527
+ const o = new p.Vector3(e.x, e.y, e.z);
1528
+ return this.frustum.containsPoint(o);
1529
+ }
1530
+ /**
1531
+ * Checks if a sphere is inside or intersects the frustum
1532
+ */
1533
+ isSphereVisible(e, o) {
1534
+ if (!this.enabled) return !0;
1535
+ const t = new p.Sphere(
1536
+ new p.Vector3(e.x, e.y, e.z),
1537
+ o
1538
+ );
1539
+ return this.frustum.intersectsSphere(t);
1540
+ }
1541
+ /**
1542
+ * Checks if a line segment is potentially visible
1543
+ */
1544
+ isLineVisible(e, o) {
1545
+ if (!this.enabled) return !0;
1546
+ const t = new p.Vector3(e.x, e.y, e.z), n = new p.Vector3(o.x, o.y, o.z);
1547
+ if (this.frustum.containsPoint(t) || this.frustum.containsPoint(n))
1548
+ return !0;
1549
+ const i = new p.Vector3(
1550
+ (e.x + o.x) / 2,
1551
+ (e.y + o.y) / 2,
1552
+ (e.z + o.z) / 2
1553
+ ), a = i.distanceTo(t), l = new p.Sphere(i, a);
1554
+ return this.frustum.intersectsSphere(l);
1555
+ }
1556
+ /**
1557
+ * Enables/disables frustum culling
1558
+ */
1559
+ setEnabled(e) {
1560
+ this.enabled = e;
1561
+ }
1562
+ }
1563
+ class Ct {
1564
+ constructor(e, o) {
1565
+ r(this, "sceneManager");
1566
+ r(this, "raycaster");
1567
+ r(this, "mouse");
1568
+ r(this, "container");
1569
+ r(this, "onNodeClick", null);
1570
+ r(this, "onNodeHover", null);
1571
+ r(this, "onEdgeHover", null);
1572
+ r(this, "hoveredNodeId", null);
1573
+ r(this, "hoveredEdgeKey", null);
1574
+ r(this, "nodeObjects", []);
1575
+ r(this, "edgeObjects", []);
1576
+ this.sceneManager = e, this.container = o, this.raycaster = new p.Raycaster(), this.raycaster.params.Line = { threshold: 0.5 }, this.mouse = new p.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), o.addEventListener("click", this.handleClick), o.addEventListener("mousemove", this.handleMouseMove);
1577
+ }
1578
+ /**
1579
+ * Updates the list of node objects to raycast against
1580
+ */
1581
+ setNodeObjects(e) {
1582
+ this.nodeObjects = e;
1583
+ }
1584
+ /**
1585
+ * Updates the list of edge objects to raycast against
1586
+ */
1587
+ setEdgeObjects(e) {
1588
+ this.edgeObjects = e;
1589
+ }
1590
+ /**
1591
+ * Sets the click callback
1592
+ */
1593
+ setClickCallback(e) {
1594
+ this.onNodeClick = e;
1595
+ }
1596
+ /**
1597
+ * Sets the node hover callback
1598
+ */
1599
+ setHoverCallback(e) {
1600
+ this.onNodeHover = e;
1601
+ }
1602
+ /**
1603
+ * Sets the edge hover callback
1604
+ */
1605
+ setEdgeHoverCallback(e) {
1606
+ this.onEdgeHover = e;
1607
+ }
1608
+ /**
1609
+ * Handles click events
1610
+ */
1611
+ handleClick(e) {
1612
+ const o = this.getIntersectedNode(e);
1613
+ o && this.onNodeClick && this.onNodeClick(o);
1614
+ }
1615
+ /**
1616
+ * Handles mouse move events for hover detection
1617
+ */
1618
+ handleMouseMove(e) {
1619
+ const o = this.getIntersectedNode(e), t = (o == null ? void 0 : o.id) ?? null;
1620
+ if (t !== this.hoveredNodeId && (this.hoveredNodeId = t, this.onNodeHover && this.onNodeHover(o)), o) {
1621
+ this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
1622
+ return;
1623
+ }
1624
+ const n = this.getIntersectedEdge(e), i = n ? `${n.edge.source}-${n.edge.target}` : null;
1625
+ i !== this.hoveredEdgeKey && (this.hoveredEdgeKey = i, this.onEdgeHover && this.onEdgeHover(n)), this.container.style.cursor = n ? "pointer" : "default";
1626
+ }
1627
+ /**
1628
+ * Gets the intersected node from a mouse event
1629
+ */
1630
+ getIntersectedNode(e) {
1631
+ var n;
1632
+ const o = this.container.getBoundingClientRect();
1633
+ this.mouse.x = (e.clientX - o.left) / o.width * 2 - 1, this.mouse.y = -((e.clientY - o.top) / o.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1634
+ const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
1635
+ if (t.length > 0) {
1636
+ let i = t[0].object;
1637
+ for (; i; ) {
1638
+ if ((n = i.userData) != null && n.nodeData)
1639
+ return i.userData.nodeData;
1640
+ i = i.parent;
1641
+ }
1642
+ }
1643
+ return null;
1644
+ }
1645
+ /**
1646
+ * Gets the intersected edge from a mouse event
1647
+ */
1648
+ getIntersectedEdge(e) {
1649
+ const o = this.container.getBoundingClientRect();
1650
+ this.mouse.x = (e.clientX - o.left) / o.width * 2 - 1, this.mouse.y = -((e.clientY - o.top) / o.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1651
+ const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
1652
+ if (t.length > 0) {
1653
+ const n = t[0].object, i = n.userData;
1654
+ if (i != null && i.edge && (i != null && i.sourceNode) && (i != null && i.targetNode))
1655
+ return {
1656
+ edge: i.edge,
1657
+ sourceNode: i.sourceNode,
1658
+ targetNode: i.targetNode,
1659
+ edgeLine: n
1660
+ };
1661
+ }
1662
+ return null;
1663
+ }
1664
+ /**
1665
+ * Performs a raycast and returns the intersected node ID
1666
+ */
1667
+ getIntersectedNodeId(e, o) {
1668
+ var i;
1669
+ const t = this.container.getBoundingClientRect();
1670
+ this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((o - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1671
+ const n = this.raycaster.intersectObjects(this.nodeObjects, !0);
1672
+ if (n.length > 0) {
1673
+ let a = n[0].object;
1674
+ for (; a; ) {
1675
+ if ((i = a.userData) != null && i.nodeId)
1676
+ return a.userData.nodeId;
1677
+ a = a.parent;
1678
+ }
1679
+ }
1680
+ return null;
1681
+ }
1682
+ /**
1683
+ * Dispose resources and remove event listeners
1684
+ */
1685
+ dispose() {
1686
+ this.container.removeEventListener("click", this.handleClick), this.container.removeEventListener("mousemove", this.handleMouseMove);
1687
+ }
1688
+ }
1689
+ class Nt {
1690
+ constructor(e) {
1691
+ r(this, "container");
1692
+ r(this, "panel", null);
1693
+ r(this, "currentNodeId", null);
1694
+ r(this, "visible", !1);
1695
+ r(this, "panelTemplate", null);
1696
+ r(this, "panelStyles", {});
1697
+ r(this, "onExpand", null);
1698
+ this.container = e, this.createPanel();
1699
+ }
1700
+ /**
1701
+ * Creates the panel element
1702
+ */
1703
+ createPanel() {
1704
+ this.panel = document.createElement("div"), this.panel.className = "force-graph-panel", this.panel.style.cssText = `
1705
+ position: absolute;
1706
+ right: 20px;
1707
+ top: 50%;
1708
+ transform: translateY(-50%);
1709
+ width: 280px;
1710
+ max-height: 80vh;
1711
+ overflow-y: auto;
1712
+ background: rgba(15, 15, 25, 0.85);
1713
+ backdrop-filter: blur(20px);
1714
+ -webkit-backdrop-filter: blur(20px);
1715
+ border: 1px solid rgba(255, 255, 255, 0.1);
1716
+ border-radius: 16px;
1717
+ padding: 24px;
1718
+ color: white;
1719
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
1720
+ box-shadow:
1721
+ 0 8px 32px rgba(0, 0, 0, 0.4),
1722
+ inset 0 0 0 1px rgba(255, 255, 255, 0.05);
1723
+ z-index: 1000;
1724
+ opacity: 0;
1725
+ pointer-events: none;
1726
+ transition: opacity 0.3s ease, transform 0.3s ease;
1727
+ `, Object.entries(this.panelStyles).forEach(([e, o]) => {
1728
+ this.panel.style.setProperty(e, o);
1729
+ }), this.container.appendChild(this.panel);
1730
+ }
1731
+ /**
1732
+ * Sets the panel template function
1733
+ */
1734
+ setPanelTemplate(e) {
1735
+ this.panelTemplate = e;
1736
+ }
1737
+ /**
1738
+ * Sets custom panel styles
1739
+ */
1740
+ setPanelStyles(e) {
1741
+ this.panelStyles = e, this.panel && Object.entries(e).forEach(([o, t]) => {
1742
+ this.panel.style.setProperty(o, t);
1743
+ });
1744
+ }
1745
+ /**
1746
+ * Sets the expand callback
1747
+ */
1748
+ setExpandCallback(e) {
1749
+ this.onExpand = e;
1750
+ }
1751
+ /**
1752
+ * Shows the panel with node information
1753
+ */
1754
+ show(e, o) {
1755
+ if (!this.panel) return;
1756
+ this.currentNodeId = e.id;
1757
+ let t;
1758
+ this.panelTemplate ? t = this.panelTemplate(e, o) : t = this.generateDefaultContent(e, o), this.panel.innerHTML = t;
1759
+ const n = this.panel.querySelector('[data-action="expand"]'), i = this.panel.querySelector("[data-depth-select]");
1760
+ n && this.onExpand && n.addEventListener("click", () => {
1761
+ if (this.currentNodeId) {
1762
+ const l = i ? parseInt(i.value, 10) : 1;
1763
+ this.onExpand(this.currentNodeId, l);
1764
+ }
1765
+ });
1766
+ const a = this.panel.querySelector('[data-action="close"]');
1767
+ a && a.addEventListener("click", () => {
1768
+ this.hide();
1769
+ }), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
1770
+ }
1771
+ /**
1772
+ * Generates default panel content
1773
+ */
1774
+ generateDefaultContent(e, o) {
1775
+ const t = e.color ? `#${e.color.toString(16).padStart(6, "0")}` : "#4A90E2";
1776
+ return `
1777
+ <style>
1778
+ .force-graph-panel h2 {
1779
+ margin: 0 0 16px 0;
1780
+ font-size: 20px;
1781
+ font-weight: 600;
1782
+ letter-spacing: -0.5px;
1783
+ display: flex;
1784
+ align-items: center;
1785
+ gap: 10px;
1786
+ }
1787
+ .force-graph-panel .color-dot {
1788
+ width: 12px;
1789
+ height: 12px;
1790
+ border-radius: 50%;
1791
+ background: ${t};
1792
+ box-shadow: 0 0 10px ${t}80;
1793
+ }
1794
+ .force-graph-panel .info-row {
1795
+ display: flex;
1796
+ justify-content: space-between;
1797
+ padding: 8px 0;
1798
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
1799
+ font-size: 13px;
1800
+ }
1801
+ .force-graph-panel .info-row:last-child {
1802
+ border-bottom: none;
1803
+ }
1804
+ .force-graph-panel .info-label {
1805
+ color: rgba(255, 255, 255, 0.6);
1806
+ }
1807
+ .force-graph-panel .info-value {
1808
+ color: rgba(255, 255, 255, 0.95);
1809
+ font-weight: 500;
1810
+ }
1811
+ .force-graph-panel .neighbors-section {
1812
+ margin-top: 16px;
1813
+ }
1814
+ .force-graph-panel .neighbors-title {
1815
+ font-size: 12px;
1816
+ text-transform: uppercase;
1817
+ letter-spacing: 1px;
1818
+ color: rgba(255, 255, 255, 0.5);
1819
+ margin-bottom: 8px;
1820
+ }
1821
+ .force-graph-panel .neighbor-chip {
1822
+ display: inline-block;
1823
+ padding: 4px 10px;
1824
+ margin: 2px;
1825
+ background: rgba(255, 255, 255, 0.1);
1826
+ border-radius: 12px;
1827
+ font-size: 12px;
1828
+ }
1829
+ .force-graph-panel .depth-selector {
1830
+ margin-top: 16px;
1831
+ margin-bottom: 8px;
1832
+ }
1833
+ .force-graph-panel .depth-label {
1834
+ font-size: 12px;
1835
+ color: rgba(255, 255, 255, 0.6);
1836
+ margin-bottom: 6px;
1837
+ text-transform: uppercase;
1838
+ letter-spacing: 0.5px;
1839
+ }
1840
+ .force-graph-panel select {
1841
+ width: 100%;
1842
+ padding: 8px 12px;
1843
+ background: rgba(255, 255, 255, 0.1);
1844
+ border: 1px solid rgba(255, 255, 255, 0.15);
1845
+ border-radius: 6px;
1846
+ color: white;
1847
+ font-size: 13px;
1848
+ cursor: pointer;
1849
+ outline: none;
1850
+ transition: all 0.2s ease;
1851
+ }
1852
+ .force-graph-panel select:hover {
1853
+ background: rgba(255, 255, 255, 0.15);
1854
+ }
1855
+ .force-graph-panel select:focus {
1856
+ border-color: rgba(96, 165, 250, 0.5);
1857
+ }
1858
+ .force-graph-panel .btn-row {
1859
+ display: flex;
1860
+ gap: 8px;
1861
+ margin-top: 20px;
1862
+ }
1863
+ .force-graph-panel button {
1864
+ flex: 1;
1865
+ padding: 10px 16px;
1866
+ border: none;
1867
+ border-radius: 8px;
1868
+ font-size: 13px;
1869
+ font-weight: 500;
1870
+ cursor: pointer;
1871
+ transition: all 0.2s ease;
1872
+ }
1873
+ .force-graph-panel .btn-expand {
1874
+ background: linear-gradient(135deg, #60a5fa, #a78bfa);
1875
+ color: white;
1876
+ }
1877
+ .force-graph-panel .btn-expand:hover {
1878
+ transform: translateY(-1px);
1879
+ box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
1880
+ }
1881
+ .force-graph-panel .btn-close {
1882
+ background: rgba(255, 255, 255, 0.1);
1883
+ color: rgba(255, 255, 255, 0.8);
1884
+ }
1885
+ .force-graph-panel .btn-close:hover {
1886
+ background: rgba(255, 255, 255, 0.15);
1887
+ }
1888
+ </style>
1889
+
1890
+ <h2>
1891
+ <span class="color-dot"></span>
1892
+ ${this.escapeHtml(e.label)}
1893
+ </h2>
1894
+
1895
+ <div class="info-row">
1896
+ <span class="info-label">ID</span>
1897
+ <span class="info-value">${this.escapeHtml(e.id)}</span>
1898
+ </div>
1899
+
1900
+ <div class="info-row">
1901
+ <span class="info-label">Connections</span>
1902
+ <span class="info-value">${o.length}</span>
1903
+ </div>
1904
+
1905
+ ${o.length > 0 ? `
1906
+ <div class="neighbors-section">
1907
+ <div class="neighbors-title">Connected To</div>
1908
+ ${o.slice(0, 5).map(
1909
+ (n) => `<span class="neighbor-chip">${this.escapeHtml(n.label)}</span>`
1910
+ ).join("")}
1911
+ ${o.length > 5 ? `<span class="neighbor-chip">+${o.length - 5} more</span>` : ""}
1912
+ </div>
1913
+ ` : ""}
1914
+
1915
+ <div class="depth-selector">
1916
+ <div class="depth-label">Expansion Depth</div>
1917
+ <select data-depth-select>
1918
+ <option value="1">1 Level</option>
1919
+ <option value="2">2 Levels</option>
1920
+ <option value="3" selected>3 Levels</option>
1921
+ </select>
1922
+ </div>
1923
+
1924
+
1925
+ <div class="btn-row">
1926
+ <button class="btn-expand" data-action="expand">Expand</button>
1927
+ <button class="btn-close" data-action="close">Close</button>
1928
+ </div>
1929
+ `;
1930
+ }
1931
+ /**
1932
+ * Escapes HTML to prevent XSS
1933
+ */
1934
+ escapeHtml(e) {
1935
+ const o = document.createElement("div");
1936
+ return o.textContent = e, o.innerHTML;
1937
+ }
1938
+ /**
1939
+ * Hides the panel
1940
+ */
1941
+ hide() {
1942
+ this.panel && (this.panel.style.opacity = "0", this.panel.style.pointerEvents = "none", this.panel.style.transform = "translateY(-50%) translateX(20px)", this.visible = !1, this.currentNodeId = null);
1943
+ }
1944
+ /**
1945
+ * Checks if the panel is visible
1946
+ */
1947
+ isVisible() {
1948
+ return this.visible;
1949
+ }
1950
+ /**
1951
+ * Gets the currently displayed node ID
1952
+ */
1953
+ getCurrentNodeId() {
1954
+ return this.currentNodeId;
1955
+ }
1956
+ /**
1957
+ * Dispose the panel
1958
+ */
1959
+ dispose() {
1960
+ this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
1961
+ }
1962
+ }
1963
+ class zt {
1964
+ constructor() {
1965
+ r(this, "tooltip", null);
1966
+ r(this, "visible", !1);
1967
+ this.createTooltip(), this.setupMouseTracking();
1968
+ }
1969
+ /**
1970
+ * Creates the tooltip element
1971
+ */
1972
+ createTooltip() {
1973
+ this.tooltip = document.createElement("div"), this.tooltip.className = "force-graph-edge-tooltip", this.tooltip.style.cssText = `
1974
+ position: fixed;
1975
+ padding: 10px 14px;
1976
+ background: rgba(30, 30, 30, 0.95);
1977
+ backdrop-filter: blur(10px);
1978
+ -webkit-backdrop-filter: blur(10px);
1979
+ border: 1px solid rgba(255, 255, 255, 0.15);
1980
+ border-radius: 8px;
1981
+ color: white;
1982
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
1983
+ font-size: 13px;
1984
+ pointer-events: none;
1985
+ z-index: 10000;
1986
+ opacity: 0;
1987
+ transform: translateY(5px);
1988
+ transition: opacity 0.15s ease, transform 0.15s ease;
1989
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
1990
+ max-width: 400px;
1991
+ white-space: normal;
1992
+ word-wrap: break-word;
1993
+ overflow-wrap: break-word;
1994
+ `, document.body.appendChild(this.tooltip);
1995
+ }
1996
+ /**
1997
+ * Sets up mouse tracking for tooltip following
1998
+ */
1999
+ setupMouseTracking() {
2000
+ document.addEventListener("mousemove", (e) => {
2001
+ this.visible && this.tooltip && this.positionTooltip(e.clientX, e.clientY);
2002
+ });
2003
+ }
2004
+ /**
2005
+ * Positions the tooltip near the mouse
2006
+ */
2007
+ positionTooltip(e, o) {
2008
+ if (!this.tooltip) return;
2009
+ const t = this.tooltip.getBoundingClientRect(), n = window.innerWidth, i = window.innerHeight;
2010
+ let a = e + 15, l = o + 15;
2011
+ a + t.width > n - 10 && (a = e - t.width - 15), l + t.height > i - 10 && (l = o - t.height - 15), a < 10 && (a = 10), l < 10 && (l = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${l}px`;
2012
+ }
2013
+ /**
2014
+ * Shows the tooltip with edge info
2015
+ */
2016
+ show(e, o, t, n, i) {
2017
+ if (!this.tooltip) return;
2018
+ const a = e.relationship || "connected to";
2019
+ this.tooltip.innerHTML = `
2020
+ <div style="display: flex; flex-direction: column; gap: 4px;">
2021
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2022
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(o.label)}</span>
2023
+ </div>
2024
+ <div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
2025
+ ↳ ${this.escapeHtml(a)}
2026
+ </div>
2027
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2028
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
2029
+ </div>
2030
+ </div>
2031
+ `, this.positionTooltip(n, i), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
2032
+ }
2033
+ /**
2034
+ * Updates tooltip position (called externally on mouse move)
2035
+ */
2036
+ updatePosition(e, o) {
2037
+ this.visible && this.positionTooltip(e, o);
2038
+ }
2039
+ /**
2040
+ * Hides the tooltip
2041
+ */
2042
+ hide() {
2043
+ this.tooltip && (this.tooltip.style.opacity = "0", this.tooltip.style.transform = "translateY(5px)", this.visible = !1);
2044
+ }
2045
+ /**
2046
+ * Checks if tooltip is visible
2047
+ */
2048
+ isVisible() {
2049
+ return this.visible;
2050
+ }
2051
+ /**
2052
+ * Escapes HTML to prevent XSS
2053
+ */
2054
+ escapeHtml(e) {
2055
+ const o = document.createElement("div");
2056
+ return o.textContent = e, o.innerHTML;
2057
+ }
2058
+ /**
2059
+ * Dispose the tooltip
2060
+ */
2061
+ dispose() {
2062
+ this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
2063
+ }
2064
+ }
2065
+ class Ot {
2066
+ constructor(e, o = {}) {
2067
+ // Options
2068
+ r(this, "options");
2069
+ r(this, "container");
2070
+ // Core components
2071
+ r(this, "sceneManager");
2072
+ r(this, "nodeManager");
2073
+ r(this, "edgeManager");
2074
+ r(this, "graphEngine");
2075
+ r(this, "rendererManager");
2076
+ // Factories
2077
+ r(this, "materialFactory");
2078
+ r(this, "nodeFactory");
2079
+ r(this, "edgeFactory");
2080
+ // Performance
2081
+ r(this, "lodManager");
2082
+ r(this, "frustumCuller");
2083
+ // Interaction
2084
+ r(this, "raycasterManager");
2085
+ r(this, "panelManager");
2086
+ r(this, "edgeTooltipManager");
2087
+ // Event system
2088
+ r(this, "eventCallbacks", /* @__PURE__ */ new Map());
2089
+ // State
2090
+ r(this, "initialized", !1);
2091
+ r(this, "devControls", null);
2092
+ this.options = { ...O, ...o }, this.container = ct(e), this.materialFactory = new bt(), this.nodeFactory = new vt(
2093
+ this.materialFactory,
2094
+ this.options.nodeRadius ?? O.nodeRadius,
2095
+ this.options.lodSegments ?? O.lodSegments
2096
+ ), this.edgeFactory = new Mt(
2097
+ this.materialFactory,
2098
+ this.options.edgeColor ?? O.edgeColor,
2099
+ this.options.edgeOpacity ?? O.edgeOpacity
2100
+ ), this.sceneManager = new ut(this.container, this.options), this.lodManager = new Et(
2101
+ this.sceneManager.camera,
2102
+ this.options.lodDistances ?? O.lodDistances,
2103
+ this.options.enableLOD ?? O.enableLOD
2104
+ ), this.frustumCuller = new wt(
2105
+ this.sceneManager.camera,
2106
+ this.options.enableEdgeCulling ?? O.enableEdgeCulling
2107
+ ), this.nodeManager = new mt(this.sceneManager, this.nodeFactory), this.edgeManager = new ft(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Le(
2108
+ this.nodeManager.getAllNodes(),
2109
+ this.edgeManager.getAllEdges(),
2110
+ {
2111
+ repulsionStrength: this.options.repulsionStrength,
2112
+ attractionStrength: this.options.attractionStrength,
2113
+ damping: this.options.damping,
2114
+ useBarnesHut: this.options.useBarnesHut,
2115
+ barnesHutTheta: this.options.barnesHutTheta
2116
+ }
2117
+ ), this.rendererManager = new xt(
2118
+ this.sceneManager,
2119
+ () => this.onSimulate(),
2120
+ () => this.onRender(),
2121
+ this.options.targetFPS ?? O.targetFPS
2122
+ ), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgeTooltipManager = new zt(), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
2123
+ }
2124
+ /**
2125
+ * Sets up internal callbacks
2126
+ */
2127
+ setupCallbacks() {
2128
+ this.raycasterManager.setClickCallback((e) => {
2129
+ this.onNodeClick(e);
2130
+ }), this.options.onNodeHover && this.raycasterManager.setHoverCallback(this.options.onNodeHover), this.panelManager.setExpandCallback((e, o) => {
2131
+ this.expandNode(e, o);
2132
+ }), this.options.panelTemplate && this.panelManager.setPanelTemplate(this.options.panelTemplate), this.options.panelStyles && this.panelManager.setPanelStyles(this.options.panelStyles), this.raycasterManager.setEdgeHoverCallback((e) => {
2133
+ this.onEdgeHover(e);
2134
+ });
2135
+ }
2136
+ /**
2137
+ * Handles edge hover
2138
+ */
2139
+ onEdgeHover(e) {
2140
+ if (e) {
2141
+ this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
2142
+ const o = this.container.getBoundingClientRect(), t = o.left + o.width / 2, n = o.top + o.height / 2;
2143
+ this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, n), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
2144
+ } else
2145
+ this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
2146
+ }
2147
+ /**
2148
+ * Handles node click
2149
+ */
2150
+ onNodeClick(e) {
2151
+ const t = this.edgeManager.getNeighborIds(e.id).map((n) => this.nodeManager.getNode(n)).filter((n) => n !== void 0);
2152
+ this.options.showPanel !== !1 && this.panelManager.show(e, t), this.options.onNodeClick && this.options.onNodeClick(e), this.emit("nodeClick", e);
2153
+ }
2154
+ /**
2155
+ * Called every simulation step
2156
+ */
2157
+ onSimulate() {
2158
+ this.graphEngine.simulate();
2159
+ for (const [e, o] of this.nodeManager.getAllNodes())
2160
+ if (this.nodeManager.updateNodePosition(e, o.position), this.options.enableLOD) {
2161
+ const t = this.lodManager.getLODLevel(o.position);
2162
+ this.nodeManager.updateNodeLOD(e, t);
2163
+ }
2164
+ this.edgeManager.updateEdgePositions();
2165
+ }
2166
+ /**
2167
+ * Called every render frame
2168
+ */
2169
+ onRender() {
2170
+ this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
2171
+ }
2172
+ // ==========================================================================
2173
+ // Public API
2174
+ // ==========================================================================
2175
+ /**
2176
+ * Sets the graph data
2177
+ */
2178
+ setData(e) {
2179
+ if (this.edgeManager.clear(), this.nodeManager.clear(), e.nodes && Array.isArray(e.nodes))
2180
+ for (const o of e.nodes)
2181
+ this.addNode(o);
2182
+ if (e.edges && Array.isArray(e.edges))
2183
+ for (const o of e.edges)
2184
+ this.addEdge(o);
2185
+ this.graphEngine = new Le(
2186
+ this.nodeManager.getAllNodes(),
2187
+ this.edgeManager.getAllEdges(),
2188
+ {
2189
+ repulsionStrength: this.options.repulsionStrength,
2190
+ attractionStrength: this.options.attractionStrength,
2191
+ damping: this.options.damping,
2192
+ useBarnesHut: this.options.useBarnesHut,
2193
+ barnesHutTheta: this.options.barnesHutTheta
2194
+ }
2195
+ ), this.graphEngine.restart();
2196
+ }
2197
+ /**
2198
+ * Adds a node to the graph
2199
+ * @returns true if added, false if node already exists or invalid
2200
+ */
2201
+ addNode(e) {
2202
+ if (!Ie(e))
2203
+ return !1;
2204
+ const o = this.nodeManager.addNode(e);
2205
+ return o && (this.graphEngine.restart(), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), o;
2206
+ }
2207
+ /**
2208
+ * Removes a node from the graph
2209
+ * @returns true if removed, false if not found
2210
+ */
2211
+ removeNode(e) {
2212
+ if (!ht(e))
2213
+ return !1;
2214
+ this.edgeManager.removeEdgesForNode(e);
2215
+ const o = this.nodeManager.removeNode(e);
2216
+ return o && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide()), o;
2217
+ }
2218
+ /**
2219
+ * Updates a node's properties
2220
+ */
2221
+ updateNode(e, o) {
2222
+ return this.nodeManager.updateNode(e, o);
2223
+ }
2224
+ /**
2225
+ * Adds an edge to the graph
2226
+ * @returns true if added, false if edge already exists or nodes don't exist
2227
+ */
2228
+ addEdge(e) {
2229
+ if (!Re(e))
2230
+ return !1;
2231
+ const o = this.edgeManager.addEdge(e);
2232
+ return o && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), o;
2233
+ }
2234
+ /**
2235
+ * Removes an edge from the graph
2236
+ * @returns true if removed, false if not found
2237
+ */
2238
+ removeEdge(e, o) {
2239
+ const t = this.edgeManager.removeEdge(e, o);
2240
+ return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: o }), this.emit("edgeRemove", { source: e, target: o })), t;
2241
+ }
2242
+ /**
2243
+ * Expands a node by fetching more data
2244
+ * @param nodeId - The ID of the node to expand
2245
+ * @param depth - The depth of expansion (1-3 levels, default 1)
2246
+ * @param fetchFn - Optional fetch function to override the default
2247
+ */
2248
+ async expandNode(e, o = 1, t) {
2249
+ const n = t ?? this.options.onExpand;
2250
+ if (!n)
2251
+ return console.warn("[ForceGraph3D] No expand callback provided"), !1;
2252
+ try {
2253
+ const i = await n(e, o);
2254
+ if (i.nodes && Array.isArray(i.nodes))
2255
+ for (const a of i.nodes)
2256
+ this.addNode(a);
2257
+ if (i.edges && Array.isArray(i.edges))
2258
+ for (const a of i.edges)
2259
+ this.addEdge(a);
2260
+ return this.panelManager.hide(), this.emit("expand", e, i), !0;
2261
+ } catch (i) {
2262
+ return console.error("[ForceGraph3D] Error expanding node:", i), !1;
2263
+ }
2264
+ }
2265
+ /**
2266
+ * Gets a node by ID
2267
+ */
2268
+ getNode(e) {
2269
+ return this.nodeManager.getNode(e);
2270
+ }
2271
+ /**
2272
+ * Gets neighbor nodes for a node
2273
+ */
2274
+ getNeighbors(e) {
2275
+ return this.edgeManager.getNeighborIds(e).map((t) => this.nodeManager.getNode(t)).filter((t) => t !== void 0);
2276
+ }
2277
+ /**
2278
+ * Gets the number of nodes
2279
+ */
2280
+ getNodeCount() {
2281
+ return this.nodeManager.getNodeCount();
2282
+ }
2283
+ /**
2284
+ * Gets the number of edges
2285
+ */
2286
+ getEdgeCount() {
2287
+ return this.edgeManager.getEdgeCount();
2288
+ }
2289
+ /**
2290
+ * Sets the expand callback
2291
+ * @param callback - Function that fetches expansion data given a nodeId and depth
2292
+ */
2293
+ setExpandCallback(e) {
2294
+ this.options.onExpand = e;
2295
+ }
2296
+ /**
2297
+ * Focuses the camera on a specific node with smooth animation
2298
+ */
2299
+ focusOnNode(e, o = 30) {
2300
+ const t = this.nodeManager.getNode(e);
2301
+ if (!t) {
2302
+ console.warn(`[ForceGraph3D] Node "${e}" not found`);
2303
+ return;
2304
+ }
2305
+ const n = t.position, i = this.sceneManager.camera, a = this.sceneManager.controls, l = i.position.clone().sub(a.target).normalize(), d = {
2306
+ x: n.x + l.x * o,
2307
+ y: n.y + l.y * o,
2308
+ z: n.z + l.z * o
2309
+ }, g = { x: i.position.x, y: i.position.y, z: i.position.z }, u = { x: a.target.x, y: a.target.y, z: a.target.z }, x = 800, v = performance.now(), y = () => {
2310
+ const f = performance.now() - v, M = Math.min(f / x, 1), C = 1 - Math.pow(1 - M, 3);
2311
+ i.position.x = g.x + (d.x - g.x) * C, i.position.y = g.y + (d.y - g.y) * C, i.position.z = g.z + (d.z - g.z) * C, a.target.x = u.x + (n.x - u.x) * C, a.target.y = u.y + (n.y - u.y) * C, a.target.z = u.z + (n.z - u.z) * C, a.update(), M < 1 && requestAnimationFrame(y);
2312
+ };
2313
+ y();
2314
+ }
2315
+ /**
2316
+ * Shows the info panel for a specific node
2317
+ */
2318
+ showNodePanel(e) {
2319
+ const o = this.nodeManager.getNode(e);
2320
+ if (!o) {
2321
+ console.warn(`[ForceGraph3D] Node "${e}" not found`);
2322
+ return;
2323
+ }
2324
+ const t = this.getNeighbors(e);
2325
+ this.panelManager.show(o, t);
2326
+ }
2327
+ /**
2328
+ * Searches nodes by label or ID (case-insensitive)
2329
+ * @returns Array of matching nodes
2330
+ */
2331
+ searchNodes(e) {
2332
+ if (!e || e.trim() === "")
2333
+ return [];
2334
+ const o = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), n = [];
2335
+ return t.forEach((i) => {
2336
+ var g, u, x;
2337
+ const a = (g = i.label) == null ? void 0 : g.toLowerCase().includes(o), l = (u = i.id) == null ? void 0 : u.toLowerCase().includes(o), d = (x = i.type) == null ? void 0 : x.toLowerCase().includes(o);
2338
+ (a || l || d) && n.push(i);
2339
+ }), n;
2340
+ }
2341
+ /**
2342
+ * Searches edges by relationship (case-insensitive)
2343
+ * @returns Array of matching edges with source/target node info
2344
+ */
2345
+ searchEdges(e) {
2346
+ var i;
2347
+ if (!e || e.trim() === "")
2348
+ return [];
2349
+ const o = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), n = [];
2350
+ for (const a of t)
2351
+ if ((i = a.relationship) == null ? void 0 : i.toLowerCase().includes(o)) {
2352
+ const d = this.nodeManager.getNode(a.source), g = this.nodeManager.getNode(a.target);
2353
+ d && g && n.push({ edge: a, sourceNode: d, targetNode: g });
2354
+ }
2355
+ return n;
2356
+ }
2357
+ /**
2358
+ * Gets all nodes as an array
2359
+ */
2360
+ getAllNodes() {
2361
+ const e = this.nodeManager.getAllNodes();
2362
+ return Array.from(e.values());
2363
+ }
2364
+ /**
2365
+ * Gets all edges
2366
+ */
2367
+ getAllEdges() {
2368
+ return this.edgeManager.getAllEdges();
2369
+ }
2370
+ /**
2371
+ * Checks if the graph is initialized
2372
+ */
2373
+ isInitialized() {
2374
+ return this.initialized;
2375
+ }
2376
+ /**
2377
+ * Registers an event listener
2378
+ */
2379
+ on(e, o) {
2380
+ this.eventCallbacks.has(e) || this.eventCallbacks.set(e, []), this.eventCallbacks.get(e).push(o);
2381
+ }
2382
+ /**
2383
+ * Emits an event
2384
+ */
2385
+ emit(e, ...o) {
2386
+ const t = this.eventCallbacks.get(e);
2387
+ t && t.forEach((n) => n(...o));
2388
+ }
2389
+ /**
2390
+ * Sets physics parameters
2391
+ */
2392
+ setPhysicsParams(e) {
2393
+ this.graphEngine.setPhysicsParams(e), this.graphEngine.restart();
2394
+ }
2395
+ /**
2396
+ * Creates dev mode controls (only in development)
2397
+ */
2398
+ createDevControls() {
2399
+ this.devControls = document.createElement("div"), this.devControls.className = "force-graph-dev-controls", this.devControls.innerHTML = `
2400
+ <style>
2401
+ .force-graph-dev-controls {
2402
+ position: absolute;
2403
+ top: 20px;
2404
+ right: 20px;
2405
+ background: rgba(0, 0, 0, 0.8);
2406
+ backdrop-filter: blur(10px);
2407
+ border: 1px solid rgba(255, 255, 255, 0.1);
2408
+ border-radius: 12px;
2409
+ padding: 16px;
2410
+ color: white;
2411
+ font-family: 'Inter', -apple-system, sans-serif;
2412
+ font-size: 12px;
2413
+ z-index: 1001;
2414
+ min-width: 220px;
2415
+ }
2416
+ .force-graph-dev-controls h3 {
2417
+ margin: 0 0 12px 0;
2418
+ font-size: 14px;
2419
+ color: #60a5fa;
2420
+ }
2421
+ .force-graph-dev-controls .control-group {
2422
+ margin-bottom: 12px;
2423
+ }
2424
+ .force-graph-dev-controls label {
2425
+ display: block;
2426
+ margin-bottom: 4px;
2427
+ color: rgba(255, 255, 255, 0.7);
2428
+ }
2429
+ .force-graph-dev-controls input[type="range"] {
2430
+ width: 100%;
2431
+ margin-top: 4px;
2432
+ }
2433
+ .force-graph-dev-controls .value {
2434
+ color: #60a5fa;
2435
+ font-weight: 600;
2436
+ }
2437
+ .force-graph-dev-controls .stats {
2438
+ margin-top: 12px;
2439
+ padding-top: 12px;
2440
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
2441
+ }
2442
+ </style>
2443
+ <h3>⚙️ Dev Controls</h3>
2444
+ <div class="control-group">
2445
+ <label>Repulsion: <span class="value" id="dev-repulsion-val">${this.options.repulsionStrength}</span></label>
2446
+ <input type="range" id="dev-repulsion" min="10" max="500" value="${this.options.repulsionStrength}">
2447
+ </div>
2448
+ <div class="control-group">
2449
+ <label>Attraction: <span class="value" id="dev-attraction-val">${(this.options.attractionStrength ?? 0.01).toFixed(3)}</span></label>
2450
+ <input type="range" id="dev-attraction" min="1" max="100" value="${(this.options.attractionStrength ?? 0.01) * 1e3}">
2451
+ </div>
2452
+ <div class="control-group">
2453
+ <label>Damping: <span class="value" id="dev-damping-val">${this.options.damping}</span></label>
2454
+ <input type="range" id="dev-damping" min="50" max="99" value="${(this.options.damping ?? 0.9) * 100}">
2455
+ </div>
2456
+ <div class="stats">
2457
+ <div>Nodes: <span class="value" id="dev-node-count">0</span></div>
2458
+ <div>Edges: <span class="value" id="dev-edge-count">0</span></div>
2459
+ <div>FPS: <span class="value" id="dev-fps">60</span></div>
2460
+ </div>
2461
+ `, this.container.appendChild(this.devControls);
2462
+ const e = this.devControls.querySelector("#dev-repulsion"), o = this.devControls.querySelector("#dev-attraction"), t = this.devControls.querySelector("#dev-damping");
2463
+ e == null || e.addEventListener("input", () => {
2464
+ const n = parseFloat(e.value);
2465
+ this.setPhysicsParams({ repulsionStrength: n }), this.devControls.querySelector("#dev-repulsion-val").textContent = n.toString();
2466
+ }), o == null || o.addEventListener("input", () => {
2467
+ const n = parseFloat(o.value) / 1e3;
2468
+ this.setPhysicsParams({ attractionStrength: n }), this.devControls.querySelector("#dev-attraction-val").textContent = n.toFixed(3);
2469
+ }), t == null || t.addEventListener("input", () => {
2470
+ const n = parseFloat(t.value) / 100;
2471
+ this.setPhysicsParams({ damping: n }), this.devControls.querySelector("#dev-damping-val").textContent = n.toFixed(2);
2472
+ }), setInterval(() => {
2473
+ const n = this.devControls.querySelector("#dev-node-count"), i = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
2474
+ n && (n.textContent = this.getNodeCount().toString()), i && (i.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
2475
+ }, 500);
2476
+ }
2477
+ /**
2478
+ * Destroys the graph and releases all resources
2479
+ */
2480
+ destroy() {
2481
+ this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
2482
+ }
2483
+ }
2484
+ const Fe = [
2485
+ "Alpha",
2486
+ "Beta",
2487
+ "Gamma",
2488
+ "Delta",
2489
+ "Epsilon",
2490
+ "Zeta",
2491
+ "Eta",
2492
+ "Theta",
2493
+ "Iota",
2494
+ "Kappa",
2495
+ "Lambda",
2496
+ "Mu",
2497
+ "Nu",
2498
+ "Xi",
2499
+ "Omicron",
2500
+ "Pi",
2501
+ "Rho",
2502
+ "Sigma",
2503
+ "Tau",
2504
+ "Upsilon",
2505
+ "Phi",
2506
+ "Chi",
2507
+ "Psi",
2508
+ "Omega",
2509
+ "Core",
2510
+ "Hub",
2511
+ "Node",
2512
+ "Link",
2513
+ "Point",
2514
+ "Vertex"
2515
+ ], Q = [
2516
+ "connects to",
2517
+ "links with",
2518
+ "relates to",
2519
+ "depends on",
2520
+ "references",
2521
+ "extends",
2522
+ "includes",
2523
+ "partners with",
2524
+ "collaborates with",
2525
+ "supports"
2526
+ ], ke = [
2527
+ 16777215,
2528
+ // White
2529
+ 16750950,
2530
+ // Tangerine
2531
+ 16764057,
2532
+ // Light peach
2533
+ 16772829,
2534
+ // Cream
2535
+ 16746564
2536
+ // Darker tangerine
2537
+ ];
2538
+ function Lt(c = 30) {
2539
+ const e = [], o = [];
2540
+ for (let n = 0; n < c; n++) {
2541
+ const i = n < Fe.length ? Fe[n] : `Node ${n + 1}`;
2542
+ e.push({
2543
+ id: `node-${n}`,
2544
+ label: i,
2545
+ color: ke[n % ke.length],
2546
+ position: {
2547
+ x: (Math.random() - 0.5) * 60,
2548
+ y: (Math.random() - 0.5) * 60,
2549
+ z: (Math.random() - 0.5) * 60
2550
+ }
2551
+ });
2552
+ }
2553
+ for (let n = 1; n < c; n++) {
2554
+ const i = Math.floor(Math.random() * n);
2555
+ o.push({
2556
+ source: `node-${n}`,
2557
+ target: `node-${i}`,
2558
+ relationship: Q[Math.floor(Math.random() * Q.length)]
2559
+ });
2560
+ }
2561
+ const t = Math.floor(c * 0.5);
2562
+ for (let n = 0; n < t; n++) {
2563
+ const i = Math.floor(Math.random() * c);
2564
+ let a = Math.floor(Math.random() * c);
2565
+ i === a && (a = (a + 1) % c);
2566
+ const l = `node-${i}`, d = `node-${a}`;
2567
+ o.some(
2568
+ (u) => u.source === l && u.target === d || u.source === d && u.target === l
2569
+ ) || o.push({
2570
+ source: l,
2571
+ target: d,
2572
+ relationship: Q[Math.floor(Math.random() * Q.length)]
2573
+ });
2574
+ }
2575
+ return { nodes: e, edges: o };
2576
+ }
2577
+ function Ft(c = 1e3) {
2578
+ const e = [], o = [], t = Math.ceil(c / 50), n = [];
2579
+ for (let i = 0; i < t; i++)
2580
+ n.push({
2581
+ x: (Math.random() - 0.5) * 200,
2582
+ y: (Math.random() - 0.5) * 200,
2583
+ z: (Math.random() - 0.5) * 200
2584
+ });
2585
+ for (let i = 0; i < c; i++) {
2586
+ const a = n[i % t];
2587
+ e.push({
2588
+ id: `node-${i}`,
2589
+ label: `N${i}`,
2590
+ position: {
2591
+ x: a.x + (Math.random() - 0.5) * 40,
2592
+ y: a.y + (Math.random() - 0.5) * 40,
2593
+ z: a.z + (Math.random() - 0.5) * 40
2594
+ }
2595
+ });
2596
+ }
2597
+ for (let i = 1; i < c; i++) {
2598
+ const a = Math.floor(i / 50) * 50, l = a === 0 ? Math.floor(Math.random() * i) : a + Math.floor(Math.random() * Math.min(i - a, 50));
2599
+ o.push({
2600
+ source: `node-${i}`,
2601
+ target: `node-${Math.min(l, i - 1)}`,
2602
+ relationship: "links to"
2603
+ });
2604
+ }
2605
+ for (let i = 1; i < t; i++) {
2606
+ const a = i * 50, l = (i - 1) * 50 + Math.floor(Math.random() * 50);
2607
+ o.push({
2608
+ source: `node-${a}`,
2609
+ target: `node-${l}`,
2610
+ relationship: "bridges to"
2611
+ });
2612
+ }
2613
+ return { nodes: e, edges: o };
2614
+ }
2615
+ export {
2616
+ O as DEFAULT_OPTIONS,
2617
+ Ot as ForceGraph3D,
2618
+ _ as LODLevel,
2619
+ I as createEdgeKey,
2620
+ Ft as generateLargeSampleData,
2621
+ Lt as generateSampleData,
2622
+ Re as validateEdgeData,
2623
+ Ie as validateNodeData
2624
+ };
2625
+ //# sourceMappingURL=force-3d-graph.js.map