force-3d-graph 1.2.6 → 1.3.7
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 +895 -614
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +197 -71
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +7 -1
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var l = (
|
|
4
|
-
import * as
|
|
5
|
-
import { EventDispatcher as
|
|
1
|
+
var it = Object.defineProperty;
|
|
2
|
+
var ot = (h, e, s) => e in h ? it(h, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : h[e] = s;
|
|
3
|
+
var l = (h, e, s) => ot(h, typeof e != "symbol" ? e + "" : e, s);
|
|
4
|
+
import * as x from "three";
|
|
5
|
+
import { EventDispatcher as nt, Vector3 as S, MOUSE as j, TOUCH as $, Spherical as Se, Quaternion as ze, Vector2 as k, Ray as at, Plane as rt, MathUtils as lt } from "three";
|
|
6
6
|
const P = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
@@ -28,99 +28,100 @@ 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
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
return
|
|
35
|
+
var _ = /* @__PURE__ */ ((h) => (h[h.HIGH = 0] = "HIGH", h[h.MEDIUM = 1] = "MEDIUM", h[h.LOW = 2] = "LOW", h))(_ || {});
|
|
36
|
+
function ct() {
|
|
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 dt(
|
|
47
|
-
return
|
|
47
|
+
function dt(h) {
|
|
48
|
+
return h && h instanceof HTMLElement ? h : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ct());
|
|
48
49
|
}
|
|
49
|
-
function ke(
|
|
50
|
-
const e =
|
|
50
|
+
function ke(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 Fe(h) {
|
|
58
|
+
if (!h || typeof h != "object")
|
|
58
59
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
59
|
-
const e =
|
|
60
|
-
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 && !
|
|
60
|
+
const e = h;
|
|
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 && !pt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
|
|
61
62
|
}
|
|
62
|
-
function De(
|
|
63
|
-
if (!
|
|
63
|
+
function De(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
|
|
69
|
-
return typeof
|
|
69
|
+
function ht(h) {
|
|
70
|
+
return typeof h != "string" || h.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
|
|
70
71
|
}
|
|
71
|
-
function
|
|
72
|
-
if (!
|
|
73
|
-
const e =
|
|
72
|
+
function pt(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 Dt(h, e) {
|
|
78
|
+
return h === e ? `${h}-${e}` : h < e ? `${h}-${e}` : `${e}-${h}`;
|
|
78
79
|
}
|
|
79
|
-
const
|
|
80
|
-
class
|
|
80
|
+
const Te = { type: "change" }, le = { type: "start" }, Pe = { type: "end" }, Q = new at(), Le = new rt(), gt = Math.cos(70 * lt.DEG2RAD);
|
|
81
|
+
class ut extends nt {
|
|
81
82
|
constructor(e, s) {
|
|
82
|
-
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:
|
|
83
|
+
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: j.ROTATE, MIDDLE: j.DOLLY, RIGHT: j.PAN }, this.touches = { ONE: $.ROTATE, TWO: $.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
83
84
|
return r.phi;
|
|
84
85
|
}, this.getAzimuthalAngle = function() {
|
|
85
86
|
return r.theta;
|
|
86
87
|
}, this.getDistance = function() {
|
|
87
88
|
return this.object.position.distanceTo(this.target);
|
|
88
|
-
}, this.listenToKeyEvents = function(
|
|
89
|
-
|
|
89
|
+
}, this.listenToKeyEvents = function(a) {
|
|
90
|
+
a.addEventListener("keydown", ae), this._domElementKeyEvents = a;
|
|
90
91
|
}, this.stopListenToKeyEvents = function() {
|
|
91
|
-
this._domElementKeyEvents.removeEventListener("keydown",
|
|
92
|
+
this._domElementKeyEvents.removeEventListener("keydown", ae), this._domElementKeyEvents = null;
|
|
92
93
|
}, this.saveState = function() {
|
|
93
94
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
94
95
|
}, this.reset = function() {
|
|
95
|
-
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(
|
|
96
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Te), t.update(), o = i.NONE;
|
|
96
97
|
}, this.update = function() {
|
|
97
|
-
const
|
|
98
|
-
return function(
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
let
|
|
102
|
-
isFinite(
|
|
103
|
-
let
|
|
104
|
-
if (t.zoomToCursor &&
|
|
105
|
-
let
|
|
98
|
+
const a = new S(), g = new ze().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), M = new S(), C = new ze(), F = new S(), z = 2 * Math.PI;
|
|
99
|
+
return function(st = null) {
|
|
100
|
+
const Ne = t.object.position;
|
|
101
|
+
a.copy(Ne).sub(t.target), a.applyQuaternion(g), r.setFromVector3(a), t.autoRotate && o === i.NONE && Y(He(st)), t.enableDamping ? (r.theta += d.theta * t.dampingFactor, r.phi += d.phi * t.dampingFactor) : (r.theta += d.theta, r.phi += d.phi);
|
|
102
|
+
let R = t.minAzimuthAngle, I = t.maxAzimuthAngle;
|
|
103
|
+
isFinite(R) && isFinite(I) && (R < -Math.PI ? R += z : R > Math.PI && (R -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), R <= I ? r.theta = Math.max(R, Math.min(I, r.theta)) : r.theta = r.theta > (R + I) / 2 ? Math.max(R, r.theta) : Math.min(I, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && D || t.object.isOrthographicCamera ? r.radius = oe(r.radius) : r.radius = oe(r.radius * c), a.setFromSpherical(r), a.applyQuaternion(v), Ne.copy(t.target).add(a), t.object.lookAt(t.target), t.enableDamping === !0 ? (d.theta *= 1 - t.dampingFactor, d.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (d.set(0, 0, 0), p.set(0, 0, 0));
|
|
104
|
+
let re = !1;
|
|
105
|
+
if (t.zoomToCursor && D) {
|
|
106
|
+
let K = null;
|
|
106
107
|
if (t.object.isPerspectiveCamera) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
t.object.position.addScaledVector(
|
|
108
|
+
const U = a.length();
|
|
109
|
+
K = oe(U * c);
|
|
110
|
+
const Z = U - K;
|
|
111
|
+
t.object.position.addScaledVector(G, Z), t.object.updateMatrixWorld();
|
|
111
112
|
} else if (t.object.isOrthographicCamera) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
113
|
+
const U = new S(T.x, T.y, 0);
|
|
114
|
+
U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0;
|
|
115
|
+
const Z = new S(T.x, T.y, 0);
|
|
116
|
+
Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(), K = a.length();
|
|
116
117
|
} else
|
|
117
118
|
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
|
|
118
|
-
|
|
119
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
120
|
-
return
|
|
119
|
+
K !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(K).add(t.object.position) : (Q.origin.copy(t.object.position), Q.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Q.direction)) < gt ? e.lookAt(t.target) : (Le.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Le, t.target))));
|
|
120
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0);
|
|
121
|
+
return c = 1, D = !1, re || M.distanceToSquared(t.object.position) > n || 8 * (1 - C.dot(t.object.quaternion)) > n || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Te), M.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
|
|
121
122
|
};
|
|
122
123
|
}(), this.dispose = function() {
|
|
123
|
-
t.domElement.removeEventListener("contextmenu",
|
|
124
|
+
t.domElement.removeEventListener("contextmenu", Ee), t.domElement.removeEventListener("pointerdown", ve), t.domElement.removeEventListener("pointercancel", B), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ae), t._domElementKeyEvents = null);
|
|
124
125
|
};
|
|
125
126
|
const t = this, i = {
|
|
126
127
|
NONE: -1,
|
|
@@ -133,174 +134,174 @@ class ft extends at {
|
|
|
133
134
|
TOUCH_DOLLY_ROTATE: 6
|
|
134
135
|
};
|
|
135
136
|
let o = i.NONE;
|
|
136
|
-
const
|
|
137
|
-
let
|
|
138
|
-
const p = new S(),
|
|
139
|
-
let
|
|
140
|
-
const E = [],
|
|
141
|
-
let
|
|
142
|
-
function
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
function
|
|
146
|
-
const g = Math.abs(
|
|
137
|
+
const n = 1e-6, r = new Se(), d = new Se();
|
|
138
|
+
let c = 1;
|
|
139
|
+
const p = new S(), f = new k(), m = new k(), u = new k(), y = new k(), b = new k(), w = new k(), N = new k(), O = new k(), L = new k(), G = new S(), T = new k();
|
|
140
|
+
let D = !1;
|
|
141
|
+
const E = [], V = {};
|
|
142
|
+
let te = !1;
|
|
143
|
+
function He(a) {
|
|
144
|
+
return a !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * a : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
|
|
145
|
+
}
|
|
146
|
+
function q(a) {
|
|
147
|
+
const g = Math.abs(a * 0.01);
|
|
147
148
|
return Math.pow(0.95, t.zoomSpeed * g);
|
|
148
149
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
150
|
+
function Y(a) {
|
|
151
|
+
d.theta -= a;
|
|
151
152
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
153
|
+
function W(a) {
|
|
154
|
+
d.phi -= a;
|
|
154
155
|
}
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
return function(v,
|
|
158
|
-
|
|
156
|
+
const ce = function() {
|
|
157
|
+
const a = new S();
|
|
158
|
+
return function(v, M) {
|
|
159
|
+
a.setFromMatrixColumn(M, 0), a.multiplyScalar(-v), p.add(a);
|
|
159
160
|
};
|
|
160
161
|
}(), de = function() {
|
|
161
|
-
const
|
|
162
|
-
return function(v,
|
|
163
|
-
t.screenSpacePanning === !0 ?
|
|
162
|
+
const a = new S();
|
|
163
|
+
return function(v, M) {
|
|
164
|
+
t.screenSpacePanning === !0 ? a.setFromMatrixColumn(M, 1) : (a.setFromMatrixColumn(M, 0), a.crossVectors(t.object.up, a)), a.multiplyScalar(v), p.add(a);
|
|
164
165
|
};
|
|
165
|
-
}(),
|
|
166
|
-
const
|
|
167
|
-
return function(v,
|
|
166
|
+
}(), H = function() {
|
|
167
|
+
const a = new S();
|
|
168
|
+
return function(v, M) {
|
|
168
169
|
const C = t.domElement;
|
|
169
170
|
if (t.object.isPerspectiveCamera) {
|
|
170
171
|
const F = t.object.position;
|
|
171
|
-
|
|
172
|
-
let z =
|
|
173
|
-
z *= Math.tan(t.object.fov / 2 * Math.PI / 180),
|
|
174
|
-
} else t.object.isOrthographicCamera ? (
|
|
172
|
+
a.copy(F).sub(t.target);
|
|
173
|
+
let z = a.length();
|
|
174
|
+
z *= Math.tan(t.object.fov / 2 * Math.PI / 180), ce(2 * v * z / C.clientHeight, t.object.matrix), de(2 * M * z / C.clientHeight, t.object.matrix);
|
|
175
|
+
} else t.object.isOrthographicCamera ? (ce(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), de(M * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
175
176
|
};
|
|
176
177
|
}();
|
|
177
|
-
function
|
|
178
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
178
|
+
function se(a) {
|
|
179
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c /= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
179
180
|
}
|
|
180
|
-
function
|
|
181
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
181
|
+
function he(a) {
|
|
182
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c *= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
182
183
|
}
|
|
183
|
-
function
|
|
184
|
+
function ie(a, g) {
|
|
184
185
|
if (!t.zoomToCursor)
|
|
185
186
|
return;
|
|
186
|
-
|
|
187
|
-
const v = t.domElement.getBoundingClientRect(),
|
|
188
|
-
|
|
187
|
+
D = !0;
|
|
188
|
+
const v = t.domElement.getBoundingClientRect(), M = a - v.left, C = g - v.top, F = v.width, z = v.height;
|
|
189
|
+
T.x = M / F * 2 - 1, T.y = -(C / z) * 2 + 1, G.set(T.x, T.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
189
190
|
}
|
|
190
|
-
function
|
|
191
|
-
return Math.max(t.minDistance, Math.min(t.maxDistance,
|
|
191
|
+
function oe(a) {
|
|
192
|
+
return Math.max(t.minDistance, Math.min(t.maxDistance, a));
|
|
192
193
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
194
|
+
function pe(a) {
|
|
195
|
+
f.set(a.clientX, a.clientY);
|
|
195
196
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
197
|
+
function Ae(a) {
|
|
198
|
+
ie(a.clientX, a.clientX), N.set(a.clientX, a.clientY);
|
|
198
199
|
}
|
|
199
|
-
function
|
|
200
|
-
|
|
200
|
+
function ge(a) {
|
|
201
|
+
y.set(a.clientX, a.clientY);
|
|
201
202
|
}
|
|
202
|
-
function
|
|
203
|
-
m.set(
|
|
203
|
+
function je(a) {
|
|
204
|
+
m.set(a.clientX, a.clientY), u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
|
|
204
205
|
const g = t.domElement;
|
|
205
|
-
|
|
206
|
+
Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m), t.update();
|
|
206
207
|
}
|
|
207
|
-
function
|
|
208
|
-
O.set(
|
|
208
|
+
function $e(a) {
|
|
209
|
+
O.set(a.clientX, a.clientY), L.subVectors(O, N), L.y > 0 ? se(q(L.y)) : L.y < 0 && he(q(L.y)), N.copy(O), t.update();
|
|
209
210
|
}
|
|
210
|
-
function
|
|
211
|
-
b.set(
|
|
211
|
+
function Ge(a) {
|
|
212
|
+
b.set(a.clientX, a.clientY), w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b), t.update();
|
|
212
213
|
}
|
|
213
|
-
function
|
|
214
|
-
|
|
214
|
+
function Ye(a) {
|
|
215
|
+
ie(a.clientX, a.clientY), a.deltaY < 0 ? he(q(a.deltaY)) : a.deltaY > 0 && se(q(a.deltaY)), t.update();
|
|
215
216
|
}
|
|
216
|
-
function
|
|
217
|
+
function Be(a) {
|
|
217
218
|
let g = !1;
|
|
218
|
-
switch (
|
|
219
|
+
switch (a.code) {
|
|
219
220
|
case t.keys.UP:
|
|
220
|
-
|
|
221
|
+
a.ctrlKey || a.metaKey || a.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, t.keyPanSpeed), g = !0;
|
|
221
222
|
break;
|
|
222
223
|
case t.keys.BOTTOM:
|
|
223
|
-
|
|
224
|
+
a.ctrlKey || a.metaKey || a.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, -t.keyPanSpeed), g = !0;
|
|
224
225
|
break;
|
|
225
226
|
case t.keys.LEFT:
|
|
226
|
-
|
|
227
|
+
a.ctrlKey || a.metaKey || a.shiftKey ? Y(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(t.keyPanSpeed, 0), g = !0;
|
|
227
228
|
break;
|
|
228
229
|
case t.keys.RIGHT:
|
|
229
|
-
|
|
230
|
+
a.ctrlKey || a.metaKey || a.shiftKey ? Y(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(-t.keyPanSpeed, 0), g = !0;
|
|
230
231
|
break;
|
|
231
232
|
}
|
|
232
|
-
g && (
|
|
233
|
+
g && (a.preventDefault(), t.update());
|
|
233
234
|
}
|
|
234
|
-
function
|
|
235
|
+
function ue(a) {
|
|
235
236
|
if (E.length === 1)
|
|
236
|
-
|
|
237
|
+
f.set(a.pageX, a.pageY);
|
|
237
238
|
else {
|
|
238
|
-
const g =
|
|
239
|
-
|
|
239
|
+
const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
|
|
240
|
+
f.set(v, M);
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
|
-
function
|
|
243
|
+
function fe(a) {
|
|
243
244
|
if (E.length === 1)
|
|
244
|
-
|
|
245
|
+
y.set(a.pageX, a.pageY);
|
|
245
246
|
else {
|
|
246
|
-
const g =
|
|
247
|
-
|
|
247
|
+
const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
|
|
248
|
+
y.set(v, M);
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
|
-
function
|
|
251
|
-
const g =
|
|
251
|
+
function me(a) {
|
|
252
|
+
const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
|
|
252
253
|
N.set(0, C);
|
|
253
254
|
}
|
|
254
|
-
function
|
|
255
|
-
t.enableZoom &&
|
|
255
|
+
function Ke(a) {
|
|
256
|
+
t.enableZoom && me(a), t.enablePan && fe(a);
|
|
256
257
|
}
|
|
257
|
-
function
|
|
258
|
-
t.enableZoom &&
|
|
258
|
+
function Ue(a) {
|
|
259
|
+
t.enableZoom && me(a), t.enableRotate && ue(a);
|
|
259
260
|
}
|
|
260
|
-
function
|
|
261
|
+
function ye(a) {
|
|
261
262
|
if (E.length == 1)
|
|
262
|
-
m.set(
|
|
263
|
+
m.set(a.pageX, a.pageY);
|
|
263
264
|
else {
|
|
264
|
-
const v =
|
|
265
|
-
m.set(
|
|
265
|
+
const v = A(a), M = 0.5 * (a.pageX + v.x), C = 0.5 * (a.pageY + v.y);
|
|
266
|
+
m.set(M, C);
|
|
266
267
|
}
|
|
267
|
-
u.subVectors(m,
|
|
268
|
+
u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
|
|
268
269
|
const g = t.domElement;
|
|
269
|
-
|
|
270
|
+
Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m);
|
|
270
271
|
}
|
|
271
|
-
function
|
|
272
|
+
function xe(a) {
|
|
272
273
|
if (E.length === 1)
|
|
273
|
-
b.set(
|
|
274
|
+
b.set(a.pageX, a.pageY);
|
|
274
275
|
else {
|
|
275
|
-
const g =
|
|
276
|
-
b.set(v,
|
|
276
|
+
const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
|
|
277
|
+
b.set(v, M);
|
|
277
278
|
}
|
|
278
|
-
|
|
279
|
+
w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b);
|
|
279
280
|
}
|
|
280
|
-
function
|
|
281
|
-
const g =
|
|
282
|
-
O.set(0, C),
|
|
283
|
-
const F = (
|
|
284
|
-
|
|
281
|
+
function be(a) {
|
|
282
|
+
const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
|
|
283
|
+
O.set(0, C), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), se(L.y), N.copy(O);
|
|
284
|
+
const F = (a.pageX + g.x) * 0.5, z = (a.pageY + g.y) * 0.5;
|
|
285
|
+
ie(F, z);
|
|
285
286
|
}
|
|
286
|
-
function
|
|
287
|
-
t.enableZoom &&
|
|
287
|
+
function _e(a) {
|
|
288
|
+
t.enableZoom && be(a), t.enablePan && xe(a);
|
|
288
289
|
}
|
|
289
|
-
function
|
|
290
|
-
t.enableZoom &&
|
|
290
|
+
function Xe(a) {
|
|
291
|
+
t.enableZoom && be(a), t.enableRotate && ye(a);
|
|
291
292
|
}
|
|
292
|
-
function
|
|
293
|
-
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(
|
|
293
|
+
function ve(a) {
|
|
294
|
+
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(a.pointerId), t.domElement.addEventListener("pointermove", ne), t.domElement.addEventListener("pointerup", B)), et(a), a.pointerType === "touch" ? Qe(a) : Ve(a));
|
|
294
295
|
}
|
|
295
|
-
function
|
|
296
|
-
t.enabled !== !1 && (
|
|
296
|
+
function ne(a) {
|
|
297
|
+
t.enabled !== !1 && (a.pointerType === "touch" ? Je(a) : qe(a));
|
|
297
298
|
}
|
|
298
|
-
function
|
|
299
|
-
|
|
299
|
+
function B(a) {
|
|
300
|
+
tt(a), E.length === 0 && (t.domElement.releasePointerCapture(a.pointerId), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B)), t.dispatchEvent(Pe), o = i.NONE;
|
|
300
301
|
}
|
|
301
|
-
function
|
|
302
|
+
function Ve(a) {
|
|
302
303
|
let g;
|
|
303
|
-
switch (
|
|
304
|
+
switch (a.button) {
|
|
304
305
|
case 0:
|
|
305
306
|
g = t.mouseButtons.LEFT;
|
|
306
307
|
break;
|
|
@@ -314,57 +315,57 @@ class ft extends at {
|
|
|
314
315
|
g = -1;
|
|
315
316
|
}
|
|
316
317
|
switch (g) {
|
|
317
|
-
case
|
|
318
|
+
case j.DOLLY:
|
|
318
319
|
if (t.enableZoom === !1) return;
|
|
319
|
-
|
|
320
|
+
Ae(a), o = i.DOLLY;
|
|
320
321
|
break;
|
|
321
|
-
case
|
|
322
|
-
if (
|
|
322
|
+
case j.ROTATE:
|
|
323
|
+
if (a.ctrlKey || a.metaKey || a.shiftKey) {
|
|
323
324
|
if (t.enablePan === !1) return;
|
|
324
|
-
|
|
325
|
+
ge(a), o = i.PAN;
|
|
325
326
|
} else {
|
|
326
327
|
if (t.enableRotate === !1) return;
|
|
327
|
-
|
|
328
|
+
pe(a), o = i.ROTATE;
|
|
328
329
|
}
|
|
329
330
|
break;
|
|
330
|
-
case
|
|
331
|
-
if (
|
|
331
|
+
case j.PAN:
|
|
332
|
+
if (a.ctrlKey || a.metaKey || a.shiftKey) {
|
|
332
333
|
if (t.enableRotate === !1) return;
|
|
333
|
-
|
|
334
|
+
pe(a), o = i.ROTATE;
|
|
334
335
|
} else {
|
|
335
336
|
if (t.enablePan === !1) return;
|
|
336
|
-
|
|
337
|
+
ge(a), o = i.PAN;
|
|
337
338
|
}
|
|
338
339
|
break;
|
|
339
340
|
default:
|
|
340
341
|
o = i.NONE;
|
|
341
342
|
}
|
|
342
|
-
o !== i.NONE && t.dispatchEvent(
|
|
343
|
+
o !== i.NONE && t.dispatchEvent(le);
|
|
343
344
|
}
|
|
344
|
-
function
|
|
345
|
+
function qe(a) {
|
|
345
346
|
switch (o) {
|
|
346
347
|
case i.ROTATE:
|
|
347
348
|
if (t.enableRotate === !1) return;
|
|
348
|
-
|
|
349
|
+
je(a);
|
|
349
350
|
break;
|
|
350
351
|
case i.DOLLY:
|
|
351
352
|
if (t.enableZoom === !1) return;
|
|
352
|
-
|
|
353
|
+
$e(a);
|
|
353
354
|
break;
|
|
354
355
|
case i.PAN:
|
|
355
356
|
if (t.enablePan === !1) return;
|
|
356
|
-
|
|
357
|
+
Ge(a);
|
|
357
358
|
break;
|
|
358
359
|
}
|
|
359
360
|
}
|
|
360
|
-
function we(
|
|
361
|
-
t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (
|
|
361
|
+
function we(a) {
|
|
362
|
+
t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (a.preventDefault(), t.dispatchEvent(le), Ye(We(a)), t.dispatchEvent(Pe));
|
|
362
363
|
}
|
|
363
|
-
function
|
|
364
|
-
const g =
|
|
365
|
-
clientX:
|
|
366
|
-
clientY:
|
|
367
|
-
deltaY:
|
|
364
|
+
function We(a) {
|
|
365
|
+
const g = a.deltaMode, v = {
|
|
366
|
+
clientX: a.clientX,
|
|
367
|
+
clientY: a.clientY,
|
|
368
|
+
deltaY: a.deltaY
|
|
368
369
|
};
|
|
369
370
|
switch (g) {
|
|
370
371
|
case 1:
|
|
@@ -374,28 +375,28 @@ class ft extends at {
|
|
|
374
375
|
v.deltaY *= 100;
|
|
375
376
|
break;
|
|
376
377
|
}
|
|
377
|
-
return
|
|
378
|
+
return a.ctrlKey && !te && (v.deltaY *= 10), v;
|
|
378
379
|
}
|
|
379
|
-
function
|
|
380
|
-
|
|
380
|
+
function Ze(a) {
|
|
381
|
+
a.key === "Control" && (te = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
381
382
|
}
|
|
382
|
-
function
|
|
383
|
-
|
|
383
|
+
function Me(a) {
|
|
384
|
+
a.key === "Control" && (te = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
384
385
|
}
|
|
385
|
-
function
|
|
386
|
-
t.enabled === !1 || t.enablePan === !1 ||
|
|
386
|
+
function ae(a) {
|
|
387
|
+
t.enabled === !1 || t.enablePan === !1 || Be(a);
|
|
387
388
|
}
|
|
388
|
-
function
|
|
389
|
-
switch (
|
|
389
|
+
function Qe(a) {
|
|
390
|
+
switch (Ce(a), E.length) {
|
|
390
391
|
case 1:
|
|
391
392
|
switch (t.touches.ONE) {
|
|
392
|
-
case
|
|
393
|
+
case $.ROTATE:
|
|
393
394
|
if (t.enableRotate === !1) return;
|
|
394
|
-
|
|
395
|
+
ue(a), o = i.TOUCH_ROTATE;
|
|
395
396
|
break;
|
|
396
|
-
case
|
|
397
|
+
case $.PAN:
|
|
397
398
|
if (t.enablePan === !1) return;
|
|
398
|
-
|
|
399
|
+
fe(a), o = i.TOUCH_PAN;
|
|
399
400
|
break;
|
|
400
401
|
default:
|
|
401
402
|
o = i.NONE;
|
|
@@ -403,13 +404,13 @@ class ft extends at {
|
|
|
403
404
|
break;
|
|
404
405
|
case 2:
|
|
405
406
|
switch (t.touches.TWO) {
|
|
406
|
-
case
|
|
407
|
+
case $.DOLLY_PAN:
|
|
407
408
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
408
|
-
|
|
409
|
+
Ke(a), o = i.TOUCH_DOLLY_PAN;
|
|
409
410
|
break;
|
|
410
|
-
case
|
|
411
|
+
case $.DOLLY_ROTATE:
|
|
411
412
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
412
|
-
|
|
413
|
+
Ue(a), o = i.TOUCH_DOLLY_ROTATE;
|
|
413
414
|
break;
|
|
414
415
|
default:
|
|
415
416
|
o = i.NONE;
|
|
@@ -418,56 +419,56 @@ class ft extends at {
|
|
|
418
419
|
default:
|
|
419
420
|
o = i.NONE;
|
|
420
421
|
}
|
|
421
|
-
o !== i.NONE && t.dispatchEvent(
|
|
422
|
+
o !== i.NONE && t.dispatchEvent(le);
|
|
422
423
|
}
|
|
423
|
-
function
|
|
424
|
-
switch (
|
|
424
|
+
function Je(a) {
|
|
425
|
+
switch (Ce(a), o) {
|
|
425
426
|
case i.TOUCH_ROTATE:
|
|
426
427
|
if (t.enableRotate === !1) return;
|
|
427
|
-
|
|
428
|
+
ye(a), t.update();
|
|
428
429
|
break;
|
|
429
430
|
case i.TOUCH_PAN:
|
|
430
431
|
if (t.enablePan === !1) return;
|
|
431
|
-
|
|
432
|
+
xe(a), t.update();
|
|
432
433
|
break;
|
|
433
434
|
case i.TOUCH_DOLLY_PAN:
|
|
434
435
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
435
|
-
|
|
436
|
+
_e(a), t.update();
|
|
436
437
|
break;
|
|
437
438
|
case i.TOUCH_DOLLY_ROTATE:
|
|
438
439
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
439
|
-
|
|
440
|
+
Xe(a), t.update();
|
|
440
441
|
break;
|
|
441
442
|
default:
|
|
442
443
|
o = i.NONE;
|
|
443
444
|
}
|
|
444
445
|
}
|
|
445
|
-
function
|
|
446
|
-
t.enabled !== !1 &&
|
|
446
|
+
function Ee(a) {
|
|
447
|
+
t.enabled !== !1 && a.preventDefault();
|
|
447
448
|
}
|
|
448
|
-
function
|
|
449
|
-
E.push(
|
|
449
|
+
function et(a) {
|
|
450
|
+
E.push(a.pointerId);
|
|
450
451
|
}
|
|
451
|
-
function
|
|
452
|
-
delete
|
|
452
|
+
function tt(a) {
|
|
453
|
+
delete V[a.pointerId];
|
|
453
454
|
for (let g = 0; g < E.length; g++)
|
|
454
|
-
if (E[g] ==
|
|
455
|
+
if (E[g] == a.pointerId) {
|
|
455
456
|
E.splice(g, 1);
|
|
456
457
|
return;
|
|
457
458
|
}
|
|
458
459
|
}
|
|
459
|
-
function
|
|
460
|
-
let g =
|
|
461
|
-
g === void 0 && (g = new
|
|
460
|
+
function Ce(a) {
|
|
461
|
+
let g = V[a.pointerId];
|
|
462
|
+
g === void 0 && (g = new k(), V[a.pointerId] = g), g.set(a.pageX, a.pageY);
|
|
462
463
|
}
|
|
463
|
-
function
|
|
464
|
-
const g =
|
|
465
|
-
return
|
|
464
|
+
function A(a) {
|
|
465
|
+
const g = a.pointerId === E[0] ? E[1] : E[0];
|
|
466
|
+
return V[g];
|
|
466
467
|
}
|
|
467
|
-
t.domElement.addEventListener("contextmenu",
|
|
468
|
+
t.domElement.addEventListener("contextmenu", Ee), t.domElement.addEventListener("pointerdown", ve), t.domElement.addEventListener("pointercancel", B), t.domElement.addEventListener("wheel", we, { passive: !1 }), document.addEventListener("keydown", Ze, { passive: !0, capture: !0 }), this.update();
|
|
468
469
|
}
|
|
469
470
|
}
|
|
470
|
-
class
|
|
471
|
+
class ft {
|
|
471
472
|
constructor(e, s) {
|
|
472
473
|
l(this, "scene");
|
|
473
474
|
l(this, "camera");
|
|
@@ -475,35 +476,35 @@ class mt {
|
|
|
475
476
|
l(this, "controls");
|
|
476
477
|
l(this, "container");
|
|
477
478
|
l(this, "resizeHandler");
|
|
478
|
-
this.container = e, this.scene = new
|
|
479
|
+
this.container = e, this.scene = new x.Scene(), this.scene.background = new x.Color(
|
|
479
480
|
s.backgroundColor ?? 657930
|
|
480
481
|
);
|
|
481
482
|
const { width: t, height: i } = ke(e), o = s.cameraFov ?? 75;
|
|
482
|
-
this.camera = new
|
|
483
|
-
const
|
|
484
|
-
this.camera.position.set(
|
|
483
|
+
this.camera = new x.PerspectiveCamera(o, t / i, 0.1, 2e3);
|
|
484
|
+
const n = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
485
|
+
this.camera.position.set(n.x, n.y, n.z), this.renderer = new x.WebGLRenderer({
|
|
485
486
|
antialias: !0,
|
|
486
487
|
alpha: !0,
|
|
487
488
|
powerPreference: "high-performance"
|
|
488
|
-
}), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping =
|
|
489
|
+
}), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = x.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = x.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
489
490
|
}
|
|
490
491
|
/**
|
|
491
492
|
* Sets up scene lighting for gradient glass on dark background
|
|
492
493
|
*/
|
|
493
494
|
setupLighting() {
|
|
494
|
-
const e = new
|
|
495
|
+
const e = new x.AmbientLight(16777215, 0.4);
|
|
495
496
|
this.scene.add(e);
|
|
496
|
-
const s = new
|
|
497
|
+
const s = new x.DirectionalLight(16777215, 0.9);
|
|
497
498
|
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
498
|
-
const t = new
|
|
499
|
+
const t = new x.DirectionalLight(16773344, 0.4);
|
|
499
500
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
500
|
-
const i = new
|
|
501
|
+
const i = new x.DirectionalLight(16777215, 0.3);
|
|
501
502
|
i.position.set(0, -30, -50), this.scene.add(i);
|
|
502
|
-
const o = new
|
|
503
|
+
const o = new x.PointLight(16750950, 0.5, 150);
|
|
503
504
|
o.position.set(40, 20, 40), this.scene.add(o);
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
const r = new
|
|
505
|
+
const n = new x.PointLight(16764057, 0.4, 150);
|
|
506
|
+
n.position.set(-40, -20, 40), this.scene.add(n);
|
|
507
|
+
const r = new x.PointLight(6724095, 0.2, 100);
|
|
507
508
|
r.position.set(0, 40, -40), this.scene.add(r);
|
|
508
509
|
}
|
|
509
510
|
/**
|
|
@@ -545,7 +546,7 @@ class mt {
|
|
|
545
546
|
* Gets the camera's forward direction
|
|
546
547
|
*/
|
|
547
548
|
getCameraDirection() {
|
|
548
|
-
const e = new
|
|
549
|
+
const e = new x.Vector3();
|
|
549
550
|
return this.camera.getWorldDirection(e), e;
|
|
550
551
|
}
|
|
551
552
|
/**
|
|
@@ -558,7 +559,7 @@ class mt {
|
|
|
558
559
|
}
|
|
559
560
|
}
|
|
560
561
|
}
|
|
561
|
-
const
|
|
562
|
+
const X = class X {
|
|
562
563
|
constructor(e, s) {
|
|
563
564
|
l(this, "sceneManager");
|
|
564
565
|
l(this, "nodeFactory");
|
|
@@ -577,18 +578,18 @@ const V = class V {
|
|
|
577
578
|
* Call this before adding nodes in bulk.
|
|
578
579
|
*/
|
|
579
580
|
static setExpectedNodeCount(e) {
|
|
580
|
-
|
|
581
|
+
X.expectedNodeCount = e;
|
|
581
582
|
}
|
|
582
583
|
/**
|
|
583
584
|
* Adds a node to the graph
|
|
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 (!Fe(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;
|
|
591
|
-
const t = Math.max(50, Math.cbrt(
|
|
592
|
+
const t = Math.max(50, Math.cbrt(X.expectedNodeCount) * 10), i = e.position ?? {
|
|
592
593
|
x: (Math.random() - 0.5) * t,
|
|
593
594
|
y: (Math.random() - 0.5) * t,
|
|
594
595
|
z: (Math.random() - 0.5) * t
|
|
@@ -597,11 +598,11 @@ const V = class V {
|
|
|
597
598
|
position: i,
|
|
598
599
|
velocity: { x: 0, y: 0, z: 0 },
|
|
599
600
|
mass: 1
|
|
600
|
-
},
|
|
601
|
+
}, n = this.nodeFactory.createNode(
|
|
601
602
|
{ ...e, position: i },
|
|
602
603
|
s
|
|
603
604
|
);
|
|
604
|
-
return this.sceneManager.add(
|
|
605
|
+
return this.sceneManager.add(n.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, n), !0;
|
|
605
606
|
}
|
|
606
607
|
/**
|
|
607
608
|
* Removes a node from the graph
|
|
@@ -692,16 +693,23 @@ const V = class V {
|
|
|
692
693
|
}
|
|
693
694
|
};
|
|
694
695
|
// Scale spawn volume with expected graph size
|
|
695
|
-
l(
|
|
696
|
-
let
|
|
697
|
-
class
|
|
696
|
+
l(X, "expectedNodeCount", 100);
|
|
697
|
+
let ee = X;
|
|
698
|
+
class mt {
|
|
698
699
|
constructor(e, s, t) {
|
|
699
700
|
l(this, "sceneManager");
|
|
700
701
|
l(this, "nodeManager");
|
|
701
702
|
l(this, "edgeFactory");
|
|
703
|
+
// Full relationship list (includes duplicates between same pair)
|
|
702
704
|
l(this, "edges", []);
|
|
705
|
+
// One visual line per directed pair (source -> target)
|
|
703
706
|
l(this, "edgeObjects", []);
|
|
707
|
+
// Tracks which directed pairs currently have at least one relationship
|
|
704
708
|
l(this, "edgeKeySet", /* @__PURE__ */ new Set());
|
|
709
|
+
// Maps directed pair key to the relationship list for that pair
|
|
710
|
+
l(this, "edgeGroups", /* @__PURE__ */ new Map());
|
|
711
|
+
// Maps directed pair key to the corresponding visual line
|
|
712
|
+
l(this, "edgeObjectMap", /* @__PURE__ */ new Map());
|
|
705
713
|
l(this, "highlightedEdgeKey", null);
|
|
706
714
|
this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
|
|
707
715
|
}
|
|
@@ -709,12 +717,12 @@ class yt {
|
|
|
709
717
|
* Checks if an edge exists
|
|
710
718
|
*/
|
|
711
719
|
hasEdge(e, s) {
|
|
712
|
-
const t =
|
|
720
|
+
const t = this.createDirectedEdgeKey(e, s);
|
|
713
721
|
return this.edgeKeySet.has(t);
|
|
714
722
|
}
|
|
715
723
|
/**
|
|
716
724
|
* Adds an edge to the graph
|
|
717
|
-
* @returns true if added, false if
|
|
725
|
+
* @returns true if added, false if invalid or nodes don't exist
|
|
718
726
|
*/
|
|
719
727
|
addEdge(e) {
|
|
720
728
|
if (!De(e))
|
|
@@ -723,54 +731,78 @@ class yt {
|
|
|
723
731
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
724
732
|
if (!this.nodeManager.hasNode(e.target))
|
|
725
733
|
return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
|
|
726
|
-
const s =
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
734
|
+
const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target), i = this.createDirectedEdgeKey(e.source, e.target);
|
|
735
|
+
this.edges.push(e);
|
|
736
|
+
const o = this.edgeGroups.get(i);
|
|
737
|
+
if (o) {
|
|
738
|
+
o.push(e);
|
|
739
|
+
const r = this.edgeObjectMap.get(i);
|
|
740
|
+
return r && this.updateEdgeUserData(r, o, s, t), !0;
|
|
741
|
+
}
|
|
742
|
+
const n = this.edgeFactory.createEdge(
|
|
730
743
|
e,
|
|
744
|
+
s,
|
|
731
745
|
t,
|
|
732
|
-
|
|
733
|
-
t.position
|
|
734
|
-
i.position
|
|
746
|
+
s.position,
|
|
747
|
+
t.position
|
|
735
748
|
);
|
|
736
|
-
return this.sceneManager.add(
|
|
749
|
+
return this.sceneManager.add(n.line), this.edgeGroups.set(i, [e]), this.edgeObjectMap.set(i, n), this.edgeObjects.push(n), this.edgeKeySet.add(i), this.updateEdgeUserData(n, [e], s, t), !0;
|
|
737
750
|
}
|
|
738
751
|
/**
|
|
739
752
|
* Removes an edge from the graph
|
|
753
|
+
* Removes one relationship instance for source -> target.
|
|
740
754
|
* @returns true if removed, false if not found
|
|
741
755
|
*/
|
|
742
756
|
removeEdge(e, s) {
|
|
743
|
-
const t =
|
|
757
|
+
const t = this.createDirectedEdgeKey(e, s);
|
|
744
758
|
if (!this.edgeKeySet.has(t))
|
|
745
759
|
return !1;
|
|
746
760
|
const i = this.edges.findIndex(
|
|
747
|
-
(
|
|
761
|
+
(c) => c.source === e && c.target === s
|
|
748
762
|
);
|
|
749
763
|
if (i === -1)
|
|
750
764
|
return !1;
|
|
751
|
-
const o = this.
|
|
752
|
-
|
|
765
|
+
const o = this.edges[i];
|
|
766
|
+
this.edges.splice(i, 1);
|
|
767
|
+
const n = this.edgeGroups.get(t);
|
|
768
|
+
if (!n)
|
|
769
|
+
return !0;
|
|
770
|
+
const r = n.findIndex((c) => c === o);
|
|
771
|
+
if (r !== -1)
|
|
772
|
+
n.splice(r, 1);
|
|
773
|
+
else {
|
|
774
|
+
const c = n.findIndex((p) => p.source === e && p.target === s);
|
|
775
|
+
c !== -1 && n.splice(c, 1);
|
|
776
|
+
}
|
|
777
|
+
const d = this.edgeObjectMap.get(t);
|
|
778
|
+
if (!d)
|
|
779
|
+
return !0;
|
|
780
|
+
if (n.length === 0) {
|
|
781
|
+
this.sceneManager.remove(d.line), this.edgeFactory.disposeEdge(d);
|
|
782
|
+
const c = this.edgeObjects.findIndex((p) => p === d);
|
|
783
|
+
c !== -1 && this.edgeObjects.splice(c, 1), this.edgeGroups.delete(t), this.edgeObjectMap.delete(t), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null);
|
|
784
|
+
} else {
|
|
785
|
+
const c = this.nodeManager.getNode(e), p = this.nodeManager.getNode(s);
|
|
786
|
+
c && p && this.updateEdgeUserData(d, n, c, p);
|
|
787
|
+
}
|
|
788
|
+
return !0;
|
|
753
789
|
}
|
|
754
790
|
/**
|
|
755
791
|
* Highlights an edge
|
|
756
792
|
*/
|
|
757
793
|
highlightEdge(e, s) {
|
|
758
|
-
const t =
|
|
794
|
+
const t = this.createDirectedEdgeKey(e, s);
|
|
759
795
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
760
|
-
const i = this.
|
|
761
|
-
|
|
762
|
-
);
|
|
763
|
-
i !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[i]), this.highlightedEdgeKey = t);
|
|
796
|
+
const i = this.edgeObjectMap.get(t);
|
|
797
|
+
i && (this.edgeFactory.highlightEdge(i), this.highlightedEdgeKey = t);
|
|
764
798
|
}
|
|
765
799
|
/**
|
|
766
800
|
* Unhighlights the currently highlighted edge
|
|
767
801
|
*/
|
|
768
802
|
unhighlightCurrentEdge() {
|
|
769
803
|
if (!this.highlightedEdgeKey) return;
|
|
770
|
-
const e = this.
|
|
771
|
-
|
|
772
|
-
);
|
|
773
|
-
e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
|
|
804
|
+
const e = this.edgeObjectMap.get(this.highlightedEdgeKey);
|
|
805
|
+
e && this.edgeFactory.unhighlightEdge(e), this.highlightedEdgeKey = null;
|
|
774
806
|
}
|
|
775
807
|
/**
|
|
776
808
|
* Removes all edges connected to a node
|
|
@@ -802,12 +834,12 @@ class yt {
|
|
|
802
834
|
* Updates all edge positions based on current node positions
|
|
803
835
|
*/
|
|
804
836
|
updateEdgePositions() {
|
|
805
|
-
this.edgeObjects.forEach((e
|
|
806
|
-
const
|
|
807
|
-
|
|
837
|
+
this.edgeObjects.forEach((e) => {
|
|
838
|
+
const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target);
|
|
839
|
+
s && t && this.edgeFactory.updateEdgePositions(
|
|
808
840
|
e,
|
|
809
|
-
|
|
810
|
-
|
|
841
|
+
s.position,
|
|
842
|
+
t.position
|
|
811
843
|
);
|
|
812
844
|
});
|
|
813
845
|
}
|
|
@@ -835,7 +867,29 @@ class yt {
|
|
|
835
867
|
clear() {
|
|
836
868
|
this.edgeObjects.forEach((e) => {
|
|
837
869
|
this.sceneManager.remove(e.line), this.edgeFactory.disposeEdge(e);
|
|
838
|
-
}), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.highlightedEdgeKey = null;
|
|
870
|
+
}), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Creates a directed key (source -> target)
|
|
874
|
+
*/
|
|
875
|
+
createDirectedEdgeKey(e, s) {
|
|
876
|
+
return `${e}->${s}`;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Syncs relationship metadata to the line for hover/click UI
|
|
880
|
+
*/
|
|
881
|
+
updateEdgeUserData(e, s, t, i) {
|
|
882
|
+
const o = s[0];
|
|
883
|
+
e.line.userData = {
|
|
884
|
+
...e.line.userData,
|
|
885
|
+
source: e.source,
|
|
886
|
+
target: e.target,
|
|
887
|
+
edge: o,
|
|
888
|
+
relationships: [...s],
|
|
889
|
+
relationshipCount: s.length,
|
|
890
|
+
sourceNode: t,
|
|
891
|
+
targetNode: i
|
|
892
|
+
};
|
|
839
893
|
}
|
|
840
894
|
/**
|
|
841
895
|
* Dispose resources
|
|
@@ -844,7 +898,7 @@ class yt {
|
|
|
844
898
|
this.clear();
|
|
845
899
|
}
|
|
846
900
|
}
|
|
847
|
-
class
|
|
901
|
+
class Re {
|
|
848
902
|
constructor(e, s, t = {}) {
|
|
849
903
|
l(this, "nodes");
|
|
850
904
|
l(this, "edges");
|
|
@@ -880,17 +934,19 @@ class Le {
|
|
|
880
934
|
const e = /* @__PURE__ */ new Map();
|
|
881
935
|
for (const i of this.edges)
|
|
882
936
|
e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
|
|
937
|
+
console.log(`[ForceGraph3D] initializeNodeMassAndPin: ${this.edges.length} edges, ${this.nodes.size} nodes, ${e.size} unique endpoints`);
|
|
883
938
|
let s = 0, t = null;
|
|
884
939
|
for (const [i, o] of e) {
|
|
885
940
|
o > s && (s = o, t = i);
|
|
886
|
-
const
|
|
887
|
-
|
|
941
|
+
const n = this.nodes.get(i);
|
|
942
|
+
n && (n.mass = 1 + Math.log2(1 + o));
|
|
888
943
|
}
|
|
889
|
-
if (t && s > 10) {
|
|
944
|
+
if (console.log(`[ForceGraph3D] Hub detected: id="${t}", degree=${s}, threshold=10`), t && s > 10) {
|
|
890
945
|
this.pinnedNodeId = t;
|
|
891
946
|
const i = this.nodes.get(t);
|
|
892
|
-
i
|
|
893
|
-
}
|
|
947
|
+
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!`);
|
|
948
|
+
} else
|
|
949
|
+
console.log(`[ForceGraph3D] No hub pinned (maxDegree=${s} <= 10 or hubId=${t})`);
|
|
894
950
|
}
|
|
895
951
|
/**
|
|
896
952
|
* Computes effective repulsion strength scaled by node count.
|
|
@@ -915,13 +971,13 @@ class Le {
|
|
|
915
971
|
const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
|
|
916
972
|
for (let i = 0; i < s; i++) {
|
|
917
973
|
const o = e[i];
|
|
918
|
-
for (let
|
|
919
|
-
const r = e[
|
|
920
|
-
let
|
|
921
|
-
if (
|
|
922
|
-
|
|
923
|
-
const m = Math.sqrt(
|
|
924
|
-
o.velocity.x -=
|
|
974
|
+
for (let n = i + 1; n < s; n++) {
|
|
975
|
+
const r = e[n], d = r.position.x - o.position.x, c = r.position.y - o.position.y, p = r.position.z - o.position.z;
|
|
976
|
+
let f = d * d + c * c + p * p;
|
|
977
|
+
if (f > this.REPULSION_CUTOFF_SQ) continue;
|
|
978
|
+
f < 0.01 && (f = 0.01);
|
|
979
|
+
const m = Math.sqrt(f), u = t * this.alpha / f, y = d / m * u, b = c / m * u, w = p / m * u;
|
|
980
|
+
o.velocity.x -= y / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= w / o.mass, r.velocity.x += y / r.mass, r.velocity.y += b / r.mass, r.velocity.z += w / r.mass;
|
|
925
981
|
}
|
|
926
982
|
}
|
|
927
983
|
}
|
|
@@ -929,7 +985,7 @@ class Le {
|
|
|
929
985
|
* Barnes-Hut approximation - O(n log n)
|
|
930
986
|
*/
|
|
931
987
|
calculateRepulsionBarnesHut() {
|
|
932
|
-
const e = Array.from(this.nodes.values()), s = new
|
|
988
|
+
const e = Array.from(this.nodes.values()), s = new yt(e);
|
|
933
989
|
for (const t of e)
|
|
934
990
|
this.calculateForceFromOctree(t, s.root);
|
|
935
991
|
}
|
|
@@ -942,26 +998,26 @@ class Le {
|
|
|
942
998
|
return;
|
|
943
999
|
}
|
|
944
1000
|
if (s.mass === 0) return;
|
|
945
|
-
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z,
|
|
946
|
-
if (
|
|
947
|
-
const r = Math.sqrt(
|
|
1001
|
+
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z, n = t * t + i * i + o * o;
|
|
1002
|
+
if (n > this.REPULSION_CUTOFF_SQ) return;
|
|
1003
|
+
const r = Math.sqrt(n), d = this.getEffectiveRepulsion();
|
|
948
1004
|
if (r > 0 && s.size / r < this.barnesHutTheta) {
|
|
949
|
-
const
|
|
1005
|
+
const c = Math.max(n, 0.01), p = d * this.alpha * s.mass / c;
|
|
950
1006
|
e.velocity.x -= t / r * p / e.mass, e.velocity.y -= i / r * p / e.mass, e.velocity.z -= o / r * p / e.mass;
|
|
951
1007
|
} else
|
|
952
|
-
for (const
|
|
953
|
-
|
|
1008
|
+
for (const c of s.children)
|
|
1009
|
+
c && this.calculateForceFromOctree(e, c);
|
|
954
1010
|
}
|
|
955
1011
|
/**
|
|
956
1012
|
* Apply repulsion between two nodes (with cutoff)
|
|
957
1013
|
*/
|
|
958
1014
|
applyRepulsionBetween(e, s) {
|
|
959
1015
|
const t = s.position.x - e.position.x, i = s.position.y - e.position.y, o = s.position.z - e.position.z;
|
|
960
|
-
let
|
|
961
|
-
if (
|
|
962
|
-
|
|
963
|
-
const r = Math.sqrt(
|
|
964
|
-
e.velocity.x -= t / r *
|
|
1016
|
+
let n = t * t + i * i + o * o;
|
|
1017
|
+
if (n > this.REPULSION_CUTOFF_SQ) return;
|
|
1018
|
+
n < 0.01 && (n = 0.01);
|
|
1019
|
+
const r = Math.sqrt(n), c = this.getEffectiveRepulsion() * this.alpha / n;
|
|
1020
|
+
e.velocity.x -= t / r * c / e.mass, e.velocity.y -= i / r * c / e.mass, e.velocity.z -= o / r * c / e.mass;
|
|
965
1021
|
}
|
|
966
1022
|
/**
|
|
967
1023
|
* Calculate attraction forces along edges
|
|
@@ -971,10 +1027,10 @@ class Le {
|
|
|
971
1027
|
for (const t of this.edges) {
|
|
972
1028
|
const i = this.nodes.get(t.source), o = this.nodes.get(t.target);
|
|
973
1029
|
if (!i || !o) continue;
|
|
974
|
-
const
|
|
975
|
-
if (
|
|
976
|
-
const
|
|
977
|
-
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z +=
|
|
1030
|
+
const n = o.position.x - i.position.x, r = o.position.y - i.position.y, d = o.position.z - i.position.z, c = Math.sqrt(n * n + r * r + d * d);
|
|
1031
|
+
if (c < 0.01) continue;
|
|
1032
|
+
const f = (c - 15) * this.attractionStrength * s * this.alpha, m = n / c * f, u = r / c * f, y = d / c * f;
|
|
1033
|
+
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += y / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= y / o.mass;
|
|
978
1034
|
}
|
|
979
1035
|
}
|
|
980
1036
|
/**
|
|
@@ -1031,7 +1087,7 @@ class Le {
|
|
|
1031
1087
|
e.repulsionStrength !== void 0 && (this.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.damping = e.damping);
|
|
1032
1088
|
}
|
|
1033
1089
|
}
|
|
1034
|
-
class
|
|
1090
|
+
class yt {
|
|
1035
1091
|
constructor(e) {
|
|
1036
1092
|
l(this, "root");
|
|
1037
1093
|
const s = this.calculateBounds(e);
|
|
@@ -1067,54 +1123,54 @@ class xt {
|
|
|
1067
1123
|
};
|
|
1068
1124
|
if (e.length === 1 || t > 20) {
|
|
1069
1125
|
let u = 0;
|
|
1070
|
-
const
|
|
1126
|
+
const y = { x: 0, y: 0, z: 0 };
|
|
1071
1127
|
for (const b of e)
|
|
1072
|
-
u += b.mass,
|
|
1073
|
-
return u > 0 && (
|
|
1128
|
+
u += b.mass, y.x += b.position.x * b.mass, y.y += b.position.y * b.mass, y.z += b.position.z * b.mass;
|
|
1129
|
+
return u > 0 && (y.x /= u, y.y /= u, y.z /= u), {
|
|
1074
1130
|
bounds: s,
|
|
1075
1131
|
size: i,
|
|
1076
|
-
centerOfMass:
|
|
1132
|
+
centerOfMass: y,
|
|
1077
1133
|
mass: u,
|
|
1078
1134
|
isLeaf: !0,
|
|
1079
1135
|
node: e[0],
|
|
1080
1136
|
children: []
|
|
1081
1137
|
};
|
|
1082
1138
|
}
|
|
1083
|
-
const o = (s.min.x + s.max.x) / 2,
|
|
1139
|
+
const o = (s.min.x + s.max.x) / 2, n = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, d = [[], [], [], [], [], [], [], []];
|
|
1084
1140
|
for (const u of e) {
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1088
|
-
const
|
|
1089
|
-
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y:
|
|
1090
|
-
{ min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y:
|
|
1091
|
-
{ min: { x: s.min.x, y:
|
|
1092
|
-
{ min: { x: o, y:
|
|
1093
|
-
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y:
|
|
1094
|
-
{ min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y:
|
|
1095
|
-
{ min: { x: s.min.x, y:
|
|
1096
|
-
{ min: { x: o, y:
|
|
1141
|
+
const y = (u.position.x >= o ? 1 : 0) + (u.position.y >= n ? 2 : 0) + (u.position.z >= r ? 4 : 0);
|
|
1142
|
+
d[y].push(u);
|
|
1143
|
+
}
|
|
1144
|
+
const c = [
|
|
1145
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: n, z: r } },
|
|
1146
|
+
{ min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: n, z: r } },
|
|
1147
|
+
{ min: { x: s.min.x, y: n, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
|
|
1148
|
+
{ min: { x: o, y: n, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
|
|
1149
|
+
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y: n, z: s.max.z } },
|
|
1150
|
+
{ min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y: n, z: s.max.z } },
|
|
1151
|
+
{ min: { x: s.min.x, y: n, z: r }, max: { x: o, y: s.max.y, z: s.max.z } },
|
|
1152
|
+
{ min: { x: o, y: n, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1097
1153
|
], p = [];
|
|
1098
|
-
let
|
|
1154
|
+
let f = 0;
|
|
1099
1155
|
const m = { x: 0, y: 0, z: 0 };
|
|
1100
1156
|
for (let u = 0; u < 8; u++)
|
|
1101
|
-
if (
|
|
1102
|
-
const
|
|
1103
|
-
p.push(
|
|
1157
|
+
if (d[u].length > 0) {
|
|
1158
|
+
const y = this.buildTree(d[u], c[u], t + 1);
|
|
1159
|
+
p.push(y), f += y.mass, m.x += y.centerOfMass.x * y.mass, m.y += y.centerOfMass.y * y.mass, m.z += y.centerOfMass.z * y.mass;
|
|
1104
1160
|
} else
|
|
1105
1161
|
p.push(null);
|
|
1106
|
-
return
|
|
1162
|
+
return f > 0 && (m.x /= f, m.y /= f, m.z /= f), {
|
|
1107
1163
|
bounds: s,
|
|
1108
1164
|
size: i,
|
|
1109
1165
|
centerOfMass: m,
|
|
1110
|
-
mass:
|
|
1166
|
+
mass: f,
|
|
1111
1167
|
isLeaf: !1,
|
|
1112
1168
|
node: null,
|
|
1113
1169
|
children: p
|
|
1114
1170
|
};
|
|
1115
1171
|
}
|
|
1116
1172
|
}
|
|
1117
|
-
class
|
|
1173
|
+
class xt {
|
|
1118
1174
|
constructor(e, s, t, i = 60) {
|
|
1119
1175
|
l(this, "sceneManager");
|
|
1120
1176
|
l(this, "animationId", null);
|
|
@@ -1179,7 +1235,7 @@ class bt {
|
|
|
1179
1235
|
this.stop();
|
|
1180
1236
|
}
|
|
1181
1237
|
}
|
|
1182
|
-
class
|
|
1238
|
+
class bt {
|
|
1183
1239
|
constructor() {
|
|
1184
1240
|
l(this, "envMap", null);
|
|
1185
1241
|
l(this, "materialCache", /* @__PURE__ */ new Map());
|
|
@@ -1220,7 +1276,7 @@ class vt {
|
|
|
1220
1276
|
for (const i of t) {
|
|
1221
1277
|
const o = document.createElement("canvas");
|
|
1222
1278
|
o.width = 256, o.height = 256;
|
|
1223
|
-
const
|
|
1279
|
+
const n = o.getContext("2d"), r = n.createRadialGradient(
|
|
1224
1280
|
256 / 2,
|
|
1225
1281
|
256 / 2,
|
|
1226
1282
|
0,
|
|
@@ -1228,15 +1284,15 @@ class vt {
|
|
|
1228
1284
|
256 / 2,
|
|
1229
1285
|
256 * 0.8
|
|
1230
1286
|
);
|
|
1231
|
-
r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]),
|
|
1232
|
-
const
|
|
1233
|
-
for (let
|
|
1287
|
+
r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), n.fillStyle = r, n.fillRect(0, 0, 256, 256);
|
|
1288
|
+
const d = n.getImageData(0, 0, 256, 256);
|
|
1289
|
+
for (let c = 0; c < d.data.length; c += 4) {
|
|
1234
1290
|
const p = (Math.random() - 0.5) * 5;
|
|
1235
|
-
|
|
1291
|
+
d.data[c] = Math.min(255, Math.max(0, d.data[c] + p)), d.data[c + 1] = Math.min(255, Math.max(0, d.data[c + 1] + p)), d.data[c + 2] = Math.min(255, Math.max(0, d.data[c + 2] + p));
|
|
1236
1292
|
}
|
|
1237
|
-
|
|
1293
|
+
n.putImageData(d, 0, 0), s.push(o);
|
|
1238
1294
|
}
|
|
1239
|
-
this.envMap = new
|
|
1295
|
+
this.envMap = new x.CubeTexture(s.map((i) => {
|
|
1240
1296
|
const o = new Image();
|
|
1241
1297
|
return o.src = i.toDataURL(), o;
|
|
1242
1298
|
})), this.envMap.needsUpdate = !0;
|
|
@@ -1255,11 +1311,11 @@ class vt {
|
|
|
1255
1311
|
const t = "glass-single";
|
|
1256
1312
|
if (this.materialCache.has(t))
|
|
1257
1313
|
return this.materialCache.get(t).clone();
|
|
1258
|
-
const i = new
|
|
1314
|
+
const i = new x.Color(16750950), o = new x.ShaderMaterial({
|
|
1259
1315
|
uniforms: {
|
|
1260
1316
|
uColor: { value: i },
|
|
1261
1317
|
uEnvMap: { value: this.envMap },
|
|
1262
|
-
uGlowColor: { value: new
|
|
1318
|
+
uGlowColor: { value: new x.Color(16777215) },
|
|
1263
1319
|
uGlowIntensity: { value: 0.8 },
|
|
1264
1320
|
uReflectivity: { value: 0.4 },
|
|
1265
1321
|
uFresnelPower: { value: 2.5 }
|
|
@@ -1325,9 +1381,9 @@ class vt {
|
|
|
1325
1381
|
}
|
|
1326
1382
|
`,
|
|
1327
1383
|
transparent: !0,
|
|
1328
|
-
side:
|
|
1384
|
+
side: x.FrontSide,
|
|
1329
1385
|
depthWrite: !0,
|
|
1330
|
-
blending:
|
|
1386
|
+
blending: x.NormalBlending
|
|
1331
1387
|
});
|
|
1332
1388
|
return this.materialCache.set(t, o), o.clone();
|
|
1333
1389
|
}
|
|
@@ -1335,7 +1391,7 @@ class vt {
|
|
|
1335
1391
|
* Creates material for edges (light color for dark background)
|
|
1336
1392
|
*/
|
|
1337
1393
|
createEdgeMaterial(e = 6710886, s = 0.4) {
|
|
1338
|
-
return new
|
|
1394
|
+
return new x.LineBasicMaterial({
|
|
1339
1395
|
color: e,
|
|
1340
1396
|
transparent: !0,
|
|
1341
1397
|
opacity: s,
|
|
@@ -1346,7 +1402,7 @@ class vt {
|
|
|
1346
1402
|
* Creates highlighted edge material
|
|
1347
1403
|
*/
|
|
1348
1404
|
createHighlightedEdgeMaterial() {
|
|
1349
|
-
return new
|
|
1405
|
+
return new x.LineBasicMaterial({
|
|
1350
1406
|
color: 16750950,
|
|
1351
1407
|
// Tangerine highlight
|
|
1352
1408
|
transparent: !1,
|
|
@@ -1360,10 +1416,10 @@ class vt {
|
|
|
1360
1416
|
createLabelMaterial(e, s = 24) {
|
|
1361
1417
|
const t = document.createElement("canvas"), i = t.getContext("2d");
|
|
1362
1418
|
i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1363
|
-
const
|
|
1364
|
-
t.width = Math.max(128,
|
|
1365
|
-
const r = new
|
|
1366
|
-
return r.needsUpdate = !0, new
|
|
1419
|
+
const n = i.measureText(e).width;
|
|
1420
|
+
t.width = Math.max(128, n + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
|
|
1421
|
+
const r = new x.CanvasTexture(t);
|
|
1422
|
+
return r.needsUpdate = !0, new x.SpriteMaterial({
|
|
1367
1423
|
map: r,
|
|
1368
1424
|
transparent: !0,
|
|
1369
1425
|
depthTest: !1,
|
|
@@ -1383,7 +1439,7 @@ class vt {
|
|
|
1383
1439
|
this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
|
|
1384
1440
|
}
|
|
1385
1441
|
}
|
|
1386
|
-
class
|
|
1442
|
+
class vt {
|
|
1387
1443
|
constructor(e, s = 2, t = [32, 16, 8], i = 16750950) {
|
|
1388
1444
|
l(this, "materialFactory");
|
|
1389
1445
|
l(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
@@ -1400,7 +1456,7 @@ class Mt {
|
|
|
1400
1456
|
const t = `lod-${s}`;
|
|
1401
1457
|
this.geometryCache.set(
|
|
1402
1458
|
t,
|
|
1403
|
-
new
|
|
1459
|
+
new x.SphereGeometry(this.nodeRadius, e, e)
|
|
1404
1460
|
);
|
|
1405
1461
|
});
|
|
1406
1462
|
}
|
|
@@ -1415,21 +1471,21 @@ class Mt {
|
|
|
1415
1471
|
* Creates a node visual (glass ball + label)
|
|
1416
1472
|
*/
|
|
1417
1473
|
createNode(e, s = 0) {
|
|
1418
|
-
const t = new
|
|
1474
|
+
const t = new x.Group();
|
|
1419
1475
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1420
1476
|
const i = this.getGeometry(s), o = this.materialFactory.createGlassMaterial(
|
|
1421
1477
|
e.color ?? this.defaultNodeColor
|
|
1422
|
-
),
|
|
1423
|
-
|
|
1424
|
-
const r = this.materialFactory.createLabelMaterial(e.label),
|
|
1425
|
-
return
|
|
1478
|
+
), n = new x.Mesh(i, o);
|
|
1479
|
+
n.castShadow = !0, n.receiveShadow = !0, t.add(n);
|
|
1480
|
+
const r = this.materialFactory.createLabelMaterial(e.label), d = new x.Sprite(r);
|
|
1481
|
+
return d.position.y = this.nodeRadius + 1.5, d.scale.set(4, 1, 1), t.add(d), e.position && t.position.set(
|
|
1426
1482
|
e.position.x,
|
|
1427
1483
|
e.position.y,
|
|
1428
1484
|
e.position.z
|
|
1429
1485
|
), {
|
|
1430
1486
|
group: t,
|
|
1431
|
-
sphere:
|
|
1432
|
-
label:
|
|
1487
|
+
sphere: n,
|
|
1488
|
+
label: d,
|
|
1433
1489
|
lodLevel: s
|
|
1434
1490
|
};
|
|
1435
1491
|
}
|
|
@@ -1445,19 +1501,19 @@ class Mt {
|
|
|
1445
1501
|
* Updates the color of a node
|
|
1446
1502
|
*/
|
|
1447
1503
|
updateNodeColor(e, s) {
|
|
1448
|
-
e.sphere.material instanceof
|
|
1504
|
+
e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
|
|
1449
1505
|
}
|
|
1450
1506
|
/**
|
|
1451
1507
|
* Updates the label of a node
|
|
1452
1508
|
*/
|
|
1453
1509
|
updateNodeLabel(e, s) {
|
|
1454
|
-
e.label.material instanceof
|
|
1510
|
+
e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
|
|
1455
1511
|
}
|
|
1456
1512
|
/**
|
|
1457
1513
|
* Disposes a node's resources
|
|
1458
1514
|
*/
|
|
1459
1515
|
disposeNode(e) {
|
|
1460
|
-
e.sphere.material instanceof
|
|
1516
|
+
e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
|
|
1461
1517
|
}
|
|
1462
1518
|
/**
|
|
1463
1519
|
* Dispose factory resources
|
|
@@ -1494,7 +1550,7 @@ class wt {
|
|
|
1494
1550
|
* Creates an edge line between two positions
|
|
1495
1551
|
*/
|
|
1496
1552
|
createEdge(e, s, t, i, o) {
|
|
1497
|
-
const
|
|
1553
|
+
const n = new x.BufferGeometry(), r = new Float32Array([
|
|
1498
1554
|
i.x,
|
|
1499
1555
|
i.y,
|
|
1500
1556
|
i.z,
|
|
@@ -1502,16 +1558,16 @@ class wt {
|
|
|
1502
1558
|
o.y,
|
|
1503
1559
|
o.z
|
|
1504
1560
|
]);
|
|
1505
|
-
|
|
1506
|
-
const
|
|
1507
|
-
return
|
|
1561
|
+
n.setAttribute("position", new x.BufferAttribute(r, 3));
|
|
1562
|
+
const d = this.getDefaultMaterial().clone(), c = new x.Line(n, d);
|
|
1563
|
+
return c.name = `edge-${e.source}-${e.target}`, c.userData = {
|
|
1508
1564
|
source: e.source,
|
|
1509
1565
|
target: e.target,
|
|
1510
1566
|
edge: e,
|
|
1511
1567
|
sourceNode: s,
|
|
1512
1568
|
targetNode: t
|
|
1513
|
-
},
|
|
1514
|
-
line:
|
|
1569
|
+
}, c.frustumCulled = !0, {
|
|
1570
|
+
line: c,
|
|
1515
1571
|
source: e.source,
|
|
1516
1572
|
target: e.target
|
|
1517
1573
|
};
|
|
@@ -1520,13 +1576,13 @@ class wt {
|
|
|
1520
1576
|
* Highlights an edge
|
|
1521
1577
|
*/
|
|
1522
1578
|
highlightEdge(e) {
|
|
1523
|
-
e.line.material instanceof
|
|
1579
|
+
e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
|
|
1524
1580
|
}
|
|
1525
1581
|
/**
|
|
1526
1582
|
* Resets an edge to default appearance
|
|
1527
1583
|
*/
|
|
1528
1584
|
unhighlightEdge(e) {
|
|
1529
|
-
e.line.material instanceof
|
|
1585
|
+
e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
|
|
1530
1586
|
}
|
|
1531
1587
|
/**
|
|
1532
1588
|
* Updates an edge's positions
|
|
@@ -1539,7 +1595,7 @@ class wt {
|
|
|
1539
1595
|
* Disposes an edge's resources
|
|
1540
1596
|
*/
|
|
1541
1597
|
disposeEdge(e) {
|
|
1542
|
-
e.line.geometry.dispose(), e.line.material instanceof
|
|
1598
|
+
e.line.geometry.dispose(), e.line.material instanceof x.Material && e.line.material.dispose();
|
|
1543
1599
|
}
|
|
1544
1600
|
/**
|
|
1545
1601
|
* Dispose factory resources
|
|
@@ -1548,7 +1604,7 @@ class wt {
|
|
|
1548
1604
|
this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
|
|
1549
1605
|
}
|
|
1550
1606
|
}
|
|
1551
|
-
class
|
|
1607
|
+
class Mt {
|
|
1552
1608
|
constructor(e, s = [50, 100, 200], t = !0) {
|
|
1553
1609
|
l(this, "camera");
|
|
1554
1610
|
l(this, "lodDistances");
|
|
@@ -1560,9 +1616,9 @@ class Et {
|
|
|
1560
1616
|
*/
|
|
1561
1617
|
getLODLevel(e) {
|
|
1562
1618
|
if (!this.enabled)
|
|
1563
|
-
return
|
|
1619
|
+
return _.HIGH;
|
|
1564
1620
|
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, o = Math.sqrt(s * s + t * t + i * i);
|
|
1565
|
-
return o < this.lodDistances[0] ?
|
|
1621
|
+
return o < this.lodDistances[0] ? _.HIGH : o < this.lodDistances[1] ? _.MEDIUM : _.LOW;
|
|
1566
1622
|
}
|
|
1567
1623
|
/**
|
|
1568
1624
|
* Checks if a node should be visible based on distance
|
|
@@ -1584,13 +1640,13 @@ class Et {
|
|
|
1584
1640
|
this.enabled = e;
|
|
1585
1641
|
}
|
|
1586
1642
|
}
|
|
1587
|
-
class
|
|
1643
|
+
class Et {
|
|
1588
1644
|
constructor(e, s = !0) {
|
|
1589
1645
|
l(this, "camera");
|
|
1590
1646
|
l(this, "frustum");
|
|
1591
1647
|
l(this, "projScreenMatrix");
|
|
1592
1648
|
l(this, "enabled");
|
|
1593
|
-
this.camera = e, this.frustum = new
|
|
1649
|
+
this.camera = e, this.frustum = new x.Frustum(), this.projScreenMatrix = new x.Matrix4(), this.enabled = s;
|
|
1594
1650
|
}
|
|
1595
1651
|
/**
|
|
1596
1652
|
* Updates the frustum from the camera
|
|
@@ -1606,7 +1662,7 @@ class Ct {
|
|
|
1606
1662
|
*/
|
|
1607
1663
|
isPointVisible(e) {
|
|
1608
1664
|
if (!this.enabled) return !0;
|
|
1609
|
-
const s = new
|
|
1665
|
+
const s = new x.Vector3(e.x, e.y, e.z);
|
|
1610
1666
|
return this.frustum.containsPoint(s);
|
|
1611
1667
|
}
|
|
1612
1668
|
/**
|
|
@@ -1614,8 +1670,8 @@ class Ct {
|
|
|
1614
1670
|
*/
|
|
1615
1671
|
isSphereVisible(e, s) {
|
|
1616
1672
|
if (!this.enabled) return !0;
|
|
1617
|
-
const t = new
|
|
1618
|
-
new
|
|
1673
|
+
const t = new x.Sphere(
|
|
1674
|
+
new x.Vector3(e.x, e.y, e.z),
|
|
1619
1675
|
s
|
|
1620
1676
|
);
|
|
1621
1677
|
return this.frustum.intersectsSphere(t);
|
|
@@ -1625,14 +1681,14 @@ class Ct {
|
|
|
1625
1681
|
*/
|
|
1626
1682
|
isLineVisible(e, s) {
|
|
1627
1683
|
if (!this.enabled) return !0;
|
|
1628
|
-
const t = new
|
|
1684
|
+
const t = new x.Vector3(e.x, e.y, e.z), i = new x.Vector3(s.x, s.y, s.z);
|
|
1629
1685
|
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
|
|
1630
1686
|
return !0;
|
|
1631
|
-
const o = new
|
|
1687
|
+
const o = new x.Vector3(
|
|
1632
1688
|
(e.x + s.x) / 2,
|
|
1633
1689
|
(e.y + s.y) / 2,
|
|
1634
1690
|
(e.z + s.z) / 2
|
|
1635
|
-
),
|
|
1691
|
+
), n = o.distanceTo(t), r = new x.Sphere(o, n);
|
|
1636
1692
|
return this.frustum.intersectsSphere(r);
|
|
1637
1693
|
}
|
|
1638
1694
|
/**
|
|
@@ -1642,7 +1698,7 @@ class Ct {
|
|
|
1642
1698
|
this.enabled = e;
|
|
1643
1699
|
}
|
|
1644
1700
|
}
|
|
1645
|
-
class
|
|
1701
|
+
class Ct {
|
|
1646
1702
|
constructor(e, s) {
|
|
1647
1703
|
l(this, "sceneManager");
|
|
1648
1704
|
l(this, "raycaster");
|
|
@@ -1656,7 +1712,7 @@ class Nt {
|
|
|
1656
1712
|
l(this, "hoveredEdgeKey", null);
|
|
1657
1713
|
l(this, "nodeObjects", []);
|
|
1658
1714
|
l(this, "edgeObjects", []);
|
|
1659
|
-
this.sceneManager = e, this.container = s, this.raycaster = new
|
|
1715
|
+
this.sceneManager = e, this.container = s, this.raycaster = new x.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new x.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
|
|
1660
1716
|
}
|
|
1661
1717
|
/**
|
|
1662
1718
|
* Updates the list of node objects to raycast against
|
|
@@ -1745,13 +1801,17 @@ class Nt {
|
|
|
1745
1801
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1746
1802
|
if (t.length > 0) {
|
|
1747
1803
|
const i = t[0].object, o = i.userData;
|
|
1748
|
-
if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode))
|
|
1804
|
+
if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode)) {
|
|
1805
|
+
const n = Array.isArray(o.relationships) ? o.relationships : [o.edge];
|
|
1749
1806
|
return {
|
|
1750
1807
|
edge: o.edge,
|
|
1808
|
+
relationships: n,
|
|
1809
|
+
relationshipCount: typeof o.relationshipCount == "number" ? o.relationshipCount : n.length,
|
|
1751
1810
|
sourceNode: o.sourceNode,
|
|
1752
1811
|
targetNode: o.targetNode,
|
|
1753
1812
|
edgeLine: i
|
|
1754
1813
|
};
|
|
1814
|
+
}
|
|
1755
1815
|
}
|
|
1756
1816
|
return null;
|
|
1757
1817
|
}
|
|
@@ -1764,11 +1824,11 @@ class Nt {
|
|
|
1764
1824
|
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);
|
|
1765
1825
|
const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1766
1826
|
if (i.length > 0) {
|
|
1767
|
-
let
|
|
1768
|
-
for (;
|
|
1769
|
-
if ((o =
|
|
1770
|
-
return
|
|
1771
|
-
|
|
1827
|
+
let n = i[0].object;
|
|
1828
|
+
for (; n; ) {
|
|
1829
|
+
if ((o = n.userData) != null && o.nodeId)
|
|
1830
|
+
return n.userData.nodeId;
|
|
1831
|
+
n = n.parent;
|
|
1772
1832
|
}
|
|
1773
1833
|
}
|
|
1774
1834
|
return null;
|
|
@@ -1780,7 +1840,7 @@ class Nt {
|
|
|
1780
1840
|
this.container.removeEventListener("click", this.handleClick), this.container.removeEventListener("mousemove", this.handleMouseMove);
|
|
1781
1841
|
}
|
|
1782
1842
|
}
|
|
1783
|
-
class
|
|
1843
|
+
class Nt {
|
|
1784
1844
|
constructor(e) {
|
|
1785
1845
|
l(this, "container");
|
|
1786
1846
|
l(this, "panel", null);
|
|
@@ -1803,17 +1863,15 @@ class St {
|
|
|
1803
1863
|
width: 280px;
|
|
1804
1864
|
max-height: 80vh;
|
|
1805
1865
|
overflow-y: auto;
|
|
1806
|
-
background: rgba(
|
|
1866
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1807
1867
|
backdrop-filter: blur(20px);
|
|
1808
1868
|
-webkit-backdrop-filter: blur(20px);
|
|
1809
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
1810
|
-
border-radius:
|
|
1869
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1870
|
+
border-radius: 12px;
|
|
1811
1871
|
padding: 24px;
|
|
1812
1872
|
color: white;
|
|
1813
|
-
font-family:
|
|
1814
|
-
box-shadow:
|
|
1815
|
-
0 8px 32px rgba(0, 0, 0, 0.4),
|
|
1816
|
-
inset 0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
1873
|
+
font-family: inherit;
|
|
1874
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
1817
1875
|
z-index: 1000;
|
|
1818
1876
|
opacity: 0;
|
|
1819
1877
|
pointer-events: none;
|
|
@@ -1857,8 +1915,8 @@ class St {
|
|
|
1857
1915
|
this.onExpand(this.currentNodeId, r);
|
|
1858
1916
|
}
|
|
1859
1917
|
});
|
|
1860
|
-
const
|
|
1861
|
-
|
|
1918
|
+
const n = this.panel.querySelector('[data-action="close"]');
|
|
1919
|
+
n && n.addEventListener("click", () => {
|
|
1862
1920
|
this.hide();
|
|
1863
1921
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
1864
1922
|
}
|
|
@@ -1871,8 +1929,8 @@ class St {
|
|
|
1871
1929
|
<style>
|
|
1872
1930
|
.force-graph-panel h2 {
|
|
1873
1931
|
margin: 0 0 16px 0;
|
|
1874
|
-
font-size:
|
|
1875
|
-
font-weight:
|
|
1932
|
+
font-size: 19px;
|
|
1933
|
+
font-weight: 650;
|
|
1876
1934
|
letter-spacing: -0.5px;
|
|
1877
1935
|
display: flex;
|
|
1878
1936
|
align-items: center;
|
|
@@ -1890,13 +1948,13 @@ class St {
|
|
|
1890
1948
|
height: 12px;
|
|
1891
1949
|
border-radius: 50%;
|
|
1892
1950
|
background: ${t};
|
|
1893
|
-
box-shadow: 0 0
|
|
1951
|
+
box-shadow: 0 0 12px ${t}80;
|
|
1894
1952
|
}
|
|
1895
1953
|
.force-graph-panel .info-row {
|
|
1896
1954
|
display: flex;
|
|
1897
1955
|
justify-content: space-between;
|
|
1898
1956
|
padding: 8px 0;
|
|
1899
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.
|
|
1957
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
1900
1958
|
font-size: 13px;
|
|
1901
1959
|
}
|
|
1902
1960
|
.force-graph-panel .info-row:last-child {
|
|
@@ -1916,19 +1974,24 @@ class St {
|
|
|
1916
1974
|
}
|
|
1917
1975
|
.force-graph-panel .neighbors-section {
|
|
1918
1976
|
margin-top: 16px;
|
|
1977
|
+
padding: 10px;
|
|
1978
|
+
background: rgba(255, 255, 255, 0.04);
|
|
1979
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
1980
|
+
border-radius: 10px;
|
|
1919
1981
|
}
|
|
1920
1982
|
.force-graph-panel .neighbors-title {
|
|
1921
1983
|
font-size: 12px;
|
|
1922
1984
|
text-transform: uppercase;
|
|
1923
1985
|
letter-spacing: 1px;
|
|
1924
|
-
color: rgba(255, 255, 255, 0.
|
|
1986
|
+
color: rgba(255, 255, 255, 0.55);
|
|
1925
1987
|
margin-bottom: 8px;
|
|
1926
1988
|
}
|
|
1927
1989
|
.force-graph-panel .neighbor-chip {
|
|
1928
1990
|
display: inline-block;
|
|
1929
1991
|
padding: 4px 10px;
|
|
1930
1992
|
margin: 2px;
|
|
1931
|
-
background: rgba(255, 255, 255, 0.
|
|
1993
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1994
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1932
1995
|
border-radius: 12px;
|
|
1933
1996
|
font-size: 12px;
|
|
1934
1997
|
}
|
|
@@ -1938,7 +2001,7 @@ class St {
|
|
|
1938
2001
|
}
|
|
1939
2002
|
.force-graph-panel .depth-label {
|
|
1940
2003
|
font-size: 12px;
|
|
1941
|
-
color: rgba(255, 255, 255, 0.
|
|
2004
|
+
color: rgba(255, 255, 255, 0.65);
|
|
1942
2005
|
margin-bottom: 6px;
|
|
1943
2006
|
text-transform: uppercase;
|
|
1944
2007
|
letter-spacing: 0.5px;
|
|
@@ -1946,9 +2009,9 @@ class St {
|
|
|
1946
2009
|
.force-graph-panel select {
|
|
1947
2010
|
width: 100%;
|
|
1948
2011
|
padding: 8px 12px;
|
|
1949
|
-
background: rgba(255, 255, 255, 0.
|
|
1950
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
1951
|
-
border-radius:
|
|
2012
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2013
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2014
|
+
border-radius: 8px;
|
|
1952
2015
|
color: white;
|
|
1953
2016
|
font-size: 13px;
|
|
1954
2017
|
cursor: pointer;
|
|
@@ -1956,10 +2019,11 @@ class St {
|
|
|
1956
2019
|
transition: all 0.2s ease;
|
|
1957
2020
|
}
|
|
1958
2021
|
.force-graph-panel select:hover {
|
|
1959
|
-
background: rgba(255, 255, 255, 0.
|
|
2022
|
+
background: rgba(255, 255, 255, 0.12);
|
|
1960
2023
|
}
|
|
1961
2024
|
.force-graph-panel select:focus {
|
|
1962
|
-
border-color: rgba(
|
|
2025
|
+
border-color: rgba(255, 153, 102, 0.5);
|
|
2026
|
+
box-shadow: 0 0 20px rgba(255, 153, 102, 0.15);
|
|
1963
2027
|
}
|
|
1964
2028
|
.force-graph-panel .btn-row {
|
|
1965
2029
|
display: flex;
|
|
@@ -1969,28 +2033,33 @@ class St {
|
|
|
1969
2033
|
.force-graph-panel button {
|
|
1970
2034
|
flex: 1;
|
|
1971
2035
|
padding: 10px 16px;
|
|
1972
|
-
border:
|
|
2036
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1973
2037
|
border-radius: 8px;
|
|
1974
2038
|
font-size: 13px;
|
|
1975
|
-
font-weight:
|
|
2039
|
+
font-weight: 600;
|
|
1976
2040
|
cursor: pointer;
|
|
1977
2041
|
transition: all 0.2s ease;
|
|
1978
2042
|
}
|
|
1979
2043
|
.force-graph-panel .btn-expand {
|
|
1980
|
-
background: linear-gradient(135deg,
|
|
2044
|
+
background: linear-gradient(135deg, rgba(255, 153, 102, 0.3), rgba(255, 102, 153, 0.2));
|
|
2045
|
+
border-color: rgba(255, 153, 102, 0.35);
|
|
1981
2046
|
color: white;
|
|
1982
2047
|
}
|
|
1983
2048
|
.force-graph-panel .btn-expand:hover {
|
|
1984
2049
|
transform: translateY(-1px);
|
|
1985
|
-
box-shadow: 0
|
|
2050
|
+
box-shadow: 0 0 15px rgba(255, 153, 102, 0.2);
|
|
1986
2051
|
}
|
|
1987
2052
|
.force-graph-panel .btn-close {
|
|
1988
|
-
background: rgba(255, 255, 255, 0.
|
|
2053
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1989
2054
|
color: rgba(255, 255, 255, 0.8);
|
|
1990
2055
|
}
|
|
1991
2056
|
.force-graph-panel .btn-close:hover {
|
|
1992
|
-
background: rgba(255, 255, 255, 0.
|
|
2057
|
+
background: rgba(255, 255, 255, 0.12);
|
|
1993
2058
|
}
|
|
2059
|
+
.force-graph-panel::-webkit-scrollbar { width: 6px; }
|
|
2060
|
+
.force-graph-panel::-webkit-scrollbar-track { background: transparent; }
|
|
2061
|
+
.force-graph-panel::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
|
|
2062
|
+
.force-graph-panel::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); }
|
|
1994
2063
|
</style>
|
|
1995
2064
|
|
|
1996
2065
|
<h2>
|
|
@@ -2066,7 +2135,7 @@ class St {
|
|
|
2066
2135
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2067
2136
|
}
|
|
2068
2137
|
}
|
|
2069
|
-
class
|
|
2138
|
+
class St {
|
|
2070
2139
|
constructor(e) {
|
|
2071
2140
|
l(this, "container");
|
|
2072
2141
|
l(this, "panel", null);
|
|
@@ -2089,17 +2158,15 @@ class zt {
|
|
|
2089
2158
|
width: 300px;
|
|
2090
2159
|
max-height: 80vh;
|
|
2091
2160
|
overflow-y: auto;
|
|
2092
|
-
background: rgba(
|
|
2161
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2093
2162
|
backdrop-filter: blur(20px);
|
|
2094
2163
|
-webkit-backdrop-filter: blur(20px);
|
|
2095
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
2096
|
-
border-radius:
|
|
2164
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2165
|
+
border-radius: 12px;
|
|
2097
2166
|
padding: 24px;
|
|
2098
2167
|
color: white;
|
|
2099
|
-
font-family:
|
|
2100
|
-
box-shadow:
|
|
2101
|
-
0 8px 32px rgba(0, 0, 0, 0.4),
|
|
2102
|
-
inset 0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
2168
|
+
font-family: inherit;
|
|
2169
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
2103
2170
|
z-index: 1000;
|
|
2104
2171
|
opacity: 0;
|
|
2105
2172
|
pointer-events: none;
|
|
@@ -2127,29 +2194,34 @@ class zt {
|
|
|
2127
2194
|
/**
|
|
2128
2195
|
* Shows the panel with edge/relationship information
|
|
2129
2196
|
*/
|
|
2130
|
-
show(e, s, t) {
|
|
2197
|
+
show(e, s, t, i = [e]) {
|
|
2131
2198
|
if (!this.panel) return;
|
|
2132
2199
|
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2133
|
-
let
|
|
2134
|
-
this.panelTemplate ?
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2200
|
+
let o;
|
|
2201
|
+
this.panelTemplate ? o = this.panelTemplate(e, s, t) : o = this.generateDefaultContent(e, s, t, i), this.panel.innerHTML = o;
|
|
2202
|
+
const n = this.panel.querySelector('[data-action="close"]');
|
|
2203
|
+
n && n.addEventListener("click", () => {
|
|
2137
2204
|
this.hide(), this.onClose && this.onClose();
|
|
2138
2205
|
});
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2206
|
+
const r = this.panel.querySelector('[data-action="goto-source"]');
|
|
2207
|
+
r && this.onNodeClick && r.addEventListener("click", () => {
|
|
2141
2208
|
this.onNodeClick && this.onNodeClick(e.source);
|
|
2142
2209
|
});
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2210
|
+
const d = this.panel.querySelector('[data-action="goto-target"]');
|
|
2211
|
+
d && this.onNodeClick && d.addEventListener("click", () => {
|
|
2145
2212
|
this.onNodeClick && this.onNodeClick(e.target);
|
|
2146
2213
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
2147
2214
|
}
|
|
2148
2215
|
/**
|
|
2149
2216
|
* Generates default panel content
|
|
2150
2217
|
*/
|
|
2151
|
-
generateDefaultContent(e, s, t) {
|
|
2152
|
-
const
|
|
2218
|
+
generateDefaultContent(e, s, t, i = [e]) {
|
|
2219
|
+
const o = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", n = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", r = this.getRelationshipLabel(e), d = i.length > 0 ? i : [e], c = this.summarizeRelationships(d), p = c.slice(0, 10).map((m) => `
|
|
2220
|
+
<div class="relationship-item">
|
|
2221
|
+
<span class="relationship-item-label">${this.escapeHtml(m.label)}</span>
|
|
2222
|
+
<span class="relationship-item-count">${m.count}</span>
|
|
2223
|
+
</div>
|
|
2224
|
+
`).join(""), f = c.length - Math.min(c.length, 10);
|
|
2153
2225
|
return `
|
|
2154
2226
|
<style>
|
|
2155
2227
|
.force-graph-edge-panel .panel-header {
|
|
@@ -2159,28 +2231,72 @@ class zt {
|
|
|
2159
2231
|
margin-bottom: 20px;
|
|
2160
2232
|
}
|
|
2161
2233
|
.force-graph-edge-panel .panel-title {
|
|
2162
|
-
font-size:
|
|
2234
|
+
font-size: 12px;
|
|
2163
2235
|
text-transform: uppercase;
|
|
2164
2236
|
letter-spacing: 1px;
|
|
2165
|
-
color: rgba(255, 255, 255, 0.
|
|
2237
|
+
color: rgba(255, 255, 255, 0.55);
|
|
2166
2238
|
margin: 0;
|
|
2167
2239
|
}
|
|
2168
2240
|
.force-graph-edge-panel .relationship-section {
|
|
2169
|
-
background: rgba(255, 255, 255, 0.
|
|
2241
|
+
background: rgba(255, 255, 255, 0.04);
|
|
2242
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
2170
2243
|
border-radius: 12px;
|
|
2171
2244
|
padding: 16px;
|
|
2172
2245
|
text-align: center;
|
|
2173
|
-
margin-bottom:
|
|
2246
|
+
margin-bottom: 14px;
|
|
2174
2247
|
}
|
|
2175
2248
|
.force-graph-edge-panel .relationship-label {
|
|
2176
2249
|
font-size: 18px;
|
|
2177
2250
|
font-weight: 600;
|
|
2178
|
-
color:
|
|
2251
|
+
color: rgba(255, 153, 102, 0.95);
|
|
2179
2252
|
letter-spacing: 0.5px;
|
|
2180
2253
|
}
|
|
2254
|
+
.force-graph-edge-panel .relationship-count {
|
|
2255
|
+
margin-top: 6px;
|
|
2256
|
+
font-size: 11px;
|
|
2257
|
+
text-transform: uppercase;
|
|
2258
|
+
letter-spacing: 0.8px;
|
|
2259
|
+
color: rgba(255, 255, 255, 0.6);
|
|
2260
|
+
}
|
|
2261
|
+
.force-graph-edge-panel .relationship-list {
|
|
2262
|
+
margin-bottom: 16px;
|
|
2263
|
+
max-height: 190px;
|
|
2264
|
+
overflow-y: auto;
|
|
2265
|
+
padding-right: 4px;
|
|
2266
|
+
}
|
|
2267
|
+
.force-graph-edge-panel .relationship-item {
|
|
2268
|
+
display: flex;
|
|
2269
|
+
justify-content: space-between;
|
|
2270
|
+
gap: 12px;
|
|
2271
|
+
padding: 8px 10px;
|
|
2272
|
+
background: rgba(255, 255, 255, 0.04);
|
|
2273
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
2274
|
+
border-radius: 8px;
|
|
2275
|
+
margin-bottom: 6px;
|
|
2276
|
+
}
|
|
2277
|
+
.force-graph-edge-panel .relationship-item-label {
|
|
2278
|
+
font-size: 12px;
|
|
2279
|
+
color: rgba(255, 255, 255, 0.88);
|
|
2280
|
+
overflow: hidden;
|
|
2281
|
+
text-overflow: ellipsis;
|
|
2282
|
+
white-space: nowrap;
|
|
2283
|
+
}
|
|
2284
|
+
.force-graph-edge-panel .relationship-item-count {
|
|
2285
|
+
font-size: 12px;
|
|
2286
|
+
font-weight: 700;
|
|
2287
|
+
color: rgba(255, 153, 102, 0.95);
|
|
2288
|
+
min-width: 24px;
|
|
2289
|
+
text-align: right;
|
|
2290
|
+
}
|
|
2291
|
+
.force-graph-edge-panel .relationship-more {
|
|
2292
|
+
font-size: 11px;
|
|
2293
|
+
color: rgba(255, 255, 255, 0.45);
|
|
2294
|
+
text-align: center;
|
|
2295
|
+
margin-top: 2px;
|
|
2296
|
+
}
|
|
2181
2297
|
.force-graph-edge-panel .node-card {
|
|
2182
|
-
background: rgba(255, 255, 255, 0.
|
|
2183
|
-
border: 1px solid rgba(255, 255, 255, 0.
|
|
2298
|
+
background: rgba(255, 255, 255, 0.06);
|
|
2299
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2184
2300
|
border-radius: 12px;
|
|
2185
2301
|
padding: 14px;
|
|
2186
2302
|
margin-bottom: 12px;
|
|
@@ -2189,8 +2305,9 @@ class zt {
|
|
|
2189
2305
|
}
|
|
2190
2306
|
.force-graph-edge-panel .node-card:hover {
|
|
2191
2307
|
background: rgba(255, 255, 255, 0.1);
|
|
2192
|
-
border-color: rgba(255,
|
|
2308
|
+
border-color: rgba(255, 153, 102, 0.35);
|
|
2193
2309
|
transform: translateX(4px);
|
|
2310
|
+
box-shadow: 0 0 15px rgba(255, 153, 102, 0.15);
|
|
2194
2311
|
}
|
|
2195
2312
|
.force-graph-edge-panel .node-card-header {
|
|
2196
2313
|
display: flex;
|
|
@@ -2227,18 +2344,18 @@ class zt {
|
|
|
2227
2344
|
.force-graph-edge-panel .btn-close {
|
|
2228
2345
|
width: 100%;
|
|
2229
2346
|
padding: 12px 16px;
|
|
2230
|
-
border:
|
|
2347
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2231
2348
|
border-radius: 8px;
|
|
2232
2349
|
font-size: 13px;
|
|
2233
|
-
font-weight:
|
|
2350
|
+
font-weight: 600;
|
|
2234
2351
|
cursor: pointer;
|
|
2235
2352
|
transition: all 0.2s ease;
|
|
2236
|
-
background: rgba(255, 255, 255, 0.
|
|
2353
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2237
2354
|
color: rgba(255, 255, 255, 0.8);
|
|
2238
2355
|
margin-top: 16px;
|
|
2239
2356
|
}
|
|
2240
2357
|
.force-graph-edge-panel .btn-close:hover {
|
|
2241
|
-
background: rgba(255, 255, 255, 0.
|
|
2358
|
+
background: rgba(255, 255, 255, 0.12);
|
|
2242
2359
|
}
|
|
2243
2360
|
.force-graph-edge-panel .hint-text {
|
|
2244
2361
|
font-size: 11px;
|
|
@@ -2246,6 +2363,10 @@ class zt {
|
|
|
2246
2363
|
text-align: center;
|
|
2247
2364
|
margin-top: 8px;
|
|
2248
2365
|
}
|
|
2366
|
+
.force-graph-edge-panel .relationship-list::-webkit-scrollbar { width: 6px; }
|
|
2367
|
+
.force-graph-edge-panel .relationship-list::-webkit-scrollbar-track { background: transparent; }
|
|
2368
|
+
.force-graph-edge-panel .relationship-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
|
|
2369
|
+
.force-graph-edge-panel .relationship-list::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); }
|
|
2249
2370
|
</style>
|
|
2250
2371
|
|
|
2251
2372
|
<div class="panel-header">
|
|
@@ -2253,13 +2374,19 @@ class zt {
|
|
|
2253
2374
|
</div>
|
|
2254
2375
|
|
|
2255
2376
|
<div class="relationship-section">
|
|
2256
|
-
<span class="relationship-label">${this.escapeHtml(
|
|
2377
|
+
<span class="relationship-label">${this.escapeHtml(r)}</span>
|
|
2378
|
+
<div class="relationship-count">${d.length} relationships</div>
|
|
2379
|
+
</div>
|
|
2380
|
+
|
|
2381
|
+
<div class="relationship-list">
|
|
2382
|
+
${p}
|
|
2383
|
+
${f > 0 ? `<div class="relationship-more">+ ${f} more</div>` : ""}
|
|
2257
2384
|
</div>
|
|
2258
2385
|
|
|
2259
2386
|
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2260
2387
|
<div class="node-type">Source</div>
|
|
2261
2388
|
<div class="node-card-header">
|
|
2262
|
-
<span class="color-dot" style="background: ${
|
|
2389
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2263
2390
|
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2264
2391
|
</div>
|
|
2265
2392
|
</div>
|
|
@@ -2269,7 +2396,7 @@ class zt {
|
|
|
2269
2396
|
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2270
2397
|
<div class="node-type">Target</div>
|
|
2271
2398
|
<div class="node-card-header">
|
|
2272
|
-
<span class="color-dot" style="background: ${
|
|
2399
|
+
<span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
|
|
2273
2400
|
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2274
2401
|
</div>
|
|
2275
2402
|
</div>
|
|
@@ -2286,6 +2413,17 @@ class zt {
|
|
|
2286
2413
|
const s = document.createElement("div");
|
|
2287
2414
|
return s.textContent = e, s.innerHTML;
|
|
2288
2415
|
}
|
|
2416
|
+
getRelationshipLabel(e) {
|
|
2417
|
+
return e.relationship || e.label || "connected to";
|
|
2418
|
+
}
|
|
2419
|
+
summarizeRelationships(e) {
|
|
2420
|
+
const s = /* @__PURE__ */ new Map();
|
|
2421
|
+
for (const t of e) {
|
|
2422
|
+
const i = this.getRelationshipLabel(t);
|
|
2423
|
+
s.set(i, (s.get(i) || 0) + 1);
|
|
2424
|
+
}
|
|
2425
|
+
return Array.from(s.entries()).map(([t, i]) => ({ label: t, count: i })).sort((t, i) => i.count - t.count);
|
|
2426
|
+
}
|
|
2289
2427
|
/**
|
|
2290
2428
|
* Hides the panel
|
|
2291
2429
|
*/
|
|
@@ -2311,7 +2449,7 @@ class zt {
|
|
|
2311
2449
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2312
2450
|
}
|
|
2313
2451
|
}
|
|
2314
|
-
class
|
|
2452
|
+
class zt {
|
|
2315
2453
|
constructor() {
|
|
2316
2454
|
l(this, "tooltip", null);
|
|
2317
2455
|
l(this, "visible", !1);
|
|
@@ -2358,28 +2496,51 @@ class Tt {
|
|
|
2358
2496
|
positionTooltip(e, s) {
|
|
2359
2497
|
if (!this.tooltip) return;
|
|
2360
2498
|
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, o = window.innerHeight;
|
|
2361
|
-
let
|
|
2362
|
-
|
|
2499
|
+
let n = e + 15, r = s + 15;
|
|
2500
|
+
n + t.width > i - 10 && (n = e - t.width - 15), r + t.height > o - 10 && (r = s - t.height - 15), n < 10 && (n = 10), r < 10 && (r = 10), this.tooltip.style.left = `${n}px`, this.tooltip.style.top = `${r}px`;
|
|
2363
2501
|
}
|
|
2364
2502
|
/**
|
|
2365
2503
|
* Shows the tooltip with edge info
|
|
2366
2504
|
*/
|
|
2367
|
-
show(e, s, t, i, o) {
|
|
2505
|
+
show(e, s, t, i, o, n = [e]) {
|
|
2368
2506
|
if (!this.tooltip) return;
|
|
2369
|
-
const
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2507
|
+
const r = n.length > 0 ? n : [e], d = this.summarizeRelationships(r), c = r.length;
|
|
2508
|
+
if (c <= 1) {
|
|
2509
|
+
const p = this.getRelationshipLabel(r[0]);
|
|
2510
|
+
this.tooltip.innerHTML = `
|
|
2511
|
+
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
2512
|
+
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2513
|
+
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
|
|
2514
|
+
</div>
|
|
2515
|
+
<div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
|
|
2516
|
+
↳ ${this.escapeHtml(p)}
|
|
2517
|
+
</div>
|
|
2518
|
+
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2519
|
+
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
2520
|
+
</div>
|
|
2377
2521
|
</div>
|
|
2378
|
-
|
|
2379
|
-
|
|
2522
|
+
`;
|
|
2523
|
+
} else {
|
|
2524
|
+
const p = d.slice(0, 4), f = d.length - p.length, m = p.map((u) => `<div style="display:flex; justify-content:space-between; gap:10px; font-size:12px; color: rgba(255,255,255,0.8);">
|
|
2525
|
+
<span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${this.escapeHtml(u.label)}</span>
|
|
2526
|
+
<span style="color: rgba(255,153,102,0.9); font-weight:600;">${u.count}</span>
|
|
2527
|
+
</div>`).join("");
|
|
2528
|
+
this.tooltip.innerHTML = `
|
|
2529
|
+
<div style="display:flex; flex-direction:column; gap:8px; min-width:220px;">
|
|
2530
|
+
<div style="font-size:13px; font-weight:600; color:#ff9966;">
|
|
2531
|
+
${this.escapeHtml(s.label)} → ${this.escapeHtml(t.label)}
|
|
2532
|
+
</div>
|
|
2533
|
+
<div style="font-size:11px; letter-spacing:0.4px; text-transform:uppercase; color: rgba(255,255,255,0.6);">
|
|
2534
|
+
${c} relationships
|
|
2535
|
+
</div>
|
|
2536
|
+
<div style="display:flex; flex-direction:column; gap:4px;">
|
|
2537
|
+
${m}
|
|
2538
|
+
</div>
|
|
2539
|
+
${f > 0 ? `<div style="font-size:11px; color: rgba(255,255,255,0.45);">+ ${f} more</div>` : ""}
|
|
2380
2540
|
</div>
|
|
2381
|
-
|
|
2382
|
-
|
|
2541
|
+
`;
|
|
2542
|
+
}
|
|
2543
|
+
this.positionTooltip(i, o), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2383
2544
|
}
|
|
2384
2545
|
/**
|
|
2385
2546
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2406,6 +2567,17 @@ class Tt {
|
|
|
2406
2567
|
const s = document.createElement("div");
|
|
2407
2568
|
return s.textContent = e, s.innerHTML;
|
|
2408
2569
|
}
|
|
2570
|
+
getRelationshipLabel(e) {
|
|
2571
|
+
return e.relationship || e.label || "connected to";
|
|
2572
|
+
}
|
|
2573
|
+
summarizeRelationships(e) {
|
|
2574
|
+
const s = /* @__PURE__ */ new Map();
|
|
2575
|
+
for (const t of e) {
|
|
2576
|
+
const i = this.getRelationshipLabel(t);
|
|
2577
|
+
s.set(i, (s.get(i) || 0) + 1);
|
|
2578
|
+
}
|
|
2579
|
+
return Array.from(s.entries()).map(([t, i]) => ({ label: t, count: i })).sort((t, i) => i.count - t.count);
|
|
2580
|
+
}
|
|
2409
2581
|
/**
|
|
2410
2582
|
* Dispose the tooltip
|
|
2411
2583
|
*/
|
|
@@ -2558,24 +2730,24 @@ class kt {
|
|
|
2558
2730
|
}
|
|
2559
2731
|
let i = "";
|
|
2560
2732
|
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((o) => {
|
|
2561
|
-
const
|
|
2733
|
+
const n = o.type || "Node";
|
|
2562
2734
|
i += `
|
|
2563
2735
|
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.id)}">
|
|
2564
2736
|
<div class="f3d-result-label">${this.escapeHtml(o.label)}</div>
|
|
2565
|
-
<div class="f3d-result-type">${this.escapeHtml(
|
|
2737
|
+
<div class="f3d-result-type">${this.escapeHtml(n)}</div>
|
|
2566
2738
|
</div>
|
|
2567
2739
|
`;
|
|
2568
|
-
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: o, sourceNode:
|
|
2740
|
+
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: o, sourceNode: n, targetNode: r }) => {
|
|
2569
2741
|
i += `
|
|
2570
2742
|
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.source)}">
|
|
2571
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2743
|
+
<div class="f3d-result-label">${this.escapeHtml(n.label)} → ${this.escapeHtml(r.label)}</div>
|
|
2572
2744
|
<div class="f3d-result-relationship">${this.escapeHtml(o.relationship || "connected")}</div>
|
|
2573
2745
|
</div>
|
|
2574
2746
|
`;
|
|
2575
2747
|
}), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((o) => {
|
|
2576
2748
|
o.addEventListener("click", () => {
|
|
2577
|
-
const
|
|
2578
|
-
|
|
2749
|
+
const n = o.dataset.nodeId;
|
|
2750
|
+
n && (this.onResultClick(n), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2579
2751
|
});
|
|
2580
2752
|
});
|
|
2581
2753
|
}
|
|
@@ -2587,7 +2759,7 @@ class kt {
|
|
|
2587
2759
|
this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
|
|
2588
2760
|
}
|
|
2589
2761
|
}
|
|
2590
|
-
class
|
|
2762
|
+
class Tt {
|
|
2591
2763
|
constructor(e, s) {
|
|
2592
2764
|
l(this, "container");
|
|
2593
2765
|
l(this, "toggleContainer", null);
|
|
@@ -2679,7 +2851,90 @@ class Pt {
|
|
|
2679
2851
|
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2680
2852
|
}
|
|
2681
2853
|
}
|
|
2682
|
-
|
|
2854
|
+
class Pt {
|
|
2855
|
+
constructor(e) {
|
|
2856
|
+
l(this, "container");
|
|
2857
|
+
l(this, "legendContainer", null);
|
|
2858
|
+
l(this, "nodeCountElement", null);
|
|
2859
|
+
l(this, "linkCountElement", null);
|
|
2860
|
+
this.container = e, this.init();
|
|
2861
|
+
}
|
|
2862
|
+
init() {
|
|
2863
|
+
this.createLegendUI(), this.injectStyles();
|
|
2864
|
+
}
|
|
2865
|
+
createLegendUI() {
|
|
2866
|
+
this.legendContainer = document.createElement("div"), this.legendContainer.className = "f3d-legend-container";
|
|
2867
|
+
const e = document.createElement("div");
|
|
2868
|
+
e.className = "f3d-legend-title", e.textContent = "Graph";
|
|
2869
|
+
const s = this.createStatRow("Nodes"), t = this.createStatRow("Links");
|
|
2870
|
+
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);
|
|
2871
|
+
}
|
|
2872
|
+
createStatRow(e) {
|
|
2873
|
+
const s = document.createElement("div");
|
|
2874
|
+
s.className = "f3d-legend-row";
|
|
2875
|
+
const t = document.createElement("span");
|
|
2876
|
+
t.className = "f3d-legend-label", t.textContent = e;
|
|
2877
|
+
const i = document.createElement("span");
|
|
2878
|
+
return i.className = "f3d-legend-value", i.textContent = "0", s.appendChild(t), s.appendChild(i), { rowElement: s, valueElement: i };
|
|
2879
|
+
}
|
|
2880
|
+
injectStyles() {
|
|
2881
|
+
const e = "f3d-legend-styles";
|
|
2882
|
+
if (document.getElementById(e))
|
|
2883
|
+
return;
|
|
2884
|
+
const s = document.createElement("style");
|
|
2885
|
+
s.id = e, s.textContent = `
|
|
2886
|
+
.f3d-legend-container {
|
|
2887
|
+
position: absolute;
|
|
2888
|
+
right: 20px;
|
|
2889
|
+
bottom: 20px;
|
|
2890
|
+
z-index: 100;
|
|
2891
|
+
min-width: 140px;
|
|
2892
|
+
padding: 10px 12px;
|
|
2893
|
+
display: flex;
|
|
2894
|
+
flex-direction: column;
|
|
2895
|
+
gap: 8px;
|
|
2896
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2897
|
+
backdrop-filter: blur(20px);
|
|
2898
|
+
-webkit-backdrop-filter: blur(20px);
|
|
2899
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2900
|
+
border-radius: 12px;
|
|
2901
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
2902
|
+
color: white;
|
|
2903
|
+
font-family: inherit;
|
|
2904
|
+
pointer-events: none;
|
|
2905
|
+
}
|
|
2906
|
+
.f3d-legend-title {
|
|
2907
|
+
font-size: 10px;
|
|
2908
|
+
letter-spacing: 1px;
|
|
2909
|
+
text-transform: uppercase;
|
|
2910
|
+
font-weight: 700;
|
|
2911
|
+
color: rgba(255, 255, 255, 0.55);
|
|
2912
|
+
}
|
|
2913
|
+
.f3d-legend-row {
|
|
2914
|
+
display: flex;
|
|
2915
|
+
align-items: center;
|
|
2916
|
+
justify-content: space-between;
|
|
2917
|
+
gap: 12px;
|
|
2918
|
+
}
|
|
2919
|
+
.f3d-legend-label {
|
|
2920
|
+
font-size: 12px;
|
|
2921
|
+
color: rgba(255, 255, 255, 0.75);
|
|
2922
|
+
}
|
|
2923
|
+
.f3d-legend-value {
|
|
2924
|
+
font-size: 13px;
|
|
2925
|
+
font-weight: 700;
|
|
2926
|
+
color: rgba(255, 153, 102, 0.95);
|
|
2927
|
+
}
|
|
2928
|
+
`, document.head.appendChild(s);
|
|
2929
|
+
}
|
|
2930
|
+
updateCounts(e, s) {
|
|
2931
|
+
this.nodeCountElement && (this.nodeCountElement.textContent = e.toString()), this.linkCountElement && (this.linkCountElement.textContent = s.toString());
|
|
2932
|
+
}
|
|
2933
|
+
dispose() {
|
|
2934
|
+
this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
const Lt = {
|
|
2683
2938
|
backgroundColor: "#0a0a0a",
|
|
2684
2939
|
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
2685
2940
|
nodeRadius: 24,
|
|
@@ -2692,7 +2947,7 @@ const Rt = {
|
|
|
2692
2947
|
damping: 0.85
|
|
2693
2948
|
// Fast energy dissipation
|
|
2694
2949
|
};
|
|
2695
|
-
class
|
|
2950
|
+
class Rt {
|
|
2696
2951
|
constructor(e, s = {}) {
|
|
2697
2952
|
l(this, "container");
|
|
2698
2953
|
l(this, "canvas");
|
|
@@ -2719,7 +2974,7 @@ class It {
|
|
|
2719
2974
|
l(this, "isSimulating", !0);
|
|
2720
2975
|
// Resize handler
|
|
2721
2976
|
l(this, "resizeHandler");
|
|
2722
|
-
this.container = e, this.options = { ...
|
|
2977
|
+
this.container = e, this.options = { ...Lt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
|
|
2723
2978
|
const t = this.canvas.getContext("2d");
|
|
2724
2979
|
if (!t)
|
|
2725
2980
|
throw new Error("Failed to get 2D context");
|
|
@@ -2732,21 +2987,21 @@ class It {
|
|
|
2732
2987
|
setupInteractions() {
|
|
2733
2988
|
this.canvas.addEventListener("wheel", (e) => {
|
|
2734
2989
|
e.preventDefault();
|
|
2735
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1,
|
|
2736
|
-
this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale =
|
|
2990
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1, n = Math.max(0.1, Math.min(5, this.transform.scale * o)), r = n / this.transform.scale;
|
|
2991
|
+
this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale = n;
|
|
2737
2992
|
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2738
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i),
|
|
2739
|
-
this.dragStartPos = { x: e.clientX, y: e.clientY },
|
|
2993
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i), n = this.findNodeAt(o.x, o.y);
|
|
2994
|
+
this.dragStartPos = { x: e.clientX, y: e.clientY }, n ? (this.isDragging = !0, this.draggedNode = n, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2740
2995
|
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2741
2996
|
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i);
|
|
2742
2997
|
if (this.isDragging && this.draggedNode)
|
|
2743
2998
|
this.draggedNode.x = o.x, this.draggedNode.y = o.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2744
2999
|
else if (this.isPanning) {
|
|
2745
|
-
const
|
|
2746
|
-
this.transform.x +=
|
|
3000
|
+
const n = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
|
|
3001
|
+
this.transform.x += n, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2747
3002
|
} else {
|
|
2748
|
-
const
|
|
2749
|
-
if (
|
|
3003
|
+
const n = this.findNodeAt(o.x, o.y);
|
|
3004
|
+
if (n !== this.hoveredNode && (this.hoveredNode = n, n ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(n.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !n) {
|
|
2750
3005
|
const r = this.findEdgeAt(o.x, o.y);
|
|
2751
3006
|
r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
|
|
2752
3007
|
}
|
|
@@ -2756,7 +3011,7 @@ class It {
|
|
|
2756
3011
|
if (this.isDragging && this.draggedNode)
|
|
2757
3012
|
i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
2758
3013
|
else if (i) {
|
|
2759
|
-
const o = this.canvas.getBoundingClientRect(),
|
|
3014
|
+
const o = this.canvas.getBoundingClientRect(), n = this.screenToWorld(e.clientX - o.left, e.clientY - o.top), r = this.findEdgeAt(n.x, n.y);
|
|
2760
3015
|
r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
|
|
2761
3016
|
}
|
|
2762
3017
|
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
@@ -2780,12 +3035,12 @@ class It {
|
|
|
2780
3035
|
}
|
|
2781
3036
|
findEdgeAt(e, s) {
|
|
2782
3037
|
for (const i of this.edges) {
|
|
2783
|
-
const o = this.nodes.get(i.source),
|
|
2784
|
-
if (!o || !
|
|
2785
|
-
const r =
|
|
2786
|
-
if (
|
|
2787
|
-
const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) *
|
|
2788
|
-
if (Math.sqrt(u * u +
|
|
3038
|
+
const o = this.nodes.get(i.source), n = this.nodes.get(i.target);
|
|
3039
|
+
if (!o || !n) continue;
|
|
3040
|
+
const r = n.x - o.x, d = n.y - o.y, c = r * r + d * d;
|
|
3041
|
+
if (c === 0) continue;
|
|
3042
|
+
const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * d) / c)), f = o.x + p * r, m = o.y + p * d, u = e - f, y = s - m;
|
|
3043
|
+
if (Math.sqrt(u * u + y * y) < 12)
|
|
2789
3044
|
return i;
|
|
2790
3045
|
}
|
|
2791
3046
|
return null;
|
|
@@ -2802,29 +3057,29 @@ class It {
|
|
|
2802
3057
|
const t = 60, i = 5;
|
|
2803
3058
|
let o = 0;
|
|
2804
3059
|
for (let r = 0; r < s; r++)
|
|
2805
|
-
for (let
|
|
2806
|
-
const
|
|
2807
|
-
let
|
|
3060
|
+
for (let d = r + 1; d < s; d++) {
|
|
3061
|
+
const c = e[r], p = e[d];
|
|
3062
|
+
let f = p.x - c.x, m = p.y - c.y, u = Math.sqrt(f * f + m * m);
|
|
2808
3063
|
if (u < t * 3) {
|
|
2809
3064
|
u < 1 && (u = 1);
|
|
2810
|
-
const
|
|
2811
|
-
|
|
3065
|
+
const y = this.options.repulsionStrength / (u * u), b = f / u * y, w = m / u * y;
|
|
3066
|
+
c.vx -= b, c.vy -= w, p.vx += b, p.vy += w;
|
|
2812
3067
|
}
|
|
2813
3068
|
}
|
|
2814
|
-
const
|
|
3069
|
+
const n = 80;
|
|
2815
3070
|
for (const r of this.edges) {
|
|
2816
|
-
const
|
|
2817
|
-
if (!
|
|
2818
|
-
let p =
|
|
3071
|
+
const d = this.nodes.get(r.source), c = this.nodes.get(r.target);
|
|
3072
|
+
if (!d || !c) continue;
|
|
3073
|
+
let p = c.x - d.x, f = c.y - d.y, m = Math.sqrt(p * p + f * f);
|
|
2819
3074
|
m < 1 && (m = 1);
|
|
2820
|
-
const
|
|
2821
|
-
|
|
3075
|
+
const y = (m - n) * this.options.attractionStrength, b = p / m * y, w = f / m * y;
|
|
3076
|
+
d.vx += b, d.vy += w, c.vx -= b, c.vy -= w;
|
|
2822
3077
|
}
|
|
2823
3078
|
for (const r of e) {
|
|
2824
3079
|
if (this.draggedNode === r) continue;
|
|
2825
3080
|
r.vx *= this.options.damping, r.vy *= this.options.damping;
|
|
2826
|
-
const
|
|
2827
|
-
|
|
3081
|
+
const d = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
|
|
3082
|
+
d > i && (r.vx = r.vx / d * i, r.vy = r.vy / d * i), r.x += r.vx, r.y += r.vy, o += r.vx * r.vx + r.vy * r.vy;
|
|
2828
3083
|
}
|
|
2829
3084
|
o < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
2830
3085
|
}
|
|
@@ -2833,49 +3088,49 @@ class It {
|
|
|
2833
3088
|
e.fillStyle = this.options.backgroundColor, e.fillRect(0, 0, s, t), this.renderGrid(s, t), e.save(), e.translate(this.transform.x, this.transform.y), e.scale(this.transform.scale, this.transform.scale), this.renderEdges(), this.renderNodes(), e.restore();
|
|
2834
3089
|
}
|
|
2835
3090
|
renderGrid(e, s) {
|
|
2836
|
-
const t = this.ctx, i = 40 * this.transform.scale, o = 1.5,
|
|
3091
|
+
const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, n = this.transform.x % i, r = this.transform.y % i;
|
|
2837
3092
|
t.fillStyle = this.options.gridColor;
|
|
2838
|
-
for (let
|
|
2839
|
-
for (let
|
|
2840
|
-
t.beginPath(), t.arc(
|
|
3093
|
+
for (let d = n; d < e; d += i)
|
|
3094
|
+
for (let c = r; c < s; c += i)
|
|
3095
|
+
t.beginPath(), t.arc(d, c, o, 0, Math.PI * 2), t.fill();
|
|
2841
3096
|
}
|
|
2842
3097
|
renderEdges() {
|
|
2843
3098
|
const e = this.ctx;
|
|
2844
3099
|
for (const s of this.edges) {
|
|
2845
3100
|
const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
|
|
2846
3101
|
if (!t || !i) continue;
|
|
2847
|
-
const o = s === this.hoveredEdge,
|
|
2848
|
-
o ? (
|
|
3102
|
+
const o = s === this.hoveredEdge, n = e.createLinearGradient(t.x, t.y, i.x, i.y);
|
|
3103
|
+
o ? (n.addColorStop(0, "rgba(255, 153, 102, 0.8)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), n.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (n.addColorStop(0, "rgba(255, 153, 102, 0.3)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), n.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = n, e.stroke();
|
|
2849
3104
|
}
|
|
2850
3105
|
}
|
|
2851
3106
|
renderNodes() {
|
|
2852
3107
|
const e = this.ctx;
|
|
2853
3108
|
for (const s of this.nodes.values()) {
|
|
2854
|
-
const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target),
|
|
3109
|
+
const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), n = s.radius * (t ? 1.1 : 1);
|
|
2855
3110
|
if (t || i || o) {
|
|
2856
|
-
const
|
|
3111
|
+
const y = e.createRadialGradient(
|
|
2857
3112
|
s.x,
|
|
2858
3113
|
s.y,
|
|
2859
|
-
|
|
3114
|
+
n * 0.5,
|
|
2860
3115
|
s.x,
|
|
2861
3116
|
s.y,
|
|
2862
|
-
|
|
3117
|
+
n * 2
|
|
2863
3118
|
), b = t || i ? 0.4 : 0.25;
|
|
2864
|
-
|
|
3119
|
+
y.addColorStop(0, `rgba(255, 153, 102, ${b})`), y.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = y, e.beginPath(), e.arc(s.x, s.y, n * 2, 0, Math.PI * 2), e.fill();
|
|
2865
3120
|
}
|
|
2866
3121
|
const r = e.createRadialGradient(
|
|
2867
|
-
s.x -
|
|
2868
|
-
s.y -
|
|
3122
|
+
s.x - n * 0.3,
|
|
3123
|
+
s.y - n * 0.3,
|
|
2869
3124
|
0,
|
|
2870
3125
|
s.x,
|
|
2871
3126
|
s.y,
|
|
2872
|
-
|
|
2873
|
-
),
|
|
2874
|
-
r.addColorStop(0, `rgba(${Math.min(255,
|
|
2875
|
-
const
|
|
3127
|
+
n
|
|
3128
|
+
), d = s.color >> 16 & 255, c = s.color >> 8 & 255, p = s.color & 255;
|
|
3129
|
+
r.addColorStop(0, `rgba(${Math.min(255, d + 60)}, ${Math.min(255, c + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${d}, ${c}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, d - 40)}, ${Math.max(0, c - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, n, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - n * 0.25, s.y - n * 0.25, n * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
|
|
3130
|
+
const f = n * 1.6;
|
|
2876
3131
|
let m = s.label, u = e.measureText(m).width;
|
|
2877
|
-
if (u >
|
|
2878
|
-
for (; u >
|
|
3132
|
+
if (u > f) {
|
|
3133
|
+
for (; u > f && m.length > 3; )
|
|
2879
3134
|
m = m.slice(0, -1), u = e.measureText(m + "...").width;
|
|
2880
3135
|
m += "...";
|
|
2881
3136
|
}
|
|
@@ -2978,11 +3233,11 @@ class It {
|
|
|
2978
3233
|
focusOnNode(e) {
|
|
2979
3234
|
const s = this.nodes.get(e);
|
|
2980
3235
|
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,
|
|
2982
|
-
const p = performance.now() -
|
|
2983
|
-
this.transform.x = o + (t - o) * m, this.transform.y =
|
|
3236
|
+
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, n = this.transform.y, r = 500, d = performance.now(), c = () => {
|
|
3237
|
+
const p = performance.now() - d, f = Math.min(p / r, 1), m = 1 - Math.pow(1 - f, 3);
|
|
3238
|
+
this.transform.x = o + (t - o) * m, this.transform.y = n + (i - n) * m, f < 1 ? requestAnimationFrame(c) : this.selectedNode = s;
|
|
2984
3239
|
};
|
|
2985
|
-
|
|
3240
|
+
c();
|
|
2986
3241
|
}
|
|
2987
3242
|
/**
|
|
2988
3243
|
* Updates node positions from 3D data
|
|
@@ -3050,6 +3305,7 @@ class Ht {
|
|
|
3050
3305
|
l(this, "edgeTooltipManager");
|
|
3051
3306
|
l(this, "searchManager", null);
|
|
3052
3307
|
l(this, "viewToggleManager", null);
|
|
3308
|
+
l(this, "legendManager", null);
|
|
3053
3309
|
// 2D Renderer
|
|
3054
3310
|
l(this, "forceGraph2D", null);
|
|
3055
3311
|
// Event system
|
|
@@ -3059,7 +3315,7 @@ class Ht {
|
|
|
3059
3315
|
l(this, "devControls", null);
|
|
3060
3316
|
l(this, "viewMode", "3d");
|
|
3061
3317
|
l(this, "graphData", null);
|
|
3062
|
-
this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new
|
|
3318
|
+
this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new bt(), this.nodeFactory = new vt(
|
|
3063
3319
|
this.materialFactory,
|
|
3064
3320
|
this.options.nodeRadius ?? P.nodeRadius,
|
|
3065
3321
|
this.options.lodSegments ?? P.lodSegments,
|
|
@@ -3068,14 +3324,14 @@ class Ht {
|
|
|
3068
3324
|
this.materialFactory,
|
|
3069
3325
|
this.options.edgeColor ?? P.edgeColor,
|
|
3070
3326
|
this.options.edgeOpacity ?? P.edgeOpacity
|
|
3071
|
-
), this.sceneManager = new
|
|
3327
|
+
), this.sceneManager = new ft(this.container, this.options), this.lodManager = new Mt(
|
|
3072
3328
|
this.sceneManager.camera,
|
|
3073
3329
|
this.options.lodDistances ?? P.lodDistances,
|
|
3074
3330
|
this.options.enableLOD ?? P.enableLOD
|
|
3075
|
-
), this.frustumCuller = new
|
|
3331
|
+
), this.frustumCuller = new Et(
|
|
3076
3332
|
this.sceneManager.camera,
|
|
3077
3333
|
this.options.enableEdgeCulling ?? P.enableEdgeCulling
|
|
3078
|
-
), this.nodeManager = new
|
|
3334
|
+
), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Re(
|
|
3079
3335
|
this.nodeManager.getAllNodes(),
|
|
3080
3336
|
this.edgeManager.getAllEdges(),
|
|
3081
3337
|
{
|
|
@@ -3085,12 +3341,12 @@ class Ht {
|
|
|
3085
3341
|
useBarnesHut: this.options.useBarnesHut,
|
|
3086
3342
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3087
3343
|
}
|
|
3088
|
-
), this.rendererManager = new
|
|
3344
|
+
), this.rendererManager = new xt(
|
|
3089
3345
|
this.sceneManager,
|
|
3090
3346
|
() => this.onSimulate(),
|
|
3091
3347
|
() => this.onRender(),
|
|
3092
3348
|
this.options.targetFPS ?? P.targetFPS
|
|
3093
|
-
), this.raycasterManager = new
|
|
3349
|
+
), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgePanelManager = new St(this.container), this.edgeTooltipManager = new zt(), this.edgePanelManager.setNodeClickCallback((t) => {
|
|
3094
3350
|
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3095
3351
|
this.showNodePanel(t);
|
|
3096
3352
|
}, 400);
|
|
@@ -3105,12 +3361,12 @@ class Ht {
|
|
|
3105
3361
|
this.showNodePanel(t);
|
|
3106
3362
|
}, 400);
|
|
3107
3363
|
}
|
|
3108
|
-
})), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new
|
|
3364
|
+
})), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Tt(this.container, {
|
|
3109
3365
|
initialMode: this.viewMode,
|
|
3110
3366
|
onViewChange: (t) => {
|
|
3111
3367
|
this.switchView(t);
|
|
3112
3368
|
}
|
|
3113
|
-
})), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
3369
|
+
})), this.options.showLegend !== !1 && (this.legendManager = new Pt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
3114
3370
|
}
|
|
3115
3371
|
/**
|
|
3116
3372
|
* Sets up internal callbacks
|
|
@@ -3133,7 +3389,14 @@ class Ht {
|
|
|
3133
3389
|
if (e) {
|
|
3134
3390
|
this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
|
|
3135
3391
|
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, i = s.top + s.height / 2;
|
|
3136
|
-
this.edgeTooltipManager.show(
|
|
3392
|
+
this.edgeTooltipManager.show(
|
|
3393
|
+
e.edge,
|
|
3394
|
+
e.sourceNode,
|
|
3395
|
+
e.targetNode,
|
|
3396
|
+
t,
|
|
3397
|
+
i,
|
|
3398
|
+
e.relationships
|
|
3399
|
+
), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
|
|
3137
3400
|
} else
|
|
3138
3401
|
this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
|
|
3139
3402
|
}
|
|
@@ -3149,7 +3412,7 @@ class Ht {
|
|
|
3149
3412
|
* Handles edge click
|
|
3150
3413
|
*/
|
|
3151
3414
|
onEdgeClick(e) {
|
|
3152
|
-
this.panelManager.hide(), this.edgeTooltipManager.hide(), this.edgeManager.highlightEdge(e.edge.source, e.edge.target), this.edgePanelManager.show(e.edge, e.sourceNode, e.targetNode), this.focusOnEdge(e.edge.source, e.edge.target), this.emit("edgeClick", e.edge, e.sourceNode, e.targetNode);
|
|
3415
|
+
this.panelManager.hide(), this.edgeTooltipManager.hide(), this.edgeManager.highlightEdge(e.edge.source, e.edge.target), this.edgePanelManager.show(e.edge, e.sourceNode, e.targetNode, e.relationships), this.focusOnEdge(e.edge.source, e.edge.target), this.emit("edgeClick", e.edge, e.sourceNode, e.targetNode);
|
|
3153
3416
|
}
|
|
3154
3417
|
/**
|
|
3155
3418
|
* Called every simulation step
|
|
@@ -3169,6 +3432,12 @@ class Ht {
|
|
|
3169
3432
|
onRender() {
|
|
3170
3433
|
this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
|
|
3171
3434
|
}
|
|
3435
|
+
/**
|
|
3436
|
+
* Updates bottom-right legend counts
|
|
3437
|
+
*/
|
|
3438
|
+
updateLegendCounts() {
|
|
3439
|
+
this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
|
|
3440
|
+
}
|
|
3172
3441
|
// ==========================================================================
|
|
3173
3442
|
// Public API
|
|
3174
3443
|
// ==========================================================================
|
|
@@ -3176,8 +3445,8 @@ class Ht {
|
|
|
3176
3445
|
* Sets the graph data
|
|
3177
3446
|
*/
|
|
3178
3447
|
setData(e) {
|
|
3179
|
-
var
|
|
3180
|
-
const s = (
|
|
3448
|
+
var n;
|
|
3449
|
+
const s = (n = e.data) != null && n.nodes ? e.data : e, i = (s.edges || s.links || []).map((r) => ({
|
|
3181
3450
|
...r,
|
|
3182
3451
|
relationship: r.relationship || r.label || "related_to"
|
|
3183
3452
|
})), o = {
|
|
@@ -3185,14 +3454,14 @@ class Ht {
|
|
|
3185
3454
|
edges: i
|
|
3186
3455
|
};
|
|
3187
3456
|
if (this.graphData = o, this.edgeManager.clear(), this.nodeManager.clear(), o.nodes && Array.isArray(o.nodes)) {
|
|
3188
|
-
|
|
3457
|
+
ee.setExpectedNodeCount(o.nodes.length);
|
|
3189
3458
|
for (const r of o.nodes)
|
|
3190
3459
|
this.addNode(r);
|
|
3191
3460
|
}
|
|
3192
3461
|
if (o.edges && Array.isArray(o.edges))
|
|
3193
3462
|
for (const r of o.edges)
|
|
3194
3463
|
this.addEdge(r);
|
|
3195
|
-
this.graphEngine = new
|
|
3464
|
+
this.graphEngine = new Re(
|
|
3196
3465
|
this.nodeManager.getAllNodes(),
|
|
3197
3466
|
this.edgeManager.getAllEdges(),
|
|
3198
3467
|
{
|
|
@@ -3202,28 +3471,28 @@ class Ht {
|
|
|
3202
3471
|
useBarnesHut: this.options.useBarnesHut,
|
|
3203
3472
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3204
3473
|
}
|
|
3205
|
-
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o);
|
|
3474
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o), this.updateLegendCounts();
|
|
3206
3475
|
}
|
|
3207
3476
|
/**
|
|
3208
3477
|
* Adds a node to the graph
|
|
3209
3478
|
* @returns true if added, false if node already exists or invalid
|
|
3210
3479
|
*/
|
|
3211
3480
|
addNode(e) {
|
|
3212
|
-
if (!
|
|
3481
|
+
if (!Fe(e))
|
|
3213
3482
|
return !1;
|
|
3214
3483
|
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;
|
|
3484
|
+
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
|
|
3216
3485
|
}
|
|
3217
3486
|
/**
|
|
3218
3487
|
* Removes a node from the graph
|
|
3219
3488
|
* @returns true if removed, false if not found
|
|
3220
3489
|
*/
|
|
3221
3490
|
removeNode(e) {
|
|
3222
|
-
if (!
|
|
3491
|
+
if (!ht(e))
|
|
3223
3492
|
return !1;
|
|
3224
3493
|
this.edgeManager.removeEdgesForNode(e);
|
|
3225
3494
|
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;
|
|
3495
|
+
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
|
|
3227
3496
|
}
|
|
3228
3497
|
/**
|
|
3229
3498
|
* Updates a node's properties
|
|
@@ -3233,13 +3502,13 @@ class Ht {
|
|
|
3233
3502
|
}
|
|
3234
3503
|
/**
|
|
3235
3504
|
* Adds an edge to the graph
|
|
3236
|
-
* @returns true if added, false if
|
|
3505
|
+
* @returns true if added, false if invalid or nodes don't exist
|
|
3237
3506
|
*/
|
|
3238
3507
|
addEdge(e) {
|
|
3239
3508
|
if (!De(e))
|
|
3240
3509
|
return !1;
|
|
3241
3510
|
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;
|
|
3511
|
+
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
|
|
3243
3512
|
}
|
|
3244
3513
|
/**
|
|
3245
3514
|
* Removes an edge from the graph
|
|
@@ -3247,7 +3516,7 @@ class Ht {
|
|
|
3247
3516
|
*/
|
|
3248
3517
|
removeEdge(e, s) {
|
|
3249
3518
|
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;
|
|
3519
|
+
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
|
|
3251
3520
|
}
|
|
3252
3521
|
/**
|
|
3253
3522
|
* Expands a node by fetching more data
|
|
@@ -3262,11 +3531,11 @@ class Ht {
|
|
|
3262
3531
|
try {
|
|
3263
3532
|
const o = await i(e, s);
|
|
3264
3533
|
if (o.nodes && Array.isArray(o.nodes))
|
|
3265
|
-
for (const
|
|
3266
|
-
this.addNode(
|
|
3534
|
+
for (const n of o.nodes)
|
|
3535
|
+
this.addNode(n);
|
|
3267
3536
|
if (o.edges && Array.isArray(o.edges))
|
|
3268
|
-
for (const
|
|
3269
|
-
this.addEdge(
|
|
3537
|
+
for (const n of o.edges)
|
|
3538
|
+
this.addEdge(n);
|
|
3270
3539
|
return this.panelManager.hide(), this.emit("expand", e, o), !0;
|
|
3271
3540
|
} catch (o) {
|
|
3272
3541
|
return console.error("[ForceGraph3D] Error expanding node:", o), !1;
|
|
@@ -3316,13 +3585,13 @@ class Ht {
|
|
|
3316
3585
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
3317
3586
|
return;
|
|
3318
3587
|
}
|
|
3319
|
-
const i = t.position, o = this.sceneManager.camera,
|
|
3588
|
+
const i = t.position, o = this.sceneManager.camera, n = this.sceneManager.controls, r = o.position.clone().sub(n.target).normalize(), d = {
|
|
3320
3589
|
x: i.x + r.x * s,
|
|
3321
3590
|
y: i.y + r.y * s,
|
|
3322
3591
|
z: i.z + r.z * s
|
|
3323
|
-
},
|
|
3324
|
-
const
|
|
3325
|
-
o.position.x =
|
|
3592
|
+
}, c = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: n.target.x, y: n.target.y, z: n.target.z }, f = 800, m = performance.now(), u = () => {
|
|
3593
|
+
const y = performance.now() - m, b = Math.min(y / f, 1), w = 1 - Math.pow(1 - b, 3);
|
|
3594
|
+
o.position.x = c.x + (d.x - c.x) * w, o.position.y = c.y + (d.y - c.y) * w, o.position.z = c.z + (d.z - c.z) * w, n.target.x = p.x + (i.x - p.x) * w, n.target.y = p.y + (i.y - p.y) * w, n.target.z = p.z + (i.z - p.z) * w, n.update(), b < 1 && requestAnimationFrame(u);
|
|
3326
3595
|
};
|
|
3327
3596
|
u();
|
|
3328
3597
|
}
|
|
@@ -3336,19 +3605,19 @@ class Ht {
|
|
|
3336
3605
|
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3337
3606
|
return;
|
|
3338
3607
|
}
|
|
3339
|
-
const
|
|
3608
|
+
const n = this.sceneManager.camera, r = this.sceneManager.controls, d = {
|
|
3340
3609
|
x: (i.position.x + o.position.x) / 2,
|
|
3341
3610
|
y: (i.position.y + o.position.y) / 2,
|
|
3342
3611
|
z: (i.position.z + o.position.z) / 2
|
|
3343
|
-
},
|
|
3344
|
-
x:
|
|
3345
|
-
y:
|
|
3346
|
-
z:
|
|
3347
|
-
},
|
|
3348
|
-
const
|
|
3349
|
-
|
|
3612
|
+
}, c = o.position.x - i.position.x, p = o.position.y - i.position.y, f = o.position.z - i.position.z, m = Math.sqrt(c * c + p * p + f * f), u = Math.max(m * t, 40), y = n.position.clone().sub(r.target).normalize(), b = {
|
|
3613
|
+
x: d.x + y.x * u,
|
|
3614
|
+
y: d.y + y.y * u,
|
|
3615
|
+
z: d.z + y.z * u
|
|
3616
|
+
}, w = { x: n.position.x, y: n.position.y, z: n.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, L = performance.now(), G = () => {
|
|
3617
|
+
const T = performance.now() - L, D = Math.min(T / O, 1), E = 1 - Math.pow(1 - D, 3);
|
|
3618
|
+
n.position.x = w.x + (b.x - w.x) * E, n.position.y = w.y + (b.y - w.y) * E, n.position.z = w.z + (b.z - w.z) * E, r.target.x = N.x + (d.x - N.x) * E, r.target.y = N.y + (d.y - N.y) * E, r.target.z = N.z + (d.z - N.z) * E, r.update(), D < 1 && requestAnimationFrame(G);
|
|
3350
3619
|
};
|
|
3351
|
-
|
|
3620
|
+
G();
|
|
3352
3621
|
}
|
|
3353
3622
|
/**
|
|
3354
3623
|
* Shows the info panel for a specific node
|
|
@@ -3371,9 +3640,9 @@ class Ht {
|
|
|
3371
3640
|
return [];
|
|
3372
3641
|
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
3373
3642
|
return t.forEach((o) => {
|
|
3374
|
-
var
|
|
3375
|
-
const
|
|
3376
|
-
(
|
|
3643
|
+
var c, p, f;
|
|
3644
|
+
const n = (c = o.label) == null ? void 0 : c.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), d = (f = o.type) == null ? void 0 : f.toLowerCase().includes(s);
|
|
3645
|
+
(n || r || d) && i.push(o);
|
|
3377
3646
|
}), i;
|
|
3378
3647
|
}
|
|
3379
3648
|
/**
|
|
@@ -3385,10 +3654,10 @@ class Ht {
|
|
|
3385
3654
|
if (!e || e.trim() === "")
|
|
3386
3655
|
return [];
|
|
3387
3656
|
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
3388
|
-
for (const
|
|
3389
|
-
if ((o =
|
|
3390
|
-
const
|
|
3391
|
-
|
|
3657
|
+
for (const n of t)
|
|
3658
|
+
if ((o = n.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
|
|
3659
|
+
const d = this.nodeManager.getNode(n.source), c = this.nodeManager.getNode(n.target);
|
|
3660
|
+
d && c && i.push({ edge: n, sourceNode: d, targetNode: c });
|
|
3392
3661
|
}
|
|
3393
3662
|
return i;
|
|
3394
3663
|
}
|
|
@@ -3421,7 +3690,7 @@ class Ht {
|
|
|
3421
3690
|
* Switches between 2D and 3D view modes
|
|
3422
3691
|
*/
|
|
3423
3692
|
switchView(e) {
|
|
3424
|
-
this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new
|
|
3693
|
+
this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new Rt(this.container, {
|
|
3425
3694
|
backgroundColor: "#0a0a0a",
|
|
3426
3695
|
nodeRadius: 24,
|
|
3427
3696
|
onNodeClick: (s) => {
|
|
@@ -3433,19 +3702,31 @@ class Ht {
|
|
|
3433
3702
|
onEdgeHover: (s, t) => {
|
|
3434
3703
|
if (s && t) {
|
|
3435
3704
|
const i = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
|
|
3436
|
-
i && o
|
|
3437
|
-
s
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3705
|
+
if (i && o) {
|
|
3706
|
+
const n = this.edgeManager.getAllEdges().filter((r) => r.source === s.source && r.target === s.target);
|
|
3707
|
+
this.edgeTooltipManager.show(
|
|
3708
|
+
s,
|
|
3709
|
+
i,
|
|
3710
|
+
o,
|
|
3711
|
+
t.clientX,
|
|
3712
|
+
t.clientY,
|
|
3713
|
+
n.length > 0 ? n : [s]
|
|
3714
|
+
);
|
|
3715
|
+
}
|
|
3443
3716
|
} else
|
|
3444
3717
|
this.edgeTooltipManager.hide();
|
|
3445
3718
|
},
|
|
3446
3719
|
onEdgeClick: (s) => {
|
|
3447
3720
|
const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3448
|
-
t && i
|
|
3721
|
+
if (t && i) {
|
|
3722
|
+
const o = this.edgeManager.getAllEdges().filter((n) => n.source === s.source && n.target === s.target);
|
|
3723
|
+
this.edgePanelManager.show(
|
|
3724
|
+
s,
|
|
3725
|
+
t,
|
|
3726
|
+
i,
|
|
3727
|
+
o.length > 0 ? o : [s]
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3449
3730
|
}
|
|
3450
3731
|
}), this.graphData && (this.forceGraph2D.setData(this.graphData), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())))) : (this.forceGraph2D && this.forceGraph2D.hide(), this.sceneManager.renderer.domElement.style.display = "block", this.rendererManager.start()), this.emit("viewChange", e));
|
|
3451
3732
|
}
|
|
@@ -3546,18 +3827,18 @@ class Ht {
|
|
|
3546
3827
|
const i = parseFloat(t.value) / 100;
|
|
3547
3828
|
this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
|
|
3548
3829
|
}), setInterval(() => {
|
|
3549
|
-
const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"),
|
|
3550
|
-
i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()),
|
|
3830
|
+
const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"), n = this.devControls.querySelector("#dev-fps");
|
|
3831
|
+
i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()), n && (n.textContent = this.rendererManager.getFPS().toString());
|
|
3551
3832
|
}, 500);
|
|
3552
3833
|
}
|
|
3553
3834
|
/**
|
|
3554
3835
|
* Destroys the graph and releases all resources
|
|
3555
3836
|
*/
|
|
3556
3837
|
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;
|
|
3838
|
+
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
3558
3839
|
}
|
|
3559
3840
|
}
|
|
3560
|
-
const
|
|
3841
|
+
const Ie = [
|
|
3561
3842
|
"Alpha",
|
|
3562
3843
|
"Beta",
|
|
3563
3844
|
"Gamma",
|
|
@@ -3588,7 +3869,7 @@ const Oe = [
|
|
|
3588
3869
|
"Link",
|
|
3589
3870
|
"Point",
|
|
3590
3871
|
"Vertex"
|
|
3591
|
-
],
|
|
3872
|
+
], J = [
|
|
3592
3873
|
"connects to",
|
|
3593
3874
|
"links with",
|
|
3594
3875
|
"relates to",
|
|
@@ -3599,7 +3880,7 @@ const Oe = [
|
|
|
3599
3880
|
"partners with",
|
|
3600
3881
|
"collaborates with",
|
|
3601
3882
|
"supports"
|
|
3602
|
-
],
|
|
3883
|
+
], Oe = [
|
|
3603
3884
|
16777215,
|
|
3604
3885
|
// White
|
|
3605
3886
|
16750950,
|
|
@@ -3611,14 +3892,14 @@ const Oe = [
|
|
|
3611
3892
|
16746564
|
|
3612
3893
|
// Darker tangerine
|
|
3613
3894
|
];
|
|
3614
|
-
function
|
|
3895
|
+
function At(h = 30) {
|
|
3615
3896
|
const e = [], s = [];
|
|
3616
|
-
for (let i = 0; i <
|
|
3617
|
-
const o = i <
|
|
3897
|
+
for (let i = 0; i < h; i++) {
|
|
3898
|
+
const o = i < Ie.length ? Ie[i] : `Node ${i + 1}`;
|
|
3618
3899
|
e.push({
|
|
3619
3900
|
id: `node-${i}`,
|
|
3620
3901
|
label: o,
|
|
3621
|
-
color:
|
|
3902
|
+
color: Oe[i % Oe.length],
|
|
3622
3903
|
position: {
|
|
3623
3904
|
x: (Math.random() - 0.5) * 60,
|
|
3624
3905
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -3626,52 +3907,52 @@ function Dt(d = 30) {
|
|
|
3626
3907
|
}
|
|
3627
3908
|
});
|
|
3628
3909
|
}
|
|
3629
|
-
for (let i = 1; i <
|
|
3910
|
+
for (let i = 1; i < h; i++) {
|
|
3630
3911
|
const o = Math.floor(Math.random() * i);
|
|
3631
3912
|
s.push({
|
|
3632
3913
|
source: `node-${i}`,
|
|
3633
3914
|
target: `node-${o}`,
|
|
3634
|
-
relationship:
|
|
3915
|
+
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3635
3916
|
});
|
|
3636
3917
|
}
|
|
3637
|
-
const t = Math.floor(
|
|
3918
|
+
const t = Math.floor(h * 0.5);
|
|
3638
3919
|
for (let i = 0; i < t; i++) {
|
|
3639
|
-
const o = Math.floor(Math.random() *
|
|
3640
|
-
let
|
|
3641
|
-
o ===
|
|
3642
|
-
const r = `node-${o}`,
|
|
3920
|
+
const o = Math.floor(Math.random() * h);
|
|
3921
|
+
let n = Math.floor(Math.random() * h);
|
|
3922
|
+
o === n && (n = (n + 1) % h);
|
|
3923
|
+
const r = `node-${o}`, d = `node-${n}`;
|
|
3643
3924
|
s.some(
|
|
3644
|
-
(p) => p.source === r && p.target ===
|
|
3925
|
+
(p) => p.source === r && p.target === d || p.source === d && p.target === r
|
|
3645
3926
|
) || s.push({
|
|
3646
3927
|
source: r,
|
|
3647
|
-
target:
|
|
3648
|
-
relationship:
|
|
3928
|
+
target: d,
|
|
3929
|
+
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3649
3930
|
});
|
|
3650
3931
|
}
|
|
3651
3932
|
return { nodes: e, edges: s };
|
|
3652
3933
|
}
|
|
3653
|
-
function
|
|
3654
|
-
const e = [], s = [], t = Math.ceil(
|
|
3934
|
+
function jt(h = 1e3) {
|
|
3935
|
+
const e = [], s = [], t = Math.ceil(h / 50), i = [];
|
|
3655
3936
|
for (let o = 0; o < t; o++)
|
|
3656
3937
|
i.push({
|
|
3657
3938
|
x: (Math.random() - 0.5) * 200,
|
|
3658
3939
|
y: (Math.random() - 0.5) * 200,
|
|
3659
3940
|
z: (Math.random() - 0.5) * 200
|
|
3660
3941
|
});
|
|
3661
|
-
for (let o = 0; o <
|
|
3662
|
-
const
|
|
3942
|
+
for (let o = 0; o < h; o++) {
|
|
3943
|
+
const n = i[o % t];
|
|
3663
3944
|
e.push({
|
|
3664
3945
|
id: `node-${o}`,
|
|
3665
3946
|
label: `N${o}`,
|
|
3666
3947
|
position: {
|
|
3667
|
-
x:
|
|
3668
|
-
y:
|
|
3669
|
-
z:
|
|
3948
|
+
x: n.x + (Math.random() - 0.5) * 40,
|
|
3949
|
+
y: n.y + (Math.random() - 0.5) * 40,
|
|
3950
|
+
z: n.z + (Math.random() - 0.5) * 40
|
|
3670
3951
|
}
|
|
3671
3952
|
});
|
|
3672
3953
|
}
|
|
3673
|
-
for (let o = 1; o <
|
|
3674
|
-
const
|
|
3954
|
+
for (let o = 1; o < h; o++) {
|
|
3955
|
+
const n = Math.floor(o / 50) * 50, r = n === 0 ? Math.floor(Math.random() * o) : n + Math.floor(Math.random() * Math.min(o - n, 50));
|
|
3675
3956
|
s.push({
|
|
3676
3957
|
source: `node-${o}`,
|
|
3677
3958
|
target: `node-${Math.min(r, o - 1)}`,
|
|
@@ -3679,9 +3960,9 @@ function At(d = 1e3) {
|
|
|
3679
3960
|
});
|
|
3680
3961
|
}
|
|
3681
3962
|
for (let o = 1; o < t; o++) {
|
|
3682
|
-
const
|
|
3963
|
+
const n = o * 50, r = (o - 1) * 50 + Math.floor(Math.random() * 50);
|
|
3683
3964
|
s.push({
|
|
3684
|
-
source: `node-${
|
|
3965
|
+
source: `node-${n}`,
|
|
3685
3966
|
target: `node-${r}`,
|
|
3686
3967
|
relationship: "bridges to"
|
|
3687
3968
|
});
|
|
@@ -3691,11 +3972,11 @@ function At(d = 1e3) {
|
|
|
3691
3972
|
export {
|
|
3692
3973
|
P as DEFAULT_OPTIONS,
|
|
3693
3974
|
Ht as ForceGraph3D,
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3975
|
+
_ as LODLevel,
|
|
3976
|
+
Dt as createEdgeKey,
|
|
3977
|
+
jt as generateLargeSampleData,
|
|
3978
|
+
At as generateSampleData,
|
|
3698
3979
|
De as validateEdgeData,
|
|
3699
|
-
|
|
3980
|
+
Fe as validateNodeData
|
|
3700
3981
|
};
|
|
3701
3982
|
//# sourceMappingURL=force-3d-graph.js.map
|