force-3d-graph 1.2.6 → 1.3.6
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 +239 -146
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +48 -5
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +6 -0
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
var ot = Object.defineProperty;
|
|
2
|
-
var nt = (
|
|
3
|
-
var l = (
|
|
2
|
+
var nt = (h, e, s) => e in h ? ot(h, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : h[e] = s;
|
|
3
|
+
var l = (h, e, s) => nt(h, typeof e != "symbol" ? e + "" : e, s);
|
|
4
4
|
import * as y from "three";
|
|
5
|
-
import { EventDispatcher as at, Vector3 as S, MOUSE as $, TOUCH as G, Spherical as ze, Quaternion as
|
|
5
|
+
import { EventDispatcher as at, Vector3 as S, MOUSE as $, TOUCH as G, Spherical as ze, Quaternion as ke, Vector2 as k, Ray as rt, Plane as lt, MathUtils as ct } from "three";
|
|
6
6
|
const P = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
@@ -28,55 +28,56 @@ const P = {
|
|
|
28
28
|
searchPlaceholder: "Search nodes or relationships...",
|
|
29
29
|
initialViewMode: "3d",
|
|
30
30
|
showViewToggle: !0,
|
|
31
|
+
showLegend: !0,
|
|
31
32
|
targetFPS: 60,
|
|
32
33
|
maxVisibleNodes: 1e4
|
|
33
34
|
};
|
|
34
|
-
var X = /* @__PURE__ */ ((
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
return
|
|
35
|
+
var X = /* @__PURE__ */ ((h) => (h[h.HIGH = 0] = "HIGH", h[h.MEDIUM = 1] = "MEDIUM", h[h.LOW = 2] = "LOW", h))(X || {});
|
|
36
|
+
function dt() {
|
|
37
|
+
const h = document.createElement("div");
|
|
38
|
+
return h.id = "force-graph-3d-container", h.style.cssText = `
|
|
38
39
|
width: 100%;
|
|
39
40
|
height: 100%;
|
|
40
41
|
position: absolute;
|
|
41
42
|
top: 0;
|
|
42
43
|
left: 0;
|
|
43
44
|
overflow: hidden;
|
|
44
|
-
`, document.body.appendChild(
|
|
45
|
+
`, document.body.appendChild(h), h;
|
|
45
46
|
}
|
|
46
|
-
function
|
|
47
|
-
return
|
|
47
|
+
function ht(h) {
|
|
48
|
+
return h && h instanceof HTMLElement ? h : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), dt());
|
|
48
49
|
}
|
|
49
|
-
function
|
|
50
|
-
const e =
|
|
50
|
+
function Te(h) {
|
|
51
|
+
const e = h.getBoundingClientRect();
|
|
51
52
|
return {
|
|
52
53
|
width: e.width || window.innerWidth,
|
|
53
54
|
height: e.height || window.innerHeight
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
function
|
|
57
|
-
if (!
|
|
57
|
+
function De(h) {
|
|
58
|
+
if (!h || typeof h != "object")
|
|
58
59
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
59
|
-
const e =
|
|
60
|
+
const e = h;
|
|
60
61
|
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 && !gt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
|
|
61
62
|
}
|
|
62
|
-
function
|
|
63
|
-
if (!
|
|
63
|
+
function He(h) {
|
|
64
|
+
if (!h || typeof h != "object")
|
|
64
65
|
return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
|
|
65
|
-
const e =
|
|
66
|
+
const e = h;
|
|
66
67
|
return typeof e.source != "string" || e.source.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: source must be a non-empty string"), !1) : typeof e.target != "string" || e.target.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: target must be a non-empty string"), !1) : !0;
|
|
67
68
|
}
|
|
68
|
-
function pt(
|
|
69
|
-
return typeof
|
|
69
|
+
function pt(h) {
|
|
70
|
+
return typeof h != "string" || h.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
|
|
70
71
|
}
|
|
71
|
-
function gt(
|
|
72
|
-
if (!
|
|
73
|
-
const e =
|
|
72
|
+
function gt(h) {
|
|
73
|
+
if (!h || typeof h != "object") return !1;
|
|
74
|
+
const e = h;
|
|
74
75
|
return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
|
|
75
76
|
}
|
|
76
|
-
function
|
|
77
|
-
return
|
|
77
|
+
function H(h, e) {
|
|
78
|
+
return h === e ? `${h}-${e}` : h < e ? `${h}-${e}` : `${e}-${h}`;
|
|
78
79
|
}
|
|
79
|
-
const Pe = { type: "change" }, ce = { type: "start" },
|
|
80
|
+
const Pe = { type: "change" }, ce = { type: "start" }, Le = { type: "end" }, J = new rt(), Re = new lt(), ut = Math.cos(70 * ct.DEG2RAD);
|
|
80
81
|
class ft extends at {
|
|
81
82
|
constructor(e, s) {
|
|
82
83
|
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: $.ROTATE, MIDDLE: $.DOLLY, RIGHT: $.PAN }, this.touches = { ONE: G.ROTATE, TWO: G.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
@@ -94,30 +95,30 @@ class ft extends at {
|
|
|
94
95
|
}, this.reset = function() {
|
|
95
96
|
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(), o = i.NONE;
|
|
96
97
|
}, this.update = function() {
|
|
97
|
-
const n = new S(), g = new
|
|
98
|
+
const n = new S(), g = new ke().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), w = new S(), C = new ke(), F = new S(), z = 2 * Math.PI;
|
|
98
99
|
return function(it = null) {
|
|
99
100
|
const Se = t.object.position;
|
|
100
101
|
n.copy(Se).sub(t.target), n.applyQuaternion(g), r.setFromVector3(n), t.autoRotate && o === i.NONE && B(Ae(it)), t.enableDamping ? (r.theta += c.theta * t.dampingFactor, r.phi += c.phi * t.dampingFactor) : (r.theta += c.theta, r.phi += c.phi);
|
|
101
|
-
let
|
|
102
|
-
isFinite(
|
|
102
|
+
let R = t.minAzimuthAngle, I = t.maxAzimuthAngle;
|
|
103
|
+
isFinite(R) && isFinite(I) && (R < -Math.PI ? R += z : R > Math.PI && (R -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), R <= I ? r.theta = Math.max(R, Math.min(I, r.theta)) : r.theta = r.theta > (R + I) / 2 ? Math.max(R, r.theta) : Math.min(I, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && D || t.object.isOrthographicCamera ? r.radius = ne(r.radius) : r.radius = ne(r.radius * d), n.setFromSpherical(r), n.applyQuaternion(v), Se.copy(t.target).add(n), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), p.set(0, 0, 0));
|
|
103
104
|
let le = !1;
|
|
104
|
-
if (t.zoomToCursor &&
|
|
105
|
+
if (t.zoomToCursor && D) {
|
|
105
106
|
let U = null;
|
|
106
107
|
if (t.object.isPerspectiveCamera) {
|
|
107
108
|
const _ = n.length();
|
|
108
|
-
U = ne(_ *
|
|
109
|
+
U = ne(_ * d);
|
|
109
110
|
const Q = _ - U;
|
|
110
111
|
t.object.position.addScaledVector(Y, Q), t.object.updateMatrixWorld();
|
|
111
112
|
} else if (t.object.isOrthographicCamera) {
|
|
112
|
-
const _ = new S(
|
|
113
|
-
_.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
114
|
-
const Q = new S(
|
|
113
|
+
const _ = new S(T.x, T.y, 0);
|
|
114
|
+
_.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / d)), t.object.updateProjectionMatrix(), le = !0;
|
|
115
|
+
const Q = new S(T.x, T.y, 0);
|
|
115
116
|
Q.unproject(t.object), t.object.position.sub(Q).add(_), t.object.updateMatrixWorld(), U = n.length();
|
|
116
117
|
} else
|
|
117
118
|
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
|
|
118
|
-
U !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(U).add(t.object.position) : (J.origin.copy(t.object.position), J.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(J.direction)) < ut ? e.lookAt(t.target) : (
|
|
119
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
120
|
-
return
|
|
119
|
+
U !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(U).add(t.object.position) : (J.origin.copy(t.object.position), J.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(J.direction)) < ut ? e.lookAt(t.target) : (Re.setFromNormalAndCoplanarPoint(t.object.up, t.target), J.intersectPlane(Re, t.target))));
|
|
120
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / d)), t.object.updateProjectionMatrix(), le = !0);
|
|
121
|
+
return d = 1, D = !1, le || w.distanceToSquared(t.object.position) > a || 8 * (1 - C.dot(t.object.quaternion)) > a || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Pe), w.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
|
|
121
122
|
};
|
|
122
123
|
}(), this.dispose = function() {
|
|
123
124
|
t.domElement.removeEventListener("contextmenu", Ce), t.domElement.removeEventListener("pointerdown", Me), t.domElement.removeEventListener("pointercancel", K), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", re), t._domElementKeyEvents = null);
|
|
@@ -134,9 +135,9 @@ class ft extends at {
|
|
|
134
135
|
};
|
|
135
136
|
let o = i.NONE;
|
|
136
137
|
const a = 1e-6, r = new ze(), c = new ze();
|
|
137
|
-
let
|
|
138
|
-
const p = new S(), x = new
|
|
139
|
-
let
|
|
138
|
+
let d = 1;
|
|
139
|
+
const p = new S(), x = new k(), m = new k(), u = new k(), f = new k(), b = new k(), M = new k(), N = new k(), O = new k(), L = new k(), Y = new S(), T = new k();
|
|
140
|
+
let D = !1;
|
|
140
141
|
const E = [], q = {};
|
|
141
142
|
let se = !1;
|
|
142
143
|
function Ae(n) {
|
|
@@ -152,12 +153,12 @@ class ft extends at {
|
|
|
152
153
|
function Z(n) {
|
|
153
154
|
c.phi -= n;
|
|
154
155
|
}
|
|
155
|
-
const
|
|
156
|
+
const de = function() {
|
|
156
157
|
const n = new S();
|
|
157
158
|
return function(v, w) {
|
|
158
159
|
n.setFromMatrixColumn(w, 0), n.multiplyScalar(-v), p.add(n);
|
|
159
160
|
};
|
|
160
|
-
}(),
|
|
161
|
+
}(), he = function() {
|
|
161
162
|
const n = new S();
|
|
162
163
|
return function(v, w) {
|
|
163
164
|
t.screenSpacePanning === !0 ? n.setFromMatrixColumn(w, 1) : (n.setFromMatrixColumn(w, 0), n.crossVectors(t.object.up, n)), n.multiplyScalar(v), p.add(n);
|
|
@@ -170,22 +171,22 @@ class ft extends at {
|
|
|
170
171
|
const F = t.object.position;
|
|
171
172
|
n.copy(F).sub(t.target);
|
|
172
173
|
let z = n.length();
|
|
173
|
-
z *= Math.tan(t.object.fov / 2 * Math.PI / 180),
|
|
174
|
-
} else t.object.isOrthographicCamera ? (
|
|
174
|
+
z *= Math.tan(t.object.fov / 2 * Math.PI / 180), de(2 * v * z / C.clientHeight, t.object.matrix), he(2 * w * z / C.clientHeight, t.object.matrix);
|
|
175
|
+
} else t.object.isOrthographicCamera ? (de(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), he(w * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
175
176
|
};
|
|
176
177
|
}();
|
|
177
178
|
function ie(n) {
|
|
178
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
179
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? d /= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
179
180
|
}
|
|
180
181
|
function pe(n) {
|
|
181
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
182
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? d *= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
182
183
|
}
|
|
183
184
|
function oe(n, g) {
|
|
184
185
|
if (!t.zoomToCursor)
|
|
185
186
|
return;
|
|
186
|
-
|
|
187
|
+
D = !0;
|
|
187
188
|
const v = t.domElement.getBoundingClientRect(), w = n - v.left, C = g - v.top, F = v.width, z = v.height;
|
|
188
|
-
|
|
189
|
+
T.x = w / F * 2 - 1, T.y = -(C / z) * 2 + 1, Y.set(T.x, T.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
189
190
|
}
|
|
190
191
|
function ne(n) {
|
|
191
192
|
return Math.max(t.minDistance, Math.min(t.maxDistance, n));
|
|
@@ -205,7 +206,7 @@ class ft extends at {
|
|
|
205
206
|
B(2 * Math.PI * u.x / g.clientHeight), Z(2 * Math.PI * u.y / g.clientHeight), x.copy(m), t.update();
|
|
206
207
|
}
|
|
207
208
|
function Ge(n) {
|
|
208
|
-
O.set(n.clientX, n.clientY),
|
|
209
|
+
O.set(n.clientX, n.clientY), L.subVectors(O, N), L.y > 0 ? ie(W(L.y)) : L.y < 0 && pe(W(L.y)), N.copy(O), t.update();
|
|
209
210
|
}
|
|
210
211
|
function Ye(n) {
|
|
211
212
|
b.set(n.clientX, n.clientY), M.subVectors(b, f).multiplyScalar(t.panSpeed), A(M.x, M.y), f.copy(b), t.update();
|
|
@@ -279,7 +280,7 @@ class ft extends at {
|
|
|
279
280
|
}
|
|
280
281
|
function ve(n) {
|
|
281
282
|
const g = j(n), v = n.pageX - g.x, w = n.pageY - g.y, C = Math.sqrt(v * v + w * w);
|
|
282
|
-
O.set(0, C),
|
|
283
|
+
O.set(0, C), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), ie(L.y), N.copy(O);
|
|
283
284
|
const F = (n.pageX + g.x) * 0.5, z = (n.pageY + g.y) * 0.5;
|
|
284
285
|
oe(F, z);
|
|
285
286
|
}
|
|
@@ -296,7 +297,7 @@ class ft extends at {
|
|
|
296
297
|
t.enabled !== !1 && (n.pointerType === "touch" ? et(n) : We(n));
|
|
297
298
|
}
|
|
298
299
|
function K(n) {
|
|
299
|
-
st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(
|
|
300
|
+
st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Le), o = i.NONE;
|
|
300
301
|
}
|
|
301
302
|
function qe(n) {
|
|
302
303
|
let g;
|
|
@@ -358,7 +359,7 @@ class ft extends at {
|
|
|
358
359
|
}
|
|
359
360
|
}
|
|
360
361
|
function we(n) {
|
|
361
|
-
t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (n.preventDefault(), t.dispatchEvent(ce), Be(Ze(n)), t.dispatchEvent(
|
|
362
|
+
t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (n.preventDefault(), t.dispatchEvent(ce), Be(Ze(n)), t.dispatchEvent(Le));
|
|
362
363
|
}
|
|
363
364
|
function Ze(n) {
|
|
364
365
|
const g = n.deltaMode, v = {
|
|
@@ -458,7 +459,7 @@ class ft extends at {
|
|
|
458
459
|
}
|
|
459
460
|
function Ne(n) {
|
|
460
461
|
let g = q[n.pointerId];
|
|
461
|
-
g === void 0 && (g = new
|
|
462
|
+
g === void 0 && (g = new k(), q[n.pointerId] = g), g.set(n.pageX, n.pageY);
|
|
462
463
|
}
|
|
463
464
|
function j(n) {
|
|
464
465
|
const g = n.pointerId === E[0] ? E[1] : E[0];
|
|
@@ -478,7 +479,7 @@ class mt {
|
|
|
478
479
|
this.container = e, this.scene = new y.Scene(), this.scene.background = new y.Color(
|
|
479
480
|
s.backgroundColor ?? 657930
|
|
480
481
|
);
|
|
481
|
-
const { width: t, height: i } =
|
|
482
|
+
const { width: t, height: i } = Te(e), o = s.cameraFov ?? 75;
|
|
482
483
|
this.camera = new y.PerspectiveCamera(o, t / i, 0.1, 2e3);
|
|
483
484
|
const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
484
485
|
this.camera.position.set(a.x, a.y, a.z), this.renderer = new y.WebGLRenderer({
|
|
@@ -510,7 +511,7 @@ class mt {
|
|
|
510
511
|
* Handle window resize
|
|
511
512
|
*/
|
|
512
513
|
onWindowResize() {
|
|
513
|
-
const { width: e, height: s } =
|
|
514
|
+
const { width: e, height: s } = Te(this.container);
|
|
514
515
|
this.camera.aspect = e / s, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, s);
|
|
515
516
|
}
|
|
516
517
|
/**
|
|
@@ -584,7 +585,7 @@ const V = class V {
|
|
|
584
585
|
* @returns true if added, false if node already exists or invalid
|
|
585
586
|
*/
|
|
586
587
|
addNode(e, s = 0) {
|
|
587
|
-
if (!
|
|
588
|
+
if (!De(e))
|
|
588
589
|
return !1;
|
|
589
590
|
if (this.nodes.has(e.id))
|
|
590
591
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
@@ -709,7 +710,7 @@ class yt {
|
|
|
709
710
|
* Checks if an edge exists
|
|
710
711
|
*/
|
|
711
712
|
hasEdge(e, s) {
|
|
712
|
-
const t =
|
|
713
|
+
const t = H(e, s);
|
|
713
714
|
return this.edgeKeySet.has(t);
|
|
714
715
|
}
|
|
715
716
|
/**
|
|
@@ -717,13 +718,13 @@ class yt {
|
|
|
717
718
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
718
719
|
*/
|
|
719
720
|
addEdge(e) {
|
|
720
|
-
if (!
|
|
721
|
+
if (!He(e))
|
|
721
722
|
return !1;
|
|
722
723
|
if (!this.nodeManager.hasNode(e.source))
|
|
723
724
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
724
725
|
if (!this.nodeManager.hasNode(e.target))
|
|
725
726
|
return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
|
|
726
|
-
const s =
|
|
727
|
+
const s = H(e.source, e.target);
|
|
727
728
|
if (this.edgeKeySet.has(s))
|
|
728
729
|
return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
|
|
729
730
|
const t = this.nodeManager.getNode(e.source), i = this.nodeManager.getNode(e.target), o = this.edgeFactory.createEdge(
|
|
@@ -740,11 +741,11 @@ class yt {
|
|
|
740
741
|
* @returns true if removed, false if not found
|
|
741
742
|
*/
|
|
742
743
|
removeEdge(e, s) {
|
|
743
|
-
const t =
|
|
744
|
+
const t = H(e, s);
|
|
744
745
|
if (!this.edgeKeySet.has(t))
|
|
745
746
|
return !1;
|
|
746
747
|
const i = this.edges.findIndex(
|
|
747
|
-
(a) =>
|
|
748
|
+
(a) => H(a.source, a.target) === t
|
|
748
749
|
);
|
|
749
750
|
if (i === -1)
|
|
750
751
|
return !1;
|
|
@@ -755,10 +756,10 @@ class yt {
|
|
|
755
756
|
* Highlights an edge
|
|
756
757
|
*/
|
|
757
758
|
highlightEdge(e, s) {
|
|
758
|
-
const t =
|
|
759
|
+
const t = H(e, s);
|
|
759
760
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
760
761
|
const i = this.edges.findIndex(
|
|
761
|
-
(o) =>
|
|
762
|
+
(o) => H(o.source, o.target) === t
|
|
762
763
|
);
|
|
763
764
|
i !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[i]), this.highlightedEdgeKey = t);
|
|
764
765
|
}
|
|
@@ -768,7 +769,7 @@ class yt {
|
|
|
768
769
|
unhighlightCurrentEdge() {
|
|
769
770
|
if (!this.highlightedEdgeKey) return;
|
|
770
771
|
const e = this.edges.findIndex(
|
|
771
|
-
(s) =>
|
|
772
|
+
(s) => H(s.source, s.target) === this.highlightedEdgeKey
|
|
772
773
|
);
|
|
773
774
|
e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
|
|
774
775
|
}
|
|
@@ -844,7 +845,7 @@ class yt {
|
|
|
844
845
|
this.clear();
|
|
845
846
|
}
|
|
846
847
|
}
|
|
847
|
-
class
|
|
848
|
+
class Ie {
|
|
848
849
|
constructor(e, s, t = {}) {
|
|
849
850
|
l(this, "nodes");
|
|
850
851
|
l(this, "edges");
|
|
@@ -880,17 +881,19 @@ class Le {
|
|
|
880
881
|
const e = /* @__PURE__ */ new Map();
|
|
881
882
|
for (const i of this.edges)
|
|
882
883
|
e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
|
|
884
|
+
console.log(`[ForceGraph3D] initializeNodeMassAndPin: ${this.edges.length} edges, ${this.nodes.size} nodes, ${e.size} unique endpoints`);
|
|
883
885
|
let s = 0, t = null;
|
|
884
886
|
for (const [i, o] of e) {
|
|
885
887
|
o > s && (s = o, t = i);
|
|
886
888
|
const a = this.nodes.get(i);
|
|
887
889
|
a && (a.mass = 1 + Math.log2(1 + o));
|
|
888
890
|
}
|
|
889
|
-
if (t && s > 10) {
|
|
891
|
+
if (console.log(`[ForceGraph3D] Hub detected: id="${t}", degree=${s}, threshold=10`), t && s > 10) {
|
|
890
892
|
this.pinnedNodeId = t;
|
|
891
893
|
const i = this.nodes.get(t);
|
|
892
|
-
i
|
|
893
|
-
}
|
|
894
|
+
i ? (i.position.x = 0, i.position.y = 0, i.position.z = 0, i.velocity.x = 0, i.velocity.y = 0, i.velocity.z = 0, console.log(`[ForceGraph3D] ✅ Hub node "${t}" pinned at origin`)) : console.warn(`[ForceGraph3D] ⚠️ Hub node "${t}" found in edges but NOT in nodes map!`);
|
|
895
|
+
} else
|
|
896
|
+
console.log(`[ForceGraph3D] No hub pinned (maxDegree=${s} <= 10 or hubId=${t})`);
|
|
894
897
|
}
|
|
895
898
|
/**
|
|
896
899
|
* Computes effective repulsion strength scaled by node count.
|
|
@@ -916,11 +919,11 @@ class Le {
|
|
|
916
919
|
for (let i = 0; i < s; i++) {
|
|
917
920
|
const o = e[i];
|
|
918
921
|
for (let a = i + 1; a < s; a++) {
|
|
919
|
-
const r = e[a], c = r.position.x - o.position.x,
|
|
920
|
-
let x = c * c +
|
|
922
|
+
const r = e[a], c = r.position.x - o.position.x, d = r.position.y - o.position.y, p = r.position.z - o.position.z;
|
|
923
|
+
let x = c * c + d * d + p * p;
|
|
921
924
|
if (x > this.REPULSION_CUTOFF_SQ) continue;
|
|
922
925
|
x < 0.01 && (x = 0.01);
|
|
923
|
-
const m = Math.sqrt(x), u = t * this.alpha / x, f = c / m * u, b =
|
|
926
|
+
const m = Math.sqrt(x), u = t * this.alpha / x, f = c / m * u, b = d / m * u, M = p / m * u;
|
|
924
927
|
o.velocity.x -= f / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= M / o.mass, r.velocity.x += f / r.mass, r.velocity.y += b / r.mass, r.velocity.z += M / r.mass;
|
|
925
928
|
}
|
|
926
929
|
}
|
|
@@ -946,11 +949,11 @@ class Le {
|
|
|
946
949
|
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
947
950
|
const r = Math.sqrt(a), c = this.getEffectiveRepulsion();
|
|
948
951
|
if (r > 0 && s.size / r < this.barnesHutTheta) {
|
|
949
|
-
const
|
|
952
|
+
const d = Math.max(a, 0.01), p = c * this.alpha * s.mass / d;
|
|
950
953
|
e.velocity.x -= t / r * p / e.mass, e.velocity.y -= i / r * p / e.mass, e.velocity.z -= o / r * p / e.mass;
|
|
951
954
|
} else
|
|
952
|
-
for (const
|
|
953
|
-
|
|
955
|
+
for (const d of s.children)
|
|
956
|
+
d && this.calculateForceFromOctree(e, d);
|
|
954
957
|
}
|
|
955
958
|
/**
|
|
956
959
|
* Apply repulsion between two nodes (with cutoff)
|
|
@@ -960,8 +963,8 @@ class Le {
|
|
|
960
963
|
let a = t * t + i * i + o * o;
|
|
961
964
|
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
962
965
|
a < 0.01 && (a = 0.01);
|
|
963
|
-
const r = Math.sqrt(a),
|
|
964
|
-
e.velocity.x -= t / r *
|
|
966
|
+
const r = Math.sqrt(a), d = this.getEffectiveRepulsion() * this.alpha / a;
|
|
967
|
+
e.velocity.x -= t / r * d / e.mass, e.velocity.y -= i / r * d / e.mass, e.velocity.z -= o / r * d / e.mass;
|
|
965
968
|
}
|
|
966
969
|
/**
|
|
967
970
|
* Calculate attraction forces along edges
|
|
@@ -971,9 +974,9 @@ class Le {
|
|
|
971
974
|
for (const t of this.edges) {
|
|
972
975
|
const i = this.nodes.get(t.source), o = this.nodes.get(t.target);
|
|
973
976
|
if (!i || !o) continue;
|
|
974
|
-
const a = o.position.x - i.position.x, r = o.position.y - i.position.y, c = o.position.z - i.position.z,
|
|
975
|
-
if (
|
|
976
|
-
const x = (
|
|
977
|
+
const a = o.position.x - i.position.x, r = o.position.y - i.position.y, c = o.position.z - i.position.z, d = Math.sqrt(a * a + r * r + c * c);
|
|
978
|
+
if (d < 0.01) continue;
|
|
979
|
+
const x = (d - 15) * this.attractionStrength * s * this.alpha, m = a / d * x, u = r / d * x, f = c / d * x;
|
|
977
980
|
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += f / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= f / o.mass;
|
|
978
981
|
}
|
|
979
982
|
}
|
|
@@ -1085,7 +1088,7 @@ class xt {
|
|
|
1085
1088
|
const f = (u.position.x >= o ? 1 : 0) + (u.position.y >= a ? 2 : 0) + (u.position.z >= r ? 4 : 0);
|
|
1086
1089
|
c[f].push(u);
|
|
1087
1090
|
}
|
|
1088
|
-
const
|
|
1091
|
+
const d = [
|
|
1089
1092
|
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: a, z: r } },
|
|
1090
1093
|
{ min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: r } },
|
|
1091
1094
|
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
|
|
@@ -1099,7 +1102,7 @@ class xt {
|
|
|
1099
1102
|
const m = { x: 0, y: 0, z: 0 };
|
|
1100
1103
|
for (let u = 0; u < 8; u++)
|
|
1101
1104
|
if (c[u].length > 0) {
|
|
1102
|
-
const f = this.buildTree(c[u],
|
|
1105
|
+
const f = this.buildTree(c[u], d[u], t + 1);
|
|
1103
1106
|
p.push(f), x += f.mass, m.x += f.centerOfMass.x * f.mass, m.y += f.centerOfMass.y * f.mass, m.z += f.centerOfMass.z * f.mass;
|
|
1104
1107
|
} else
|
|
1105
1108
|
p.push(null);
|
|
@@ -1230,9 +1233,9 @@ class vt {
|
|
|
1230
1233
|
);
|
|
1231
1234
|
r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), a.fillStyle = r, a.fillRect(0, 0, 256, 256);
|
|
1232
1235
|
const c = a.getImageData(0, 0, 256, 256);
|
|
1233
|
-
for (let
|
|
1236
|
+
for (let d = 0; d < c.data.length; d += 4) {
|
|
1234
1237
|
const p = (Math.random() - 0.5) * 5;
|
|
1235
|
-
c.data[
|
|
1238
|
+
c.data[d] = Math.min(255, Math.max(0, c.data[d] + p)), c.data[d + 1] = Math.min(255, Math.max(0, c.data[d + 1] + p)), c.data[d + 2] = Math.min(255, Math.max(0, c.data[d + 2] + p));
|
|
1236
1239
|
}
|
|
1237
1240
|
a.putImageData(c, 0, 0), s.push(o);
|
|
1238
1241
|
}
|
|
@@ -1503,15 +1506,15 @@ class wt {
|
|
|
1503
1506
|
o.z
|
|
1504
1507
|
]);
|
|
1505
1508
|
a.setAttribute("position", new y.BufferAttribute(r, 3));
|
|
1506
|
-
const c = this.getDefaultMaterial().clone(),
|
|
1507
|
-
return
|
|
1509
|
+
const c = this.getDefaultMaterial().clone(), d = new y.Line(a, c);
|
|
1510
|
+
return d.name = `edge-${e.source}-${e.target}`, d.userData = {
|
|
1508
1511
|
source: e.source,
|
|
1509
1512
|
target: e.target,
|
|
1510
1513
|
edge: e,
|
|
1511
1514
|
sourceNode: s,
|
|
1512
1515
|
targetNode: t
|
|
1513
|
-
},
|
|
1514
|
-
line:
|
|
1516
|
+
}, d.frustumCulled = !0, {
|
|
1517
|
+
line: d,
|
|
1515
1518
|
source: e.source,
|
|
1516
1519
|
target: e.target
|
|
1517
1520
|
};
|
|
@@ -2311,7 +2314,7 @@ class zt {
|
|
|
2311
2314
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2312
2315
|
}
|
|
2313
2316
|
}
|
|
2314
|
-
class
|
|
2317
|
+
class kt {
|
|
2315
2318
|
constructor() {
|
|
2316
2319
|
l(this, "tooltip", null);
|
|
2317
2320
|
l(this, "visible", !1);
|
|
@@ -2413,7 +2416,7 @@ class Tt {
|
|
|
2413
2416
|
this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
|
|
2414
2417
|
}
|
|
2415
2418
|
}
|
|
2416
|
-
class
|
|
2419
|
+
class Tt {
|
|
2417
2420
|
constructor(e, s) {
|
|
2418
2421
|
l(this, "container");
|
|
2419
2422
|
l(this, "searchContainer", null);
|
|
@@ -2679,6 +2682,89 @@ class Pt {
|
|
|
2679
2682
|
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2680
2683
|
}
|
|
2681
2684
|
}
|
|
2685
|
+
class Lt {
|
|
2686
|
+
constructor(e) {
|
|
2687
|
+
l(this, "container");
|
|
2688
|
+
l(this, "legendContainer", null);
|
|
2689
|
+
l(this, "nodeCountElement", null);
|
|
2690
|
+
l(this, "linkCountElement", null);
|
|
2691
|
+
this.container = e, this.init();
|
|
2692
|
+
}
|
|
2693
|
+
init() {
|
|
2694
|
+
this.createLegendUI(), this.injectStyles();
|
|
2695
|
+
}
|
|
2696
|
+
createLegendUI() {
|
|
2697
|
+
this.legendContainer = document.createElement("div"), this.legendContainer.className = "f3d-legend-container";
|
|
2698
|
+
const e = document.createElement("div");
|
|
2699
|
+
e.className = "f3d-legend-title", e.textContent = "Graph";
|
|
2700
|
+
const s = this.createStatRow("Nodes"), t = this.createStatRow("Links");
|
|
2701
|
+
this.nodeCountElement = s.valueElement, this.linkCountElement = t.valueElement, this.legendContainer.appendChild(e), this.legendContainer.appendChild(s.rowElement), this.legendContainer.appendChild(t.rowElement), this.container.appendChild(this.legendContainer);
|
|
2702
|
+
}
|
|
2703
|
+
createStatRow(e) {
|
|
2704
|
+
const s = document.createElement("div");
|
|
2705
|
+
s.className = "f3d-legend-row";
|
|
2706
|
+
const t = document.createElement("span");
|
|
2707
|
+
t.className = "f3d-legend-label", t.textContent = e;
|
|
2708
|
+
const i = document.createElement("span");
|
|
2709
|
+
return i.className = "f3d-legend-value", i.textContent = "0", s.appendChild(t), s.appendChild(i), { rowElement: s, valueElement: i };
|
|
2710
|
+
}
|
|
2711
|
+
injectStyles() {
|
|
2712
|
+
const e = "f3d-legend-styles";
|
|
2713
|
+
if (document.getElementById(e))
|
|
2714
|
+
return;
|
|
2715
|
+
const s = document.createElement("style");
|
|
2716
|
+
s.id = e, s.textContent = `
|
|
2717
|
+
.f3d-legend-container {
|
|
2718
|
+
position: absolute;
|
|
2719
|
+
right: 20px;
|
|
2720
|
+
bottom: 20px;
|
|
2721
|
+
z-index: 100;
|
|
2722
|
+
min-width: 140px;
|
|
2723
|
+
padding: 10px 12px;
|
|
2724
|
+
display: flex;
|
|
2725
|
+
flex-direction: column;
|
|
2726
|
+
gap: 8px;
|
|
2727
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2728
|
+
backdrop-filter: blur(20px);
|
|
2729
|
+
-webkit-backdrop-filter: blur(20px);
|
|
2730
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2731
|
+
border-radius: 12px;
|
|
2732
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
2733
|
+
color: white;
|
|
2734
|
+
font-family: inherit;
|
|
2735
|
+
pointer-events: none;
|
|
2736
|
+
}
|
|
2737
|
+
.f3d-legend-title {
|
|
2738
|
+
font-size: 10px;
|
|
2739
|
+
letter-spacing: 1px;
|
|
2740
|
+
text-transform: uppercase;
|
|
2741
|
+
font-weight: 700;
|
|
2742
|
+
color: rgba(255, 255, 255, 0.55);
|
|
2743
|
+
}
|
|
2744
|
+
.f3d-legend-row {
|
|
2745
|
+
display: flex;
|
|
2746
|
+
align-items: center;
|
|
2747
|
+
justify-content: space-between;
|
|
2748
|
+
gap: 12px;
|
|
2749
|
+
}
|
|
2750
|
+
.f3d-legend-label {
|
|
2751
|
+
font-size: 12px;
|
|
2752
|
+
color: rgba(255, 255, 255, 0.75);
|
|
2753
|
+
}
|
|
2754
|
+
.f3d-legend-value {
|
|
2755
|
+
font-size: 13px;
|
|
2756
|
+
font-weight: 700;
|
|
2757
|
+
color: rgba(255, 153, 102, 0.95);
|
|
2758
|
+
}
|
|
2759
|
+
`, document.head.appendChild(s);
|
|
2760
|
+
}
|
|
2761
|
+
updateCounts(e, s) {
|
|
2762
|
+
this.nodeCountElement && (this.nodeCountElement.textContent = e.toString()), this.linkCountElement && (this.linkCountElement.textContent = s.toString());
|
|
2763
|
+
}
|
|
2764
|
+
dispose() {
|
|
2765
|
+
this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2682
2768
|
const Rt = {
|
|
2683
2769
|
backgroundColor: "#0a0a0a",
|
|
2684
2770
|
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
@@ -2782,9 +2868,9 @@ class It {
|
|
|
2782
2868
|
for (const i of this.edges) {
|
|
2783
2869
|
const o = this.nodes.get(i.source), a = this.nodes.get(i.target);
|
|
2784
2870
|
if (!o || !a) continue;
|
|
2785
|
-
const r = a.x - o.x, c = a.y - o.y,
|
|
2786
|
-
if (
|
|
2787
|
-
const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * c) /
|
|
2871
|
+
const r = a.x - o.x, c = a.y - o.y, d = r * r + c * c;
|
|
2872
|
+
if (d === 0) continue;
|
|
2873
|
+
const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * c) / d)), x = o.x + p * r, m = o.y + p * c, u = e - x, f = s - m;
|
|
2788
2874
|
if (Math.sqrt(u * u + f * f) < 12)
|
|
2789
2875
|
return i;
|
|
2790
2876
|
}
|
|
@@ -2803,22 +2889,22 @@ class It {
|
|
|
2803
2889
|
let o = 0;
|
|
2804
2890
|
for (let r = 0; r < s; r++)
|
|
2805
2891
|
for (let c = r + 1; c < s; c++) {
|
|
2806
|
-
const
|
|
2807
|
-
let x = p.x -
|
|
2892
|
+
const d = e[r], p = e[c];
|
|
2893
|
+
let x = p.x - d.x, m = p.y - d.y, u = Math.sqrt(x * x + m * m);
|
|
2808
2894
|
if (u < t * 3) {
|
|
2809
2895
|
u < 1 && (u = 1);
|
|
2810
2896
|
const f = this.options.repulsionStrength / (u * u), b = x / u * f, M = m / u * f;
|
|
2811
|
-
|
|
2897
|
+
d.vx -= b, d.vy -= M, p.vx += b, p.vy += M;
|
|
2812
2898
|
}
|
|
2813
2899
|
}
|
|
2814
2900
|
const a = 80;
|
|
2815
2901
|
for (const r of this.edges) {
|
|
2816
|
-
const c = this.nodes.get(r.source),
|
|
2817
|
-
if (!c || !
|
|
2818
|
-
let p =
|
|
2902
|
+
const c = this.nodes.get(r.source), d = this.nodes.get(r.target);
|
|
2903
|
+
if (!c || !d) continue;
|
|
2904
|
+
let p = d.x - c.x, x = d.y - c.y, m = Math.sqrt(p * p + x * x);
|
|
2819
2905
|
m < 1 && (m = 1);
|
|
2820
2906
|
const f = (m - a) * this.options.attractionStrength, b = p / m * f, M = x / m * f;
|
|
2821
|
-
c.vx += b, c.vy += M,
|
|
2907
|
+
c.vx += b, c.vy += M, d.vx -= b, d.vy -= M;
|
|
2822
2908
|
}
|
|
2823
2909
|
for (const r of e) {
|
|
2824
2910
|
if (this.draggedNode === r) continue;
|
|
@@ -2836,8 +2922,8 @@ class It {
|
|
|
2836
2922
|
const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, a = this.transform.x % i, r = this.transform.y % i;
|
|
2837
2923
|
t.fillStyle = this.options.gridColor;
|
|
2838
2924
|
for (let c = a; c < e; c += i)
|
|
2839
|
-
for (let
|
|
2840
|
-
t.beginPath(), t.arc(c,
|
|
2925
|
+
for (let d = r; d < s; d += i)
|
|
2926
|
+
t.beginPath(), t.arc(c, d, o, 0, Math.PI * 2), t.fill();
|
|
2841
2927
|
}
|
|
2842
2928
|
renderEdges() {
|
|
2843
2929
|
const e = this.ctx;
|
|
@@ -2870,8 +2956,8 @@ class It {
|
|
|
2870
2956
|
s.x,
|
|
2871
2957
|
s.y,
|
|
2872
2958
|
a
|
|
2873
|
-
), c = s.color >> 16 & 255,
|
|
2874
|
-
r.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255,
|
|
2959
|
+
), c = s.color >> 16 & 255, d = s.color >> 8 & 255, p = s.color & 255;
|
|
2960
|
+
r.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255, d + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${c}, ${d}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, c - 40)}, ${Math.max(0, d - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, a, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - a * 0.25, s.y - a * 0.25, a * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
|
|
2875
2961
|
const x = a * 1.6;
|
|
2876
2962
|
let m = s.label, u = e.measureText(m).width;
|
|
2877
2963
|
if (u > x) {
|
|
@@ -2978,11 +3064,11 @@ class It {
|
|
|
2978
3064
|
focusOnNode(e) {
|
|
2979
3065
|
const s = this.nodes.get(e);
|
|
2980
3066
|
if (!s) return;
|
|
2981
|
-
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, a = this.transform.y, r = 500, c = performance.now(),
|
|
3067
|
+
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, a = this.transform.y, r = 500, c = performance.now(), d = () => {
|
|
2982
3068
|
const p = performance.now() - c, x = Math.min(p / r, 1), m = 1 - Math.pow(1 - x, 3);
|
|
2983
|
-
this.transform.x = o + (t - o) * m, this.transform.y = a + (i - a) * m, x < 1 ? requestAnimationFrame(
|
|
3069
|
+
this.transform.x = o + (t - o) * m, this.transform.y = a + (i - a) * m, x < 1 ? requestAnimationFrame(d) : this.selectedNode = s;
|
|
2984
3070
|
};
|
|
2985
|
-
|
|
3071
|
+
d();
|
|
2986
3072
|
}
|
|
2987
3073
|
/**
|
|
2988
3074
|
* Updates node positions from 3D data
|
|
@@ -3050,6 +3136,7 @@ class Ht {
|
|
|
3050
3136
|
l(this, "edgeTooltipManager");
|
|
3051
3137
|
l(this, "searchManager", null);
|
|
3052
3138
|
l(this, "viewToggleManager", null);
|
|
3139
|
+
l(this, "legendManager", null);
|
|
3053
3140
|
// 2D Renderer
|
|
3054
3141
|
l(this, "forceGraph2D", null);
|
|
3055
3142
|
// Event system
|
|
@@ -3059,7 +3146,7 @@ class Ht {
|
|
|
3059
3146
|
l(this, "devControls", null);
|
|
3060
3147
|
l(this, "viewMode", "3d");
|
|
3061
3148
|
l(this, "graphData", null);
|
|
3062
|
-
this.options = { ...P, ...s }, this.container =
|
|
3149
|
+
this.options = { ...P, ...s }, this.container = ht(e), this.materialFactory = new vt(), this.nodeFactory = new Mt(
|
|
3063
3150
|
this.materialFactory,
|
|
3064
3151
|
this.options.nodeRadius ?? P.nodeRadius,
|
|
3065
3152
|
this.options.lodSegments ?? P.lodSegments,
|
|
@@ -3075,7 +3162,7 @@ class Ht {
|
|
|
3075
3162
|
), this.frustumCuller = new Ct(
|
|
3076
3163
|
this.sceneManager.camera,
|
|
3077
3164
|
this.options.enableEdgeCulling ?? P.enableEdgeCulling
|
|
3078
|
-
), this.nodeManager = new te(this.sceneManager, this.nodeFactory), this.edgeManager = new yt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new
|
|
3165
|
+
), this.nodeManager = new te(this.sceneManager, this.nodeFactory), this.edgeManager = new yt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Ie(
|
|
3079
3166
|
this.nodeManager.getAllNodes(),
|
|
3080
3167
|
this.edgeManager.getAllEdges(),
|
|
3081
3168
|
{
|
|
@@ -3090,11 +3177,11 @@ class Ht {
|
|
|
3090
3177
|
() => this.onSimulate(),
|
|
3091
3178
|
() => this.onRender(),
|
|
3092
3179
|
this.options.targetFPS ?? P.targetFPS
|
|
3093
|
-
), this.raycasterManager = new Nt(this.sceneManager, this.container), this.panelManager = new St(this.container), this.edgePanelManager = new zt(this.container), this.edgeTooltipManager = new
|
|
3180
|
+
), this.raycasterManager = new Nt(this.sceneManager, this.container), this.panelManager = new St(this.container), this.edgePanelManager = new zt(this.container), this.edgeTooltipManager = new kt(), this.edgePanelManager.setNodeClickCallback((t) => {
|
|
3094
3181
|
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3095
3182
|
this.showNodePanel(t);
|
|
3096
3183
|
}, 400);
|
|
3097
|
-
}), this.options.showSearch !== !1 && (this.searchManager = new
|
|
3184
|
+
}), this.options.showSearch !== !1 && (this.searchManager = new Tt(this.container, {
|
|
3098
3185
|
placeholder: this.options.searchPlaceholder,
|
|
3099
3186
|
onSearch: (t) => ({
|
|
3100
3187
|
nodeResults: this.searchNodes(t),
|
|
@@ -3110,7 +3197,7 @@ class Ht {
|
|
|
3110
3197
|
onViewChange: (t) => {
|
|
3111
3198
|
this.switchView(t);
|
|
3112
3199
|
}
|
|
3113
|
-
})), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
3200
|
+
})), this.options.showLegend !== !1 && (this.legendManager = new Lt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
3114
3201
|
}
|
|
3115
3202
|
/**
|
|
3116
3203
|
* Sets up internal callbacks
|
|
@@ -3169,6 +3256,12 @@ class Ht {
|
|
|
3169
3256
|
onRender() {
|
|
3170
3257
|
this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
|
|
3171
3258
|
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Updates bottom-right legend counts
|
|
3261
|
+
*/
|
|
3262
|
+
updateLegendCounts() {
|
|
3263
|
+
this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
|
|
3264
|
+
}
|
|
3172
3265
|
// ==========================================================================
|
|
3173
3266
|
// Public API
|
|
3174
3267
|
// ==========================================================================
|
|
@@ -3192,7 +3285,7 @@ class Ht {
|
|
|
3192
3285
|
if (o.edges && Array.isArray(o.edges))
|
|
3193
3286
|
for (const r of o.edges)
|
|
3194
3287
|
this.addEdge(r);
|
|
3195
|
-
this.graphEngine = new
|
|
3288
|
+
this.graphEngine = new Ie(
|
|
3196
3289
|
this.nodeManager.getAllNodes(),
|
|
3197
3290
|
this.edgeManager.getAllEdges(),
|
|
3198
3291
|
{
|
|
@@ -3202,17 +3295,17 @@ class Ht {
|
|
|
3202
3295
|
useBarnesHut: this.options.useBarnesHut,
|
|
3203
3296
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3204
3297
|
}
|
|
3205
|
-
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o);
|
|
3298
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o), this.updateLegendCounts();
|
|
3206
3299
|
}
|
|
3207
3300
|
/**
|
|
3208
3301
|
* Adds a node to the graph
|
|
3209
3302
|
* @returns true if added, false if node already exists or invalid
|
|
3210
3303
|
*/
|
|
3211
3304
|
addNode(e) {
|
|
3212
|
-
if (!
|
|
3305
|
+
if (!De(e))
|
|
3213
3306
|
return !1;
|
|
3214
3307
|
const s = this.nodeManager.addNode(e);
|
|
3215
|
-
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
|
|
3308
|
+
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
|
|
3216
3309
|
}
|
|
3217
3310
|
/**
|
|
3218
3311
|
* Removes a node from the graph
|
|
@@ -3223,7 +3316,7 @@ class Ht {
|
|
|
3223
3316
|
return !1;
|
|
3224
3317
|
this.edgeManager.removeEdgesForNode(e);
|
|
3225
3318
|
const s = this.nodeManager.removeNode(e);
|
|
3226
|
-
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide()), s;
|
|
3319
|
+
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
|
|
3227
3320
|
}
|
|
3228
3321
|
/**
|
|
3229
3322
|
* Updates a node's properties
|
|
@@ -3236,10 +3329,10 @@ class Ht {
|
|
|
3236
3329
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
3237
3330
|
*/
|
|
3238
3331
|
addEdge(e) {
|
|
3239
|
-
if (!
|
|
3332
|
+
if (!He(e))
|
|
3240
3333
|
return !1;
|
|
3241
3334
|
const s = this.edgeManager.addEdge(e);
|
|
3242
|
-
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), s;
|
|
3335
|
+
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
|
|
3243
3336
|
}
|
|
3244
3337
|
/**
|
|
3245
3338
|
* Removes an edge from the graph
|
|
@@ -3247,7 +3340,7 @@ class Ht {
|
|
|
3247
3340
|
*/
|
|
3248
3341
|
removeEdge(e, s) {
|
|
3249
3342
|
const t = this.edgeManager.removeEdge(e, s);
|
|
3250
|
-
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;
|
|
3343
|
+
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
|
|
3251
3344
|
}
|
|
3252
3345
|
/**
|
|
3253
3346
|
* Expands a node by fetching more data
|
|
@@ -3320,9 +3413,9 @@ class Ht {
|
|
|
3320
3413
|
x: i.x + r.x * s,
|
|
3321
3414
|
y: i.y + r.y * s,
|
|
3322
3415
|
z: i.z + r.z * s
|
|
3323
|
-
},
|
|
3416
|
+
}, d = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: a.target.x, y: a.target.y, z: a.target.z }, x = 800, m = performance.now(), u = () => {
|
|
3324
3417
|
const f = performance.now() - m, b = Math.min(f / x, 1), M = 1 - Math.pow(1 - b, 3);
|
|
3325
|
-
o.position.x =
|
|
3418
|
+
o.position.x = d.x + (c.x - d.x) * M, o.position.y = d.y + (c.y - d.y) * M, o.position.z = d.z + (c.z - d.z) * M, a.target.x = p.x + (i.x - p.x) * M, a.target.y = p.y + (i.y - p.y) * M, a.target.z = p.z + (i.z - p.z) * M, a.update(), b < 1 && requestAnimationFrame(u);
|
|
3326
3419
|
};
|
|
3327
3420
|
u();
|
|
3328
3421
|
}
|
|
@@ -3340,13 +3433,13 @@ class Ht {
|
|
|
3340
3433
|
x: (i.position.x + o.position.x) / 2,
|
|
3341
3434
|
y: (i.position.y + o.position.y) / 2,
|
|
3342
3435
|
z: (i.position.z + o.position.z) / 2
|
|
3343
|
-
},
|
|
3436
|
+
}, d = o.position.x - i.position.x, p = o.position.y - i.position.y, x = o.position.z - i.position.z, m = Math.sqrt(d * d + p * p + x * x), u = Math.max(m * t, 40), f = a.position.clone().sub(r.target).normalize(), b = {
|
|
3344
3437
|
x: c.x + f.x * u,
|
|
3345
3438
|
y: c.y + f.y * u,
|
|
3346
3439
|
z: c.z + f.z * u
|
|
3347
|
-
}, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800,
|
|
3348
|
-
const
|
|
3349
|
-
a.position.x = M.x + (b.x - M.x) * E, a.position.y = M.y + (b.y - M.y) * E, a.position.z = M.z + (b.z - M.z) * E, r.target.x = N.x + (c.x - N.x) * E, r.target.y = N.y + (c.y - N.y) * E, r.target.z = N.z + (c.z - N.z) * E, r.update(),
|
|
3440
|
+
}, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, L = performance.now(), Y = () => {
|
|
3441
|
+
const T = performance.now() - L, D = Math.min(T / O, 1), E = 1 - Math.pow(1 - D, 3);
|
|
3442
|
+
a.position.x = M.x + (b.x - M.x) * E, a.position.y = M.y + (b.y - M.y) * E, a.position.z = M.z + (b.z - M.z) * E, r.target.x = N.x + (c.x - N.x) * E, r.target.y = N.y + (c.y - N.y) * E, r.target.z = N.z + (c.z - N.z) * E, r.update(), D < 1 && requestAnimationFrame(Y);
|
|
3350
3443
|
};
|
|
3351
3444
|
Y();
|
|
3352
3445
|
}
|
|
@@ -3371,8 +3464,8 @@ class Ht {
|
|
|
3371
3464
|
return [];
|
|
3372
3465
|
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
3373
3466
|
return t.forEach((o) => {
|
|
3374
|
-
var
|
|
3375
|
-
const a = (
|
|
3467
|
+
var d, p, x;
|
|
3468
|
+
const a = (d = o.label) == null ? void 0 : d.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), c = (x = o.type) == null ? void 0 : x.toLowerCase().includes(s);
|
|
3376
3469
|
(a || r || c) && i.push(o);
|
|
3377
3470
|
}), i;
|
|
3378
3471
|
}
|
|
@@ -3387,8 +3480,8 @@ class Ht {
|
|
|
3387
3480
|
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
3388
3481
|
for (const a of t)
|
|
3389
3482
|
if ((o = a.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
|
|
3390
|
-
const c = this.nodeManager.getNode(a.source),
|
|
3391
|
-
c &&
|
|
3483
|
+
const c = this.nodeManager.getNode(a.source), d = this.nodeManager.getNode(a.target);
|
|
3484
|
+
c && d && i.push({ edge: a, sourceNode: c, targetNode: d });
|
|
3392
3485
|
}
|
|
3393
3486
|
return i;
|
|
3394
3487
|
}
|
|
@@ -3554,7 +3647,7 @@ class Ht {
|
|
|
3554
3647
|
* Destroys the graph and releases all resources
|
|
3555
3648
|
*/
|
|
3556
3649
|
destroy() {
|
|
3557
|
-
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
3650
|
+
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
3558
3651
|
}
|
|
3559
3652
|
}
|
|
3560
3653
|
const Oe = [
|
|
@@ -3611,9 +3704,9 @@ const Oe = [
|
|
|
3611
3704
|
16746564
|
|
3612
3705
|
// Darker tangerine
|
|
3613
3706
|
];
|
|
3614
|
-
function
|
|
3707
|
+
function At(h = 30) {
|
|
3615
3708
|
const e = [], s = [];
|
|
3616
|
-
for (let i = 0; i <
|
|
3709
|
+
for (let i = 0; i < h; i++) {
|
|
3617
3710
|
const o = i < Oe.length ? Oe[i] : `Node ${i + 1}`;
|
|
3618
3711
|
e.push({
|
|
3619
3712
|
id: `node-${i}`,
|
|
@@ -3626,7 +3719,7 @@ function Dt(d = 30) {
|
|
|
3626
3719
|
}
|
|
3627
3720
|
});
|
|
3628
3721
|
}
|
|
3629
|
-
for (let i = 1; i <
|
|
3722
|
+
for (let i = 1; i < h; i++) {
|
|
3630
3723
|
const o = Math.floor(Math.random() * i);
|
|
3631
3724
|
s.push({
|
|
3632
3725
|
source: `node-${i}`,
|
|
@@ -3634,11 +3727,11 @@ function Dt(d = 30) {
|
|
|
3634
3727
|
relationship: ee[Math.floor(Math.random() * ee.length)]
|
|
3635
3728
|
});
|
|
3636
3729
|
}
|
|
3637
|
-
const t = Math.floor(
|
|
3730
|
+
const t = Math.floor(h * 0.5);
|
|
3638
3731
|
for (let i = 0; i < t; i++) {
|
|
3639
|
-
const o = Math.floor(Math.random() *
|
|
3640
|
-
let a = Math.floor(Math.random() *
|
|
3641
|
-
o === a && (a = (a + 1) %
|
|
3732
|
+
const o = Math.floor(Math.random() * h);
|
|
3733
|
+
let a = Math.floor(Math.random() * h);
|
|
3734
|
+
o === a && (a = (a + 1) % h);
|
|
3642
3735
|
const r = `node-${o}`, c = `node-${a}`;
|
|
3643
3736
|
s.some(
|
|
3644
3737
|
(p) => p.source === r && p.target === c || p.source === c && p.target === r
|
|
@@ -3650,15 +3743,15 @@ function Dt(d = 30) {
|
|
|
3650
3743
|
}
|
|
3651
3744
|
return { nodes: e, edges: s };
|
|
3652
3745
|
}
|
|
3653
|
-
function
|
|
3654
|
-
const e = [], s = [], t = Math.ceil(
|
|
3746
|
+
function jt(h = 1e3) {
|
|
3747
|
+
const e = [], s = [], t = Math.ceil(h / 50), i = [];
|
|
3655
3748
|
for (let o = 0; o < t; o++)
|
|
3656
3749
|
i.push({
|
|
3657
3750
|
x: (Math.random() - 0.5) * 200,
|
|
3658
3751
|
y: (Math.random() - 0.5) * 200,
|
|
3659
3752
|
z: (Math.random() - 0.5) * 200
|
|
3660
3753
|
});
|
|
3661
|
-
for (let o = 0; o <
|
|
3754
|
+
for (let o = 0; o < h; o++) {
|
|
3662
3755
|
const a = i[o % t];
|
|
3663
3756
|
e.push({
|
|
3664
3757
|
id: `node-${o}`,
|
|
@@ -3670,7 +3763,7 @@ function At(d = 1e3) {
|
|
|
3670
3763
|
}
|
|
3671
3764
|
});
|
|
3672
3765
|
}
|
|
3673
|
-
for (let o = 1; o <
|
|
3766
|
+
for (let o = 1; o < h; o++) {
|
|
3674
3767
|
const a = Math.floor(o / 50) * 50, r = a === 0 ? Math.floor(Math.random() * o) : a + Math.floor(Math.random() * Math.min(o - a, 50));
|
|
3675
3768
|
s.push({
|
|
3676
3769
|
source: `node-${o}`,
|
|
@@ -3692,10 +3785,10 @@ export {
|
|
|
3692
3785
|
P as DEFAULT_OPTIONS,
|
|
3693
3786
|
Ht as ForceGraph3D,
|
|
3694
3787
|
X as LODLevel,
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3788
|
+
H as createEdgeKey,
|
|
3789
|
+
jt as generateLargeSampleData,
|
|
3790
|
+
At as generateSampleData,
|
|
3791
|
+
He as validateEdgeData,
|
|
3792
|
+
De as validateNodeData
|
|
3700
3793
|
};
|
|
3701
3794
|
//# sourceMappingURL=force-3d-graph.js.map
|