force-3d-graph 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/force-3d-graph.js +846 -647
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +92 -15
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var r = (c, e,
|
|
1
|
+
var st = Object.defineProperty;
|
|
2
|
+
var it = (c, e, s) => e in c ? st(c, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : c[e] = s;
|
|
3
|
+
var r = (c, e, s) => it(c, typeof e != "symbol" ? e + "" : e, s);
|
|
4
4
|
import * as p from "three";
|
|
5
|
-
import { EventDispatcher as
|
|
6
|
-
const
|
|
5
|
+
import { EventDispatcher as nt, Vector3 as N, MOUSE as D, TOUCH as $, Spherical as Ne, Quaternion as Se, Vector2 as z, Ray as ot, Plane as at, MathUtils as rt } from "three";
|
|
6
|
+
const L = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
9
9
|
cameraFov: 75,
|
|
@@ -23,10 +23,12 @@ const O = {
|
|
|
23
23
|
edgeOpacity: 0.4,
|
|
24
24
|
enableEdgeCulling: !0,
|
|
25
25
|
showPanel: !0,
|
|
26
|
+
showSearch: !0,
|
|
27
|
+
searchPlaceholder: "Search nodes or relationships...",
|
|
26
28
|
targetFPS: 60,
|
|
27
29
|
maxVisibleNodes: 1e4
|
|
28
30
|
};
|
|
29
|
-
var
|
|
31
|
+
var U = /* @__PURE__ */ ((c) => (c[c.HIGH = 0] = "HIGH", c[c.MEDIUM = 1] = "MEDIUM", c[c.LOW = 2] = "LOW", c))(U || {});
|
|
30
32
|
function lt() {
|
|
31
33
|
const c = document.createElement("div");
|
|
32
34
|
return c.id = "force-graph-3d-container", c.style.cssText = `
|
|
@@ -41,20 +43,20 @@ function lt() {
|
|
|
41
43
|
function ct(c) {
|
|
42
44
|
return c && c instanceof HTMLElement ? c : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), lt());
|
|
43
45
|
}
|
|
44
|
-
function
|
|
46
|
+
function ze(c) {
|
|
45
47
|
const e = c.getBoundingClientRect();
|
|
46
48
|
return {
|
|
47
49
|
width: e.width || window.innerWidth,
|
|
48
50
|
height: e.height || window.innerHeight
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
|
-
function
|
|
53
|
+
function Re(c) {
|
|
52
54
|
if (!c || typeof c != "object")
|
|
53
55
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
54
56
|
const e = c;
|
|
55
57
|
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
58
|
}
|
|
57
|
-
function
|
|
59
|
+
function Fe(c) {
|
|
58
60
|
if (!c || typeof c != "object")
|
|
59
61
|
return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
|
|
60
62
|
const e = c;
|
|
@@ -68,56 +70,56 @@ function dt(c) {
|
|
|
68
70
|
const e = c;
|
|
69
71
|
return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
|
|
70
72
|
}
|
|
71
|
-
function
|
|
73
|
+
function R(c, e) {
|
|
72
74
|
return c === e ? `${c}-${e}` : c < e ? `${c}-${e}` : `${e}-${c}`;
|
|
73
75
|
}
|
|
74
|
-
const
|
|
75
|
-
class
|
|
76
|
-
constructor(e,
|
|
77
|
-
super(), this.object = e, this.domElement =
|
|
76
|
+
const Te = { type: "change" }, ae = { type: "start" }, Pe = { type: "end" }, Z = new ot(), Le = new at(), pt = Math.cos(70 * rt.DEG2RAD);
|
|
77
|
+
class ut extends nt {
|
|
78
|
+
constructor(e, s) {
|
|
79
|
+
super(), this.object = e, this.domElement = s, 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: D.ROTATE, MIDDLE: D.DOLLY, RIGHT: D.PAN }, this.touches = { ONE: $.ROTATE, TWO: $.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
78
80
|
return l.phi;
|
|
79
81
|
}, this.getAzimuthalAngle = function() {
|
|
80
82
|
return l.theta;
|
|
81
83
|
}, this.getDistance = function() {
|
|
82
84
|
return this.object.position.distanceTo(this.target);
|
|
83
|
-
}, this.listenToKeyEvents = function(
|
|
84
|
-
|
|
85
|
+
}, this.listenToKeyEvents = function(o) {
|
|
86
|
+
o.addEventListener("keydown", ne), this._domElementKeyEvents = o;
|
|
85
87
|
}, this.stopListenToKeyEvents = function() {
|
|
86
|
-
this._domElementKeyEvents.removeEventListener("keydown",
|
|
88
|
+
this._domElementKeyEvents.removeEventListener("keydown", ne), this._domElementKeyEvents = null;
|
|
87
89
|
}, this.saveState = function() {
|
|
88
90
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
89
91
|
}, 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(
|
|
92
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Te), t.update(), n = i.NONE;
|
|
91
93
|
}, this.update = function() {
|
|
92
|
-
const
|
|
94
|
+
const o = new N(), h = new Se().setFromUnitVectors(e.up, new N(0, 1, 0)), m = h.clone().invert(), x = new N(), w = new Se(), k = new N(), S = 2 * Math.PI;
|
|
93
95
|
return function(tt = null) {
|
|
94
96
|
const Ce = t.object.position;
|
|
95
|
-
|
|
96
|
-
let
|
|
97
|
-
isFinite(
|
|
98
|
-
let
|
|
99
|
-
if (t.zoomToCursor &&
|
|
97
|
+
o.copy(Ce).sub(t.target), o.applyQuaternion(h), l.setFromVector3(o), t.autoRotate && n === i.NONE && K(He(tt)), t.enableDamping ? (l.theta += d.theta * t.dampingFactor, l.phi += d.phi * t.dampingFactor) : (l.theta += d.theta, l.phi += d.phi);
|
|
98
|
+
let T = t.minAzimuthAngle, P = t.maxAzimuthAngle;
|
|
99
|
+
isFinite(T) && isFinite(P) && (T < -Math.PI ? T += S : T > Math.PI && (T -= S), P < -Math.PI ? P += S : P > Math.PI && (P -= S), T <= P ? l.theta = Math.max(T, Math.min(P, l.theta)) : l.theta = l.theta > (T + P) / 2 ? Math.max(T, l.theta) : Math.min(P, l.theta)), l.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, l.phi)), l.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(g, t.dampingFactor) : t.target.add(g), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && _ || t.object.isOrthographicCamera ? l.radius = se(l.radius) : l.radius = se(l.radius * u), o.setFromSpherical(l), o.applyQuaternion(m), Ce.copy(t.target).add(o), t.object.lookAt(t.target), t.enableDamping === !0 ? (d.theta *= 1 - t.dampingFactor, d.phi *= 1 - t.dampingFactor, g.multiplyScalar(1 - t.dampingFactor)) : (d.set(0, 0, 0), g.set(0, 0, 0));
|
|
100
|
+
let oe = !1;
|
|
101
|
+
if (t.zoomToCursor && _) {
|
|
100
102
|
let Y = null;
|
|
101
103
|
if (t.object.isPerspectiveCamera) {
|
|
102
|
-
const B =
|
|
103
|
-
Y =
|
|
104
|
+
const B = o.length();
|
|
105
|
+
Y = se(B * u);
|
|
104
106
|
const q = B - Y;
|
|
105
107
|
t.object.position.addScaledVector(re, q), t.object.updateMatrixWorld();
|
|
106
108
|
} else if (t.object.isOrthographicCamera) {
|
|
107
|
-
const B = new N(
|
|
108
|
-
B.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
109
|
-
const q = new N(
|
|
110
|
-
q.unproject(t.object), t.object.position.sub(q).add(B), t.object.updateMatrixWorld(), Y =
|
|
109
|
+
const B = new N(O.x, O.y, 0);
|
|
110
|
+
B.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / u)), t.object.updateProjectionMatrix(), oe = !0;
|
|
111
|
+
const q = new N(O.x, O.y, 0);
|
|
112
|
+
q.unproject(t.object), t.object.position.sub(q).add(B), t.object.updateMatrixWorld(), Y = o.length();
|
|
111
113
|
} else
|
|
112
114
|
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) : (
|
|
114
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
115
|
-
return
|
|
115
|
+
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) : (Le.setFromNormalAndCoplanarPoint(t.object.up, t.target), Z.intersectPlane(Le, t.target))));
|
|
116
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / u)), t.object.updateProjectionMatrix(), oe = !0);
|
|
117
|
+
return u = 1, _ = !1, oe || x.distanceToSquared(t.object.position) > a || 8 * (1 - w.dot(t.object.quaternion)) > a || k.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Te), x.copy(t.object.position), w.copy(t.object.quaternion), k.copy(t.target), !0) : !1;
|
|
116
118
|
};
|
|
117
119
|
}(), this.dispose = function() {
|
|
118
|
-
t.domElement.removeEventListener("contextmenu",
|
|
120
|
+
t.domElement.removeEventListener("contextmenu", we), t.domElement.removeEventListener("pointerdown", xe), t.domElement.removeEventListener("pointercancel", G), t.domElement.removeEventListener("wheel", ve), t.domElement.removeEventListener("pointermove", ie), t.domElement.removeEventListener("pointerup", G), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ne), t._domElementKeyEvents = null);
|
|
119
121
|
};
|
|
120
|
-
const t = this,
|
|
122
|
+
const t = this, i = {
|
|
121
123
|
NONE: -1,
|
|
122
124
|
ROTATE: 0,
|
|
123
125
|
DOLLY: 1,
|
|
@@ -127,175 +129,175 @@ class gt extends it {
|
|
|
127
129
|
TOUCH_DOLLY_PAN: 5,
|
|
128
130
|
TOUCH_DOLLY_ROTATE: 6
|
|
129
131
|
};
|
|
130
|
-
let
|
|
132
|
+
let n = i.NONE;
|
|
131
133
|
const a = 1e-6, l = new Ne(), d = new Ne();
|
|
132
|
-
let
|
|
133
|
-
const
|
|
134
|
-
let
|
|
135
|
-
const
|
|
134
|
+
let u = 1;
|
|
135
|
+
const g = new N(), b = new z(), v = new z(), y = new z(), f = new z(), M = new z(), C = new z(), F = new z(), H = new z(), I = new z(), re = new N(), O = new z();
|
|
136
|
+
let _ = !1;
|
|
137
|
+
const E = [], V = {};
|
|
136
138
|
let J = !1;
|
|
137
|
-
function
|
|
138
|
-
return
|
|
139
|
+
function He(o) {
|
|
140
|
+
return o !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * o : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
|
|
139
141
|
}
|
|
140
|
-
function X(
|
|
141
|
-
const h = Math.abs(
|
|
142
|
+
function X(o) {
|
|
143
|
+
const h = Math.abs(o * 0.01);
|
|
142
144
|
return Math.pow(0.95, t.zoomSpeed * h);
|
|
143
145
|
}
|
|
144
|
-
function
|
|
145
|
-
d.theta -=
|
|
146
|
+
function K(o) {
|
|
147
|
+
d.theta -= o;
|
|
146
148
|
}
|
|
147
|
-
function W(
|
|
148
|
-
d.phi -=
|
|
149
|
+
function W(o) {
|
|
150
|
+
d.phi -= o;
|
|
149
151
|
}
|
|
150
152
|
const le = function() {
|
|
151
|
-
const
|
|
152
|
-
return function(m,
|
|
153
|
-
|
|
153
|
+
const o = new N();
|
|
154
|
+
return function(m, x) {
|
|
155
|
+
o.setFromMatrixColumn(x, 0), o.multiplyScalar(-m), g.add(o);
|
|
154
156
|
};
|
|
155
157
|
}(), ce = function() {
|
|
156
|
-
const
|
|
157
|
-
return function(m,
|
|
158
|
-
t.screenSpacePanning === !0 ?
|
|
158
|
+
const o = new N();
|
|
159
|
+
return function(m, x) {
|
|
160
|
+
t.screenSpacePanning === !0 ? o.setFromMatrixColumn(x, 1) : (o.setFromMatrixColumn(x, 0), o.crossVectors(t.object.up, o)), o.multiplyScalar(m), g.add(o);
|
|
159
161
|
};
|
|
160
|
-
}(),
|
|
161
|
-
const
|
|
162
|
-
return function(m,
|
|
163
|
-
const
|
|
162
|
+
}(), A = function() {
|
|
163
|
+
const o = new N();
|
|
164
|
+
return function(m, x) {
|
|
165
|
+
const w = t.domElement;
|
|
164
166
|
if (t.object.isPerspectiveCamera) {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
let
|
|
168
|
-
|
|
169
|
-
} else t.object.isOrthographicCamera ? (le(m * (t.object.right - t.object.left) / t.object.zoom /
|
|
167
|
+
const k = t.object.position;
|
|
168
|
+
o.copy(k).sub(t.target);
|
|
169
|
+
let S = o.length();
|
|
170
|
+
S *= Math.tan(t.object.fov / 2 * Math.PI / 180), le(2 * m * S / w.clientHeight, t.object.matrix), ce(2 * x * S / w.clientHeight, t.object.matrix);
|
|
171
|
+
} else t.object.isOrthographicCamera ? (le(m * (t.object.right - t.object.left) / t.object.zoom / w.clientWidth, t.object.matrix), ce(x * (t.object.top - t.object.bottom) / t.object.zoom / w.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
170
172
|
};
|
|
171
173
|
}();
|
|
172
|
-
function ee(
|
|
173
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
174
|
+
function ee(o) {
|
|
175
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? u /= o : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
174
176
|
}
|
|
175
|
-
function he(
|
|
176
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
177
|
+
function he(o) {
|
|
178
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? u *= o : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
177
179
|
}
|
|
178
|
-
function te(
|
|
180
|
+
function te(o, h) {
|
|
179
181
|
if (!t.zoomToCursor)
|
|
180
182
|
return;
|
|
181
|
-
|
|
182
|
-
const m = t.domElement.getBoundingClientRect(),
|
|
183
|
-
|
|
183
|
+
_ = !0;
|
|
184
|
+
const m = t.domElement.getBoundingClientRect(), x = o - m.left, w = h - m.top, k = m.width, S = m.height;
|
|
185
|
+
O.x = x / k * 2 - 1, O.y = -(w / S) * 2 + 1, re.set(O.x, O.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
184
186
|
}
|
|
185
|
-
function
|
|
186
|
-
return Math.max(t.minDistance, Math.min(t.maxDistance,
|
|
187
|
+
function se(o) {
|
|
188
|
+
return Math.max(t.minDistance, Math.min(t.maxDistance, o));
|
|
187
189
|
}
|
|
188
|
-
function de(
|
|
189
|
-
|
|
190
|
+
function de(o) {
|
|
191
|
+
b.set(o.clientX, o.clientY);
|
|
190
192
|
}
|
|
191
|
-
function
|
|
192
|
-
te(
|
|
193
|
+
function Ae(o) {
|
|
194
|
+
te(o.clientX, o.clientX), F.set(o.clientX, o.clientY);
|
|
193
195
|
}
|
|
194
|
-
function pe(
|
|
195
|
-
f.set(
|
|
196
|
+
function pe(o) {
|
|
197
|
+
f.set(o.clientX, o.clientY);
|
|
196
198
|
}
|
|
197
|
-
function
|
|
198
|
-
v.set(
|
|
199
|
+
function je(o) {
|
|
200
|
+
v.set(o.clientX, o.clientY), y.subVectors(v, b).multiplyScalar(t.rotateSpeed);
|
|
199
201
|
const h = t.domElement;
|
|
200
|
-
|
|
202
|
+
K(2 * Math.PI * y.x / h.clientHeight), W(2 * Math.PI * y.y / h.clientHeight), b.copy(v), t.update();
|
|
201
203
|
}
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
+
function De(o) {
|
|
205
|
+
H.set(o.clientX, o.clientY), I.subVectors(H, F), I.y > 0 ? ee(X(I.y)) : I.y < 0 && he(X(I.y)), F.copy(H), t.update();
|
|
204
206
|
}
|
|
205
|
-
function
|
|
206
|
-
M.set(
|
|
207
|
+
function $e(o) {
|
|
208
|
+
M.set(o.clientX, o.clientY), C.subVectors(M, f).multiplyScalar(t.panSpeed), A(C.x, C.y), f.copy(M), t.update();
|
|
207
209
|
}
|
|
208
|
-
function
|
|
209
|
-
te(
|
|
210
|
+
function Ke(o) {
|
|
211
|
+
te(o.clientX, o.clientY), o.deltaY < 0 ? he(X(o.deltaY)) : o.deltaY > 0 && ee(X(o.deltaY)), t.update();
|
|
210
212
|
}
|
|
211
|
-
function
|
|
213
|
+
function Ge(o) {
|
|
212
214
|
let h = !1;
|
|
213
|
-
switch (
|
|
215
|
+
switch (o.code) {
|
|
214
216
|
case t.keys.UP:
|
|
215
|
-
|
|
217
|
+
o.ctrlKey || o.metaKey || o.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), h = !0;
|
|
216
218
|
break;
|
|
217
219
|
case t.keys.BOTTOM:
|
|
218
|
-
|
|
220
|
+
o.ctrlKey || o.metaKey || o.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), h = !0;
|
|
219
221
|
break;
|
|
220
222
|
case t.keys.LEFT:
|
|
221
|
-
|
|
223
|
+
o.ctrlKey || o.metaKey || o.shiftKey ? K(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), h = !0;
|
|
222
224
|
break;
|
|
223
225
|
case t.keys.RIGHT:
|
|
224
|
-
|
|
226
|
+
o.ctrlKey || o.metaKey || o.shiftKey ? K(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(-t.keyPanSpeed, 0), h = !0;
|
|
225
227
|
break;
|
|
226
228
|
}
|
|
227
|
-
h && (
|
|
229
|
+
h && (o.preventDefault(), t.update());
|
|
228
230
|
}
|
|
229
|
-
function
|
|
230
|
-
if (
|
|
231
|
-
|
|
231
|
+
function ue(o) {
|
|
232
|
+
if (E.length === 1)
|
|
233
|
+
b.set(o.pageX, o.pageY);
|
|
232
234
|
else {
|
|
233
|
-
const h =
|
|
234
|
-
|
|
235
|
+
const h = j(o), m = 0.5 * (o.pageX + h.x), x = 0.5 * (o.pageY + h.y);
|
|
236
|
+
b.set(m, x);
|
|
235
237
|
}
|
|
236
238
|
}
|
|
237
|
-
function
|
|
238
|
-
if (
|
|
239
|
-
f.set(
|
|
239
|
+
function ge(o) {
|
|
240
|
+
if (E.length === 1)
|
|
241
|
+
f.set(o.pageX, o.pageY);
|
|
240
242
|
else {
|
|
241
|
-
const h =
|
|
242
|
-
f.set(m,
|
|
243
|
+
const h = j(o), m = 0.5 * (o.pageX + h.x), x = 0.5 * (o.pageY + h.y);
|
|
244
|
+
f.set(m, x);
|
|
243
245
|
}
|
|
244
246
|
}
|
|
245
|
-
function me(
|
|
246
|
-
const h =
|
|
247
|
-
|
|
247
|
+
function me(o) {
|
|
248
|
+
const h = j(o), m = o.pageX - h.x, x = o.pageY - h.y, w = Math.sqrt(m * m + x * x);
|
|
249
|
+
F.set(0, w);
|
|
248
250
|
}
|
|
249
|
-
function Ye(
|
|
250
|
-
t.enableZoom && me(
|
|
251
|
+
function Ye(o) {
|
|
252
|
+
t.enableZoom && me(o), t.enablePan && ge(o);
|
|
251
253
|
}
|
|
252
|
-
function Be(
|
|
253
|
-
t.enableZoom && me(
|
|
254
|
+
function Be(o) {
|
|
255
|
+
t.enableZoom && me(o), t.enableRotate && ue(o);
|
|
254
256
|
}
|
|
255
|
-
function fe(
|
|
256
|
-
if (
|
|
257
|
-
v.set(
|
|
257
|
+
function fe(o) {
|
|
258
|
+
if (E.length == 1)
|
|
259
|
+
v.set(o.pageX, o.pageY);
|
|
258
260
|
else {
|
|
259
|
-
const m =
|
|
260
|
-
v.set(
|
|
261
|
+
const m = j(o), x = 0.5 * (o.pageX + m.x), w = 0.5 * (o.pageY + m.y);
|
|
262
|
+
v.set(x, w);
|
|
261
263
|
}
|
|
262
|
-
y.subVectors(v,
|
|
264
|
+
y.subVectors(v, b).multiplyScalar(t.rotateSpeed);
|
|
263
265
|
const h = t.domElement;
|
|
264
|
-
|
|
266
|
+
K(2 * Math.PI * y.x / h.clientHeight), W(2 * Math.PI * y.y / h.clientHeight), b.copy(v);
|
|
265
267
|
}
|
|
266
|
-
function ye(
|
|
267
|
-
if (
|
|
268
|
-
M.set(
|
|
268
|
+
function ye(o) {
|
|
269
|
+
if (E.length === 1)
|
|
270
|
+
M.set(o.pageX, o.pageY);
|
|
269
271
|
else {
|
|
270
|
-
const h =
|
|
271
|
-
M.set(m,
|
|
272
|
+
const h = j(o), m = 0.5 * (o.pageX + h.x), x = 0.5 * (o.pageY + h.y);
|
|
273
|
+
M.set(m, x);
|
|
272
274
|
}
|
|
273
|
-
C.subVectors(M, f).multiplyScalar(t.panSpeed),
|
|
275
|
+
C.subVectors(M, f).multiplyScalar(t.panSpeed), A(C.x, C.y), f.copy(M);
|
|
274
276
|
}
|
|
275
|
-
function
|
|
276
|
-
const h =
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
te(
|
|
277
|
+
function be(o) {
|
|
278
|
+
const h = j(o), m = o.pageX - h.x, x = o.pageY - h.y, w = Math.sqrt(m * m + x * x);
|
|
279
|
+
H.set(0, w), I.set(0, Math.pow(H.y / F.y, t.zoomSpeed)), ee(I.y), F.copy(H);
|
|
280
|
+
const k = (o.pageX + h.x) * 0.5, S = (o.pageY + h.y) * 0.5;
|
|
281
|
+
te(k, S);
|
|
280
282
|
}
|
|
281
|
-
function
|
|
282
|
-
t.enableZoom &&
|
|
283
|
+
function Ue(o) {
|
|
284
|
+
t.enableZoom && be(o), t.enablePan && ye(o);
|
|
283
285
|
}
|
|
284
|
-
function
|
|
285
|
-
t.enableZoom &&
|
|
286
|
+
function _e(o) {
|
|
287
|
+
t.enableZoom && be(o), t.enableRotate && fe(o);
|
|
286
288
|
}
|
|
287
|
-
function
|
|
288
|
-
t.enabled !== !1 && (
|
|
289
|
+
function xe(o) {
|
|
290
|
+
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(o.pointerId), t.domElement.addEventListener("pointermove", ie), t.domElement.addEventListener("pointerup", G)), Je(o), o.pointerType === "touch" ? Ze(o) : Ve(o));
|
|
289
291
|
}
|
|
290
|
-
function
|
|
291
|
-
t.enabled !== !1 && (
|
|
292
|
+
function ie(o) {
|
|
293
|
+
t.enabled !== !1 && (o.pointerType === "touch" ? Qe(o) : Xe(o));
|
|
292
294
|
}
|
|
293
|
-
function
|
|
294
|
-
et(
|
|
295
|
+
function G(o) {
|
|
296
|
+
et(o), E.length === 0 && (t.domElement.releasePointerCapture(o.pointerId), t.domElement.removeEventListener("pointermove", ie), t.domElement.removeEventListener("pointerup", G)), t.dispatchEvent(Pe), n = i.NONE;
|
|
295
297
|
}
|
|
296
|
-
function Ve(
|
|
298
|
+
function Ve(o) {
|
|
297
299
|
let h;
|
|
298
|
-
switch (
|
|
300
|
+
switch (o.button) {
|
|
299
301
|
case 0:
|
|
300
302
|
h = t.mouseButtons.LEFT;
|
|
301
303
|
break;
|
|
@@ -309,57 +311,57 @@ class gt extends it {
|
|
|
309
311
|
h = -1;
|
|
310
312
|
}
|
|
311
313
|
switch (h) {
|
|
312
|
-
case
|
|
314
|
+
case D.DOLLY:
|
|
313
315
|
if (t.enableZoom === !1) return;
|
|
314
|
-
|
|
316
|
+
Ae(o), n = i.DOLLY;
|
|
315
317
|
break;
|
|
316
|
-
case
|
|
317
|
-
if (
|
|
318
|
+
case D.ROTATE:
|
|
319
|
+
if (o.ctrlKey || o.metaKey || o.shiftKey) {
|
|
318
320
|
if (t.enablePan === !1) return;
|
|
319
|
-
pe(
|
|
321
|
+
pe(o), n = i.PAN;
|
|
320
322
|
} else {
|
|
321
323
|
if (t.enableRotate === !1) return;
|
|
322
|
-
de(
|
|
324
|
+
de(o), n = i.ROTATE;
|
|
323
325
|
}
|
|
324
326
|
break;
|
|
325
|
-
case
|
|
326
|
-
if (
|
|
327
|
+
case D.PAN:
|
|
328
|
+
if (o.ctrlKey || o.metaKey || o.shiftKey) {
|
|
327
329
|
if (t.enableRotate === !1) return;
|
|
328
|
-
de(
|
|
330
|
+
de(o), n = i.ROTATE;
|
|
329
331
|
} else {
|
|
330
332
|
if (t.enablePan === !1) return;
|
|
331
|
-
pe(
|
|
333
|
+
pe(o), n = i.PAN;
|
|
332
334
|
}
|
|
333
335
|
break;
|
|
334
336
|
default:
|
|
335
|
-
|
|
337
|
+
n = i.NONE;
|
|
336
338
|
}
|
|
337
|
-
|
|
339
|
+
n !== i.NONE && t.dispatchEvent(ae);
|
|
338
340
|
}
|
|
339
|
-
function Xe(
|
|
340
|
-
switch (
|
|
341
|
-
case
|
|
341
|
+
function Xe(o) {
|
|
342
|
+
switch (n) {
|
|
343
|
+
case i.ROTATE:
|
|
342
344
|
if (t.enableRotate === !1) return;
|
|
343
|
-
|
|
345
|
+
je(o);
|
|
344
346
|
break;
|
|
345
|
-
case
|
|
347
|
+
case i.DOLLY:
|
|
346
348
|
if (t.enableZoom === !1) return;
|
|
347
|
-
|
|
349
|
+
De(o);
|
|
348
350
|
break;
|
|
349
|
-
case
|
|
351
|
+
case i.PAN:
|
|
350
352
|
if (t.enablePan === !1) return;
|
|
351
|
-
|
|
353
|
+
$e(o);
|
|
352
354
|
break;
|
|
353
355
|
}
|
|
354
356
|
}
|
|
355
|
-
function ve(
|
|
356
|
-
t.enabled === !1 || t.enableZoom === !1 ||
|
|
357
|
+
function ve(o) {
|
|
358
|
+
t.enabled === !1 || t.enableZoom === !1 || n !== i.NONE || (o.preventDefault(), t.dispatchEvent(ae), Ke(We(o)), t.dispatchEvent(Pe));
|
|
357
359
|
}
|
|
358
|
-
function We(
|
|
359
|
-
const h =
|
|
360
|
-
clientX:
|
|
361
|
-
clientY:
|
|
362
|
-
deltaY:
|
|
360
|
+
function We(o) {
|
|
361
|
+
const h = o.deltaMode, m = {
|
|
362
|
+
clientX: o.clientX,
|
|
363
|
+
clientY: o.clientY,
|
|
364
|
+
deltaY: o.deltaY
|
|
363
365
|
};
|
|
364
366
|
switch (h) {
|
|
365
367
|
case 1:
|
|
@@ -369,101 +371,101 @@ class gt extends it {
|
|
|
369
371
|
m.deltaY *= 100;
|
|
370
372
|
break;
|
|
371
373
|
}
|
|
372
|
-
return
|
|
374
|
+
return o.ctrlKey && !J && (m.deltaY *= 10), m;
|
|
373
375
|
}
|
|
374
|
-
function qe(
|
|
375
|
-
|
|
376
|
+
function qe(o) {
|
|
377
|
+
o.key === "Control" && (J = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
376
378
|
}
|
|
377
|
-
function Me(
|
|
378
|
-
|
|
379
|
+
function Me(o) {
|
|
380
|
+
o.key === "Control" && (J = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
379
381
|
}
|
|
380
|
-
function
|
|
381
|
-
t.enabled === !1 || t.enablePan === !1 ||
|
|
382
|
+
function ne(o) {
|
|
383
|
+
t.enabled === !1 || t.enablePan === !1 || Ge(o);
|
|
382
384
|
}
|
|
383
|
-
function Ze(
|
|
384
|
-
switch (
|
|
385
|
+
function Ze(o) {
|
|
386
|
+
switch (Ee(o), E.length) {
|
|
385
387
|
case 1:
|
|
386
388
|
switch (t.touches.ONE) {
|
|
387
|
-
case
|
|
389
|
+
case $.ROTATE:
|
|
388
390
|
if (t.enableRotate === !1) return;
|
|
389
|
-
|
|
391
|
+
ue(o), n = i.TOUCH_ROTATE;
|
|
390
392
|
break;
|
|
391
|
-
case
|
|
393
|
+
case $.PAN:
|
|
392
394
|
if (t.enablePan === !1) return;
|
|
393
|
-
|
|
395
|
+
ge(o), n = i.TOUCH_PAN;
|
|
394
396
|
break;
|
|
395
397
|
default:
|
|
396
|
-
|
|
398
|
+
n = i.NONE;
|
|
397
399
|
}
|
|
398
400
|
break;
|
|
399
401
|
case 2:
|
|
400
402
|
switch (t.touches.TWO) {
|
|
401
|
-
case
|
|
403
|
+
case $.DOLLY_PAN:
|
|
402
404
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
403
|
-
Ye(
|
|
405
|
+
Ye(o), n = i.TOUCH_DOLLY_PAN;
|
|
404
406
|
break;
|
|
405
|
-
case
|
|
407
|
+
case $.DOLLY_ROTATE:
|
|
406
408
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
407
|
-
Be(
|
|
409
|
+
Be(o), n = i.TOUCH_DOLLY_ROTATE;
|
|
408
410
|
break;
|
|
409
411
|
default:
|
|
410
|
-
|
|
412
|
+
n = i.NONE;
|
|
411
413
|
}
|
|
412
414
|
break;
|
|
413
415
|
default:
|
|
414
|
-
|
|
416
|
+
n = i.NONE;
|
|
415
417
|
}
|
|
416
|
-
|
|
418
|
+
n !== i.NONE && t.dispatchEvent(ae);
|
|
417
419
|
}
|
|
418
|
-
function Qe(
|
|
419
|
-
switch (
|
|
420
|
-
case
|
|
420
|
+
function Qe(o) {
|
|
421
|
+
switch (Ee(o), n) {
|
|
422
|
+
case i.TOUCH_ROTATE:
|
|
421
423
|
if (t.enableRotate === !1) return;
|
|
422
|
-
fe(
|
|
424
|
+
fe(o), t.update();
|
|
423
425
|
break;
|
|
424
|
-
case
|
|
426
|
+
case i.TOUCH_PAN:
|
|
425
427
|
if (t.enablePan === !1) return;
|
|
426
|
-
ye(
|
|
428
|
+
ye(o), t.update();
|
|
427
429
|
break;
|
|
428
|
-
case
|
|
430
|
+
case i.TOUCH_DOLLY_PAN:
|
|
429
431
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
430
|
-
|
|
432
|
+
Ue(o), t.update();
|
|
431
433
|
break;
|
|
432
|
-
case
|
|
434
|
+
case i.TOUCH_DOLLY_ROTATE:
|
|
433
435
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
434
|
-
|
|
436
|
+
_e(o), t.update();
|
|
435
437
|
break;
|
|
436
438
|
default:
|
|
437
|
-
|
|
439
|
+
n = i.NONE;
|
|
438
440
|
}
|
|
439
441
|
}
|
|
440
|
-
function
|
|
441
|
-
t.enabled !== !1 &&
|
|
442
|
+
function we(o) {
|
|
443
|
+
t.enabled !== !1 && o.preventDefault();
|
|
442
444
|
}
|
|
443
|
-
function Je(
|
|
444
|
-
|
|
445
|
+
function Je(o) {
|
|
446
|
+
E.push(o.pointerId);
|
|
445
447
|
}
|
|
446
|
-
function et(
|
|
447
|
-
delete V[
|
|
448
|
-
for (let h = 0; h <
|
|
449
|
-
if (
|
|
450
|
-
|
|
448
|
+
function et(o) {
|
|
449
|
+
delete V[o.pointerId];
|
|
450
|
+
for (let h = 0; h < E.length; h++)
|
|
451
|
+
if (E[h] == o.pointerId) {
|
|
452
|
+
E.splice(h, 1);
|
|
451
453
|
return;
|
|
452
454
|
}
|
|
453
455
|
}
|
|
454
|
-
function
|
|
455
|
-
let h = V[
|
|
456
|
-
h === void 0 && (h = new
|
|
456
|
+
function Ee(o) {
|
|
457
|
+
let h = V[o.pointerId];
|
|
458
|
+
h === void 0 && (h = new z(), V[o.pointerId] = h), h.set(o.pageX, o.pageY);
|
|
457
459
|
}
|
|
458
|
-
function
|
|
459
|
-
const h =
|
|
460
|
+
function j(o) {
|
|
461
|
+
const h = o.pointerId === E[0] ? E[1] : E[0];
|
|
460
462
|
return V[h];
|
|
461
463
|
}
|
|
462
|
-
t.domElement.addEventListener("contextmenu",
|
|
464
|
+
t.domElement.addEventListener("contextmenu", we), t.domElement.addEventListener("pointerdown", xe), t.domElement.addEventListener("pointercancel", G), t.domElement.addEventListener("wheel", ve, { passive: !1 }), document.addEventListener("keydown", qe, { passive: !0, capture: !0 }), this.update();
|
|
463
465
|
}
|
|
464
466
|
}
|
|
465
|
-
class
|
|
466
|
-
constructor(e,
|
|
467
|
+
class gt {
|
|
468
|
+
constructor(e, s) {
|
|
467
469
|
r(this, "scene");
|
|
468
470
|
r(this, "camera");
|
|
469
471
|
r(this, "renderer");
|
|
@@ -471,16 +473,16 @@ class ut {
|
|
|
471
473
|
r(this, "container");
|
|
472
474
|
r(this, "resizeHandler");
|
|
473
475
|
this.container = e, this.scene = new p.Scene(), this.scene.background = new p.Color(
|
|
474
|
-
|
|
476
|
+
s.backgroundColor ?? 657930
|
|
475
477
|
);
|
|
476
|
-
const { width: t, height:
|
|
477
|
-
this.camera = new p.PerspectiveCamera(
|
|
478
|
-
const a =
|
|
478
|
+
const { width: t, height: i } = ze(e), n = s.cameraFov ?? 75;
|
|
479
|
+
this.camera = new p.PerspectiveCamera(n, t / i, 0.1, 2e3);
|
|
480
|
+
const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
479
481
|
this.camera.position.set(a.x, a.y, a.z), this.renderer = new p.WebGLRenderer({
|
|
480
482
|
antialias: !0,
|
|
481
483
|
alpha: !0,
|
|
482
484
|
powerPreference: "high-performance"
|
|
483
|
-
}), this.renderer.setSize(t,
|
|
485
|
+
}), this.renderer.setSize(t, i), 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 ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 500, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
484
486
|
}
|
|
485
487
|
/**
|
|
486
488
|
* Sets up scene lighting for gradient glass on dark background
|
|
@@ -488,14 +490,14 @@ class ut {
|
|
|
488
490
|
setupLighting() {
|
|
489
491
|
const e = new p.AmbientLight(16777215, 0.4);
|
|
490
492
|
this.scene.add(e);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
+
const s = new p.DirectionalLight(16777215, 0.9);
|
|
494
|
+
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
493
495
|
const t = new p.DirectionalLight(16773344, 0.4);
|
|
494
496
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
497
|
+
const i = new p.DirectionalLight(16777215, 0.3);
|
|
498
|
+
i.position.set(0, -30, -50), this.scene.add(i);
|
|
499
|
+
const n = new p.PointLight(16750950, 0.5, 150);
|
|
500
|
+
n.position.set(40, 20, 40), this.scene.add(n);
|
|
499
501
|
const a = new p.PointLight(16764057, 0.4, 150);
|
|
500
502
|
a.position.set(-40, -20, 40), this.scene.add(a);
|
|
501
503
|
const l = new p.PointLight(6724095, 0.2, 100);
|
|
@@ -505,8 +507,8 @@ class ut {
|
|
|
505
507
|
* Handle window resize
|
|
506
508
|
*/
|
|
507
509
|
onWindowResize() {
|
|
508
|
-
const { width: e, height:
|
|
509
|
-
this.camera.aspect = e /
|
|
510
|
+
const { width: e, height: s } = ze(this.container);
|
|
511
|
+
this.camera.aspect = e / s, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, s);
|
|
510
512
|
}
|
|
511
513
|
/**
|
|
512
514
|
* Adds an object to the scene
|
|
@@ -554,12 +556,12 @@ class ut {
|
|
|
554
556
|
}
|
|
555
557
|
}
|
|
556
558
|
class mt {
|
|
557
|
-
constructor(e,
|
|
559
|
+
constructor(e, s) {
|
|
558
560
|
r(this, "sceneManager");
|
|
559
561
|
r(this, "nodeFactory");
|
|
560
562
|
r(this, "nodes", /* @__PURE__ */ new Map());
|
|
561
563
|
r(this, "nodeObjects", /* @__PURE__ */ new Map());
|
|
562
|
-
this.sceneManager = e, this.nodeFactory =
|
|
564
|
+
this.sceneManager = e, this.nodeFactory = s;
|
|
563
565
|
}
|
|
564
566
|
/**
|
|
565
567
|
* Checks if a node exists
|
|
@@ -571,8 +573,8 @@ class mt {
|
|
|
571
573
|
* Adds a node to the graph
|
|
572
574
|
* @returns true if added, false if node already exists or invalid
|
|
573
575
|
*/
|
|
574
|
-
addNode(e,
|
|
575
|
-
if (!
|
|
576
|
+
addNode(e, s = 0) {
|
|
577
|
+
if (!Re(e))
|
|
576
578
|
return !1;
|
|
577
579
|
if (this.nodes.has(e.id))
|
|
578
580
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
@@ -580,47 +582,47 @@ class mt {
|
|
|
580
582
|
x: (Math.random() - 0.5) * 50,
|
|
581
583
|
y: (Math.random() - 0.5) * 50,
|
|
582
584
|
z: (Math.random() - 0.5) * 50
|
|
583
|
-
},
|
|
585
|
+
}, i = {
|
|
584
586
|
...e,
|
|
585
587
|
position: t,
|
|
586
588
|
velocity: { x: 0, y: 0, z: 0 },
|
|
587
589
|
mass: 1
|
|
588
|
-
},
|
|
590
|
+
}, n = this.nodeFactory.createNode(
|
|
589
591
|
{ ...e, position: t },
|
|
590
|
-
|
|
592
|
+
s
|
|
591
593
|
);
|
|
592
|
-
return this.sceneManager.add(
|
|
594
|
+
return this.sceneManager.add(n.group), this.nodes.set(e.id, i), this.nodeObjects.set(e.id, n), !0;
|
|
593
595
|
}
|
|
594
596
|
/**
|
|
595
597
|
* Removes a node from the graph
|
|
596
598
|
* @returns true if removed, false if not found
|
|
597
599
|
*/
|
|
598
600
|
removeNode(e) {
|
|
599
|
-
const
|
|
600
|
-
return !
|
|
601
|
+
const s = this.nodes.get(e), t = this.nodeObjects.get(e);
|
|
602
|
+
return !s || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), !0);
|
|
601
603
|
}
|
|
602
604
|
/**
|
|
603
605
|
* Updates a node's properties
|
|
604
606
|
*/
|
|
605
|
-
updateNode(e,
|
|
606
|
-
const t = this.nodes.get(e),
|
|
607
|
-
return !t || !
|
|
608
|
-
|
|
607
|
+
updateNode(e, s) {
|
|
608
|
+
const t = this.nodes.get(e), i = this.nodeObjects.get(e);
|
|
609
|
+
return !t || !i ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(i, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(i, s.color)), Object.keys(s).forEach((n) => {
|
|
610
|
+
n !== "id" && n !== "label" && n !== "color" && n !== "position" && (t[n] = s[n]);
|
|
609
611
|
}), !0);
|
|
610
612
|
}
|
|
611
613
|
/**
|
|
612
614
|
* Updates a node's position (called by physics engine)
|
|
613
615
|
*/
|
|
614
|
-
updateNodePosition(e,
|
|
615
|
-
const t = this.nodes.get(e),
|
|
616
|
-
t &&
|
|
616
|
+
updateNodePosition(e, s) {
|
|
617
|
+
const t = this.nodes.get(e), i = this.nodeObjects.get(e);
|
|
618
|
+
t && i && (t.position = s, i.group.position.set(s.x, s.y, s.z));
|
|
617
619
|
}
|
|
618
620
|
/**
|
|
619
621
|
* Updates a node's LOD level
|
|
620
622
|
*/
|
|
621
|
-
updateNodeLOD(e,
|
|
623
|
+
updateNodeLOD(e, s) {
|
|
622
624
|
const t = this.nodeObjects.get(e);
|
|
623
|
-
t && this.nodeFactory.updateNodeLOD(t,
|
|
625
|
+
t && this.nodeFactory.updateNodeLOD(t, s);
|
|
624
626
|
}
|
|
625
627
|
/**
|
|
626
628
|
* Gets a node by ID
|
|
@@ -645,8 +647,8 @@ class mt {
|
|
|
645
647
|
*/
|
|
646
648
|
getAllNodeObjects() {
|
|
647
649
|
const e = [];
|
|
648
|
-
return this.nodeObjects.forEach((
|
|
649
|
-
e.push(
|
|
650
|
+
return this.nodeObjects.forEach((s) => {
|
|
651
|
+
e.push(s.group);
|
|
650
652
|
}), e;
|
|
651
653
|
}
|
|
652
654
|
/**
|
|
@@ -654,8 +656,8 @@ class mt {
|
|
|
654
656
|
*/
|
|
655
657
|
getAllSpheres() {
|
|
656
658
|
const e = [];
|
|
657
|
-
return this.nodeObjects.forEach((
|
|
658
|
-
e.push(
|
|
659
|
+
return this.nodeObjects.forEach((s) => {
|
|
660
|
+
e.push(s.sphere);
|
|
659
661
|
}), e;
|
|
660
662
|
}
|
|
661
663
|
/**
|
|
@@ -668,8 +670,8 @@ class mt {
|
|
|
668
670
|
* Clears all nodes
|
|
669
671
|
*/
|
|
670
672
|
clear() {
|
|
671
|
-
this.nodes.forEach((e,
|
|
672
|
-
this.removeNode(
|
|
673
|
+
this.nodes.forEach((e, s) => {
|
|
674
|
+
this.removeNode(s);
|
|
673
675
|
});
|
|
674
676
|
}
|
|
675
677
|
/**
|
|
@@ -680,7 +682,7 @@ class mt {
|
|
|
680
682
|
}
|
|
681
683
|
}
|
|
682
684
|
class ft {
|
|
683
|
-
constructor(e,
|
|
685
|
+
constructor(e, s, t) {
|
|
684
686
|
r(this, "sceneManager");
|
|
685
687
|
r(this, "nodeManager");
|
|
686
688
|
r(this, "edgeFactory");
|
|
@@ -688,13 +690,13 @@ class ft {
|
|
|
688
690
|
r(this, "edgeObjects", []);
|
|
689
691
|
r(this, "edgeKeySet", /* @__PURE__ */ new Set());
|
|
690
692
|
r(this, "highlightedEdgeKey", null);
|
|
691
|
-
this.sceneManager = e, this.nodeManager =
|
|
693
|
+
this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
|
|
692
694
|
}
|
|
693
695
|
/**
|
|
694
696
|
* Checks if an edge exists
|
|
695
697
|
*/
|
|
696
|
-
hasEdge(e,
|
|
697
|
-
const t =
|
|
698
|
+
hasEdge(e, s) {
|
|
699
|
+
const t = R(e, s);
|
|
698
700
|
return this.edgeKeySet.has(t);
|
|
699
701
|
}
|
|
700
702
|
/**
|
|
@@ -702,50 +704,50 @@ class ft {
|
|
|
702
704
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
703
705
|
*/
|
|
704
706
|
addEdge(e) {
|
|
705
|
-
if (!
|
|
707
|
+
if (!Fe(e))
|
|
706
708
|
return !1;
|
|
707
709
|
if (!this.nodeManager.hasNode(e.source))
|
|
708
710
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
709
711
|
if (!this.nodeManager.hasNode(e.target))
|
|
710
712
|
return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
|
|
711
|
-
const
|
|
712
|
-
if (this.edgeKeySet.has(
|
|
713
|
+
const s = R(e.source, e.target);
|
|
714
|
+
if (this.edgeKeySet.has(s))
|
|
713
715
|
return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
|
|
714
|
-
const t = this.nodeManager.getNode(e.source),
|
|
716
|
+
const t = this.nodeManager.getNode(e.source), i = this.nodeManager.getNode(e.target), n = this.edgeFactory.createEdge(
|
|
715
717
|
e,
|
|
716
718
|
t,
|
|
717
|
-
|
|
719
|
+
i,
|
|
718
720
|
t.position,
|
|
719
|
-
|
|
721
|
+
i.position
|
|
720
722
|
);
|
|
721
|
-
return this.sceneManager.add(
|
|
723
|
+
return this.sceneManager.add(n.line), this.edges.push(e), this.edgeObjects.push(n), this.edgeKeySet.add(s), !0;
|
|
722
724
|
}
|
|
723
725
|
/**
|
|
724
726
|
* Removes an edge from the graph
|
|
725
727
|
* @returns true if removed, false if not found
|
|
726
728
|
*/
|
|
727
|
-
removeEdge(e,
|
|
728
|
-
const t =
|
|
729
|
+
removeEdge(e, s) {
|
|
730
|
+
const t = R(e, s);
|
|
729
731
|
if (!this.edgeKeySet.has(t))
|
|
730
732
|
return !1;
|
|
731
|
-
const
|
|
732
|
-
(a) =>
|
|
733
|
+
const i = this.edges.findIndex(
|
|
734
|
+
(a) => R(a.source, a.target) === t
|
|
733
735
|
);
|
|
734
|
-
if (
|
|
736
|
+
if (i === -1)
|
|
735
737
|
return !1;
|
|
736
|
-
const
|
|
737
|
-
return this.sceneManager.remove(
|
|
738
|
+
const n = this.edgeObjects[i];
|
|
739
|
+
return this.sceneManager.remove(n.line), this.edgeFactory.disposeEdge(n), this.edges.splice(i, 1), this.edgeObjects.splice(i, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
|
|
738
740
|
}
|
|
739
741
|
/**
|
|
740
742
|
* Highlights an edge
|
|
741
743
|
*/
|
|
742
|
-
highlightEdge(e,
|
|
743
|
-
const t =
|
|
744
|
+
highlightEdge(e, s) {
|
|
745
|
+
const t = R(e, s);
|
|
744
746
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
745
|
-
const
|
|
746
|
-
(
|
|
747
|
+
const i = this.edges.findIndex(
|
|
748
|
+
(n) => R(n.source, n.target) === t
|
|
747
749
|
);
|
|
748
|
-
|
|
750
|
+
i !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[i]), this.highlightedEdgeKey = t);
|
|
749
751
|
}
|
|
750
752
|
/**
|
|
751
753
|
* Unhighlights the currently highlighted edge
|
|
@@ -753,7 +755,7 @@ class ft {
|
|
|
753
755
|
unhighlightCurrentEdge() {
|
|
754
756
|
if (!this.highlightedEdgeKey) return;
|
|
755
757
|
const e = this.edges.findIndex(
|
|
756
|
-
(
|
|
758
|
+
(s) => R(s.source, s.target) === this.highlightedEdgeKey
|
|
757
759
|
);
|
|
758
760
|
e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
|
|
759
761
|
}
|
|
@@ -761,10 +763,10 @@ class ft {
|
|
|
761
763
|
* Removes all edges connected to a node
|
|
762
764
|
*/
|
|
763
765
|
removeEdgesForNode(e) {
|
|
764
|
-
const
|
|
766
|
+
const s = [];
|
|
765
767
|
this.edges.forEach((t) => {
|
|
766
|
-
(t.source === e || t.target === e) &&
|
|
767
|
-
}),
|
|
768
|
+
(t.source === e || t.target === e) && s.push({ source: t.source, target: t.target });
|
|
769
|
+
}), s.forEach((t) => {
|
|
768
770
|
this.removeEdge(t.source, t.target);
|
|
769
771
|
});
|
|
770
772
|
}
|
|
@@ -772,27 +774,27 @@ class ft {
|
|
|
772
774
|
* Gets all edges for a node
|
|
773
775
|
*/
|
|
774
776
|
getEdgesForNode(e) {
|
|
775
|
-
return this.edges.filter((
|
|
777
|
+
return this.edges.filter((s) => s.source === e || s.target === e);
|
|
776
778
|
}
|
|
777
779
|
/**
|
|
778
780
|
* Gets neighbor node IDs for a node
|
|
779
781
|
*/
|
|
780
782
|
getNeighborIds(e) {
|
|
781
|
-
const
|
|
783
|
+
const s = [];
|
|
782
784
|
return this.edges.forEach((t) => {
|
|
783
|
-
t.source === e ?
|
|
784
|
-
}), [...new Set(
|
|
785
|
+
t.source === e ? s.push(t.target) : t.target === e && s.push(t.source);
|
|
786
|
+
}), [...new Set(s)];
|
|
785
787
|
}
|
|
786
788
|
/**
|
|
787
789
|
* Updates all edge positions based on current node positions
|
|
788
790
|
*/
|
|
789
791
|
updateEdgePositions() {
|
|
790
|
-
this.edgeObjects.forEach((e,
|
|
791
|
-
const t = this.edges[
|
|
792
|
-
|
|
792
|
+
this.edgeObjects.forEach((e, s) => {
|
|
793
|
+
const t = this.edges[s], i = this.nodeManager.getNode(t.source), n = this.nodeManager.getNode(t.target);
|
|
794
|
+
i && n && this.edgeFactory.updateEdgePositions(
|
|
793
795
|
e,
|
|
794
|
-
|
|
795
|
-
|
|
796
|
+
i.position,
|
|
797
|
+
n.position
|
|
796
798
|
);
|
|
797
799
|
});
|
|
798
800
|
}
|
|
@@ -829,8 +831,8 @@ class ft {
|
|
|
829
831
|
this.clear();
|
|
830
832
|
}
|
|
831
833
|
}
|
|
832
|
-
class
|
|
833
|
-
constructor(e,
|
|
834
|
+
class Oe {
|
|
835
|
+
constructor(e, s, t = {}) {
|
|
834
836
|
r(this, "nodes");
|
|
835
837
|
r(this, "edges");
|
|
836
838
|
// Physics parameters
|
|
@@ -844,7 +846,7 @@ class Le {
|
|
|
844
846
|
r(this, "alphaDecay", 0.0228);
|
|
845
847
|
r(this, "alphaMin", 1e-3);
|
|
846
848
|
r(this, "alphaTarget", 0);
|
|
847
|
-
this.nodes = e, this.edges =
|
|
849
|
+
this.nodes = e, this.edges = s, 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
850
|
}
|
|
849
851
|
/**
|
|
850
852
|
* Runs one simulation step
|
|
@@ -858,15 +860,15 @@ class Le {
|
|
|
858
860
|
* Brute force repulsion - O(n²)
|
|
859
861
|
*/
|
|
860
862
|
calculateRepulsionBruteForce() {
|
|
861
|
-
const e = Array.from(this.nodes.values()),
|
|
862
|
-
for (let t = 0; t <
|
|
863
|
-
const
|
|
864
|
-
for (let
|
|
865
|
-
const a = e[
|
|
866
|
-
let
|
|
867
|
-
|
|
868
|
-
const
|
|
869
|
-
|
|
863
|
+
const e = Array.from(this.nodes.values()), s = e.length;
|
|
864
|
+
for (let t = 0; t < s; t++) {
|
|
865
|
+
const i = e[t];
|
|
866
|
+
for (let n = t + 1; n < s; n++) {
|
|
867
|
+
const a = e[n], l = a.position.x - i.position.x, d = a.position.y - i.position.y, u = a.position.z - i.position.z;
|
|
868
|
+
let g = l * l + d * d + u * u;
|
|
869
|
+
g < 0.01 && (g = 0.01);
|
|
870
|
+
const b = Math.sqrt(g), v = this.repulsionStrength * this.alpha / g, y = l / b * v, f = d / b * v, M = u / b * v;
|
|
871
|
+
i.velocity.x -= y / i.mass, i.velocity.y -= f / i.mass, i.velocity.z -= M / i.mass, a.velocity.x += y / a.mass, a.velocity.y += f / a.mass, a.velocity.z += M / a.mass;
|
|
870
872
|
}
|
|
871
873
|
}
|
|
872
874
|
}
|
|
@@ -874,48 +876,48 @@ class Le {
|
|
|
874
876
|
* Barnes-Hut approximation - O(n log n)
|
|
875
877
|
*/
|
|
876
878
|
calculateRepulsionBarnesHut() {
|
|
877
|
-
const e = Array.from(this.nodes.values()),
|
|
879
|
+
const e = Array.from(this.nodes.values()), s = new yt(e);
|
|
878
880
|
for (const t of e)
|
|
879
|
-
this.calculateForceFromOctree(t,
|
|
881
|
+
this.calculateForceFromOctree(t, s.root);
|
|
880
882
|
}
|
|
881
883
|
/**
|
|
882
884
|
* Recursive force calculation using octree
|
|
883
885
|
*/
|
|
884
|
-
calculateForceFromOctree(e,
|
|
885
|
-
if (
|
|
886
|
-
|
|
886
|
+
calculateForceFromOctree(e, s) {
|
|
887
|
+
if (s.isLeaf) {
|
|
888
|
+
s.node && s.node.id !== e.id && this.applyRepulsionBetween(e, s.node);
|
|
887
889
|
return;
|
|
888
890
|
}
|
|
889
|
-
if (
|
|
890
|
-
const t =
|
|
891
|
-
if (
|
|
892
|
-
const l = Math.max(a * a, 0.01), d = this.repulsionStrength * this.alpha *
|
|
893
|
-
e.velocity.x -= t / a * d / e.mass, e.velocity.y -=
|
|
891
|
+
if (s.mass === 0) return;
|
|
892
|
+
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, n = s.centerOfMass.z - e.position.z, a = Math.sqrt(t * t + i * i + n * n);
|
|
893
|
+
if (s.size / a < this.barnesHutTheta) {
|
|
894
|
+
const l = Math.max(a * a, 0.01), d = this.repulsionStrength * this.alpha * s.mass / l;
|
|
895
|
+
e.velocity.x -= t / a * d / e.mass, e.velocity.y -= i / a * d / e.mass, e.velocity.z -= n / a * d / e.mass;
|
|
894
896
|
} else
|
|
895
|
-
for (const l of
|
|
897
|
+
for (const l of s.children)
|
|
896
898
|
l && this.calculateForceFromOctree(e, l);
|
|
897
899
|
}
|
|
898
900
|
/**
|
|
899
901
|
* Apply repulsion between two nodes
|
|
900
902
|
*/
|
|
901
|
-
applyRepulsionBetween(e,
|
|
902
|
-
const t =
|
|
903
|
-
let a = t * t +
|
|
903
|
+
applyRepulsionBetween(e, s) {
|
|
904
|
+
const t = s.position.x - e.position.x, i = s.position.y - e.position.y, n = s.position.z - e.position.z;
|
|
905
|
+
let a = t * t + i * i + n * n;
|
|
904
906
|
a < 0.01 && (a = 0.01);
|
|
905
907
|
const l = Math.sqrt(a), d = this.repulsionStrength * this.alpha / a;
|
|
906
|
-
e.velocity.x -= t / l * d / e.mass, e.velocity.y -=
|
|
908
|
+
e.velocity.x -= t / l * d / e.mass, e.velocity.y -= i / l * d / e.mass, e.velocity.z -= n / l * d / e.mass;
|
|
907
909
|
}
|
|
908
910
|
/**
|
|
909
911
|
* Calculate attraction forces along edges
|
|
910
912
|
*/
|
|
911
913
|
calculateAttraction() {
|
|
912
914
|
for (const e of this.edges) {
|
|
913
|
-
const
|
|
914
|
-
if (!
|
|
915
|
-
const
|
|
915
|
+
const s = this.nodes.get(e.source), t = this.nodes.get(e.target);
|
|
916
|
+
if (!s || !t) continue;
|
|
917
|
+
const i = t.position.x - s.position.x, n = t.position.y - s.position.y, a = t.position.z - s.position.z, l = Math.sqrt(i * i + n * n + a * a);
|
|
916
918
|
if (l < 0.01) continue;
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
+
const u = (l - 15) * this.attractionStrength * this.alpha, g = i / l * u, b = n / l * u, v = a / l * u;
|
|
920
|
+
s.velocity.x += g / s.mass, s.velocity.y += b / s.mass, s.velocity.z += v / s.mass, t.velocity.x -= g / t.mass, t.velocity.y -= b / t.mass, t.velocity.z -= v / t.mass;
|
|
919
921
|
}
|
|
920
922
|
}
|
|
921
923
|
/**
|
|
@@ -953,8 +955,8 @@ class Le {
|
|
|
953
955
|
class yt {
|
|
954
956
|
constructor(e) {
|
|
955
957
|
r(this, "root");
|
|
956
|
-
const
|
|
957
|
-
this.root = this.buildTree(e,
|
|
958
|
+
const s = this.calculateBounds(e);
|
|
959
|
+
this.root = this.buildTree(e, s);
|
|
958
960
|
}
|
|
959
961
|
calculateBounds(e) {
|
|
960
962
|
if (e.length === 0)
|
|
@@ -962,22 +964,22 @@ class yt {
|
|
|
962
964
|
min: { x: -100, y: -100, z: -100 },
|
|
963
965
|
max: { x: 100, y: 100, z: 100 }
|
|
964
966
|
};
|
|
965
|
-
const
|
|
966
|
-
for (const
|
|
967
|
-
|
|
968
|
-
const
|
|
969
|
-
return
|
|
970
|
-
}
|
|
971
|
-
buildTree(e,
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
967
|
+
const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
|
|
968
|
+
for (const n of e)
|
|
969
|
+
s.x = Math.min(s.x, n.position.x), s.y = Math.min(s.y, n.position.y), s.z = Math.min(s.z, n.position.z), t.x = Math.max(t.x, n.position.x), t.y = Math.max(t.y, n.position.y), t.z = Math.max(t.z, n.position.z);
|
|
970
|
+
const i = 10;
|
|
971
|
+
return s.x -= i, s.y -= i, s.z -= i, t.x += i, t.y += i, t.z += i, { min: s, max: t };
|
|
972
|
+
}
|
|
973
|
+
buildTree(e, s, t = 0) {
|
|
974
|
+
const i = Math.max(
|
|
975
|
+
s.max.x - s.min.x,
|
|
976
|
+
s.max.y - s.min.y,
|
|
977
|
+
s.max.z - s.min.z
|
|
976
978
|
);
|
|
977
979
|
if (e.length === 0)
|
|
978
980
|
return {
|
|
979
|
-
bounds:
|
|
980
|
-
size:
|
|
981
|
+
bounds: s,
|
|
982
|
+
size: i,
|
|
981
983
|
centerOfMass: { x: 0, y: 0, z: 0 },
|
|
982
984
|
mass: 0,
|
|
983
985
|
isLeaf: !0,
|
|
@@ -990,8 +992,8 @@ class yt {
|
|
|
990
992
|
for (const M of e)
|
|
991
993
|
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
994
|
return y > 0 && (f.x /= y, f.y /= y, f.z /= y), {
|
|
993
|
-
bounds:
|
|
994
|
-
size:
|
|
995
|
+
bounds: s,
|
|
996
|
+
size: i,
|
|
995
997
|
centerOfMass: f,
|
|
996
998
|
mass: y,
|
|
997
999
|
isLeaf: !0,
|
|
@@ -999,42 +1001,42 @@ class yt {
|
|
|
999
1001
|
children: []
|
|
1000
1002
|
};
|
|
1001
1003
|
}
|
|
1002
|
-
const
|
|
1004
|
+
const n = (s.min.x + s.max.x) / 2, a = (s.min.y + s.max.y) / 2, l = (s.min.z + s.max.z) / 2, d = [[], [], [], [], [], [], [], []];
|
|
1003
1005
|
for (const y of e) {
|
|
1004
|
-
const f = (y.position.x >=
|
|
1006
|
+
const f = (y.position.x >= n ? 1 : 0) + (y.position.y >= a ? 2 : 0) + (y.position.z >= l ? 4 : 0);
|
|
1005
1007
|
d[f].push(y);
|
|
1006
1008
|
}
|
|
1007
|
-
const
|
|
1008
|
-
{ min: { x:
|
|
1009
|
-
{ min: { x:
|
|
1010
|
-
{ min: { x:
|
|
1011
|
-
{ min: { x:
|
|
1012
|
-
{ min: { x:
|
|
1013
|
-
{ min: { x:
|
|
1014
|
-
{ min: { x:
|
|
1015
|
-
{ min: { x:
|
|
1016
|
-
],
|
|
1017
|
-
let
|
|
1009
|
+
const u = [
|
|
1010
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: n, y: a, z: l } },
|
|
1011
|
+
{ min: { x: n, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: l } },
|
|
1012
|
+
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x: n, y: s.max.y, z: l } },
|
|
1013
|
+
{ min: { x: n, y: a, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: l } },
|
|
1014
|
+
{ min: { x: s.min.x, y: s.min.y, z: l }, max: { x: n, y: a, z: s.max.z } },
|
|
1015
|
+
{ min: { x: n, y: s.min.y, z: l }, max: { x: s.max.x, y: a, z: s.max.z } },
|
|
1016
|
+
{ min: { x: s.min.x, y: a, z: l }, max: { x: n, y: s.max.y, z: s.max.z } },
|
|
1017
|
+
{ min: { x: n, y: a, z: l }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1018
|
+
], g = [];
|
|
1019
|
+
let b = 0;
|
|
1018
1020
|
const v = { x: 0, y: 0, z: 0 };
|
|
1019
1021
|
for (let y = 0; y < 8; y++)
|
|
1020
1022
|
if (d[y].length > 0) {
|
|
1021
|
-
const f = this.buildTree(d[y],
|
|
1022
|
-
|
|
1023
|
+
const f = this.buildTree(d[y], u[y], t + 1);
|
|
1024
|
+
g.push(f), b += f.mass, v.x += f.centerOfMass.x * f.mass, v.y += f.centerOfMass.y * f.mass, v.z += f.centerOfMass.z * f.mass;
|
|
1023
1025
|
} else
|
|
1024
|
-
|
|
1025
|
-
return
|
|
1026
|
-
bounds:
|
|
1027
|
-
size:
|
|
1026
|
+
g.push(null);
|
|
1027
|
+
return b > 0 && (v.x /= b, v.y /= b, v.z /= b), {
|
|
1028
|
+
bounds: s,
|
|
1029
|
+
size: i,
|
|
1028
1030
|
centerOfMass: v,
|
|
1029
|
-
mass:
|
|
1031
|
+
mass: b,
|
|
1030
1032
|
isLeaf: !1,
|
|
1031
1033
|
node: null,
|
|
1032
|
-
children:
|
|
1034
|
+
children: g
|
|
1033
1035
|
};
|
|
1034
1036
|
}
|
|
1035
1037
|
}
|
|
1036
|
-
class
|
|
1037
|
-
constructor(e,
|
|
1038
|
+
class bt {
|
|
1039
|
+
constructor(e, s, t, i = 60) {
|
|
1038
1040
|
r(this, "sceneManager");
|
|
1039
1041
|
r(this, "animationId", null);
|
|
1040
1042
|
r(this, "isRunning", !1);
|
|
@@ -1052,14 +1054,14 @@ class xt {
|
|
|
1052
1054
|
r(this, "animate", () => {
|
|
1053
1055
|
if (!this.isRunning) return;
|
|
1054
1056
|
this.animationId = requestAnimationFrame(this.animate);
|
|
1055
|
-
const e = performance.now(),
|
|
1056
|
-
if (
|
|
1057
|
+
const e = performance.now(), s = e - this.lastFrameTime;
|
|
1058
|
+
if (s < this.frameInterval)
|
|
1057
1059
|
return;
|
|
1058
|
-
this.lastFrameTime = e -
|
|
1060
|
+
this.lastFrameTime = e - s % this.frameInterval, this.frameCount++;
|
|
1059
1061
|
const t = e - this.fpsStartTime;
|
|
1060
1062
|
t >= 1e3 && (this.currentFPS = this.frameCount / (t / 1e3), this.frameCount = 0, this.fpsStartTime = e), this.onSimulate(), this.onRender(), this.sceneManager.render();
|
|
1061
1063
|
});
|
|
1062
|
-
this.sceneManager = e, this.onSimulate =
|
|
1064
|
+
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 / i;
|
|
1063
1065
|
}
|
|
1064
1066
|
/**
|
|
1065
1067
|
* Starts the animation loop
|
|
@@ -1098,7 +1100,7 @@ class xt {
|
|
|
1098
1100
|
this.stop();
|
|
1099
1101
|
}
|
|
1100
1102
|
}
|
|
1101
|
-
class
|
|
1103
|
+
class xt {
|
|
1102
1104
|
constructor() {
|
|
1103
1105
|
r(this, "envMap", null);
|
|
1104
1106
|
r(this, "materialCache", /* @__PURE__ */ new Map());
|
|
@@ -1121,7 +1123,7 @@ class bt {
|
|
|
1121
1123
|
* Creates environment map for glass reflections on dark background
|
|
1122
1124
|
*/
|
|
1123
1125
|
createEnvironmentMap() {
|
|
1124
|
-
const
|
|
1126
|
+
const s = [], t = [
|
|
1125
1127
|
// Subtle colored gradients for interesting reflections
|
|
1126
1128
|
{ colors: ["#1a1a2e", "#16213e", "#0f3460"] },
|
|
1127
1129
|
// +x
|
|
@@ -1136,10 +1138,10 @@ class bt {
|
|
|
1136
1138
|
{ colors: ["#2d1a1a", "#1a0a0a", "#0f0505"] }
|
|
1137
1139
|
// -z
|
|
1138
1140
|
];
|
|
1139
|
-
for (const
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
const a =
|
|
1141
|
+
for (const i of t) {
|
|
1142
|
+
const n = document.createElement("canvas");
|
|
1143
|
+
n.width = 256, n.height = 256;
|
|
1144
|
+
const a = n.getContext("2d"), l = a.createRadialGradient(
|
|
1143
1145
|
256 / 2,
|
|
1144
1146
|
256 / 2,
|
|
1145
1147
|
0,
|
|
@@ -1147,17 +1149,17 @@ class bt {
|
|
|
1147
1149
|
256 / 2,
|
|
1148
1150
|
256 * 0.8
|
|
1149
1151
|
);
|
|
1150
|
-
l.addColorStop(0,
|
|
1152
|
+
l.addColorStop(0, i.colors[0]), l.addColorStop(0.5, i.colors[1]), l.addColorStop(1, i.colors[2]), a.fillStyle = l, a.fillRect(0, 0, 256, 256);
|
|
1151
1153
|
const d = a.getImageData(0, 0, 256, 256);
|
|
1152
|
-
for (let
|
|
1153
|
-
const
|
|
1154
|
-
d.data[
|
|
1154
|
+
for (let u = 0; u < d.data.length; u += 4) {
|
|
1155
|
+
const g = (Math.random() - 0.5) * 5;
|
|
1156
|
+
d.data[u] = Math.min(255, Math.max(0, d.data[u] + g)), d.data[u + 1] = Math.min(255, Math.max(0, d.data[u + 1] + g)), d.data[u + 2] = Math.min(255, Math.max(0, d.data[u + 2] + g));
|
|
1155
1157
|
}
|
|
1156
|
-
a.putImageData(d, 0, 0),
|
|
1158
|
+
a.putImageData(d, 0, 0), s.push(n);
|
|
1157
1159
|
}
|
|
1158
|
-
this.envMap = new p.CubeTexture(
|
|
1159
|
-
const
|
|
1160
|
-
return
|
|
1160
|
+
this.envMap = new p.CubeTexture(s.map((i) => {
|
|
1161
|
+
const n = new Image();
|
|
1162
|
+
return n.src = i.toDataURL(), n;
|
|
1161
1163
|
})), this.envMap.needsUpdate = !0;
|
|
1162
1164
|
}
|
|
1163
1165
|
/**
|
|
@@ -1174,9 +1176,9 @@ class bt {
|
|
|
1174
1176
|
const t = "glass-single";
|
|
1175
1177
|
if (this.materialCache.has(t))
|
|
1176
1178
|
return this.materialCache.get(t).clone();
|
|
1177
|
-
const
|
|
1179
|
+
const i = new p.Color(16750950), n = new p.ShaderMaterial({
|
|
1178
1180
|
uniforms: {
|
|
1179
|
-
uColor: { value:
|
|
1181
|
+
uColor: { value: i },
|
|
1180
1182
|
uEnvMap: { value: this.envMap },
|
|
1181
1183
|
uGlowColor: { value: new p.Color(16777215) },
|
|
1182
1184
|
uGlowIntensity: { value: 0.8 },
|
|
@@ -1248,16 +1250,16 @@ class bt {
|
|
|
1248
1250
|
depthWrite: !0,
|
|
1249
1251
|
blending: p.NormalBlending
|
|
1250
1252
|
});
|
|
1251
|
-
return this.materialCache.set(t,
|
|
1253
|
+
return this.materialCache.set(t, n), n.clone();
|
|
1252
1254
|
}
|
|
1253
1255
|
/**
|
|
1254
1256
|
* Creates material for edges (light color for dark background)
|
|
1255
1257
|
*/
|
|
1256
|
-
createEdgeMaterial(e = 6710886,
|
|
1258
|
+
createEdgeMaterial(e = 6710886, s = 0.4) {
|
|
1257
1259
|
return new p.LineBasicMaterial({
|
|
1258
1260
|
color: e,
|
|
1259
1261
|
transparent: !0,
|
|
1260
|
-
opacity:
|
|
1262
|
+
opacity: s,
|
|
1261
1263
|
linewidth: 1
|
|
1262
1264
|
});
|
|
1263
1265
|
}
|
|
@@ -1276,11 +1278,11 @@ class bt {
|
|
|
1276
1278
|
/**
|
|
1277
1279
|
* Creates a sprite material for labels (light text for dark background)
|
|
1278
1280
|
*/
|
|
1279
|
-
createLabelMaterial(e,
|
|
1280
|
-
const t = document.createElement("canvas"),
|
|
1281
|
-
|
|
1282
|
-
const a =
|
|
1283
|
-
t.width = Math.max(128, a + 24), t.height =
|
|
1281
|
+
createLabelMaterial(e, s = 24) {
|
|
1282
|
+
const t = document.createElement("canvas"), i = t.getContext("2d");
|
|
1283
|
+
i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1284
|
+
const a = i.measureText(e).width;
|
|
1285
|
+
t.width = Math.max(128, a + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
|
|
1284
1286
|
const l = new p.CanvasTexture(t);
|
|
1285
1287
|
return l.needsUpdate = !0, new p.SpriteMaterial({
|
|
1286
1288
|
map: l,
|
|
@@ -1303,19 +1305,19 @@ class bt {
|
|
|
1303
1305
|
}
|
|
1304
1306
|
}
|
|
1305
1307
|
class vt {
|
|
1306
|
-
constructor(e,
|
|
1308
|
+
constructor(e, s = 2, t = [32, 16, 8]) {
|
|
1307
1309
|
r(this, "materialFactory");
|
|
1308
1310
|
r(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
1309
1311
|
r(this, "nodeRadius");
|
|
1310
1312
|
r(this, "lodSegments");
|
|
1311
|
-
this.materialFactory = e, this.nodeRadius =
|
|
1313
|
+
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.initGeometryCache();
|
|
1312
1314
|
}
|
|
1313
1315
|
/**
|
|
1314
1316
|
* Pre-create geometries for each LOD level
|
|
1315
1317
|
*/
|
|
1316
1318
|
initGeometryCache() {
|
|
1317
|
-
this.lodSegments.forEach((e,
|
|
1318
|
-
const t = `lod-${
|
|
1319
|
+
this.lodSegments.forEach((e, s) => {
|
|
1320
|
+
const t = `lod-${s}`;
|
|
1319
1321
|
this.geometryCache.set(
|
|
1320
1322
|
t,
|
|
1321
1323
|
new p.SphereGeometry(this.nodeRadius, e, e)
|
|
@@ -1326,18 +1328,18 @@ class vt {
|
|
|
1326
1328
|
* Gets geometry for the specified LOD level
|
|
1327
1329
|
*/
|
|
1328
1330
|
getGeometry(e) {
|
|
1329
|
-
const
|
|
1330
|
-
return this.geometryCache.has(
|
|
1331
|
+
const s = `lod-${e}`;
|
|
1332
|
+
return this.geometryCache.has(s) ? this.geometryCache.get(s) : this.geometryCache.get("lod-0");
|
|
1331
1333
|
}
|
|
1332
1334
|
/**
|
|
1333
1335
|
* Creates a node visual (glass ball + label)
|
|
1334
1336
|
*/
|
|
1335
|
-
createNode(e,
|
|
1337
|
+
createNode(e, s = 0) {
|
|
1336
1338
|
const t = new p.Group();
|
|
1337
1339
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1338
|
-
const
|
|
1340
|
+
const i = this.getGeometry(s), n = this.materialFactory.createGlassMaterial(
|
|
1339
1341
|
e.color ?? 4886754
|
|
1340
|
-
), a = new p.Mesh(
|
|
1342
|
+
), a = new p.Mesh(i, n);
|
|
1341
1343
|
a.castShadow = !0, a.receiveShadow = !0, t.add(a);
|
|
1342
1344
|
const l = this.materialFactory.createLabelMaterial(e.label), d = new p.Sprite(l);
|
|
1343
1345
|
return d.position.y = this.nodeRadius + 1.5, d.scale.set(4, 1, 1), t.add(d), e.position && t.position.set(
|
|
@@ -1348,28 +1350,28 @@ class vt {
|
|
|
1348
1350
|
group: t,
|
|
1349
1351
|
sphere: a,
|
|
1350
1352
|
label: d,
|
|
1351
|
-
lodLevel:
|
|
1353
|
+
lodLevel: s
|
|
1352
1354
|
};
|
|
1353
1355
|
}
|
|
1354
1356
|
/**
|
|
1355
1357
|
* Updates the LOD level of a node
|
|
1356
1358
|
*/
|
|
1357
|
-
updateNodeLOD(e,
|
|
1358
|
-
if (e.lodLevel ===
|
|
1359
|
-
const t = this.getGeometry(
|
|
1360
|
-
e.sphere.geometry = t, e.lodLevel =
|
|
1359
|
+
updateNodeLOD(e, s) {
|
|
1360
|
+
if (e.lodLevel === s) return;
|
|
1361
|
+
const t = this.getGeometry(s);
|
|
1362
|
+
e.sphere.geometry = t, e.lodLevel = s;
|
|
1361
1363
|
}
|
|
1362
1364
|
/**
|
|
1363
1365
|
* Updates the color of a node
|
|
1364
1366
|
*/
|
|
1365
|
-
updateNodeColor(e,
|
|
1366
|
-
e.sphere.material instanceof p.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(
|
|
1367
|
+
updateNodeColor(e, s) {
|
|
1368
|
+
e.sphere.material instanceof p.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
|
|
1367
1369
|
}
|
|
1368
1370
|
/**
|
|
1369
1371
|
* Updates the label of a node
|
|
1370
1372
|
*/
|
|
1371
|
-
updateNodeLabel(e,
|
|
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(
|
|
1373
|
+
updateNodeLabel(e, s) {
|
|
1374
|
+
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(s);
|
|
1373
1375
|
}
|
|
1374
1376
|
/**
|
|
1375
1377
|
* Disposes a node's resources
|
|
@@ -1385,13 +1387,13 @@ class vt {
|
|
|
1385
1387
|
}
|
|
1386
1388
|
}
|
|
1387
1389
|
class Mt {
|
|
1388
|
-
constructor(e,
|
|
1390
|
+
constructor(e, s = 10066329, t = 0.5) {
|
|
1389
1391
|
r(this, "materialFactory");
|
|
1390
1392
|
r(this, "edgeColor");
|
|
1391
1393
|
r(this, "edgeOpacity");
|
|
1392
1394
|
r(this, "defaultMaterial", null);
|
|
1393
1395
|
r(this, "highlightMaterial", null);
|
|
1394
|
-
this.materialFactory = e, this.edgeColor =
|
|
1396
|
+
this.materialFactory = e, this.edgeColor = s, this.edgeOpacity = t;
|
|
1395
1397
|
}
|
|
1396
1398
|
/**
|
|
1397
1399
|
* Gets or creates the default edge material
|
|
@@ -1411,25 +1413,25 @@ class Mt {
|
|
|
1411
1413
|
/**
|
|
1412
1414
|
* Creates an edge line between two positions
|
|
1413
1415
|
*/
|
|
1414
|
-
createEdge(e,
|
|
1416
|
+
createEdge(e, s, t, i, n) {
|
|
1415
1417
|
const a = new p.BufferGeometry(), l = new Float32Array([
|
|
1416
|
-
n.x,
|
|
1417
|
-
n.y,
|
|
1418
|
-
n.z,
|
|
1419
1418
|
i.x,
|
|
1420
1419
|
i.y,
|
|
1421
|
-
i.z
|
|
1420
|
+
i.z,
|
|
1421
|
+
n.x,
|
|
1422
|
+
n.y,
|
|
1423
|
+
n.z
|
|
1422
1424
|
]);
|
|
1423
1425
|
a.setAttribute("position", new p.BufferAttribute(l, 3));
|
|
1424
|
-
const d = this.getDefaultMaterial().clone(),
|
|
1425
|
-
return
|
|
1426
|
+
const d = this.getDefaultMaterial().clone(), u = new p.Line(a, d);
|
|
1427
|
+
return u.name = `edge-${e.source}-${e.target}`, u.userData = {
|
|
1426
1428
|
source: e.source,
|
|
1427
1429
|
target: e.target,
|
|
1428
1430
|
edge: e,
|
|
1429
|
-
sourceNode:
|
|
1431
|
+
sourceNode: s,
|
|
1430
1432
|
targetNode: t
|
|
1431
|
-
},
|
|
1432
|
-
line:
|
|
1433
|
+
}, u.frustumCulled = !0, {
|
|
1434
|
+
line: u,
|
|
1433
1435
|
source: e.source,
|
|
1434
1436
|
target: e.target
|
|
1435
1437
|
};
|
|
@@ -1449,9 +1451,9 @@ class Mt {
|
|
|
1449
1451
|
/**
|
|
1450
1452
|
* Updates an edge's positions
|
|
1451
1453
|
*/
|
|
1452
|
-
updateEdgePositions(e,
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1454
|
+
updateEdgePositions(e, s, t) {
|
|
1455
|
+
const i = e.line.geometry.attributes.position, n = i.array;
|
|
1456
|
+
n[0] = s.x, n[1] = s.y, n[2] = s.z, n[3] = t.x, n[4] = t.y, n[5] = t.z, i.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
|
|
1455
1457
|
}
|
|
1456
1458
|
/**
|
|
1457
1459
|
* Disposes an edge's resources
|
|
@@ -1466,28 +1468,28 @@ class Mt {
|
|
|
1466
1468
|
this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
|
|
1467
1469
|
}
|
|
1468
1470
|
}
|
|
1469
|
-
class
|
|
1470
|
-
constructor(e,
|
|
1471
|
+
class wt {
|
|
1472
|
+
constructor(e, s = [50, 100, 200], t = !0) {
|
|
1471
1473
|
r(this, "camera");
|
|
1472
1474
|
r(this, "lodDistances");
|
|
1473
1475
|
r(this, "enabled");
|
|
1474
|
-
this.camera = e, this.lodDistances =
|
|
1476
|
+
this.camera = e, this.lodDistances = s, this.enabled = t;
|
|
1475
1477
|
}
|
|
1476
1478
|
/**
|
|
1477
1479
|
* Gets the LOD level for a position based on distance from camera
|
|
1478
1480
|
*/
|
|
1479
1481
|
getLODLevel(e) {
|
|
1480
1482
|
if (!this.enabled)
|
|
1481
|
-
return
|
|
1482
|
-
const
|
|
1483
|
-
return
|
|
1483
|
+
return U.HIGH;
|
|
1484
|
+
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, n = Math.sqrt(s * s + t * t + i * i);
|
|
1485
|
+
return n < this.lodDistances[0] ? U.HIGH : n < this.lodDistances[1] ? U.MEDIUM : U.LOW;
|
|
1484
1486
|
}
|
|
1485
1487
|
/**
|
|
1486
1488
|
* Checks if a node should be visible based on distance
|
|
1487
1489
|
*/
|
|
1488
|
-
shouldRenderNode(e,
|
|
1489
|
-
const t = e.x - this.camera.position.x,
|
|
1490
|
-
return Math.sqrt(t * t +
|
|
1490
|
+
shouldRenderNode(e, s = 500) {
|
|
1491
|
+
const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y, n = e.z - this.camera.position.z;
|
|
1492
|
+
return Math.sqrt(t * t + i * i + n * n) < s;
|
|
1491
1493
|
}
|
|
1492
1494
|
/**
|
|
1493
1495
|
* Sets the LOD distances
|
|
@@ -1502,13 +1504,13 @@ class Et {
|
|
|
1502
1504
|
this.enabled = e;
|
|
1503
1505
|
}
|
|
1504
1506
|
}
|
|
1505
|
-
class
|
|
1506
|
-
constructor(e,
|
|
1507
|
+
class Et {
|
|
1508
|
+
constructor(e, s = !0) {
|
|
1507
1509
|
r(this, "camera");
|
|
1508
1510
|
r(this, "frustum");
|
|
1509
1511
|
r(this, "projScreenMatrix");
|
|
1510
1512
|
r(this, "enabled");
|
|
1511
|
-
this.camera = e, this.frustum = new p.Frustum(), this.projScreenMatrix = new p.Matrix4(), this.enabled =
|
|
1513
|
+
this.camera = e, this.frustum = new p.Frustum(), this.projScreenMatrix = new p.Matrix4(), this.enabled = s;
|
|
1512
1514
|
}
|
|
1513
1515
|
/**
|
|
1514
1516
|
* Updates the frustum from the camera
|
|
@@ -1524,33 +1526,33 @@ class wt {
|
|
|
1524
1526
|
*/
|
|
1525
1527
|
isPointVisible(e) {
|
|
1526
1528
|
if (!this.enabled) return !0;
|
|
1527
|
-
const
|
|
1528
|
-
return this.frustum.containsPoint(
|
|
1529
|
+
const s = new p.Vector3(e.x, e.y, e.z);
|
|
1530
|
+
return this.frustum.containsPoint(s);
|
|
1529
1531
|
}
|
|
1530
1532
|
/**
|
|
1531
1533
|
* Checks if a sphere is inside or intersects the frustum
|
|
1532
1534
|
*/
|
|
1533
|
-
isSphereVisible(e,
|
|
1535
|
+
isSphereVisible(e, s) {
|
|
1534
1536
|
if (!this.enabled) return !0;
|
|
1535
1537
|
const t = new p.Sphere(
|
|
1536
1538
|
new p.Vector3(e.x, e.y, e.z),
|
|
1537
|
-
|
|
1539
|
+
s
|
|
1538
1540
|
);
|
|
1539
1541
|
return this.frustum.intersectsSphere(t);
|
|
1540
1542
|
}
|
|
1541
1543
|
/**
|
|
1542
1544
|
* Checks if a line segment is potentially visible
|
|
1543
1545
|
*/
|
|
1544
|
-
isLineVisible(e,
|
|
1546
|
+
isLineVisible(e, s) {
|
|
1545
1547
|
if (!this.enabled) return !0;
|
|
1546
|
-
const t = new p.Vector3(e.x, e.y, e.z),
|
|
1547
|
-
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(
|
|
1548
|
+
const t = new p.Vector3(e.x, e.y, e.z), i = new p.Vector3(s.x, s.y, s.z);
|
|
1549
|
+
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
|
|
1548
1550
|
return !0;
|
|
1549
|
-
const
|
|
1550
|
-
(e.x +
|
|
1551
|
-
(e.y +
|
|
1552
|
-
(e.z +
|
|
1553
|
-
), a =
|
|
1551
|
+
const n = new p.Vector3(
|
|
1552
|
+
(e.x + s.x) / 2,
|
|
1553
|
+
(e.y + s.y) / 2,
|
|
1554
|
+
(e.z + s.z) / 2
|
|
1555
|
+
), a = n.distanceTo(t), l = new p.Sphere(n, a);
|
|
1554
1556
|
return this.frustum.intersectsSphere(l);
|
|
1555
1557
|
}
|
|
1556
1558
|
/**
|
|
@@ -1561,7 +1563,7 @@ class wt {
|
|
|
1561
1563
|
}
|
|
1562
1564
|
}
|
|
1563
1565
|
class Ct {
|
|
1564
|
-
constructor(e,
|
|
1566
|
+
constructor(e, s) {
|
|
1565
1567
|
r(this, "sceneManager");
|
|
1566
1568
|
r(this, "raycaster");
|
|
1567
1569
|
r(this, "mouse");
|
|
@@ -1573,7 +1575,7 @@ class Ct {
|
|
|
1573
1575
|
r(this, "hoveredEdgeKey", null);
|
|
1574
1576
|
r(this, "nodeObjects", []);
|
|
1575
1577
|
r(this, "edgeObjects", []);
|
|
1576
|
-
this.sceneManager = e, this.container =
|
|
1578
|
+
this.sceneManager = e, this.container = s, 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), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
|
|
1577
1579
|
}
|
|
1578
1580
|
/**
|
|
1579
1581
|
* Updates the list of node objects to raycast against
|
|
@@ -1609,35 +1611,35 @@ class Ct {
|
|
|
1609
1611
|
* Handles click events
|
|
1610
1612
|
*/
|
|
1611
1613
|
handleClick(e) {
|
|
1612
|
-
const
|
|
1613
|
-
|
|
1614
|
+
const s = this.getIntersectedNode(e);
|
|
1615
|
+
s && this.onNodeClick && this.onNodeClick(s);
|
|
1614
1616
|
}
|
|
1615
1617
|
/**
|
|
1616
1618
|
* Handles mouse move events for hover detection
|
|
1617
1619
|
*/
|
|
1618
1620
|
handleMouseMove(e) {
|
|
1619
|
-
const
|
|
1620
|
-
if (t !== this.hoveredNodeId && (this.hoveredNodeId = t, this.onNodeHover && this.onNodeHover(
|
|
1621
|
+
const s = this.getIntersectedNode(e), t = (s == null ? void 0 : s.id) ?? null;
|
|
1622
|
+
if (t !== this.hoveredNodeId && (this.hoveredNodeId = t, this.onNodeHover && this.onNodeHover(s)), s) {
|
|
1621
1623
|
this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
|
|
1622
1624
|
return;
|
|
1623
1625
|
}
|
|
1624
|
-
const
|
|
1625
|
-
|
|
1626
|
+
const i = this.getIntersectedEdge(e), n = i ? `${i.edge.source}-${i.edge.target}` : null;
|
|
1627
|
+
n !== this.hoveredEdgeKey && (this.hoveredEdgeKey = n, this.onEdgeHover && this.onEdgeHover(i)), this.container.style.cursor = i ? "pointer" : "default";
|
|
1626
1628
|
}
|
|
1627
1629
|
/**
|
|
1628
1630
|
* Gets the intersected node from a mouse event
|
|
1629
1631
|
*/
|
|
1630
1632
|
getIntersectedNode(e) {
|
|
1631
|
-
var
|
|
1632
|
-
const
|
|
1633
|
-
this.mouse.x = (e.clientX -
|
|
1633
|
+
var i;
|
|
1634
|
+
const s = this.container.getBoundingClientRect();
|
|
1635
|
+
this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1634
1636
|
const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1635
1637
|
if (t.length > 0) {
|
|
1636
|
-
let
|
|
1637
|
-
for (;
|
|
1638
|
-
if ((
|
|
1639
|
-
return
|
|
1640
|
-
|
|
1638
|
+
let n = t[0].object;
|
|
1639
|
+
for (; n; ) {
|
|
1640
|
+
if ((i = n.userData) != null && i.nodeData)
|
|
1641
|
+
return n.userData.nodeData;
|
|
1642
|
+
n = n.parent;
|
|
1641
1643
|
}
|
|
1642
1644
|
}
|
|
1643
1645
|
return null;
|
|
@@ -1646,17 +1648,17 @@ class Ct {
|
|
|
1646
1648
|
* Gets the intersected edge from a mouse event
|
|
1647
1649
|
*/
|
|
1648
1650
|
getIntersectedEdge(e) {
|
|
1649
|
-
const
|
|
1650
|
-
this.mouse.x = (e.clientX -
|
|
1651
|
+
const s = this.container.getBoundingClientRect();
|
|
1652
|
+
this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1651
1653
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1652
1654
|
if (t.length > 0) {
|
|
1653
|
-
const
|
|
1654
|
-
if (
|
|
1655
|
+
const i = t[0].object, n = i.userData;
|
|
1656
|
+
if (n != null && n.edge && (n != null && n.sourceNode) && (n != null && n.targetNode))
|
|
1655
1657
|
return {
|
|
1656
|
-
edge:
|
|
1657
|
-
sourceNode:
|
|
1658
|
-
targetNode:
|
|
1659
|
-
edgeLine:
|
|
1658
|
+
edge: n.edge,
|
|
1659
|
+
sourceNode: n.sourceNode,
|
|
1660
|
+
targetNode: n.targetNode,
|
|
1661
|
+
edgeLine: i
|
|
1660
1662
|
};
|
|
1661
1663
|
}
|
|
1662
1664
|
return null;
|
|
@@ -1664,15 +1666,15 @@ class Ct {
|
|
|
1664
1666
|
/**
|
|
1665
1667
|
* Performs a raycast and returns the intersected node ID
|
|
1666
1668
|
*/
|
|
1667
|
-
getIntersectedNodeId(e,
|
|
1668
|
-
var
|
|
1669
|
+
getIntersectedNodeId(e, s) {
|
|
1670
|
+
var n;
|
|
1669
1671
|
const t = this.container.getBoundingClientRect();
|
|
1670
|
-
this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((
|
|
1671
|
-
const
|
|
1672
|
-
if (
|
|
1673
|
-
let a =
|
|
1672
|
+
this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((s - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1673
|
+
const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1674
|
+
if (i.length > 0) {
|
|
1675
|
+
let a = i[0].object;
|
|
1674
1676
|
for (; a; ) {
|
|
1675
|
-
if ((
|
|
1677
|
+
if ((n = a.userData) != null && n.nodeId)
|
|
1676
1678
|
return a.userData.nodeId;
|
|
1677
1679
|
a = a.parent;
|
|
1678
1680
|
}
|
|
@@ -1724,8 +1726,8 @@ class Nt {
|
|
|
1724
1726
|
opacity: 0;
|
|
1725
1727
|
pointer-events: none;
|
|
1726
1728
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
1727
|
-
`, Object.entries(this.panelStyles).forEach(([e,
|
|
1728
|
-
this.panel.style.setProperty(e,
|
|
1729
|
+
`, Object.entries(this.panelStyles).forEach(([e, s]) => {
|
|
1730
|
+
this.panel.style.setProperty(e, s);
|
|
1729
1731
|
}), this.container.appendChild(this.panel);
|
|
1730
1732
|
}
|
|
1731
1733
|
/**
|
|
@@ -1738,8 +1740,8 @@ class Nt {
|
|
|
1738
1740
|
* Sets custom panel styles
|
|
1739
1741
|
*/
|
|
1740
1742
|
setPanelStyles(e) {
|
|
1741
|
-
this.panelStyles = e, this.panel && Object.entries(e).forEach(([
|
|
1742
|
-
this.panel.style.setProperty(
|
|
1743
|
+
this.panelStyles = e, this.panel && Object.entries(e).forEach(([s, t]) => {
|
|
1744
|
+
this.panel.style.setProperty(s, t);
|
|
1743
1745
|
});
|
|
1744
1746
|
}
|
|
1745
1747
|
/**
|
|
@@ -1751,15 +1753,15 @@ class Nt {
|
|
|
1751
1753
|
/**
|
|
1752
1754
|
* Shows the panel with node information
|
|
1753
1755
|
*/
|
|
1754
|
-
show(e,
|
|
1756
|
+
show(e, s) {
|
|
1755
1757
|
if (!this.panel) return;
|
|
1756
1758
|
this.currentNodeId = e.id;
|
|
1757
1759
|
let t;
|
|
1758
|
-
this.panelTemplate ? t = this.panelTemplate(e,
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1760
|
+
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
1761
|
+
const i = this.panel.querySelector('[data-action="expand"]'), n = this.panel.querySelector("[data-depth-select]");
|
|
1762
|
+
i && this.onExpand && i.addEventListener("click", () => {
|
|
1761
1763
|
if (this.currentNodeId) {
|
|
1762
|
-
const l =
|
|
1764
|
+
const l = n ? parseInt(n.value, 10) : 1;
|
|
1763
1765
|
this.onExpand(this.currentNodeId, l);
|
|
1764
1766
|
}
|
|
1765
1767
|
});
|
|
@@ -1771,7 +1773,7 @@ class Nt {
|
|
|
1771
1773
|
/**
|
|
1772
1774
|
* Generates default panel content
|
|
1773
1775
|
*/
|
|
1774
|
-
generateDefaultContent(e,
|
|
1776
|
+
generateDefaultContent(e, s) {
|
|
1775
1777
|
const t = e.color ? `#${e.color.toString(16).padStart(6, "0")}` : "#4A90E2";
|
|
1776
1778
|
return `
|
|
1777
1779
|
<style>
|
|
@@ -1783,6 +1785,13 @@ class Nt {
|
|
|
1783
1785
|
display: flex;
|
|
1784
1786
|
align-items: center;
|
|
1785
1787
|
gap: 10px;
|
|
1788
|
+
overflow: hidden;
|
|
1789
|
+
}
|
|
1790
|
+
.force-graph-panel .node-label-text {
|
|
1791
|
+
white-space: nowrap;
|
|
1792
|
+
overflow: hidden;
|
|
1793
|
+
text-overflow: ellipsis;
|
|
1794
|
+
flex: 1;
|
|
1786
1795
|
}
|
|
1787
1796
|
.force-graph-panel .color-dot {
|
|
1788
1797
|
width: 12px;
|
|
@@ -1807,6 +1816,11 @@ class Nt {
|
|
|
1807
1816
|
.force-graph-panel .info-value {
|
|
1808
1817
|
color: rgba(255, 255, 255, 0.95);
|
|
1809
1818
|
font-weight: 500;
|
|
1819
|
+
white-space: nowrap;
|
|
1820
|
+
overflow: hidden;
|
|
1821
|
+
text-overflow: ellipsis;
|
|
1822
|
+
max-width: 180px;
|
|
1823
|
+
text-align: right;
|
|
1810
1824
|
}
|
|
1811
1825
|
.force-graph-panel .neighbors-section {
|
|
1812
1826
|
margin-top: 16px;
|
|
@@ -1889,26 +1903,26 @@ class Nt {
|
|
|
1889
1903
|
|
|
1890
1904
|
<h2>
|
|
1891
1905
|
<span class="color-dot"></span>
|
|
1892
|
-
|
|
1906
|
+
<span class="node-label-text">${this.escapeHtml(e.label)}</span>
|
|
1893
1907
|
</h2>
|
|
1894
1908
|
|
|
1895
1909
|
<div class="info-row">
|
|
1896
1910
|
<span class="info-label">ID</span>
|
|
1897
|
-
<span class="info-value">${this.escapeHtml(e.id)}</span>
|
|
1911
|
+
<span class="info-value" title="${this.escapeHtml(e.id)}">${this.escapeHtml(e.id)}</span>
|
|
1898
1912
|
</div>
|
|
1899
1913
|
|
|
1900
1914
|
<div class="info-row">
|
|
1901
1915
|
<span class="info-label">Connections</span>
|
|
1902
|
-
<span class="info-value">${
|
|
1916
|
+
<span class="info-value">${s.length}</span>
|
|
1903
1917
|
</div>
|
|
1904
1918
|
|
|
1905
|
-
${
|
|
1919
|
+
${s.length > 0 ? `
|
|
1906
1920
|
<div class="neighbors-section">
|
|
1907
1921
|
<div class="neighbors-title">Connected To</div>
|
|
1908
|
-
${
|
|
1909
|
-
(
|
|
1922
|
+
${s.slice(0, 5).map(
|
|
1923
|
+
(i) => `<span class="neighbor-chip">${this.escapeHtml(i.label)}</span>`
|
|
1910
1924
|
).join("")}
|
|
1911
|
-
${
|
|
1925
|
+
${s.length > 5 ? `<span class="neighbor-chip">+${s.length - 5} more</span>` : ""}
|
|
1912
1926
|
</div>
|
|
1913
1927
|
` : ""}
|
|
1914
1928
|
|
|
@@ -1932,8 +1946,8 @@ class Nt {
|
|
|
1932
1946
|
* Escapes HTML to prevent XSS
|
|
1933
1947
|
*/
|
|
1934
1948
|
escapeHtml(e) {
|
|
1935
|
-
const
|
|
1936
|
-
return
|
|
1949
|
+
const s = document.createElement("div");
|
|
1950
|
+
return s.textContent = e, s.innerHTML;
|
|
1937
1951
|
}
|
|
1938
1952
|
/**
|
|
1939
1953
|
* Hides the panel
|
|
@@ -1960,7 +1974,7 @@ class Nt {
|
|
|
1960
1974
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
1961
1975
|
}
|
|
1962
1976
|
}
|
|
1963
|
-
class
|
|
1977
|
+
class St {
|
|
1964
1978
|
constructor() {
|
|
1965
1979
|
r(this, "tooltip", null);
|
|
1966
1980
|
r(this, "visible", !1);
|
|
@@ -2004,22 +2018,22 @@ class zt {
|
|
|
2004
2018
|
/**
|
|
2005
2019
|
* Positions the tooltip near the mouse
|
|
2006
2020
|
*/
|
|
2007
|
-
positionTooltip(e,
|
|
2021
|
+
positionTooltip(e, s) {
|
|
2008
2022
|
if (!this.tooltip) return;
|
|
2009
|
-
const t = this.tooltip.getBoundingClientRect(),
|
|
2010
|
-
let a = e + 15, l =
|
|
2011
|
-
a + t.width >
|
|
2023
|
+
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, n = window.innerHeight;
|
|
2024
|
+
let a = e + 15, l = s + 15;
|
|
2025
|
+
a + t.width > i - 10 && (a = e - t.width - 15), l + t.height > n - 10 && (l = s - t.height - 15), a < 10 && (a = 10), l < 10 && (l = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${l}px`;
|
|
2012
2026
|
}
|
|
2013
2027
|
/**
|
|
2014
2028
|
* Shows the tooltip with edge info
|
|
2015
2029
|
*/
|
|
2016
|
-
show(e,
|
|
2030
|
+
show(e, s, t, i, n) {
|
|
2017
2031
|
if (!this.tooltip) return;
|
|
2018
2032
|
const a = e.relationship || "connected to";
|
|
2019
2033
|
this.tooltip.innerHTML = `
|
|
2020
2034
|
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
2021
2035
|
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2022
|
-
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(
|
|
2036
|
+
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
|
|
2023
2037
|
</div>
|
|
2024
2038
|
<div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
|
|
2025
2039
|
↳ ${this.escapeHtml(a)}
|
|
@@ -2028,13 +2042,13 @@ class zt {
|
|
|
2028
2042
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
2029
2043
|
</div>
|
|
2030
2044
|
</div>
|
|
2031
|
-
`, this.positionTooltip(
|
|
2045
|
+
`, this.positionTooltip(i, n), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2032
2046
|
}
|
|
2033
2047
|
/**
|
|
2034
2048
|
* Updates tooltip position (called externally on mouse move)
|
|
2035
2049
|
*/
|
|
2036
|
-
updatePosition(e,
|
|
2037
|
-
this.visible && this.positionTooltip(e,
|
|
2050
|
+
updatePosition(e, s) {
|
|
2051
|
+
this.visible && this.positionTooltip(e, s);
|
|
2038
2052
|
}
|
|
2039
2053
|
/**
|
|
2040
2054
|
* Hides the tooltip
|
|
@@ -2052,8 +2066,8 @@ class zt {
|
|
|
2052
2066
|
* Escapes HTML to prevent XSS
|
|
2053
2067
|
*/
|
|
2054
2068
|
escapeHtml(e) {
|
|
2055
|
-
const
|
|
2056
|
-
return
|
|
2069
|
+
const s = document.createElement("div");
|
|
2070
|
+
return s.textContent = e, s.innerHTML;
|
|
2057
2071
|
}
|
|
2058
2072
|
/**
|
|
2059
2073
|
* Dispose the tooltip
|
|
@@ -2062,8 +2076,181 @@ class zt {
|
|
|
2062
2076
|
this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
|
|
2063
2077
|
}
|
|
2064
2078
|
}
|
|
2079
|
+
class zt {
|
|
2080
|
+
constructor(e, s) {
|
|
2081
|
+
r(this, "container");
|
|
2082
|
+
r(this, "searchContainer", null);
|
|
2083
|
+
r(this, "searchInput", null);
|
|
2084
|
+
r(this, "searchResults", null);
|
|
2085
|
+
r(this, "searchTimeout", null);
|
|
2086
|
+
r(this, "placeholder");
|
|
2087
|
+
r(this, "onSearch");
|
|
2088
|
+
r(this, "onResultClick");
|
|
2089
|
+
this.container = e, this.placeholder = s.placeholder || "Search nodes or relationships...", this.onSearch = s.onSearch, this.onResultClick = s.onResultClick, this.init();
|
|
2090
|
+
}
|
|
2091
|
+
init() {
|
|
2092
|
+
this.createSearchUI(), this.addEventListeners();
|
|
2093
|
+
}
|
|
2094
|
+
createSearchUI() {
|
|
2095
|
+
this.searchContainer = document.createElement("div"), this.searchContainer.className = "f3d-search-container", Object.assign(this.searchContainer.style, {
|
|
2096
|
+
position: "absolute",
|
|
2097
|
+
top: "20px",
|
|
2098
|
+
left: "20px",
|
|
2099
|
+
zIndex: "100",
|
|
2100
|
+
width: "320px"
|
|
2101
|
+
});
|
|
2102
|
+
const e = document.createElement("div");
|
|
2103
|
+
e.className = "f3d-search-input-wrapper", Object.assign(e.style, {
|
|
2104
|
+
position: "relative",
|
|
2105
|
+
display: "flex",
|
|
2106
|
+
alignItems: "center"
|
|
2107
|
+
}), e.innerHTML = `
|
|
2108
|
+
<svg class="f3d-search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
2109
|
+
stroke-width="2" style="position: absolute; left: 14px; z-index: 10; color: rgba(255, 255, 255, 0.5); pointer-events: none;">
|
|
2110
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
2111
|
+
<path d="m21 21-4.35-4.35"></path>
|
|
2112
|
+
</svg>
|
|
2113
|
+
`, this.searchInput = document.createElement("input"), this.searchInput.type = "text", this.searchInput.className = "f3d-search-input", this.searchInput.placeholder = this.placeholder, this.searchInput.autocomplete = "off", Object.assign(this.searchInput.style, {
|
|
2114
|
+
width: "100%",
|
|
2115
|
+
padding: "12px 16px 12px 42px",
|
|
2116
|
+
background: "rgba(255, 255, 255, 0.08)",
|
|
2117
|
+
backdropFilter: "blur(20px)",
|
|
2118
|
+
webkitBackdropFilter: "blur(20px)",
|
|
2119
|
+
border: "1px solid rgba(255, 255, 255, 0.12)",
|
|
2120
|
+
borderRadius: "12px",
|
|
2121
|
+
color: "white",
|
|
2122
|
+
fontSize: "14px",
|
|
2123
|
+
fontFamily: "inherit",
|
|
2124
|
+
outline: "none",
|
|
2125
|
+
transition: "all 0.2s ease"
|
|
2126
|
+
}), this.searchInput.addEventListener("focus", () => {
|
|
2127
|
+
this.searchInput && (this.searchInput.style.background = "rgba(255, 255, 255, 0.12)", this.searchInput.style.borderColor = "rgba(255, 153, 102, 0.5)", this.searchInput.style.boxShadow = "0 0 20px rgba(255, 153, 102, 0.15)");
|
|
2128
|
+
}), this.searchInput.addEventListener("blur", () => {
|
|
2129
|
+
this.searchInput && (this.searchInput.style.background = "rgba(255, 255, 255, 0.08)", this.searchInput.style.borderColor = "rgba(255, 255, 255, 0.12)", this.searchInput.style.boxShadow = "none");
|
|
2130
|
+
}), this.searchResults = document.createElement("div"), this.searchResults.className = "f3d-search-results", Object.assign(this.searchResults.style, {
|
|
2131
|
+
position: "absolute",
|
|
2132
|
+
top: "100%",
|
|
2133
|
+
left: "0",
|
|
2134
|
+
right: "0",
|
|
2135
|
+
marginTop: "8px",
|
|
2136
|
+
background: "rgba(20, 20, 25, 0.95)",
|
|
2137
|
+
backdropFilter: "blur(20px)",
|
|
2138
|
+
webkitBackdropFilter: "blur(20px)",
|
|
2139
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2140
|
+
borderRadius: "12px",
|
|
2141
|
+
maxHeight: "300px",
|
|
2142
|
+
overflowY: "auto",
|
|
2143
|
+
display: "none",
|
|
2144
|
+
boxShadow: "0 10px 40px rgba(0, 0, 0, 0.4)"
|
|
2145
|
+
}), e.appendChild(this.searchInput), this.searchContainer.appendChild(e), this.searchContainer.appendChild(this.searchResults), this.container.appendChild(this.searchContainer);
|
|
2146
|
+
const s = document.createElement("style");
|
|
2147
|
+
s.textContent = `
|
|
2148
|
+
.f3d-search-result-item {
|
|
2149
|
+
padding: 10px 14px;
|
|
2150
|
+
cursor: pointer;
|
|
2151
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
2152
|
+
transition: background 0.15s ease;
|
|
2153
|
+
}
|
|
2154
|
+
.f3d-search-result-item:hover {
|
|
2155
|
+
background: rgba(255, 153, 102, 0.15);
|
|
2156
|
+
}
|
|
2157
|
+
.f3d-search-result-item:last-child {
|
|
2158
|
+
border-bottom: none;
|
|
2159
|
+
}
|
|
2160
|
+
.f3d-result-label {
|
|
2161
|
+
color: white;
|
|
2162
|
+
font-size: 13px;
|
|
2163
|
+
font-weight: 500;
|
|
2164
|
+
margin-bottom: 2px;
|
|
2165
|
+
}
|
|
2166
|
+
.f3d-result-type {
|
|
2167
|
+
color: rgba(255, 153, 102, 0.8);
|
|
2168
|
+
font-size: 11px;
|
|
2169
|
+
text-transform: uppercase;
|
|
2170
|
+
letter-spacing: 0.5px;
|
|
2171
|
+
}
|
|
2172
|
+
.f3d-result-relationship {
|
|
2173
|
+
color: rgba(255, 255, 255, 0.5);
|
|
2174
|
+
font-size: 11px;
|
|
2175
|
+
font-style: italic;
|
|
2176
|
+
}
|
|
2177
|
+
.f3d-no-results {
|
|
2178
|
+
padding: 14px;
|
|
2179
|
+
color: rgba(255, 255, 255, 0.4);
|
|
2180
|
+
font-size: 13px;
|
|
2181
|
+
text-align: center;
|
|
2182
|
+
}
|
|
2183
|
+
.f3d-search-section-header {
|
|
2184
|
+
padding: 8px 14px 6px;
|
|
2185
|
+
color: rgba(255, 255, 255, 0.3);
|
|
2186
|
+
font-size: 10px;
|
|
2187
|
+
text-transform: uppercase;
|
|
2188
|
+
letter-spacing: 1px;
|
|
2189
|
+
font-weight: 600;
|
|
2190
|
+
background: rgba(0, 0, 0, 0.2);
|
|
2191
|
+
}
|
|
2192
|
+
.f3d-search-results::-webkit-scrollbar { width: 6px; }
|
|
2193
|
+
.f3d-search-results::-webkit-scrollbar-track { background: transparent; }
|
|
2194
|
+
.f3d-search-results::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
|
|
2195
|
+
.f3d-search-results::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); }
|
|
2196
|
+
`, document.head.appendChild(s);
|
|
2197
|
+
}
|
|
2198
|
+
addEventListeners() {
|
|
2199
|
+
!this.searchInput || !this.searchResults || (this.searchInput.addEventListener("input", (e) => {
|
|
2200
|
+
const s = e.target.value;
|
|
2201
|
+
this.searchTimeout && window.clearTimeout(this.searchTimeout), this.searchTimeout = window.setTimeout(() => {
|
|
2202
|
+
this.performSearch(s);
|
|
2203
|
+
}, 150);
|
|
2204
|
+
}), this.searchInput.addEventListener("focus", () => {
|
|
2205
|
+
this.searchInput && this.searchInput.value.length > 0 && (this.searchResults.style.display = "block");
|
|
2206
|
+
}), document.addEventListener("click", (e) => {
|
|
2207
|
+
this.searchContainer && !this.searchContainer.contains(e.target) && this.searchResults && (this.searchResults.style.display = "none");
|
|
2208
|
+
}));
|
|
2209
|
+
}
|
|
2210
|
+
performSearch(e) {
|
|
2211
|
+
if (!this.searchResults) return;
|
|
2212
|
+
if (e.trim().length === 0) {
|
|
2213
|
+
this.searchResults.style.display = "none";
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
const { nodeResults: s, edgeResults: t } = this.onSearch(e);
|
|
2217
|
+
if (s.length === 0 && t.length === 0) {
|
|
2218
|
+
this.searchResults.innerHTML = '<div class="f3d-no-results">No results found</div>', this.searchResults.style.display = "block";
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
let i = "";
|
|
2222
|
+
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((n) => {
|
|
2223
|
+
const a = n.type || "Node";
|
|
2224
|
+
i += `
|
|
2225
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.id)}">
|
|
2226
|
+
<div class="f3d-result-label">${this.escapeHtml(n.label)}</div>
|
|
2227
|
+
<div class="f3d-result-type">${this.escapeHtml(a)}</div>
|
|
2228
|
+
</div>
|
|
2229
|
+
`;
|
|
2230
|
+
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: n, sourceNode: a, targetNode: l }) => {
|
|
2231
|
+
i += `
|
|
2232
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.source)}">
|
|
2233
|
+
<div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(l.label)}</div>
|
|
2234
|
+
<div class="f3d-result-relationship">${this.escapeHtml(n.relationship || "connected")}</div>
|
|
2235
|
+
</div>
|
|
2236
|
+
`;
|
|
2237
|
+
}), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((n) => {
|
|
2238
|
+
n.addEventListener("click", () => {
|
|
2239
|
+
const a = n.dataset.nodeId;
|
|
2240
|
+
a && (this.onResultClick(a), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2241
|
+
});
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
escapeHtml(e) {
|
|
2245
|
+
const s = document.createElement("div");
|
|
2246
|
+
return s.textContent = e, s.innerHTML;
|
|
2247
|
+
}
|
|
2248
|
+
dispose() {
|
|
2249
|
+
this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2065
2252
|
class Ot {
|
|
2066
|
-
constructor(e,
|
|
2253
|
+
constructor(e, s = {}) {
|
|
2067
2254
|
// Options
|
|
2068
2255
|
r(this, "options");
|
|
2069
2256
|
r(this, "container");
|
|
@@ -2084,27 +2271,28 @@ class Ot {
|
|
|
2084
2271
|
r(this, "raycasterManager");
|
|
2085
2272
|
r(this, "panelManager");
|
|
2086
2273
|
r(this, "edgeTooltipManager");
|
|
2274
|
+
r(this, "searchManager", null);
|
|
2087
2275
|
// Event system
|
|
2088
2276
|
r(this, "eventCallbacks", /* @__PURE__ */ new Map());
|
|
2089
2277
|
// State
|
|
2090
2278
|
r(this, "initialized", !1);
|
|
2091
2279
|
r(this, "devControls", null);
|
|
2092
|
-
this.options = { ...
|
|
2280
|
+
this.options = { ...L, ...s }, this.container = ct(e), this.materialFactory = new xt(), this.nodeFactory = new vt(
|
|
2093
2281
|
this.materialFactory,
|
|
2094
|
-
this.options.nodeRadius ??
|
|
2095
|
-
this.options.lodSegments ??
|
|
2282
|
+
this.options.nodeRadius ?? L.nodeRadius,
|
|
2283
|
+
this.options.lodSegments ?? L.lodSegments
|
|
2096
2284
|
), this.edgeFactory = new Mt(
|
|
2097
2285
|
this.materialFactory,
|
|
2098
|
-
this.options.edgeColor ??
|
|
2099
|
-
this.options.edgeOpacity ??
|
|
2100
|
-
), this.sceneManager = new
|
|
2286
|
+
this.options.edgeColor ?? L.edgeColor,
|
|
2287
|
+
this.options.edgeOpacity ?? L.edgeOpacity
|
|
2288
|
+
), this.sceneManager = new gt(this.container, this.options), this.lodManager = new wt(
|
|
2101
2289
|
this.sceneManager.camera,
|
|
2102
|
-
this.options.lodDistances ??
|
|
2103
|
-
this.options.enableLOD ??
|
|
2104
|
-
), this.frustumCuller = new
|
|
2290
|
+
this.options.lodDistances ?? L.lodDistances,
|
|
2291
|
+
this.options.enableLOD ?? L.enableLOD
|
|
2292
|
+
), this.frustumCuller = new Et(
|
|
2105
2293
|
this.sceneManager.camera,
|
|
2106
|
-
this.options.enableEdgeCulling ??
|
|
2107
|
-
), this.nodeManager = new mt(this.sceneManager, this.nodeFactory), this.edgeManager = new ft(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new
|
|
2294
|
+
this.options.enableEdgeCulling ?? L.enableEdgeCulling
|
|
2295
|
+
), this.nodeManager = new mt(this.sceneManager, this.nodeFactory), this.edgeManager = new ft(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Oe(
|
|
2108
2296
|
this.nodeManager.getAllNodes(),
|
|
2109
2297
|
this.edgeManager.getAllEdges(),
|
|
2110
2298
|
{
|
|
@@ -2114,12 +2302,23 @@ class Ot {
|
|
|
2114
2302
|
useBarnesHut: this.options.useBarnesHut,
|
|
2115
2303
|
barnesHutTheta: this.options.barnesHutTheta
|
|
2116
2304
|
}
|
|
2117
|
-
), this.rendererManager = new
|
|
2305
|
+
), this.rendererManager = new bt(
|
|
2118
2306
|
this.sceneManager,
|
|
2119
2307
|
() => this.onSimulate(),
|
|
2120
2308
|
() => this.onRender(),
|
|
2121
|
-
this.options.targetFPS ??
|
|
2122
|
-
), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgeTooltipManager = new
|
|
2309
|
+
this.options.targetFPS ?? L.targetFPS
|
|
2310
|
+
), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgeTooltipManager = new St(), this.options.showSearch !== !1 && (this.searchManager = new zt(this.container, {
|
|
2311
|
+
placeholder: this.options.searchPlaceholder,
|
|
2312
|
+
onSearch: (t) => ({
|
|
2313
|
+
nodeResults: this.searchNodes(t),
|
|
2314
|
+
edgeResults: this.searchEdges(t)
|
|
2315
|
+
}),
|
|
2316
|
+
onResultClick: (t) => {
|
|
2317
|
+
this.focusOnNode(t), setTimeout(() => {
|
|
2318
|
+
this.showNodePanel(t);
|
|
2319
|
+
}, 400);
|
|
2320
|
+
}
|
|
2321
|
+
})), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
2123
2322
|
}
|
|
2124
2323
|
/**
|
|
2125
2324
|
* Sets up internal callbacks
|
|
@@ -2127,8 +2326,8 @@ class Ot {
|
|
|
2127
2326
|
setupCallbacks() {
|
|
2128
2327
|
this.raycasterManager.setClickCallback((e) => {
|
|
2129
2328
|
this.onNodeClick(e);
|
|
2130
|
-
}), this.options.onNodeHover && this.raycasterManager.setHoverCallback(this.options.onNodeHover), this.panelManager.setExpandCallback((e,
|
|
2131
|
-
this.expandNode(e,
|
|
2329
|
+
}), this.options.onNodeHover && this.raycasterManager.setHoverCallback(this.options.onNodeHover), this.panelManager.setExpandCallback((e, s) => {
|
|
2330
|
+
this.expandNode(e, s);
|
|
2132
2331
|
}), this.options.panelTemplate && this.panelManager.setPanelTemplate(this.options.panelTemplate), this.options.panelStyles && this.panelManager.setPanelStyles(this.options.panelStyles), this.raycasterManager.setEdgeHoverCallback((e) => {
|
|
2133
2332
|
this.onEdgeHover(e);
|
|
2134
2333
|
});
|
|
@@ -2139,8 +2338,8 @@ class Ot {
|
|
|
2139
2338
|
onEdgeHover(e) {
|
|
2140
2339
|
if (e) {
|
|
2141
2340
|
this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
|
|
2142
|
-
const
|
|
2143
|
-
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t,
|
|
2341
|
+
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, i = s.top + s.height / 2;
|
|
2342
|
+
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, i), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
|
|
2144
2343
|
} else
|
|
2145
2344
|
this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
|
|
2146
2345
|
}
|
|
@@ -2148,7 +2347,7 @@ class Ot {
|
|
|
2148
2347
|
* Handles node click
|
|
2149
2348
|
*/
|
|
2150
2349
|
onNodeClick(e) {
|
|
2151
|
-
const t = this.edgeManager.getNeighborIds(e.id).map((
|
|
2350
|
+
const t = this.edgeManager.getNeighborIds(e.id).map((i) => this.nodeManager.getNode(i)).filter((i) => i !== void 0);
|
|
2152
2351
|
this.options.showPanel !== !1 && this.panelManager.show(e, t), this.options.onNodeClick && this.options.onNodeClick(e), this.emit("nodeClick", e);
|
|
2153
2352
|
}
|
|
2154
2353
|
/**
|
|
@@ -2156,9 +2355,9 @@ class Ot {
|
|
|
2156
2355
|
*/
|
|
2157
2356
|
onSimulate() {
|
|
2158
2357
|
this.graphEngine.simulate();
|
|
2159
|
-
for (const [e,
|
|
2160
|
-
if (this.nodeManager.updateNodePosition(e,
|
|
2161
|
-
const t = this.lodManager.getLODLevel(
|
|
2358
|
+
for (const [e, s] of this.nodeManager.getAllNodes())
|
|
2359
|
+
if (this.nodeManager.updateNodePosition(e, s.position), this.options.enableLOD) {
|
|
2360
|
+
const t = this.lodManager.getLODLevel(s.position);
|
|
2162
2361
|
this.nodeManager.updateNodeLOD(e, t);
|
|
2163
2362
|
}
|
|
2164
2363
|
this.edgeManager.updateEdgePositions();
|
|
@@ -2177,12 +2376,12 @@ class Ot {
|
|
|
2177
2376
|
*/
|
|
2178
2377
|
setData(e) {
|
|
2179
2378
|
if (this.edgeManager.clear(), this.nodeManager.clear(), e.nodes && Array.isArray(e.nodes))
|
|
2180
|
-
for (const
|
|
2181
|
-
this.addNode(
|
|
2379
|
+
for (const s of e.nodes)
|
|
2380
|
+
this.addNode(s);
|
|
2182
2381
|
if (e.edges && Array.isArray(e.edges))
|
|
2183
|
-
for (const
|
|
2184
|
-
this.addEdge(
|
|
2185
|
-
this.graphEngine = new
|
|
2382
|
+
for (const s of e.edges)
|
|
2383
|
+
this.addEdge(s);
|
|
2384
|
+
this.graphEngine = new Oe(
|
|
2186
2385
|
this.nodeManager.getAllNodes(),
|
|
2187
2386
|
this.edgeManager.getAllEdges(),
|
|
2188
2387
|
{
|
|
@@ -2199,10 +2398,10 @@ class Ot {
|
|
|
2199
2398
|
* @returns true if added, false if node already exists or invalid
|
|
2200
2399
|
*/
|
|
2201
2400
|
addNode(e) {
|
|
2202
|
-
if (!
|
|
2401
|
+
if (!Re(e))
|
|
2203
2402
|
return !1;
|
|
2204
|
-
const
|
|
2205
|
-
return
|
|
2403
|
+
const s = this.nodeManager.addNode(e);
|
|
2404
|
+
return s && (this.graphEngine.restart(), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
|
|
2206
2405
|
}
|
|
2207
2406
|
/**
|
|
2208
2407
|
* Removes a node from the graph
|
|
@@ -2212,32 +2411,32 @@ class Ot {
|
|
|
2212
2411
|
if (!ht(e))
|
|
2213
2412
|
return !1;
|
|
2214
2413
|
this.edgeManager.removeEdgesForNode(e);
|
|
2215
|
-
const
|
|
2216
|
-
return
|
|
2414
|
+
const s = this.nodeManager.removeNode(e);
|
|
2415
|
+
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide()), s;
|
|
2217
2416
|
}
|
|
2218
2417
|
/**
|
|
2219
2418
|
* Updates a node's properties
|
|
2220
2419
|
*/
|
|
2221
|
-
updateNode(e,
|
|
2222
|
-
return this.nodeManager.updateNode(e,
|
|
2420
|
+
updateNode(e, s) {
|
|
2421
|
+
return this.nodeManager.updateNode(e, s);
|
|
2223
2422
|
}
|
|
2224
2423
|
/**
|
|
2225
2424
|
* Adds an edge to the graph
|
|
2226
2425
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
2227
2426
|
*/
|
|
2228
2427
|
addEdge(e) {
|
|
2229
|
-
if (!
|
|
2428
|
+
if (!Fe(e))
|
|
2230
2429
|
return !1;
|
|
2231
|
-
const
|
|
2232
|
-
return
|
|
2430
|
+
const s = this.edgeManager.addEdge(e);
|
|
2431
|
+
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), s;
|
|
2233
2432
|
}
|
|
2234
2433
|
/**
|
|
2235
2434
|
* Removes an edge from the graph
|
|
2236
2435
|
* @returns true if removed, false if not found
|
|
2237
2436
|
*/
|
|
2238
|
-
removeEdge(e,
|
|
2239
|
-
const t = this.edgeManager.removeEdge(e,
|
|
2240
|
-
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target:
|
|
2437
|
+
removeEdge(e, s) {
|
|
2438
|
+
const t = this.edgeManager.removeEdge(e, s);
|
|
2439
|
+
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s })), t;
|
|
2241
2440
|
}
|
|
2242
2441
|
/**
|
|
2243
2442
|
* Expands a node by fetching more data
|
|
@@ -2245,21 +2444,21 @@ class Ot {
|
|
|
2245
2444
|
* @param depth - The depth of expansion (1-3 levels, default 1)
|
|
2246
2445
|
* @param fetchFn - Optional fetch function to override the default
|
|
2247
2446
|
*/
|
|
2248
|
-
async expandNode(e,
|
|
2249
|
-
const
|
|
2250
|
-
if (!
|
|
2447
|
+
async expandNode(e, s = 1, t) {
|
|
2448
|
+
const i = t ?? this.options.onExpand;
|
|
2449
|
+
if (!i)
|
|
2251
2450
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
2252
2451
|
try {
|
|
2253
|
-
const
|
|
2254
|
-
if (
|
|
2255
|
-
for (const a of
|
|
2452
|
+
const n = await i(e, s);
|
|
2453
|
+
if (n.nodes && Array.isArray(n.nodes))
|
|
2454
|
+
for (const a of n.nodes)
|
|
2256
2455
|
this.addNode(a);
|
|
2257
|
-
if (
|
|
2258
|
-
for (const a of
|
|
2456
|
+
if (n.edges && Array.isArray(n.edges))
|
|
2457
|
+
for (const a of n.edges)
|
|
2259
2458
|
this.addEdge(a);
|
|
2260
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
2261
|
-
} catch (
|
|
2262
|
-
return console.error("[ForceGraph3D] Error expanding node:",
|
|
2459
|
+
return this.panelManager.hide(), this.emit("expand", e, n), !0;
|
|
2460
|
+
} catch (n) {
|
|
2461
|
+
return console.error("[ForceGraph3D] Error expanding node:", n), !1;
|
|
2263
2462
|
}
|
|
2264
2463
|
}
|
|
2265
2464
|
/**
|
|
@@ -2296,19 +2495,19 @@ class Ot {
|
|
|
2296
2495
|
/**
|
|
2297
2496
|
* Focuses the camera on a specific node with smooth animation
|
|
2298
2497
|
*/
|
|
2299
|
-
focusOnNode(e,
|
|
2498
|
+
focusOnNode(e, s = 30) {
|
|
2300
2499
|
const t = this.nodeManager.getNode(e);
|
|
2301
2500
|
if (!t) {
|
|
2302
2501
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
2303
2502
|
return;
|
|
2304
2503
|
}
|
|
2305
|
-
const
|
|
2306
|
-
x:
|
|
2307
|
-
y:
|
|
2308
|
-
z:
|
|
2309
|
-
},
|
|
2310
|
-
const f = performance.now() - v, M = Math.min(f /
|
|
2311
|
-
|
|
2504
|
+
const i = t.position, n = this.sceneManager.camera, a = this.sceneManager.controls, l = n.position.clone().sub(a.target).normalize(), d = {
|
|
2505
|
+
x: i.x + l.x * s,
|
|
2506
|
+
y: i.y + l.y * s,
|
|
2507
|
+
z: i.z + l.z * s
|
|
2508
|
+
}, u = { x: n.position.x, y: n.position.y, z: n.position.z }, g = { x: a.target.x, y: a.target.y, z: a.target.z }, b = 800, v = performance.now(), y = () => {
|
|
2509
|
+
const f = performance.now() - v, M = Math.min(f / b, 1), C = 1 - Math.pow(1 - M, 3);
|
|
2510
|
+
n.position.x = u.x + (d.x - u.x) * C, n.position.y = u.y + (d.y - u.y) * C, n.position.z = u.z + (d.z - u.z) * C, a.target.x = g.x + (i.x - g.x) * C, a.target.y = g.y + (i.y - g.y) * C, a.target.z = g.z + (i.z - g.z) * C, a.update(), M < 1 && requestAnimationFrame(y);
|
|
2312
2511
|
};
|
|
2313
2512
|
y();
|
|
2314
2513
|
}
|
|
@@ -2316,13 +2515,13 @@ class Ot {
|
|
|
2316
2515
|
* Shows the info panel for a specific node
|
|
2317
2516
|
*/
|
|
2318
2517
|
showNodePanel(e) {
|
|
2319
|
-
const
|
|
2320
|
-
if (!
|
|
2518
|
+
const s = this.nodeManager.getNode(e);
|
|
2519
|
+
if (!s) {
|
|
2321
2520
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
2322
2521
|
return;
|
|
2323
2522
|
}
|
|
2324
2523
|
const t = this.getNeighbors(e);
|
|
2325
|
-
this.panelManager.show(
|
|
2524
|
+
this.panelManager.show(s, t);
|
|
2326
2525
|
}
|
|
2327
2526
|
/**
|
|
2328
2527
|
* Searches nodes by label or ID (case-insensitive)
|
|
@@ -2331,28 +2530,28 @@ class Ot {
|
|
|
2331
2530
|
searchNodes(e) {
|
|
2332
2531
|
if (!e || e.trim() === "")
|
|
2333
2532
|
return [];
|
|
2334
|
-
const
|
|
2335
|
-
return t.forEach((
|
|
2336
|
-
var
|
|
2337
|
-
const a = (
|
|
2338
|
-
(a || l || d) &&
|
|
2339
|
-
}),
|
|
2533
|
+
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
2534
|
+
return t.forEach((n) => {
|
|
2535
|
+
var u, g, b;
|
|
2536
|
+
const a = (u = n.label) == null ? void 0 : u.toLowerCase().includes(s), l = (g = n.id) == null ? void 0 : g.toLowerCase().includes(s), d = (b = n.type) == null ? void 0 : b.toLowerCase().includes(s);
|
|
2537
|
+
(a || l || d) && i.push(n);
|
|
2538
|
+
}), i;
|
|
2340
2539
|
}
|
|
2341
2540
|
/**
|
|
2342
2541
|
* Searches edges by relationship (case-insensitive)
|
|
2343
2542
|
* @returns Array of matching edges with source/target node info
|
|
2344
2543
|
*/
|
|
2345
2544
|
searchEdges(e) {
|
|
2346
|
-
var
|
|
2545
|
+
var n;
|
|
2347
2546
|
if (!e || e.trim() === "")
|
|
2348
2547
|
return [];
|
|
2349
|
-
const
|
|
2548
|
+
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
2350
2549
|
for (const a of t)
|
|
2351
|
-
if ((
|
|
2352
|
-
const d = this.nodeManager.getNode(a.source),
|
|
2353
|
-
d &&
|
|
2550
|
+
if ((n = a.relationship) == null ? void 0 : n.toLowerCase().includes(s)) {
|
|
2551
|
+
const d = this.nodeManager.getNode(a.source), u = this.nodeManager.getNode(a.target);
|
|
2552
|
+
d && u && i.push({ edge: a, sourceNode: d, targetNode: u });
|
|
2354
2553
|
}
|
|
2355
|
-
return
|
|
2554
|
+
return i;
|
|
2356
2555
|
}
|
|
2357
2556
|
/**
|
|
2358
2557
|
* Gets all nodes as an array
|
|
@@ -2376,15 +2575,15 @@ class Ot {
|
|
|
2376
2575
|
/**
|
|
2377
2576
|
* Registers an event listener
|
|
2378
2577
|
*/
|
|
2379
|
-
on(e,
|
|
2380
|
-
this.eventCallbacks.has(e) || this.eventCallbacks.set(e, []), this.eventCallbacks.get(e).push(
|
|
2578
|
+
on(e, s) {
|
|
2579
|
+
this.eventCallbacks.has(e) || this.eventCallbacks.set(e, []), this.eventCallbacks.get(e).push(s);
|
|
2381
2580
|
}
|
|
2382
2581
|
/**
|
|
2383
2582
|
* Emits an event
|
|
2384
2583
|
*/
|
|
2385
|
-
emit(e, ...
|
|
2584
|
+
emit(e, ...s) {
|
|
2386
2585
|
const t = this.eventCallbacks.get(e);
|
|
2387
|
-
t && t.forEach((
|
|
2586
|
+
t && t.forEach((i) => i(...s));
|
|
2388
2587
|
}
|
|
2389
2588
|
/**
|
|
2390
2589
|
* Sets physics parameters
|
|
@@ -2459,29 +2658,29 @@ class Ot {
|
|
|
2459
2658
|
<div>FPS: <span class="value" id="dev-fps">60</span></div>
|
|
2460
2659
|
</div>
|
|
2461
2660
|
`, this.container.appendChild(this.devControls);
|
|
2462
|
-
const e = this.devControls.querySelector("#dev-repulsion"),
|
|
2661
|
+
const e = this.devControls.querySelector("#dev-repulsion"), s = this.devControls.querySelector("#dev-attraction"), t = this.devControls.querySelector("#dev-damping");
|
|
2463
2662
|
e == null || e.addEventListener("input", () => {
|
|
2464
|
-
const
|
|
2465
|
-
this.setPhysicsParams({ repulsionStrength:
|
|
2466
|
-
}),
|
|
2467
|
-
const
|
|
2468
|
-
this.setPhysicsParams({ attractionStrength:
|
|
2663
|
+
const i = parseFloat(e.value);
|
|
2664
|
+
this.setPhysicsParams({ repulsionStrength: i }), this.devControls.querySelector("#dev-repulsion-val").textContent = i.toString();
|
|
2665
|
+
}), s == null || s.addEventListener("input", () => {
|
|
2666
|
+
const i = parseFloat(s.value) / 1e3;
|
|
2667
|
+
this.setPhysicsParams({ attractionStrength: i }), this.devControls.querySelector("#dev-attraction-val").textContent = i.toFixed(3);
|
|
2469
2668
|
}), t == null || t.addEventListener("input", () => {
|
|
2470
|
-
const
|
|
2471
|
-
this.setPhysicsParams({ damping:
|
|
2669
|
+
const i = parseFloat(t.value) / 100;
|
|
2670
|
+
this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
|
|
2472
2671
|
}), setInterval(() => {
|
|
2473
|
-
const
|
|
2474
|
-
|
|
2672
|
+
const i = this.devControls.querySelector("#dev-node-count"), n = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
|
|
2673
|
+
i && (i.textContent = this.getNodeCount().toString()), n && (n.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
|
|
2475
2674
|
}, 500);
|
|
2476
2675
|
}
|
|
2477
2676
|
/**
|
|
2478
2677
|
* Destroys the graph and releases all resources
|
|
2479
2678
|
*/
|
|
2480
2679
|
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;
|
|
2680
|
+
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.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
2681
|
}
|
|
2483
2682
|
}
|
|
2484
|
-
const
|
|
2683
|
+
const ke = [
|
|
2485
2684
|
"Alpha",
|
|
2486
2685
|
"Beta",
|
|
2487
2686
|
"Gamma",
|
|
@@ -2523,7 +2722,7 @@ const Fe = [
|
|
|
2523
2722
|
"partners with",
|
|
2524
2723
|
"collaborates with",
|
|
2525
2724
|
"supports"
|
|
2526
|
-
],
|
|
2725
|
+
], Ie = [
|
|
2527
2726
|
16777215,
|
|
2528
2727
|
// White
|
|
2529
2728
|
16750950,
|
|
@@ -2535,14 +2734,14 @@ const Fe = [
|
|
|
2535
2734
|
16746564
|
|
2536
2735
|
// Darker tangerine
|
|
2537
2736
|
];
|
|
2538
|
-
function
|
|
2539
|
-
const e = [],
|
|
2540
|
-
for (let
|
|
2541
|
-
const
|
|
2737
|
+
function kt(c = 30) {
|
|
2738
|
+
const e = [], s = [];
|
|
2739
|
+
for (let i = 0; i < c; i++) {
|
|
2740
|
+
const n = i < ke.length ? ke[i] : `Node ${i + 1}`;
|
|
2542
2741
|
e.push({
|
|
2543
|
-
id: `node-${
|
|
2544
|
-
label:
|
|
2545
|
-
color:
|
|
2742
|
+
id: `node-${i}`,
|
|
2743
|
+
label: n,
|
|
2744
|
+
color: Ie[i % Ie.length],
|
|
2546
2745
|
position: {
|
|
2547
2746
|
x: (Math.random() - 0.5) * 60,
|
|
2548
2747
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -2550,43 +2749,43 @@ function Lt(c = 30) {
|
|
|
2550
2749
|
}
|
|
2551
2750
|
});
|
|
2552
2751
|
}
|
|
2553
|
-
for (let
|
|
2554
|
-
const
|
|
2555
|
-
|
|
2556
|
-
source: `node-${
|
|
2557
|
-
target: `node-${
|
|
2752
|
+
for (let i = 1; i < c; i++) {
|
|
2753
|
+
const n = Math.floor(Math.random() * i);
|
|
2754
|
+
s.push({
|
|
2755
|
+
source: `node-${i}`,
|
|
2756
|
+
target: `node-${n}`,
|
|
2558
2757
|
relationship: Q[Math.floor(Math.random() * Q.length)]
|
|
2559
2758
|
});
|
|
2560
2759
|
}
|
|
2561
2760
|
const t = Math.floor(c * 0.5);
|
|
2562
|
-
for (let
|
|
2563
|
-
const
|
|
2761
|
+
for (let i = 0; i < t; i++) {
|
|
2762
|
+
const n = Math.floor(Math.random() * c);
|
|
2564
2763
|
let a = Math.floor(Math.random() * c);
|
|
2565
|
-
|
|
2566
|
-
const l = `node-${
|
|
2567
|
-
|
|
2568
|
-
(
|
|
2569
|
-
) ||
|
|
2764
|
+
n === a && (a = (a + 1) % c);
|
|
2765
|
+
const l = `node-${n}`, d = `node-${a}`;
|
|
2766
|
+
s.some(
|
|
2767
|
+
(g) => g.source === l && g.target === d || g.source === d && g.target === l
|
|
2768
|
+
) || s.push({
|
|
2570
2769
|
source: l,
|
|
2571
2770
|
target: d,
|
|
2572
2771
|
relationship: Q[Math.floor(Math.random() * Q.length)]
|
|
2573
2772
|
});
|
|
2574
2773
|
}
|
|
2575
|
-
return { nodes: e, edges:
|
|
2774
|
+
return { nodes: e, edges: s };
|
|
2576
2775
|
}
|
|
2577
|
-
function
|
|
2578
|
-
const e = [],
|
|
2579
|
-
for (let
|
|
2580
|
-
|
|
2776
|
+
function It(c = 1e3) {
|
|
2777
|
+
const e = [], s = [], t = Math.ceil(c / 50), i = [];
|
|
2778
|
+
for (let n = 0; n < t; n++)
|
|
2779
|
+
i.push({
|
|
2581
2780
|
x: (Math.random() - 0.5) * 200,
|
|
2582
2781
|
y: (Math.random() - 0.5) * 200,
|
|
2583
2782
|
z: (Math.random() - 0.5) * 200
|
|
2584
2783
|
});
|
|
2585
|
-
for (let
|
|
2586
|
-
const a = n
|
|
2784
|
+
for (let n = 0; n < c; n++) {
|
|
2785
|
+
const a = i[n % t];
|
|
2587
2786
|
e.push({
|
|
2588
|
-
id: `node-${
|
|
2589
|
-
label: `N${
|
|
2787
|
+
id: `node-${n}`,
|
|
2788
|
+
label: `N${n}`,
|
|
2590
2789
|
position: {
|
|
2591
2790
|
x: a.x + (Math.random() - 0.5) * 40,
|
|
2592
2791
|
y: a.y + (Math.random() - 0.5) * 40,
|
|
@@ -2594,32 +2793,32 @@ function Ft(c = 1e3) {
|
|
|
2594
2793
|
}
|
|
2595
2794
|
});
|
|
2596
2795
|
}
|
|
2597
|
-
for (let
|
|
2598
|
-
const a = Math.floor(
|
|
2599
|
-
|
|
2600
|
-
source: `node-${
|
|
2601
|
-
target: `node-${Math.min(l,
|
|
2796
|
+
for (let n = 1; n < c; n++) {
|
|
2797
|
+
const a = Math.floor(n / 50) * 50, l = a === 0 ? Math.floor(Math.random() * n) : a + Math.floor(Math.random() * Math.min(n - a, 50));
|
|
2798
|
+
s.push({
|
|
2799
|
+
source: `node-${n}`,
|
|
2800
|
+
target: `node-${Math.min(l, n - 1)}`,
|
|
2602
2801
|
relationship: "links to"
|
|
2603
2802
|
});
|
|
2604
2803
|
}
|
|
2605
|
-
for (let
|
|
2606
|
-
const a =
|
|
2607
|
-
|
|
2804
|
+
for (let n = 1; n < t; n++) {
|
|
2805
|
+
const a = n * 50, l = (n - 1) * 50 + Math.floor(Math.random() * 50);
|
|
2806
|
+
s.push({
|
|
2608
2807
|
source: `node-${a}`,
|
|
2609
2808
|
target: `node-${l}`,
|
|
2610
2809
|
relationship: "bridges to"
|
|
2611
2810
|
});
|
|
2612
2811
|
}
|
|
2613
|
-
return { nodes: e, edges:
|
|
2812
|
+
return { nodes: e, edges: s };
|
|
2614
2813
|
}
|
|
2615
2814
|
export {
|
|
2616
|
-
|
|
2815
|
+
L as DEFAULT_OPTIONS,
|
|
2617
2816
|
Ot as ForceGraph3D,
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2817
|
+
U as LODLevel,
|
|
2818
|
+
R as createEdgeKey,
|
|
2819
|
+
It as generateLargeSampleData,
|
|
2820
|
+
kt as generateSampleData,
|
|
2821
|
+
Fe as validateEdgeData,
|
|
2822
|
+
Re as validateNodeData
|
|
2624
2823
|
};
|
|
2625
2824
|
//# sourceMappingURL=force-3d-graph.js.map
|