force-3d-graph 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/force-3d-graph.js +1477 -689
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +202 -17
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +32 -2
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var st = Object.defineProperty;
|
|
2
|
-
var it = (
|
|
3
|
-
var
|
|
4
|
-
import * as
|
|
5
|
-
import { EventDispatcher as
|
|
6
|
-
const
|
|
2
|
+
var it = (h, e, s) => e in h ? st(h, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : h[e] = s;
|
|
3
|
+
var l = (h, e, s) => it(h, typeof e != "symbol" ? e + "" : e, s);
|
|
4
|
+
import * as m from "three";
|
|
5
|
+
import { EventDispatcher as ot, Vector3 as S, MOUSE as $, TOUCH as G, Spherical as Ne, Quaternion as Se, Vector2 as k, Ray as nt, Plane as at, MathUtils as rt } from "three";
|
|
6
|
+
const R = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
9
9
|
cameraFov: 75,
|
|
@@ -25,101 +25,103 @@ const L = {
|
|
|
25
25
|
showPanel: !0,
|
|
26
26
|
showSearch: !0,
|
|
27
27
|
searchPlaceholder: "Search nodes or relationships...",
|
|
28
|
+
initialViewMode: "3d",
|
|
29
|
+
showViewToggle: !0,
|
|
28
30
|
targetFPS: 60,
|
|
29
31
|
maxVisibleNodes: 1e4
|
|
30
32
|
};
|
|
31
|
-
var U = /* @__PURE__ */ ((
|
|
33
|
+
var U = /* @__PURE__ */ ((h) => (h[h.HIGH = 0] = "HIGH", h[h.MEDIUM = 1] = "MEDIUM", h[h.LOW = 2] = "LOW", h))(U || {});
|
|
32
34
|
function lt() {
|
|
33
|
-
const
|
|
34
|
-
return
|
|
35
|
+
const h = document.createElement("div");
|
|
36
|
+
return h.id = "force-graph-3d-container", h.style.cssText = `
|
|
35
37
|
width: 100%;
|
|
36
38
|
height: 100%;
|
|
37
39
|
position: absolute;
|
|
38
40
|
top: 0;
|
|
39
41
|
left: 0;
|
|
40
42
|
overflow: hidden;
|
|
41
|
-
`, document.body.appendChild(
|
|
43
|
+
`, document.body.appendChild(h), h;
|
|
42
44
|
}
|
|
43
|
-
function ct(
|
|
44
|
-
return
|
|
45
|
+
function ct(h) {
|
|
46
|
+
return h && h instanceof HTMLElement ? h : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), lt());
|
|
45
47
|
}
|
|
46
|
-
function ze(
|
|
47
|
-
const e =
|
|
48
|
+
function ze(h) {
|
|
49
|
+
const e = h.getBoundingClientRect();
|
|
48
50
|
return {
|
|
49
51
|
width: e.width || window.innerWidth,
|
|
50
52
|
height: e.height || window.innerHeight
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
|
-
function
|
|
54
|
-
if (!
|
|
55
|
+
function Oe(h) {
|
|
56
|
+
if (!h || typeof h != "object")
|
|
55
57
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
56
|
-
const e =
|
|
58
|
+
const e = h;
|
|
57
59
|
return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !dt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
|
|
58
60
|
}
|
|
59
|
-
function Fe(
|
|
60
|
-
if (!
|
|
61
|
+
function Fe(h) {
|
|
62
|
+
if (!h || typeof h != "object")
|
|
61
63
|
return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
|
|
62
|
-
const e =
|
|
64
|
+
const e = h;
|
|
63
65
|
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;
|
|
64
66
|
}
|
|
65
|
-
function ht(
|
|
66
|
-
return typeof
|
|
67
|
+
function ht(h) {
|
|
68
|
+
return typeof h != "string" || h.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
|
|
67
69
|
}
|
|
68
|
-
function dt(
|
|
69
|
-
if (!
|
|
70
|
-
const e =
|
|
70
|
+
function dt(h) {
|
|
71
|
+
if (!h || typeof h != "object") return !1;
|
|
72
|
+
const e = h;
|
|
71
73
|
return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
|
|
72
74
|
}
|
|
73
|
-
function
|
|
74
|
-
return
|
|
75
|
+
function D(h, e) {
|
|
76
|
+
return h === e ? `${h}-${e}` : h < e ? `${h}-${e}` : `${e}-${h}`;
|
|
75
77
|
}
|
|
76
|
-
const
|
|
77
|
-
class
|
|
78
|
+
const ke = { type: "change" }, re = { type: "start" }, Pe = { type: "end" }, Q = new nt(), Te = new at(), pt = Math.cos(70 * rt.DEG2RAD);
|
|
79
|
+
class gt extends ot {
|
|
78
80
|
constructor(e, s) {
|
|
79
|
-
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new
|
|
80
|
-
return
|
|
81
|
+
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: $.ROTATE, MIDDLE: $.DOLLY, RIGHT: $.PAN }, this.touches = { ONE: G.ROTATE, TWO: G.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
82
|
+
return r.phi;
|
|
81
83
|
}, this.getAzimuthalAngle = function() {
|
|
82
|
-
return
|
|
84
|
+
return r.theta;
|
|
83
85
|
}, this.getDistance = function() {
|
|
84
86
|
return this.object.position.distanceTo(this.target);
|
|
85
|
-
}, this.listenToKeyEvents = function(
|
|
86
|
-
|
|
87
|
+
}, this.listenToKeyEvents = function(n) {
|
|
88
|
+
n.addEventListener("keydown", ne), this._domElementKeyEvents = n;
|
|
87
89
|
}, this.stopListenToKeyEvents = function() {
|
|
88
90
|
this._domElementKeyEvents.removeEventListener("keydown", ne), this._domElementKeyEvents = null;
|
|
89
91
|
}, this.saveState = function() {
|
|
90
92
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
91
93
|
}, this.reset = function() {
|
|
92
|
-
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(
|
|
94
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(ke), t.update(), i = o.NONE;
|
|
93
95
|
}, this.update = function() {
|
|
94
|
-
const
|
|
96
|
+
const n = new S(), p = new Se().setFromUnitVectors(e.up, new S(0, 1, 0)), b = p.clone().invert(), w = new S(), C = new Se(), F = new S(), z = 2 * Math.PI;
|
|
95
97
|
return function(tt = null) {
|
|
96
98
|
const Ce = t.object.position;
|
|
97
|
-
|
|
98
|
-
let
|
|
99
|
-
isFinite(
|
|
100
|
-
let
|
|
101
|
-
if (t.zoomToCursor &&
|
|
102
|
-
let
|
|
99
|
+
n.copy(Ce).sub(t.target), n.applyQuaternion(p), r.setFromVector3(n), t.autoRotate && i === o.NONE && B(He(tt)), t.enableDamping ? (r.theta += c.theta * t.dampingFactor, r.phi += c.phi * t.dampingFactor) : (r.theta += c.theta, r.phi += c.phi);
|
|
100
|
+
let L = t.minAzimuthAngle, I = t.maxAzimuthAngle;
|
|
101
|
+
isFinite(L) && isFinite(I) && (L < -Math.PI ? L += z : L > Math.PI && (L -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), L <= I ? r.theta = Math.max(L, Math.min(I, r.theta)) : r.theta = r.theta > (L + I) / 2 ? Math.max(L, 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(g, t.dampingFactor) : t.target.add(g), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && H || t.object.isOrthographicCamera ? r.radius = ie(r.radius) : r.radius = ie(r.radius * d), n.setFromSpherical(r), n.applyQuaternion(b), Ce.copy(t.target).add(n), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, g.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), g.set(0, 0, 0));
|
|
102
|
+
let ae = !1;
|
|
103
|
+
if (t.zoomToCursor && H) {
|
|
104
|
+
let X = null;
|
|
103
105
|
if (t.object.isPerspectiveCamera) {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
t.object.position.addScaledVector(
|
|
106
|
+
const V = n.length();
|
|
107
|
+
X = ie(V * d);
|
|
108
|
+
const Z = V - X;
|
|
109
|
+
t.object.position.addScaledVector(Y, Z), t.object.updateMatrixWorld();
|
|
108
110
|
} else if (t.object.isOrthographicCamera) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
111
|
+
const V = new S(P.x, P.y, 0);
|
|
112
|
+
V.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / d)), t.object.updateProjectionMatrix(), ae = !0;
|
|
113
|
+
const Z = new S(P.x, P.y, 0);
|
|
114
|
+
Z.unproject(t.object), t.object.position.sub(Z).add(V), t.object.updateMatrixWorld(), X = n.length();
|
|
113
115
|
} else
|
|
114
116
|
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
|
|
115
|
-
|
|
116
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
117
|
-
return
|
|
117
|
+
X !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(X).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)) < pt ? e.lookAt(t.target) : (Te.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Te, t.target))));
|
|
118
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / d)), t.object.updateProjectionMatrix(), ae = !0);
|
|
119
|
+
return d = 1, H = !1, ae || w.distanceToSquared(t.object.position) > a || 8 * (1 - C.dot(t.object.quaternion)) > a || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(ke), w.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
|
|
118
120
|
};
|
|
119
121
|
}(), this.dispose = function() {
|
|
120
|
-
t.domElement.removeEventListener("contextmenu", we), t.domElement.removeEventListener("pointerdown",
|
|
122
|
+
t.domElement.removeEventListener("contextmenu", we), t.domElement.removeEventListener("pointerdown", be), t.domElement.removeEventListener("pointercancel", K), t.domElement.removeEventListener("wheel", ve), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", K), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ne), t._domElementKeyEvents = null);
|
|
121
123
|
};
|
|
122
|
-
const t = this,
|
|
124
|
+
const t = this, o = {
|
|
123
125
|
NONE: -1,
|
|
124
126
|
ROTATE: 0,
|
|
125
127
|
DOLLY: 1,
|
|
@@ -129,379 +131,379 @@ class ut extends nt {
|
|
|
129
131
|
TOUCH_DOLLY_PAN: 5,
|
|
130
132
|
TOUCH_DOLLY_ROTATE: 6
|
|
131
133
|
};
|
|
132
|
-
let
|
|
133
|
-
const a = 1e-6,
|
|
134
|
-
let
|
|
135
|
-
const g = new
|
|
136
|
-
let
|
|
137
|
-
const E = [],
|
|
138
|
-
let
|
|
139
|
-
function He(
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
function
|
|
143
|
-
const
|
|
144
|
-
return Math.pow(0.95, t.zoomSpeed *
|
|
145
|
-
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
function W(
|
|
150
|
-
|
|
134
|
+
let i = o.NONE;
|
|
135
|
+
const a = 1e-6, r = new Ne(), c = new Ne();
|
|
136
|
+
let d = 1;
|
|
137
|
+
const g = new S(), u = new k(), y = new k(), f = new k(), x = new k(), v = new k(), M = new k(), N = new k(), O = new k(), T = new k(), Y = new S(), P = new k();
|
|
138
|
+
let H = !1;
|
|
139
|
+
const E = [], _ = {};
|
|
140
|
+
let ee = !1;
|
|
141
|
+
function He(n) {
|
|
142
|
+
return n !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * n : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
|
|
143
|
+
}
|
|
144
|
+
function q(n) {
|
|
145
|
+
const p = Math.abs(n * 0.01);
|
|
146
|
+
return Math.pow(0.95, t.zoomSpeed * p);
|
|
147
|
+
}
|
|
148
|
+
function B(n) {
|
|
149
|
+
c.theta -= n;
|
|
150
|
+
}
|
|
151
|
+
function W(n) {
|
|
152
|
+
c.phi -= n;
|
|
151
153
|
}
|
|
152
154
|
const le = function() {
|
|
153
|
-
const
|
|
154
|
-
return function(
|
|
155
|
-
|
|
155
|
+
const n = new S();
|
|
156
|
+
return function(b, w) {
|
|
157
|
+
n.setFromMatrixColumn(w, 0), n.multiplyScalar(-b), g.add(n);
|
|
156
158
|
};
|
|
157
159
|
}(), ce = function() {
|
|
158
|
-
const
|
|
159
|
-
return function(
|
|
160
|
-
t.screenSpacePanning === !0 ?
|
|
160
|
+
const n = new S();
|
|
161
|
+
return function(b, w) {
|
|
162
|
+
t.screenSpacePanning === !0 ? n.setFromMatrixColumn(w, 1) : (n.setFromMatrixColumn(w, 0), n.crossVectors(t.object.up, n)), n.multiplyScalar(b), g.add(n);
|
|
161
163
|
};
|
|
162
164
|
}(), A = function() {
|
|
163
|
-
const
|
|
164
|
-
return function(
|
|
165
|
-
const
|
|
165
|
+
const n = new S();
|
|
166
|
+
return function(b, w) {
|
|
167
|
+
const C = t.domElement;
|
|
166
168
|
if (t.object.isPerspectiveCamera) {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
let
|
|
170
|
-
|
|
171
|
-
} else t.object.isOrthographicCamera ? (le(
|
|
169
|
+
const F = t.object.position;
|
|
170
|
+
n.copy(F).sub(t.target);
|
|
171
|
+
let z = n.length();
|
|
172
|
+
z *= Math.tan(t.object.fov / 2 * Math.PI / 180), le(2 * b * z / C.clientHeight, t.object.matrix), ce(2 * w * z / C.clientHeight, t.object.matrix);
|
|
173
|
+
} else t.object.isOrthographicCamera ? (le(b * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), ce(w * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
172
174
|
};
|
|
173
175
|
}();
|
|
174
|
-
function
|
|
175
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
176
|
+
function te(n) {
|
|
177
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? d /= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
176
178
|
}
|
|
177
|
-
function he(
|
|
178
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
179
|
+
function he(n) {
|
|
180
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? d *= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
179
181
|
}
|
|
180
|
-
function
|
|
182
|
+
function se(n, p) {
|
|
181
183
|
if (!t.zoomToCursor)
|
|
182
184
|
return;
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
185
|
+
H = !0;
|
|
186
|
+
const b = t.domElement.getBoundingClientRect(), w = n - b.left, C = p - b.top, F = b.width, z = b.height;
|
|
187
|
+
P.x = w / F * 2 - 1, P.y = -(C / z) * 2 + 1, Y.set(P.x, P.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
186
188
|
}
|
|
187
|
-
function
|
|
188
|
-
return Math.max(t.minDistance, Math.min(t.maxDistance,
|
|
189
|
+
function ie(n) {
|
|
190
|
+
return Math.max(t.minDistance, Math.min(t.maxDistance, n));
|
|
189
191
|
}
|
|
190
|
-
function de(
|
|
191
|
-
|
|
192
|
+
function de(n) {
|
|
193
|
+
u.set(n.clientX, n.clientY);
|
|
192
194
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
+
function De(n) {
|
|
196
|
+
se(n.clientX, n.clientX), N.set(n.clientX, n.clientY);
|
|
195
197
|
}
|
|
196
|
-
function pe(
|
|
197
|
-
|
|
198
|
+
function pe(n) {
|
|
199
|
+
x.set(n.clientX, n.clientY);
|
|
198
200
|
}
|
|
199
|
-
function
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
201
|
+
function Ae(n) {
|
|
202
|
+
y.set(n.clientX, n.clientY), f.subVectors(y, u).multiplyScalar(t.rotateSpeed);
|
|
203
|
+
const p = t.domElement;
|
|
204
|
+
B(2 * Math.PI * f.x / p.clientHeight), W(2 * Math.PI * f.y / p.clientHeight), u.copy(y), t.update();
|
|
203
205
|
}
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
+
function je(n) {
|
|
207
|
+
O.set(n.clientX, n.clientY), T.subVectors(O, N), T.y > 0 ? te(q(T.y)) : T.y < 0 && he(q(T.y)), N.copy(O), t.update();
|
|
206
208
|
}
|
|
207
|
-
function $e(
|
|
208
|
-
|
|
209
|
+
function $e(n) {
|
|
210
|
+
v.set(n.clientX, n.clientY), M.subVectors(v, x).multiplyScalar(t.panSpeed), A(M.x, M.y), x.copy(v), t.update();
|
|
209
211
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
+
function Ge(n) {
|
|
213
|
+
se(n.clientX, n.clientY), n.deltaY < 0 ? he(q(n.deltaY)) : n.deltaY > 0 && te(q(n.deltaY)), t.update();
|
|
212
214
|
}
|
|
213
|
-
function
|
|
214
|
-
let
|
|
215
|
-
switch (
|
|
215
|
+
function Ye(n) {
|
|
216
|
+
let p = !1;
|
|
217
|
+
switch (n.code) {
|
|
216
218
|
case t.keys.UP:
|
|
217
|
-
|
|
219
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), p = !0;
|
|
218
220
|
break;
|
|
219
221
|
case t.keys.BOTTOM:
|
|
220
|
-
|
|
222
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), p = !0;
|
|
221
223
|
break;
|
|
222
224
|
case t.keys.LEFT:
|
|
223
|
-
|
|
225
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? B(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), p = !0;
|
|
224
226
|
break;
|
|
225
227
|
case t.keys.RIGHT:
|
|
226
|
-
|
|
228
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? B(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(-t.keyPanSpeed, 0), p = !0;
|
|
227
229
|
break;
|
|
228
230
|
}
|
|
229
|
-
|
|
231
|
+
p && (n.preventDefault(), t.update());
|
|
230
232
|
}
|
|
231
|
-
function
|
|
233
|
+
function ge(n) {
|
|
232
234
|
if (E.length === 1)
|
|
233
|
-
|
|
235
|
+
u.set(n.pageX, n.pageY);
|
|
234
236
|
else {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
+
const p = j(n), b = 0.5 * (n.pageX + p.x), w = 0.5 * (n.pageY + p.y);
|
|
238
|
+
u.set(b, w);
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
|
-
function
|
|
241
|
+
function ue(n) {
|
|
240
242
|
if (E.length === 1)
|
|
241
|
-
|
|
243
|
+
x.set(n.pageX, n.pageY);
|
|
242
244
|
else {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
+
const p = j(n), b = 0.5 * (n.pageX + p.x), w = 0.5 * (n.pageY + p.y);
|
|
246
|
+
x.set(b, w);
|
|
245
247
|
}
|
|
246
248
|
}
|
|
247
|
-
function
|
|
248
|
-
const
|
|
249
|
-
|
|
249
|
+
function fe(n) {
|
|
250
|
+
const p = j(n), b = n.pageX - p.x, w = n.pageY - p.y, C = Math.sqrt(b * b + w * w);
|
|
251
|
+
N.set(0, C);
|
|
250
252
|
}
|
|
251
|
-
function
|
|
252
|
-
t.enableZoom &&
|
|
253
|
+
function Be(n) {
|
|
254
|
+
t.enableZoom && fe(n), t.enablePan && ue(n);
|
|
253
255
|
}
|
|
254
|
-
function
|
|
255
|
-
t.enableZoom &&
|
|
256
|
+
function Ke(n) {
|
|
257
|
+
t.enableZoom && fe(n), t.enableRotate && ge(n);
|
|
256
258
|
}
|
|
257
|
-
function
|
|
259
|
+
function me(n) {
|
|
258
260
|
if (E.length == 1)
|
|
259
|
-
|
|
261
|
+
y.set(n.pageX, n.pageY);
|
|
260
262
|
else {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
+
const b = j(n), w = 0.5 * (n.pageX + b.x), C = 0.5 * (n.pageY + b.y);
|
|
264
|
+
y.set(w, C);
|
|
263
265
|
}
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
266
|
+
f.subVectors(y, u).multiplyScalar(t.rotateSpeed);
|
|
267
|
+
const p = t.domElement;
|
|
268
|
+
B(2 * Math.PI * f.x / p.clientHeight), W(2 * Math.PI * f.y / p.clientHeight), u.copy(y);
|
|
267
269
|
}
|
|
268
|
-
function ye(
|
|
270
|
+
function ye(n) {
|
|
269
271
|
if (E.length === 1)
|
|
270
|
-
|
|
272
|
+
v.set(n.pageX, n.pageY);
|
|
271
273
|
else {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
+
const p = j(n), b = 0.5 * (n.pageX + p.x), w = 0.5 * (n.pageY + p.y);
|
|
275
|
+
v.set(b, w);
|
|
274
276
|
}
|
|
275
|
-
|
|
277
|
+
M.subVectors(v, x).multiplyScalar(t.panSpeed), A(M.x, M.y), x.copy(v);
|
|
276
278
|
}
|
|
277
|
-
function
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
279
|
+
function xe(n) {
|
|
280
|
+
const p = j(n), b = n.pageX - p.x, w = n.pageY - p.y, C = Math.sqrt(b * b + w * w);
|
|
281
|
+
O.set(0, C), T.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), te(T.y), N.copy(O);
|
|
282
|
+
const F = (n.pageX + p.x) * 0.5, z = (n.pageY + p.y) * 0.5;
|
|
283
|
+
se(F, z);
|
|
282
284
|
}
|
|
283
|
-
function
|
|
284
|
-
t.enableZoom &&
|
|
285
|
+
function Xe(n) {
|
|
286
|
+
t.enableZoom && xe(n), t.enablePan && ye(n);
|
|
285
287
|
}
|
|
286
|
-
function
|
|
287
|
-
t.enableZoom &&
|
|
288
|
+
function Ve(n) {
|
|
289
|
+
t.enableZoom && xe(n), t.enableRotate && me(n);
|
|
288
290
|
}
|
|
289
|
-
function
|
|
290
|
-
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(
|
|
291
|
+
function be(n) {
|
|
292
|
+
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(n.pointerId), t.domElement.addEventListener("pointermove", oe), t.domElement.addEventListener("pointerup", K)), Je(n), n.pointerType === "touch" ? Ze(n) : Ue(n));
|
|
291
293
|
}
|
|
292
|
-
function
|
|
293
|
-
t.enabled !== !1 && (
|
|
294
|
+
function oe(n) {
|
|
295
|
+
t.enabled !== !1 && (n.pointerType === "touch" ? Qe(n) : _e(n));
|
|
294
296
|
}
|
|
295
|
-
function
|
|
296
|
-
et(
|
|
297
|
+
function K(n) {
|
|
298
|
+
et(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Pe), i = o.NONE;
|
|
297
299
|
}
|
|
298
|
-
function
|
|
299
|
-
let
|
|
300
|
-
switch (
|
|
300
|
+
function Ue(n) {
|
|
301
|
+
let p;
|
|
302
|
+
switch (n.button) {
|
|
301
303
|
case 0:
|
|
302
|
-
|
|
304
|
+
p = t.mouseButtons.LEFT;
|
|
303
305
|
break;
|
|
304
306
|
case 1:
|
|
305
|
-
|
|
307
|
+
p = t.mouseButtons.MIDDLE;
|
|
306
308
|
break;
|
|
307
309
|
case 2:
|
|
308
|
-
|
|
310
|
+
p = t.mouseButtons.RIGHT;
|
|
309
311
|
break;
|
|
310
312
|
default:
|
|
311
|
-
|
|
313
|
+
p = -1;
|
|
312
314
|
}
|
|
313
|
-
switch (
|
|
314
|
-
case
|
|
315
|
+
switch (p) {
|
|
316
|
+
case $.DOLLY:
|
|
315
317
|
if (t.enableZoom === !1) return;
|
|
316
|
-
|
|
318
|
+
De(n), i = o.DOLLY;
|
|
317
319
|
break;
|
|
318
|
-
case
|
|
319
|
-
if (
|
|
320
|
+
case $.ROTATE:
|
|
321
|
+
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
320
322
|
if (t.enablePan === !1) return;
|
|
321
|
-
pe(
|
|
323
|
+
pe(n), i = o.PAN;
|
|
322
324
|
} else {
|
|
323
325
|
if (t.enableRotate === !1) return;
|
|
324
|
-
de(
|
|
326
|
+
de(n), i = o.ROTATE;
|
|
325
327
|
}
|
|
326
328
|
break;
|
|
327
|
-
case
|
|
328
|
-
if (
|
|
329
|
+
case $.PAN:
|
|
330
|
+
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
329
331
|
if (t.enableRotate === !1) return;
|
|
330
|
-
de(
|
|
332
|
+
de(n), i = o.ROTATE;
|
|
331
333
|
} else {
|
|
332
334
|
if (t.enablePan === !1) return;
|
|
333
|
-
pe(
|
|
335
|
+
pe(n), i = o.PAN;
|
|
334
336
|
}
|
|
335
337
|
break;
|
|
336
338
|
default:
|
|
337
|
-
|
|
339
|
+
i = o.NONE;
|
|
338
340
|
}
|
|
339
|
-
|
|
341
|
+
i !== o.NONE && t.dispatchEvent(re);
|
|
340
342
|
}
|
|
341
|
-
function
|
|
342
|
-
switch (
|
|
343
|
-
case
|
|
343
|
+
function _e(n) {
|
|
344
|
+
switch (i) {
|
|
345
|
+
case o.ROTATE:
|
|
344
346
|
if (t.enableRotate === !1) return;
|
|
345
|
-
|
|
347
|
+
Ae(n);
|
|
346
348
|
break;
|
|
347
|
-
case
|
|
349
|
+
case o.DOLLY:
|
|
348
350
|
if (t.enableZoom === !1) return;
|
|
349
|
-
|
|
351
|
+
je(n);
|
|
350
352
|
break;
|
|
351
|
-
case
|
|
353
|
+
case o.PAN:
|
|
352
354
|
if (t.enablePan === !1) return;
|
|
353
|
-
$e(
|
|
355
|
+
$e(n);
|
|
354
356
|
break;
|
|
355
357
|
}
|
|
356
358
|
}
|
|
357
|
-
function ve(
|
|
358
|
-
t.enabled === !1 || t.enableZoom === !1 ||
|
|
359
|
+
function ve(n) {
|
|
360
|
+
t.enabled === !1 || t.enableZoom === !1 || i !== o.NONE || (n.preventDefault(), t.dispatchEvent(re), Ge(qe(n)), t.dispatchEvent(Pe));
|
|
359
361
|
}
|
|
360
|
-
function
|
|
361
|
-
const
|
|
362
|
-
clientX:
|
|
363
|
-
clientY:
|
|
364
|
-
deltaY:
|
|
362
|
+
function qe(n) {
|
|
363
|
+
const p = n.deltaMode, b = {
|
|
364
|
+
clientX: n.clientX,
|
|
365
|
+
clientY: n.clientY,
|
|
366
|
+
deltaY: n.deltaY
|
|
365
367
|
};
|
|
366
|
-
switch (
|
|
368
|
+
switch (p) {
|
|
367
369
|
case 1:
|
|
368
|
-
|
|
370
|
+
b.deltaY *= 16;
|
|
369
371
|
break;
|
|
370
372
|
case 2:
|
|
371
|
-
|
|
373
|
+
b.deltaY *= 100;
|
|
372
374
|
break;
|
|
373
375
|
}
|
|
374
|
-
return
|
|
376
|
+
return n.ctrlKey && !ee && (b.deltaY *= 10), b;
|
|
375
377
|
}
|
|
376
|
-
function
|
|
377
|
-
|
|
378
|
+
function We(n) {
|
|
379
|
+
n.key === "Control" && (ee = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
378
380
|
}
|
|
379
|
-
function Me(
|
|
380
|
-
|
|
381
|
+
function Me(n) {
|
|
382
|
+
n.key === "Control" && (ee = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
|
|
381
383
|
}
|
|
382
|
-
function ne(
|
|
383
|
-
t.enabled === !1 || t.enablePan === !1 ||
|
|
384
|
+
function ne(n) {
|
|
385
|
+
t.enabled === !1 || t.enablePan === !1 || Ye(n);
|
|
384
386
|
}
|
|
385
|
-
function Ze(
|
|
386
|
-
switch (Ee(
|
|
387
|
+
function Ze(n) {
|
|
388
|
+
switch (Ee(n), E.length) {
|
|
387
389
|
case 1:
|
|
388
390
|
switch (t.touches.ONE) {
|
|
389
|
-
case
|
|
391
|
+
case G.ROTATE:
|
|
390
392
|
if (t.enableRotate === !1) return;
|
|
391
|
-
|
|
393
|
+
ge(n), i = o.TOUCH_ROTATE;
|
|
392
394
|
break;
|
|
393
|
-
case
|
|
395
|
+
case G.PAN:
|
|
394
396
|
if (t.enablePan === !1) return;
|
|
395
|
-
|
|
397
|
+
ue(n), i = o.TOUCH_PAN;
|
|
396
398
|
break;
|
|
397
399
|
default:
|
|
398
|
-
|
|
400
|
+
i = o.NONE;
|
|
399
401
|
}
|
|
400
402
|
break;
|
|
401
403
|
case 2:
|
|
402
404
|
switch (t.touches.TWO) {
|
|
403
|
-
case
|
|
405
|
+
case G.DOLLY_PAN:
|
|
404
406
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
405
|
-
|
|
407
|
+
Be(n), i = o.TOUCH_DOLLY_PAN;
|
|
406
408
|
break;
|
|
407
|
-
case
|
|
409
|
+
case G.DOLLY_ROTATE:
|
|
408
410
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
409
|
-
|
|
411
|
+
Ke(n), i = o.TOUCH_DOLLY_ROTATE;
|
|
410
412
|
break;
|
|
411
413
|
default:
|
|
412
|
-
|
|
414
|
+
i = o.NONE;
|
|
413
415
|
}
|
|
414
416
|
break;
|
|
415
417
|
default:
|
|
416
|
-
|
|
418
|
+
i = o.NONE;
|
|
417
419
|
}
|
|
418
|
-
|
|
420
|
+
i !== o.NONE && t.dispatchEvent(re);
|
|
419
421
|
}
|
|
420
|
-
function Qe(
|
|
421
|
-
switch (Ee(
|
|
422
|
-
case
|
|
422
|
+
function Qe(n) {
|
|
423
|
+
switch (Ee(n), i) {
|
|
424
|
+
case o.TOUCH_ROTATE:
|
|
423
425
|
if (t.enableRotate === !1) return;
|
|
424
|
-
|
|
426
|
+
me(n), t.update();
|
|
425
427
|
break;
|
|
426
|
-
case
|
|
428
|
+
case o.TOUCH_PAN:
|
|
427
429
|
if (t.enablePan === !1) return;
|
|
428
|
-
ye(
|
|
430
|
+
ye(n), t.update();
|
|
429
431
|
break;
|
|
430
|
-
case
|
|
432
|
+
case o.TOUCH_DOLLY_PAN:
|
|
431
433
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
432
|
-
|
|
434
|
+
Xe(n), t.update();
|
|
433
435
|
break;
|
|
434
|
-
case
|
|
436
|
+
case o.TOUCH_DOLLY_ROTATE:
|
|
435
437
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
436
|
-
|
|
438
|
+
Ve(n), t.update();
|
|
437
439
|
break;
|
|
438
440
|
default:
|
|
439
|
-
|
|
441
|
+
i = o.NONE;
|
|
440
442
|
}
|
|
441
443
|
}
|
|
442
|
-
function we(
|
|
443
|
-
t.enabled !== !1 &&
|
|
444
|
+
function we(n) {
|
|
445
|
+
t.enabled !== !1 && n.preventDefault();
|
|
444
446
|
}
|
|
445
|
-
function Je(
|
|
446
|
-
E.push(
|
|
447
|
+
function Je(n) {
|
|
448
|
+
E.push(n.pointerId);
|
|
447
449
|
}
|
|
448
|
-
function et(
|
|
449
|
-
delete
|
|
450
|
-
for (let
|
|
451
|
-
if (E[
|
|
452
|
-
E.splice(
|
|
450
|
+
function et(n) {
|
|
451
|
+
delete _[n.pointerId];
|
|
452
|
+
for (let p = 0; p < E.length; p++)
|
|
453
|
+
if (E[p] == n.pointerId) {
|
|
454
|
+
E.splice(p, 1);
|
|
453
455
|
return;
|
|
454
456
|
}
|
|
455
457
|
}
|
|
456
|
-
function Ee(
|
|
457
|
-
let
|
|
458
|
-
|
|
458
|
+
function Ee(n) {
|
|
459
|
+
let p = _[n.pointerId];
|
|
460
|
+
p === void 0 && (p = new k(), _[n.pointerId] = p), p.set(n.pageX, n.pageY);
|
|
459
461
|
}
|
|
460
|
-
function j(
|
|
461
|
-
const
|
|
462
|
-
return
|
|
462
|
+
function j(n) {
|
|
463
|
+
const p = n.pointerId === E[0] ? E[1] : E[0];
|
|
464
|
+
return _[p];
|
|
463
465
|
}
|
|
464
|
-
t.domElement.addEventListener("contextmenu", we), t.domElement.addEventListener("pointerdown",
|
|
466
|
+
t.domElement.addEventListener("contextmenu", we), t.domElement.addEventListener("pointerdown", be), t.domElement.addEventListener("pointercancel", K), t.domElement.addEventListener("wheel", ve, { passive: !1 }), document.addEventListener("keydown", We, { passive: !0, capture: !0 }), this.update();
|
|
465
467
|
}
|
|
466
468
|
}
|
|
467
|
-
class
|
|
469
|
+
class ut {
|
|
468
470
|
constructor(e, s) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
this.container = e, this.scene = new
|
|
471
|
+
l(this, "scene");
|
|
472
|
+
l(this, "camera");
|
|
473
|
+
l(this, "renderer");
|
|
474
|
+
l(this, "controls");
|
|
475
|
+
l(this, "container");
|
|
476
|
+
l(this, "resizeHandler");
|
|
477
|
+
this.container = e, this.scene = new m.Scene(), this.scene.background = new m.Color(
|
|
476
478
|
s.backgroundColor ?? 657930
|
|
477
479
|
);
|
|
478
|
-
const { width: t, height:
|
|
479
|
-
this.camera = new
|
|
480
|
+
const { width: t, height: o } = ze(e), i = s.cameraFov ?? 75;
|
|
481
|
+
this.camera = new m.PerspectiveCamera(i, t / o, 0.1, 2e3);
|
|
480
482
|
const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
481
|
-
this.camera.position.set(a.x, a.y, a.z), this.renderer = new
|
|
483
|
+
this.camera.position.set(a.x, a.y, a.z), this.renderer = new m.WebGLRenderer({
|
|
482
484
|
antialias: !0,
|
|
483
485
|
alpha: !0,
|
|
484
486
|
powerPreference: "high-performance"
|
|
485
|
-
}), this.renderer.setSize(t,
|
|
487
|
+
}), this.renderer.setSize(t, o), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = m.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = m.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new gt(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 500, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
486
488
|
}
|
|
487
489
|
/**
|
|
488
490
|
* Sets up scene lighting for gradient glass on dark background
|
|
489
491
|
*/
|
|
490
492
|
setupLighting() {
|
|
491
|
-
const e = new
|
|
493
|
+
const e = new m.AmbientLight(16777215, 0.4);
|
|
492
494
|
this.scene.add(e);
|
|
493
|
-
const s = new
|
|
495
|
+
const s = new m.DirectionalLight(16777215, 0.9);
|
|
494
496
|
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
495
|
-
const t = new
|
|
497
|
+
const t = new m.DirectionalLight(16773344, 0.4);
|
|
496
498
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
const a = new
|
|
499
|
+
const o = new m.DirectionalLight(16777215, 0.3);
|
|
500
|
+
o.position.set(0, -30, -50), this.scene.add(o);
|
|
501
|
+
const i = new m.PointLight(16750950, 0.5, 150);
|
|
502
|
+
i.position.set(40, 20, 40), this.scene.add(i);
|
|
503
|
+
const a = new m.PointLight(16764057, 0.4, 150);
|
|
502
504
|
a.position.set(-40, -20, 40), this.scene.add(a);
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
+
const r = new m.PointLight(6724095, 0.2, 100);
|
|
506
|
+
r.position.set(0, 40, -40), this.scene.add(r);
|
|
505
507
|
}
|
|
506
508
|
/**
|
|
507
509
|
* Handle window resize
|
|
@@ -542,7 +544,7 @@ class gt {
|
|
|
542
544
|
* Gets the camera's forward direction
|
|
543
545
|
*/
|
|
544
546
|
getCameraDirection() {
|
|
545
|
-
const e = new
|
|
547
|
+
const e = new m.Vector3();
|
|
546
548
|
return this.camera.getWorldDirection(e), e;
|
|
547
549
|
}
|
|
548
550
|
/**
|
|
@@ -555,12 +557,12 @@ class gt {
|
|
|
555
557
|
}
|
|
556
558
|
}
|
|
557
559
|
}
|
|
558
|
-
class
|
|
560
|
+
class ft {
|
|
559
561
|
constructor(e, s) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
562
|
+
l(this, "sceneManager");
|
|
563
|
+
l(this, "nodeFactory");
|
|
564
|
+
l(this, "nodes", /* @__PURE__ */ new Map());
|
|
565
|
+
l(this, "nodeObjects", /* @__PURE__ */ new Map());
|
|
564
566
|
this.sceneManager = e, this.nodeFactory = s;
|
|
565
567
|
}
|
|
566
568
|
/**
|
|
@@ -574,7 +576,7 @@ class mt {
|
|
|
574
576
|
* @returns true if added, false if node already exists or invalid
|
|
575
577
|
*/
|
|
576
578
|
addNode(e, s = 0) {
|
|
577
|
-
if (!
|
|
579
|
+
if (!Oe(e))
|
|
578
580
|
return !1;
|
|
579
581
|
if (this.nodes.has(e.id))
|
|
580
582
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
@@ -582,16 +584,16 @@ class mt {
|
|
|
582
584
|
x: (Math.random() - 0.5) * 50,
|
|
583
585
|
y: (Math.random() - 0.5) * 50,
|
|
584
586
|
z: (Math.random() - 0.5) * 50
|
|
585
|
-
},
|
|
587
|
+
}, o = {
|
|
586
588
|
...e,
|
|
587
589
|
position: t,
|
|
588
590
|
velocity: { x: 0, y: 0, z: 0 },
|
|
589
591
|
mass: 1
|
|
590
|
-
},
|
|
592
|
+
}, i = this.nodeFactory.createNode(
|
|
591
593
|
{ ...e, position: t },
|
|
592
594
|
s
|
|
593
595
|
);
|
|
594
|
-
return this.sceneManager.add(
|
|
596
|
+
return this.sceneManager.add(i.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, i), !0;
|
|
595
597
|
}
|
|
596
598
|
/**
|
|
597
599
|
* Removes a node from the graph
|
|
@@ -605,17 +607,17 @@ class mt {
|
|
|
605
607
|
* Updates a node's properties
|
|
606
608
|
*/
|
|
607
609
|
updateNode(e, s) {
|
|
608
|
-
const t = this.nodes.get(e),
|
|
609
|
-
return !t || !
|
|
610
|
-
|
|
610
|
+
const t = this.nodes.get(e), o = this.nodeObjects.get(e);
|
|
611
|
+
return !t || !o ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(o, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(o, s.color)), Object.keys(s).forEach((i) => {
|
|
612
|
+
i !== "id" && i !== "label" && i !== "color" && i !== "position" && (t[i] = s[i]);
|
|
611
613
|
}), !0);
|
|
612
614
|
}
|
|
613
615
|
/**
|
|
614
616
|
* Updates a node's position (called by physics engine)
|
|
615
617
|
*/
|
|
616
618
|
updateNodePosition(e, s) {
|
|
617
|
-
const t = this.nodes.get(e),
|
|
618
|
-
t &&
|
|
619
|
+
const t = this.nodes.get(e), o = this.nodeObjects.get(e);
|
|
620
|
+
t && o && (t.position = s, o.group.position.set(s.x, s.y, s.z));
|
|
619
621
|
}
|
|
620
622
|
/**
|
|
621
623
|
* Updates a node's LOD level
|
|
@@ -681,22 +683,22 @@ class mt {
|
|
|
681
683
|
this.clear();
|
|
682
684
|
}
|
|
683
685
|
}
|
|
684
|
-
class
|
|
686
|
+
class mt {
|
|
685
687
|
constructor(e, s, t) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
688
|
+
l(this, "sceneManager");
|
|
689
|
+
l(this, "nodeManager");
|
|
690
|
+
l(this, "edgeFactory");
|
|
691
|
+
l(this, "edges", []);
|
|
692
|
+
l(this, "edgeObjects", []);
|
|
693
|
+
l(this, "edgeKeySet", /* @__PURE__ */ new Set());
|
|
694
|
+
l(this, "highlightedEdgeKey", null);
|
|
693
695
|
this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
|
|
694
696
|
}
|
|
695
697
|
/**
|
|
696
698
|
* Checks if an edge exists
|
|
697
699
|
*/
|
|
698
700
|
hasEdge(e, s) {
|
|
699
|
-
const t =
|
|
701
|
+
const t = D(e, s);
|
|
700
702
|
return this.edgeKeySet.has(t);
|
|
701
703
|
}
|
|
702
704
|
/**
|
|
@@ -710,44 +712,44 @@ class ft {
|
|
|
710
712
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
711
713
|
if (!this.nodeManager.hasNode(e.target))
|
|
712
714
|
return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
|
|
713
|
-
const s =
|
|
715
|
+
const s = D(e.source, e.target);
|
|
714
716
|
if (this.edgeKeySet.has(s))
|
|
715
717
|
return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
|
|
716
|
-
const t = this.nodeManager.getNode(e.source),
|
|
718
|
+
const t = this.nodeManager.getNode(e.source), o = this.nodeManager.getNode(e.target), i = this.edgeFactory.createEdge(
|
|
717
719
|
e,
|
|
718
720
|
t,
|
|
719
|
-
|
|
721
|
+
o,
|
|
720
722
|
t.position,
|
|
721
|
-
|
|
723
|
+
o.position
|
|
722
724
|
);
|
|
723
|
-
return this.sceneManager.add(
|
|
725
|
+
return this.sceneManager.add(i.line), this.edges.push(e), this.edgeObjects.push(i), this.edgeKeySet.add(s), !0;
|
|
724
726
|
}
|
|
725
727
|
/**
|
|
726
728
|
* Removes an edge from the graph
|
|
727
729
|
* @returns true if removed, false if not found
|
|
728
730
|
*/
|
|
729
731
|
removeEdge(e, s) {
|
|
730
|
-
const t =
|
|
732
|
+
const t = D(e, s);
|
|
731
733
|
if (!this.edgeKeySet.has(t))
|
|
732
734
|
return !1;
|
|
733
|
-
const
|
|
734
|
-
(a) =>
|
|
735
|
+
const o = this.edges.findIndex(
|
|
736
|
+
(a) => D(a.source, a.target) === t
|
|
735
737
|
);
|
|
736
|
-
if (
|
|
738
|
+
if (o === -1)
|
|
737
739
|
return !1;
|
|
738
|
-
const
|
|
739
|
-
return this.sceneManager.remove(
|
|
740
|
+
const i = this.edgeObjects[o];
|
|
741
|
+
return this.sceneManager.remove(i.line), this.edgeFactory.disposeEdge(i), this.edges.splice(o, 1), this.edgeObjects.splice(o, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
|
|
740
742
|
}
|
|
741
743
|
/**
|
|
742
744
|
* Highlights an edge
|
|
743
745
|
*/
|
|
744
746
|
highlightEdge(e, s) {
|
|
745
|
-
const t =
|
|
747
|
+
const t = D(e, s);
|
|
746
748
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
747
|
-
const
|
|
748
|
-
(
|
|
749
|
+
const o = this.edges.findIndex(
|
|
750
|
+
(i) => D(i.source, i.target) === t
|
|
749
751
|
);
|
|
750
|
-
|
|
752
|
+
o !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[o]), this.highlightedEdgeKey = t);
|
|
751
753
|
}
|
|
752
754
|
/**
|
|
753
755
|
* Unhighlights the currently highlighted edge
|
|
@@ -755,7 +757,7 @@ class ft {
|
|
|
755
757
|
unhighlightCurrentEdge() {
|
|
756
758
|
if (!this.highlightedEdgeKey) return;
|
|
757
759
|
const e = this.edges.findIndex(
|
|
758
|
-
(s) =>
|
|
760
|
+
(s) => D(s.source, s.target) === this.highlightedEdgeKey
|
|
759
761
|
);
|
|
760
762
|
e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
|
|
761
763
|
}
|
|
@@ -790,11 +792,11 @@ class ft {
|
|
|
790
792
|
*/
|
|
791
793
|
updateEdgePositions() {
|
|
792
794
|
this.edgeObjects.forEach((e, s) => {
|
|
793
|
-
const t = this.edges[s],
|
|
794
|
-
|
|
795
|
+
const t = this.edges[s], o = this.nodeManager.getNode(t.source), i = this.nodeManager.getNode(t.target);
|
|
796
|
+
o && i && this.edgeFactory.updateEdgePositions(
|
|
795
797
|
e,
|
|
796
|
-
|
|
797
|
-
|
|
798
|
+
o.position,
|
|
799
|
+
i.position
|
|
798
800
|
);
|
|
799
801
|
});
|
|
800
802
|
}
|
|
@@ -831,21 +833,21 @@ class ft {
|
|
|
831
833
|
this.clear();
|
|
832
834
|
}
|
|
833
835
|
}
|
|
834
|
-
class
|
|
836
|
+
class Le {
|
|
835
837
|
constructor(e, s, t = {}) {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
+
l(this, "nodes");
|
|
839
|
+
l(this, "edges");
|
|
838
840
|
// Physics parameters
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
841
|
+
l(this, "repulsionStrength");
|
|
842
|
+
l(this, "attractionStrength");
|
|
843
|
+
l(this, "damping");
|
|
844
|
+
l(this, "useBarnesHut");
|
|
845
|
+
l(this, "barnesHutTheta");
|
|
844
846
|
// Simulation state
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
847
|
+
l(this, "alpha", 1);
|
|
848
|
+
l(this, "alphaDecay", 0.0228);
|
|
849
|
+
l(this, "alphaMin", 1e-3);
|
|
850
|
+
l(this, "alphaTarget", 0);
|
|
849
851
|
this.nodes = e, this.edges = s, this.repulsionStrength = t.repulsionStrength ?? 100, this.attractionStrength = t.attractionStrength ?? 0.01, this.damping = t.damping ?? 0.9, this.useBarnesHut = t.useBarnesHut ?? !1, this.barnesHutTheta = t.barnesHutTheta ?? 0.5;
|
|
850
852
|
}
|
|
851
853
|
/**
|
|
@@ -862,13 +864,13 @@ class Oe {
|
|
|
862
864
|
calculateRepulsionBruteForce() {
|
|
863
865
|
const e = Array.from(this.nodes.values()), s = e.length;
|
|
864
866
|
for (let t = 0; t < s; t++) {
|
|
865
|
-
const
|
|
866
|
-
for (let
|
|
867
|
-
const a = e[
|
|
868
|
-
let g =
|
|
867
|
+
const o = e[t];
|
|
868
|
+
for (let i = t + 1; i < s; i++) {
|
|
869
|
+
const a = e[i], r = a.position.x - o.position.x, c = a.position.y - o.position.y, d = a.position.z - o.position.z;
|
|
870
|
+
let g = r * r + c * c + d * d;
|
|
869
871
|
g < 0.01 && (g = 0.01);
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
+
const u = Math.sqrt(g), y = this.repulsionStrength * this.alpha / g, f = r / u * y, x = c / u * y, v = d / u * y;
|
|
873
|
+
o.velocity.x -= f / o.mass, o.velocity.y -= x / o.mass, o.velocity.z -= v / o.mass, a.velocity.x += f / a.mass, a.velocity.y += x / a.mass, a.velocity.z += v / a.mass;
|
|
872
874
|
}
|
|
873
875
|
}
|
|
874
876
|
}
|
|
@@ -889,23 +891,23 @@ class Oe {
|
|
|
889
891
|
return;
|
|
890
892
|
}
|
|
891
893
|
if (s.mass === 0) return;
|
|
892
|
-
const t = s.centerOfMass.x - e.position.x,
|
|
894
|
+
const t = s.centerOfMass.x - e.position.x, o = s.centerOfMass.y - e.position.y, i = s.centerOfMass.z - e.position.z, a = Math.sqrt(t * t + o * o + i * i);
|
|
893
895
|
if (s.size / a < this.barnesHutTheta) {
|
|
894
|
-
const
|
|
895
|
-
e.velocity.x -= t / a *
|
|
896
|
+
const r = Math.max(a * a, 0.01), c = this.repulsionStrength * this.alpha * s.mass / r;
|
|
897
|
+
e.velocity.x -= t / a * c / e.mass, e.velocity.y -= o / a * c / e.mass, e.velocity.z -= i / a * c / e.mass;
|
|
896
898
|
} else
|
|
897
|
-
for (const
|
|
898
|
-
|
|
899
|
+
for (const r of s.children)
|
|
900
|
+
r && this.calculateForceFromOctree(e, r);
|
|
899
901
|
}
|
|
900
902
|
/**
|
|
901
903
|
* Apply repulsion between two nodes
|
|
902
904
|
*/
|
|
903
905
|
applyRepulsionBetween(e, s) {
|
|
904
|
-
const t = s.position.x - e.position.x,
|
|
905
|
-
let a = t * t +
|
|
906
|
+
const t = s.position.x - e.position.x, o = s.position.y - e.position.y, i = s.position.z - e.position.z;
|
|
907
|
+
let a = t * t + o * o + i * i;
|
|
906
908
|
a < 0.01 && (a = 0.01);
|
|
907
|
-
const
|
|
908
|
-
e.velocity.x -= t /
|
|
909
|
+
const r = Math.sqrt(a), c = this.repulsionStrength * this.alpha / a;
|
|
910
|
+
e.velocity.x -= t / r * c / e.mass, e.velocity.y -= o / r * c / e.mass, e.velocity.z -= i / r * c / e.mass;
|
|
909
911
|
}
|
|
910
912
|
/**
|
|
911
913
|
* Calculate attraction forces along edges
|
|
@@ -914,10 +916,10 @@ class Oe {
|
|
|
914
916
|
for (const e of this.edges) {
|
|
915
917
|
const s = this.nodes.get(e.source), t = this.nodes.get(e.target);
|
|
916
918
|
if (!s || !t) continue;
|
|
917
|
-
const
|
|
918
|
-
if (
|
|
919
|
-
const
|
|
920
|
-
s.velocity.x += g / s.mass, s.velocity.y +=
|
|
919
|
+
const o = t.position.x - s.position.x, i = t.position.y - s.position.y, a = t.position.z - s.position.z, r = Math.sqrt(o * o + i * i + a * a);
|
|
920
|
+
if (r < 0.01) continue;
|
|
921
|
+
const d = (r - 15) * this.attractionStrength * this.alpha, g = o / r * d, u = i / r * d, y = a / r * d;
|
|
922
|
+
s.velocity.x += g / s.mass, s.velocity.y += u / s.mass, s.velocity.z += y / s.mass, t.velocity.x -= g / t.mass, t.velocity.y -= u / t.mass, t.velocity.z -= y / t.mass;
|
|
921
923
|
}
|
|
922
924
|
}
|
|
923
925
|
/**
|
|
@@ -954,7 +956,7 @@ class Oe {
|
|
|
954
956
|
}
|
|
955
957
|
class yt {
|
|
956
958
|
constructor(e) {
|
|
957
|
-
|
|
959
|
+
l(this, "root");
|
|
958
960
|
const s = this.calculateBounds(e);
|
|
959
961
|
this.root = this.buildTree(e, s);
|
|
960
962
|
}
|
|
@@ -965,13 +967,13 @@ class yt {
|
|
|
965
967
|
max: { x: 100, y: 100, z: 100 }
|
|
966
968
|
};
|
|
967
969
|
const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
|
|
968
|
-
for (const
|
|
969
|
-
s.x = Math.min(s.x,
|
|
970
|
-
const
|
|
971
|
-
return s.x -=
|
|
970
|
+
for (const i of e)
|
|
971
|
+
s.x = Math.min(s.x, i.position.x), s.y = Math.min(s.y, i.position.y), s.z = Math.min(s.z, i.position.z), t.x = Math.max(t.x, i.position.x), t.y = Math.max(t.y, i.position.y), t.z = Math.max(t.z, i.position.z);
|
|
972
|
+
const o = 10;
|
|
973
|
+
return s.x -= o, s.y -= o, s.z -= o, t.x += o, t.y += o, t.z += o, { min: s, max: t };
|
|
972
974
|
}
|
|
973
975
|
buildTree(e, s, t = 0) {
|
|
974
|
-
const
|
|
976
|
+
const o = Math.max(
|
|
975
977
|
s.max.x - s.min.x,
|
|
976
978
|
s.max.y - s.min.y,
|
|
977
979
|
s.max.z - s.min.z
|
|
@@ -979,7 +981,7 @@ class yt {
|
|
|
979
981
|
if (e.length === 0)
|
|
980
982
|
return {
|
|
981
983
|
bounds: s,
|
|
982
|
-
size:
|
|
984
|
+
size: o,
|
|
983
985
|
centerOfMass: { x: 0, y: 0, z: 0 },
|
|
984
986
|
mass: 0,
|
|
985
987
|
isLeaf: !0,
|
|
@@ -987,71 +989,71 @@ class yt {
|
|
|
987
989
|
children: []
|
|
988
990
|
};
|
|
989
991
|
if (e.length === 1 || t > 20) {
|
|
990
|
-
let
|
|
991
|
-
const
|
|
992
|
-
for (const
|
|
993
|
-
|
|
994
|
-
return
|
|
992
|
+
let f = 0;
|
|
993
|
+
const x = { x: 0, y: 0, z: 0 };
|
|
994
|
+
for (const v of e)
|
|
995
|
+
f += v.mass, x.x += v.position.x * v.mass, x.y += v.position.y * v.mass, x.z += v.position.z * v.mass;
|
|
996
|
+
return f > 0 && (x.x /= f, x.y /= f, x.z /= f), {
|
|
995
997
|
bounds: s,
|
|
996
|
-
size:
|
|
997
|
-
centerOfMass:
|
|
998
|
-
mass:
|
|
998
|
+
size: o,
|
|
999
|
+
centerOfMass: x,
|
|
1000
|
+
mass: f,
|
|
999
1001
|
isLeaf: !0,
|
|
1000
1002
|
node: e[0],
|
|
1001
1003
|
children: []
|
|
1002
1004
|
};
|
|
1003
1005
|
}
|
|
1004
|
-
const
|
|
1005
|
-
for (const
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
const
|
|
1010
|
-
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x:
|
|
1011
|
-
{ min: { x:
|
|
1012
|
-
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x:
|
|
1013
|
-
{ min: { x:
|
|
1014
|
-
{ min: { x: s.min.x, y: s.min.y, z:
|
|
1015
|
-
{ min: { x:
|
|
1016
|
-
{ min: { x: s.min.x, y: a, z:
|
|
1017
|
-
{ min: { x:
|
|
1006
|
+
const i = (s.min.x + s.max.x) / 2, a = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
|
|
1007
|
+
for (const f of e) {
|
|
1008
|
+
const x = (f.position.x >= i ? 1 : 0) + (f.position.y >= a ? 2 : 0) + (f.position.z >= r ? 4 : 0);
|
|
1009
|
+
c[x].push(f);
|
|
1010
|
+
}
|
|
1011
|
+
const d = [
|
|
1012
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: i, y: a, z: r } },
|
|
1013
|
+
{ min: { x: i, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: r } },
|
|
1014
|
+
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x: i, y: s.max.y, z: r } },
|
|
1015
|
+
{ min: { x: i, y: a, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
|
|
1016
|
+
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x: i, y: a, z: s.max.z } },
|
|
1017
|
+
{ min: { x: i, y: s.min.y, z: r }, max: { x: s.max.x, y: a, z: s.max.z } },
|
|
1018
|
+
{ min: { x: s.min.x, y: a, z: r }, max: { x: i, y: s.max.y, z: s.max.z } },
|
|
1019
|
+
{ min: { x: i, y: a, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1018
1020
|
], g = [];
|
|
1019
|
-
let
|
|
1020
|
-
const
|
|
1021
|
-
for (let
|
|
1022
|
-
if (
|
|
1023
|
-
const
|
|
1024
|
-
g.push(
|
|
1021
|
+
let u = 0;
|
|
1022
|
+
const y = { x: 0, y: 0, z: 0 };
|
|
1023
|
+
for (let f = 0; f < 8; f++)
|
|
1024
|
+
if (c[f].length > 0) {
|
|
1025
|
+
const x = this.buildTree(c[f], d[f], t + 1);
|
|
1026
|
+
g.push(x), u += x.mass, y.x += x.centerOfMass.x * x.mass, y.y += x.centerOfMass.y * x.mass, y.z += x.centerOfMass.z * x.mass;
|
|
1025
1027
|
} else
|
|
1026
1028
|
g.push(null);
|
|
1027
|
-
return
|
|
1029
|
+
return u > 0 && (y.x /= u, y.y /= u, y.z /= u), {
|
|
1028
1030
|
bounds: s,
|
|
1029
|
-
size:
|
|
1030
|
-
centerOfMass:
|
|
1031
|
-
mass:
|
|
1031
|
+
size: o,
|
|
1032
|
+
centerOfMass: y,
|
|
1033
|
+
mass: u,
|
|
1032
1034
|
isLeaf: !1,
|
|
1033
1035
|
node: null,
|
|
1034
1036
|
children: g
|
|
1035
1037
|
};
|
|
1036
1038
|
}
|
|
1037
1039
|
}
|
|
1038
|
-
class
|
|
1039
|
-
constructor(e, s, t,
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1040
|
+
class xt {
|
|
1041
|
+
constructor(e, s, t, o = 60) {
|
|
1042
|
+
l(this, "sceneManager");
|
|
1043
|
+
l(this, "animationId", null);
|
|
1044
|
+
l(this, "isRunning", !1);
|
|
1045
|
+
l(this, "frameInterval");
|
|
1046
|
+
l(this, "lastFrameTime", 0);
|
|
1047
|
+
l(this, "onSimulate");
|
|
1048
|
+
l(this, "onRender");
|
|
1047
1049
|
// Performance monitoring
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1050
|
+
l(this, "frameCount", 0);
|
|
1051
|
+
l(this, "fpsStartTime", 0);
|
|
1052
|
+
l(this, "currentFPS", 60);
|
|
1051
1053
|
/**
|
|
1052
1054
|
* Main animation loop
|
|
1053
1055
|
*/
|
|
1054
|
-
|
|
1056
|
+
l(this, "animate", () => {
|
|
1055
1057
|
if (!this.isRunning) return;
|
|
1056
1058
|
this.animationId = requestAnimationFrame(this.animate);
|
|
1057
1059
|
const e = performance.now(), s = e - this.lastFrameTime;
|
|
@@ -1061,7 +1063,7 @@ class bt {
|
|
|
1061
1063
|
const t = e - this.fpsStartTime;
|
|
1062
1064
|
t >= 1e3 && (this.currentFPS = this.frameCount / (t / 1e3), this.frameCount = 0, this.fpsStartTime = e), this.onSimulate(), this.onRender(), this.sceneManager.render();
|
|
1063
1065
|
});
|
|
1064
|
-
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 /
|
|
1066
|
+
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 / o;
|
|
1065
1067
|
}
|
|
1066
1068
|
/**
|
|
1067
1069
|
* Starts the animation loop
|
|
@@ -1100,12 +1102,12 @@ class bt {
|
|
|
1100
1102
|
this.stop();
|
|
1101
1103
|
}
|
|
1102
1104
|
}
|
|
1103
|
-
class
|
|
1105
|
+
class bt {
|
|
1104
1106
|
constructor() {
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
+
l(this, "envMap", null);
|
|
1108
|
+
l(this, "materialCache", /* @__PURE__ */ new Map());
|
|
1107
1109
|
// Color palette - white and tangerine
|
|
1108
|
-
|
|
1110
|
+
l(this, "COLORS", [
|
|
1109
1111
|
16777215,
|
|
1110
1112
|
// White
|
|
1111
1113
|
16750950,
|
|
@@ -1138,10 +1140,10 @@ class xt {
|
|
|
1138
1140
|
{ colors: ["#2d1a1a", "#1a0a0a", "#0f0505"] }
|
|
1139
1141
|
// -z
|
|
1140
1142
|
];
|
|
1141
|
-
for (const
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
const a =
|
|
1143
|
+
for (const o of t) {
|
|
1144
|
+
const i = document.createElement("canvas");
|
|
1145
|
+
i.width = 256, i.height = 256;
|
|
1146
|
+
const a = i.getContext("2d"), r = a.createRadialGradient(
|
|
1145
1147
|
256 / 2,
|
|
1146
1148
|
256 / 2,
|
|
1147
1149
|
0,
|
|
@@ -1149,17 +1151,17 @@ class xt {
|
|
|
1149
1151
|
256 / 2,
|
|
1150
1152
|
256 * 0.8
|
|
1151
1153
|
);
|
|
1152
|
-
|
|
1153
|
-
const
|
|
1154
|
-
for (let
|
|
1154
|
+
r.addColorStop(0, o.colors[0]), r.addColorStop(0.5, o.colors[1]), r.addColorStop(1, o.colors[2]), a.fillStyle = r, a.fillRect(0, 0, 256, 256);
|
|
1155
|
+
const c = a.getImageData(0, 0, 256, 256);
|
|
1156
|
+
for (let d = 0; d < c.data.length; d += 4) {
|
|
1155
1157
|
const g = (Math.random() - 0.5) * 5;
|
|
1156
|
-
|
|
1158
|
+
c.data[d] = Math.min(255, Math.max(0, c.data[d] + g)), c.data[d + 1] = Math.min(255, Math.max(0, c.data[d + 1] + g)), c.data[d + 2] = Math.min(255, Math.max(0, c.data[d + 2] + g));
|
|
1157
1159
|
}
|
|
1158
|
-
a.putImageData(
|
|
1160
|
+
a.putImageData(c, 0, 0), s.push(i);
|
|
1159
1161
|
}
|
|
1160
|
-
this.envMap = new
|
|
1161
|
-
const
|
|
1162
|
-
return
|
|
1162
|
+
this.envMap = new m.CubeTexture(s.map((o) => {
|
|
1163
|
+
const i = new Image();
|
|
1164
|
+
return i.src = o.toDataURL(), i;
|
|
1163
1165
|
})), this.envMap.needsUpdate = !0;
|
|
1164
1166
|
}
|
|
1165
1167
|
/**
|
|
@@ -1176,11 +1178,11 @@ class xt {
|
|
|
1176
1178
|
const t = "glass-single";
|
|
1177
1179
|
if (this.materialCache.has(t))
|
|
1178
1180
|
return this.materialCache.get(t).clone();
|
|
1179
|
-
const
|
|
1181
|
+
const o = new m.Color(16750950), i = new m.ShaderMaterial({
|
|
1180
1182
|
uniforms: {
|
|
1181
|
-
uColor: { value:
|
|
1183
|
+
uColor: { value: o },
|
|
1182
1184
|
uEnvMap: { value: this.envMap },
|
|
1183
|
-
uGlowColor: { value: new
|
|
1185
|
+
uGlowColor: { value: new m.Color(16777215) },
|
|
1184
1186
|
uGlowIntensity: { value: 0.8 },
|
|
1185
1187
|
uReflectivity: { value: 0.4 },
|
|
1186
1188
|
uFresnelPower: { value: 2.5 }
|
|
@@ -1246,17 +1248,17 @@ class xt {
|
|
|
1246
1248
|
}
|
|
1247
1249
|
`,
|
|
1248
1250
|
transparent: !0,
|
|
1249
|
-
side:
|
|
1251
|
+
side: m.FrontSide,
|
|
1250
1252
|
depthWrite: !0,
|
|
1251
|
-
blending:
|
|
1253
|
+
blending: m.NormalBlending
|
|
1252
1254
|
});
|
|
1253
|
-
return this.materialCache.set(t,
|
|
1255
|
+
return this.materialCache.set(t, i), i.clone();
|
|
1254
1256
|
}
|
|
1255
1257
|
/**
|
|
1256
1258
|
* Creates material for edges (light color for dark background)
|
|
1257
1259
|
*/
|
|
1258
1260
|
createEdgeMaterial(e = 6710886, s = 0.4) {
|
|
1259
|
-
return new
|
|
1261
|
+
return new m.LineBasicMaterial({
|
|
1260
1262
|
color: e,
|
|
1261
1263
|
transparent: !0,
|
|
1262
1264
|
opacity: s,
|
|
@@ -1267,7 +1269,7 @@ class xt {
|
|
|
1267
1269
|
* Creates highlighted edge material
|
|
1268
1270
|
*/
|
|
1269
1271
|
createHighlightedEdgeMaterial() {
|
|
1270
|
-
return new
|
|
1272
|
+
return new m.LineBasicMaterial({
|
|
1271
1273
|
color: 16750950,
|
|
1272
1274
|
// Tangerine highlight
|
|
1273
1275
|
transparent: !1,
|
|
@@ -1279,13 +1281,13 @@ class xt {
|
|
|
1279
1281
|
* Creates a sprite material for labels (light text for dark background)
|
|
1280
1282
|
*/
|
|
1281
1283
|
createLabelMaterial(e, s = 24) {
|
|
1282
|
-
const t = document.createElement("canvas"),
|
|
1283
|
-
|
|
1284
|
-
const a =
|
|
1285
|
-
t.width = Math.max(128, a + 24), t.height = s + 20,
|
|
1286
|
-
const
|
|
1287
|
-
return
|
|
1288
|
-
map:
|
|
1284
|
+
const t = document.createElement("canvas"), o = t.getContext("2d");
|
|
1285
|
+
o.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1286
|
+
const a = o.measureText(e).width;
|
|
1287
|
+
t.width = Math.max(128, a + 24), t.height = s + 20, o.clearRect(0, 0, t.width, t.height), o.font = `600 ${s}px Inter, -apple-system, sans-serif`, o.textAlign = "center", o.textBaseline = "middle", o.shadowColor = "rgba(0, 0, 0, 0.8)", o.shadowBlur = 4, o.shadowOffsetX = 1, o.shadowOffsetY = 1, o.fillStyle = "rgba(255, 255, 255, 0.95)", o.fillText(e, t.width / 2, t.height / 2);
|
|
1288
|
+
const r = new m.CanvasTexture(t);
|
|
1289
|
+
return r.needsUpdate = !0, new m.SpriteMaterial({
|
|
1290
|
+
map: r,
|
|
1289
1291
|
transparent: !0,
|
|
1290
1292
|
depthTest: !1,
|
|
1291
1293
|
depthWrite: !1
|
|
@@ -1306,10 +1308,10 @@ class xt {
|
|
|
1306
1308
|
}
|
|
1307
1309
|
class vt {
|
|
1308
1310
|
constructor(e, s = 2, t = [32, 16, 8]) {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1311
|
+
l(this, "materialFactory");
|
|
1312
|
+
l(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
1313
|
+
l(this, "nodeRadius");
|
|
1314
|
+
l(this, "lodSegments");
|
|
1313
1315
|
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.initGeometryCache();
|
|
1314
1316
|
}
|
|
1315
1317
|
/**
|
|
@@ -1320,7 +1322,7 @@ class vt {
|
|
|
1320
1322
|
const t = `lod-${s}`;
|
|
1321
1323
|
this.geometryCache.set(
|
|
1322
1324
|
t,
|
|
1323
|
-
new
|
|
1325
|
+
new m.SphereGeometry(this.nodeRadius, e, e)
|
|
1324
1326
|
);
|
|
1325
1327
|
});
|
|
1326
1328
|
}
|
|
@@ -1335,21 +1337,21 @@ class vt {
|
|
|
1335
1337
|
* Creates a node visual (glass ball + label)
|
|
1336
1338
|
*/
|
|
1337
1339
|
createNode(e, s = 0) {
|
|
1338
|
-
const t = new
|
|
1340
|
+
const t = new m.Group();
|
|
1339
1341
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1340
|
-
const
|
|
1342
|
+
const o = this.getGeometry(s), i = this.materialFactory.createGlassMaterial(
|
|
1341
1343
|
e.color ?? 4886754
|
|
1342
|
-
), a = new
|
|
1344
|
+
), a = new m.Mesh(o, i);
|
|
1343
1345
|
a.castShadow = !0, a.receiveShadow = !0, t.add(a);
|
|
1344
|
-
const
|
|
1345
|
-
return
|
|
1346
|
+
const r = this.materialFactory.createLabelMaterial(e.label), c = new m.Sprite(r);
|
|
1347
|
+
return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
|
|
1346
1348
|
e.position.x,
|
|
1347
1349
|
e.position.y,
|
|
1348
1350
|
e.position.z
|
|
1349
1351
|
), {
|
|
1350
1352
|
group: t,
|
|
1351
1353
|
sphere: a,
|
|
1352
|
-
label:
|
|
1354
|
+
label: c,
|
|
1353
1355
|
lodLevel: s
|
|
1354
1356
|
};
|
|
1355
1357
|
}
|
|
@@ -1365,19 +1367,19 @@ class vt {
|
|
|
1365
1367
|
* Updates the color of a node
|
|
1366
1368
|
*/
|
|
1367
1369
|
updateNodeColor(e, s) {
|
|
1368
|
-
e.sphere.material instanceof
|
|
1370
|
+
e.sphere.material instanceof m.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
|
|
1369
1371
|
}
|
|
1370
1372
|
/**
|
|
1371
1373
|
* Updates the label of a node
|
|
1372
1374
|
*/
|
|
1373
1375
|
updateNodeLabel(e, s) {
|
|
1374
|
-
e.label.material instanceof
|
|
1376
|
+
e.label.material instanceof m.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
|
|
1375
1377
|
}
|
|
1376
1378
|
/**
|
|
1377
1379
|
* Disposes a node's resources
|
|
1378
1380
|
*/
|
|
1379
1381
|
disposeNode(e) {
|
|
1380
|
-
e.sphere.material instanceof
|
|
1382
|
+
e.sphere.material instanceof m.Material && e.sphere.material.dispose(), e.label.material instanceof m.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
|
|
1381
1383
|
}
|
|
1382
1384
|
/**
|
|
1383
1385
|
* Dispose factory resources
|
|
@@ -1388,11 +1390,11 @@ class vt {
|
|
|
1388
1390
|
}
|
|
1389
1391
|
class Mt {
|
|
1390
1392
|
constructor(e, s = 10066329, t = 0.5) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1393
|
+
l(this, "materialFactory");
|
|
1394
|
+
l(this, "edgeColor");
|
|
1395
|
+
l(this, "edgeOpacity");
|
|
1396
|
+
l(this, "defaultMaterial", null);
|
|
1397
|
+
l(this, "highlightMaterial", null);
|
|
1396
1398
|
this.materialFactory = e, this.edgeColor = s, this.edgeOpacity = t;
|
|
1397
1399
|
}
|
|
1398
1400
|
/**
|
|
@@ -1413,25 +1415,25 @@ class Mt {
|
|
|
1413
1415
|
/**
|
|
1414
1416
|
* Creates an edge line between two positions
|
|
1415
1417
|
*/
|
|
1416
|
-
createEdge(e, s, t,
|
|
1417
|
-
const a = new
|
|
1418
|
+
createEdge(e, s, t, o, i) {
|
|
1419
|
+
const a = new m.BufferGeometry(), r = new Float32Array([
|
|
1420
|
+
o.x,
|
|
1421
|
+
o.y,
|
|
1422
|
+
o.z,
|
|
1418
1423
|
i.x,
|
|
1419
1424
|
i.y,
|
|
1420
|
-
i.z
|
|
1421
|
-
n.x,
|
|
1422
|
-
n.y,
|
|
1423
|
-
n.z
|
|
1425
|
+
i.z
|
|
1424
1426
|
]);
|
|
1425
|
-
a.setAttribute("position", new
|
|
1426
|
-
const
|
|
1427
|
-
return
|
|
1427
|
+
a.setAttribute("position", new m.BufferAttribute(r, 3));
|
|
1428
|
+
const c = this.getDefaultMaterial().clone(), d = new m.Line(a, c);
|
|
1429
|
+
return d.name = `edge-${e.source}-${e.target}`, d.userData = {
|
|
1428
1430
|
source: e.source,
|
|
1429
1431
|
target: e.target,
|
|
1430
1432
|
edge: e,
|
|
1431
1433
|
sourceNode: s,
|
|
1432
1434
|
targetNode: t
|
|
1433
|
-
},
|
|
1434
|
-
line:
|
|
1435
|
+
}, d.frustumCulled = !0, {
|
|
1436
|
+
line: d,
|
|
1435
1437
|
source: e.source,
|
|
1436
1438
|
target: e.target
|
|
1437
1439
|
};
|
|
@@ -1440,26 +1442,26 @@ class Mt {
|
|
|
1440
1442
|
* Highlights an edge
|
|
1441
1443
|
*/
|
|
1442
1444
|
highlightEdge(e) {
|
|
1443
|
-
e.line.material instanceof
|
|
1445
|
+
e.line.material instanceof m.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
|
|
1444
1446
|
}
|
|
1445
1447
|
/**
|
|
1446
1448
|
* Resets an edge to default appearance
|
|
1447
1449
|
*/
|
|
1448
1450
|
unhighlightEdge(e) {
|
|
1449
|
-
e.line.material instanceof
|
|
1451
|
+
e.line.material instanceof m.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
|
|
1450
1452
|
}
|
|
1451
1453
|
/**
|
|
1452
1454
|
* Updates an edge's positions
|
|
1453
1455
|
*/
|
|
1454
1456
|
updateEdgePositions(e, s, t) {
|
|
1455
|
-
const
|
|
1456
|
-
|
|
1457
|
+
const o = e.line.geometry.attributes.position, i = o.array;
|
|
1458
|
+
i[0] = s.x, i[1] = s.y, i[2] = s.z, i[3] = t.x, i[4] = t.y, i[5] = t.z, o.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
|
|
1457
1459
|
}
|
|
1458
1460
|
/**
|
|
1459
1461
|
* Disposes an edge's resources
|
|
1460
1462
|
*/
|
|
1461
1463
|
disposeEdge(e) {
|
|
1462
|
-
e.line.geometry.dispose(), e.line.material instanceof
|
|
1464
|
+
e.line.geometry.dispose(), e.line.material instanceof m.Material && e.line.material.dispose();
|
|
1463
1465
|
}
|
|
1464
1466
|
/**
|
|
1465
1467
|
* Dispose factory resources
|
|
@@ -1470,9 +1472,9 @@ class Mt {
|
|
|
1470
1472
|
}
|
|
1471
1473
|
class wt {
|
|
1472
1474
|
constructor(e, s = [50, 100, 200], t = !0) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1475
|
+
l(this, "camera");
|
|
1476
|
+
l(this, "lodDistances");
|
|
1477
|
+
l(this, "enabled");
|
|
1476
1478
|
this.camera = e, this.lodDistances = s, this.enabled = t;
|
|
1477
1479
|
}
|
|
1478
1480
|
/**
|
|
@@ -1481,15 +1483,15 @@ class wt {
|
|
|
1481
1483
|
getLODLevel(e) {
|
|
1482
1484
|
if (!this.enabled)
|
|
1483
1485
|
return U.HIGH;
|
|
1484
|
-
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y,
|
|
1485
|
-
return
|
|
1486
|
+
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, o = e.z - this.camera.position.z, i = Math.sqrt(s * s + t * t + o * o);
|
|
1487
|
+
return i < this.lodDistances[0] ? U.HIGH : i < this.lodDistances[1] ? U.MEDIUM : U.LOW;
|
|
1486
1488
|
}
|
|
1487
1489
|
/**
|
|
1488
1490
|
* Checks if a node should be visible based on distance
|
|
1489
1491
|
*/
|
|
1490
1492
|
shouldRenderNode(e, s = 500) {
|
|
1491
|
-
const t = e.x - this.camera.position.x,
|
|
1492
|
-
return Math.sqrt(t * t +
|
|
1493
|
+
const t = e.x - this.camera.position.x, o = e.y - this.camera.position.y, i = e.z - this.camera.position.z;
|
|
1494
|
+
return Math.sqrt(t * t + o * o + i * i) < s;
|
|
1493
1495
|
}
|
|
1494
1496
|
/**
|
|
1495
1497
|
* Sets the LOD distances
|
|
@@ -1506,11 +1508,11 @@ class wt {
|
|
|
1506
1508
|
}
|
|
1507
1509
|
class Et {
|
|
1508
1510
|
constructor(e, s = !0) {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
this.camera = e, this.frustum = new
|
|
1511
|
+
l(this, "camera");
|
|
1512
|
+
l(this, "frustum");
|
|
1513
|
+
l(this, "projScreenMatrix");
|
|
1514
|
+
l(this, "enabled");
|
|
1515
|
+
this.camera = e, this.frustum = new m.Frustum(), this.projScreenMatrix = new m.Matrix4(), this.enabled = s;
|
|
1514
1516
|
}
|
|
1515
1517
|
/**
|
|
1516
1518
|
* Updates the frustum from the camera
|
|
@@ -1526,7 +1528,7 @@ class Et {
|
|
|
1526
1528
|
*/
|
|
1527
1529
|
isPointVisible(e) {
|
|
1528
1530
|
if (!this.enabled) return !0;
|
|
1529
|
-
const s = new
|
|
1531
|
+
const s = new m.Vector3(e.x, e.y, e.z);
|
|
1530
1532
|
return this.frustum.containsPoint(s);
|
|
1531
1533
|
}
|
|
1532
1534
|
/**
|
|
@@ -1534,8 +1536,8 @@ class Et {
|
|
|
1534
1536
|
*/
|
|
1535
1537
|
isSphereVisible(e, s) {
|
|
1536
1538
|
if (!this.enabled) return !0;
|
|
1537
|
-
const t = new
|
|
1538
|
-
new
|
|
1539
|
+
const t = new m.Sphere(
|
|
1540
|
+
new m.Vector3(e.x, e.y, e.z),
|
|
1539
1541
|
s
|
|
1540
1542
|
);
|
|
1541
1543
|
return this.frustum.intersectsSphere(t);
|
|
@@ -1545,15 +1547,15 @@ class Et {
|
|
|
1545
1547
|
*/
|
|
1546
1548
|
isLineVisible(e, s) {
|
|
1547
1549
|
if (!this.enabled) return !0;
|
|
1548
|
-
const t = new
|
|
1549
|
-
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(
|
|
1550
|
+
const t = new m.Vector3(e.x, e.y, e.z), o = new m.Vector3(s.x, s.y, s.z);
|
|
1551
|
+
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(o))
|
|
1550
1552
|
return !0;
|
|
1551
|
-
const
|
|
1553
|
+
const i = new m.Vector3(
|
|
1552
1554
|
(e.x + s.x) / 2,
|
|
1553
1555
|
(e.y + s.y) / 2,
|
|
1554
1556
|
(e.z + s.z) / 2
|
|
1555
|
-
), a =
|
|
1556
|
-
return this.frustum.intersectsSphere(
|
|
1557
|
+
), a = i.distanceTo(t), r = new m.Sphere(i, a);
|
|
1558
|
+
return this.frustum.intersectsSphere(r);
|
|
1557
1559
|
}
|
|
1558
1560
|
/**
|
|
1559
1561
|
* Enables/disables frustum culling
|
|
@@ -1564,18 +1566,19 @@ class Et {
|
|
|
1564
1566
|
}
|
|
1565
1567
|
class Ct {
|
|
1566
1568
|
constructor(e, s) {
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1569
|
+
l(this, "sceneManager");
|
|
1570
|
+
l(this, "raycaster");
|
|
1571
|
+
l(this, "mouse");
|
|
1572
|
+
l(this, "container");
|
|
1573
|
+
l(this, "onNodeClick", null);
|
|
1574
|
+
l(this, "onNodeHover", null);
|
|
1575
|
+
l(this, "onEdgeHover", null);
|
|
1576
|
+
l(this, "onEdgeClick", null);
|
|
1577
|
+
l(this, "hoveredNodeId", null);
|
|
1578
|
+
l(this, "hoveredEdgeKey", null);
|
|
1579
|
+
l(this, "nodeObjects", []);
|
|
1580
|
+
l(this, "edgeObjects", []);
|
|
1581
|
+
this.sceneManager = e, this.container = s, this.raycaster = new m.Raycaster(), this.raycaster.params.Line = { threshold: 0.5 }, this.mouse = new m.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
|
|
1579
1582
|
}
|
|
1580
1583
|
/**
|
|
1581
1584
|
* Updates the list of node objects to raycast against
|
|
@@ -1607,12 +1610,23 @@ class Ct {
|
|
|
1607
1610
|
setEdgeHoverCallback(e) {
|
|
1608
1611
|
this.onEdgeHover = e;
|
|
1609
1612
|
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Sets the edge click callback
|
|
1615
|
+
*/
|
|
1616
|
+
setEdgeClickCallback(e) {
|
|
1617
|
+
this.onEdgeClick = e;
|
|
1618
|
+
}
|
|
1610
1619
|
/**
|
|
1611
1620
|
* Handles click events
|
|
1612
1621
|
*/
|
|
1613
1622
|
handleClick(e) {
|
|
1614
1623
|
const s = this.getIntersectedNode(e);
|
|
1615
|
-
s && this.onNodeClick
|
|
1624
|
+
if (s && this.onNodeClick) {
|
|
1625
|
+
this.onNodeClick(s);
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
const t = this.getIntersectedEdge(e);
|
|
1629
|
+
t && this.onEdgeClick && this.onEdgeClick(t);
|
|
1616
1630
|
}
|
|
1617
1631
|
/**
|
|
1618
1632
|
* Handles mouse move events for hover detection
|
|
@@ -1623,23 +1637,23 @@ class Ct {
|
|
|
1623
1637
|
this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
|
|
1624
1638
|
return;
|
|
1625
1639
|
}
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1640
|
+
const o = this.getIntersectedEdge(e), i = o ? `${o.edge.source}-${o.edge.target}` : null;
|
|
1641
|
+
i !== this.hoveredEdgeKey && (this.hoveredEdgeKey = i, this.onEdgeHover && this.onEdgeHover(o)), this.container.style.cursor = o ? "pointer" : "default";
|
|
1628
1642
|
}
|
|
1629
1643
|
/**
|
|
1630
1644
|
* Gets the intersected node from a mouse event
|
|
1631
1645
|
*/
|
|
1632
1646
|
getIntersectedNode(e) {
|
|
1633
|
-
var
|
|
1647
|
+
var o;
|
|
1634
1648
|
const s = this.container.getBoundingClientRect();
|
|
1635
1649
|
this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1636
1650
|
const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1637
1651
|
if (t.length > 0) {
|
|
1638
|
-
let
|
|
1639
|
-
for (;
|
|
1640
|
-
if ((
|
|
1641
|
-
return
|
|
1642
|
-
|
|
1652
|
+
let i = t[0].object;
|
|
1653
|
+
for (; i; ) {
|
|
1654
|
+
if ((o = i.userData) != null && o.nodeData)
|
|
1655
|
+
return i.userData.nodeData;
|
|
1656
|
+
i = i.parent;
|
|
1643
1657
|
}
|
|
1644
1658
|
}
|
|
1645
1659
|
return null;
|
|
@@ -1652,13 +1666,13 @@ class Ct {
|
|
|
1652
1666
|
this.mouse.x = (e.clientX - s.left) / s.width * 2 - 1, this.mouse.y = -((e.clientY - s.top) / s.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1653
1667
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1654
1668
|
if (t.length > 0) {
|
|
1655
|
-
const
|
|
1656
|
-
if (
|
|
1669
|
+
const o = t[0].object, i = o.userData;
|
|
1670
|
+
if (i != null && i.edge && (i != null && i.sourceNode) && (i != null && i.targetNode))
|
|
1657
1671
|
return {
|
|
1658
|
-
edge:
|
|
1659
|
-
sourceNode:
|
|
1660
|
-
targetNode:
|
|
1661
|
-
edgeLine:
|
|
1672
|
+
edge: i.edge,
|
|
1673
|
+
sourceNode: i.sourceNode,
|
|
1674
|
+
targetNode: i.targetNode,
|
|
1675
|
+
edgeLine: o
|
|
1662
1676
|
};
|
|
1663
1677
|
}
|
|
1664
1678
|
return null;
|
|
@@ -1667,14 +1681,14 @@ class Ct {
|
|
|
1667
1681
|
* Performs a raycast and returns the intersected node ID
|
|
1668
1682
|
*/
|
|
1669
1683
|
getIntersectedNodeId(e, s) {
|
|
1670
|
-
var
|
|
1684
|
+
var i;
|
|
1671
1685
|
const t = this.container.getBoundingClientRect();
|
|
1672
1686
|
this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((s - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
|
|
1673
|
-
const
|
|
1674
|
-
if (
|
|
1675
|
-
let a =
|
|
1687
|
+
const o = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1688
|
+
if (o.length > 0) {
|
|
1689
|
+
let a = o[0].object;
|
|
1676
1690
|
for (; a; ) {
|
|
1677
|
-
if ((
|
|
1691
|
+
if ((i = a.userData) != null && i.nodeId)
|
|
1678
1692
|
return a.userData.nodeId;
|
|
1679
1693
|
a = a.parent;
|
|
1680
1694
|
}
|
|
@@ -1690,13 +1704,13 @@ class Ct {
|
|
|
1690
1704
|
}
|
|
1691
1705
|
class Nt {
|
|
1692
1706
|
constructor(e) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1707
|
+
l(this, "container");
|
|
1708
|
+
l(this, "panel", null);
|
|
1709
|
+
l(this, "currentNodeId", null);
|
|
1710
|
+
l(this, "visible", !1);
|
|
1711
|
+
l(this, "panelTemplate", null);
|
|
1712
|
+
l(this, "panelStyles", {});
|
|
1713
|
+
l(this, "onExpand", null);
|
|
1700
1714
|
this.container = e, this.createPanel();
|
|
1701
1715
|
}
|
|
1702
1716
|
/**
|
|
@@ -1758,11 +1772,11 @@ class Nt {
|
|
|
1758
1772
|
this.currentNodeId = e.id;
|
|
1759
1773
|
let t;
|
|
1760
1774
|
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
1761
|
-
const
|
|
1762
|
-
|
|
1775
|
+
const o = this.panel.querySelector('[data-action="expand"]'), i = this.panel.querySelector("[data-depth-select]");
|
|
1776
|
+
o && this.onExpand && o.addEventListener("click", () => {
|
|
1763
1777
|
if (this.currentNodeId) {
|
|
1764
|
-
const
|
|
1765
|
-
this.onExpand(this.currentNodeId,
|
|
1778
|
+
const r = i ? parseInt(i.value, 10) : 1;
|
|
1779
|
+
this.onExpand(this.currentNodeId, r);
|
|
1766
1780
|
}
|
|
1767
1781
|
});
|
|
1768
1782
|
const a = this.panel.querySelector('[data-action="close"]');
|
|
@@ -1920,7 +1934,7 @@ class Nt {
|
|
|
1920
1934
|
<div class="neighbors-section">
|
|
1921
1935
|
<div class="neighbors-title">Connected To</div>
|
|
1922
1936
|
${s.slice(0, 5).map(
|
|
1923
|
-
(
|
|
1937
|
+
(o) => `<span class="neighbor-chip">${this.escapeHtml(o.label)}</span>`
|
|
1924
1938
|
).join("")}
|
|
1925
1939
|
${s.length > 5 ? `<span class="neighbor-chip">+${s.length - 5} more</span>` : ""}
|
|
1926
1940
|
</div>
|
|
@@ -1975,9 +1989,254 @@ class Nt {
|
|
|
1975
1989
|
}
|
|
1976
1990
|
}
|
|
1977
1991
|
class St {
|
|
1992
|
+
constructor(e) {
|
|
1993
|
+
l(this, "container");
|
|
1994
|
+
l(this, "panel", null);
|
|
1995
|
+
l(this, "currentEdgeKey", null);
|
|
1996
|
+
l(this, "visible", !1);
|
|
1997
|
+
l(this, "panelTemplate", null);
|
|
1998
|
+
l(this, "onClose", null);
|
|
1999
|
+
l(this, "onNodeClick", null);
|
|
2000
|
+
this.container = e, this.createPanel();
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Creates the panel element
|
|
2004
|
+
*/
|
|
2005
|
+
createPanel() {
|
|
2006
|
+
this.panel = document.createElement("div"), this.panel.className = "force-graph-edge-panel", this.panel.style.cssText = `
|
|
2007
|
+
position: absolute;
|
|
2008
|
+
right: 20px;
|
|
2009
|
+
top: 50%;
|
|
2010
|
+
transform: translateY(-50%);
|
|
2011
|
+
width: 300px;
|
|
2012
|
+
max-height: 80vh;
|
|
2013
|
+
overflow-y: auto;
|
|
2014
|
+
background: rgba(15, 15, 25, 0.85);
|
|
2015
|
+
backdrop-filter: blur(20px);
|
|
2016
|
+
-webkit-backdrop-filter: blur(20px);
|
|
2017
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
2018
|
+
border-radius: 16px;
|
|
2019
|
+
padding: 24px;
|
|
2020
|
+
color: white;
|
|
2021
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
2022
|
+
box-shadow:
|
|
2023
|
+
0 8px 32px rgba(0, 0, 0, 0.4),
|
|
2024
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
2025
|
+
z-index: 1000;
|
|
2026
|
+
opacity: 0;
|
|
2027
|
+
pointer-events: none;
|
|
2028
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
2029
|
+
`, this.container.appendChild(this.panel);
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Sets the panel template function
|
|
2033
|
+
*/
|
|
2034
|
+
setPanelTemplate(e) {
|
|
2035
|
+
this.panelTemplate = e;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Sets the close callback
|
|
2039
|
+
*/
|
|
2040
|
+
setCloseCallback(e) {
|
|
2041
|
+
this.onClose = e;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Sets the node click callback (for navigating to nodes from edge panel)
|
|
2045
|
+
*/
|
|
2046
|
+
setNodeClickCallback(e) {
|
|
2047
|
+
this.onNodeClick = e;
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Shows the panel with edge/relationship information
|
|
2051
|
+
*/
|
|
2052
|
+
show(e, s, t) {
|
|
2053
|
+
if (!this.panel) return;
|
|
2054
|
+
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2055
|
+
let o;
|
|
2056
|
+
this.panelTemplate ? o = this.panelTemplate(e, s, t) : o = this.generateDefaultContent(e, s, t), this.panel.innerHTML = o;
|
|
2057
|
+
const i = this.panel.querySelector('[data-action="close"]');
|
|
2058
|
+
i && i.addEventListener("click", () => {
|
|
2059
|
+
this.hide(), this.onClose && this.onClose();
|
|
2060
|
+
});
|
|
2061
|
+
const a = this.panel.querySelector('[data-action="goto-source"]');
|
|
2062
|
+
a && this.onNodeClick && a.addEventListener("click", () => {
|
|
2063
|
+
this.onNodeClick && this.onNodeClick(e.source);
|
|
2064
|
+
});
|
|
2065
|
+
const r = this.panel.querySelector('[data-action="goto-target"]');
|
|
2066
|
+
r && this.onNodeClick && r.addEventListener("click", () => {
|
|
2067
|
+
this.onNodeClick && this.onNodeClick(e.target);
|
|
2068
|
+
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Generates default panel content
|
|
2072
|
+
*/
|
|
2073
|
+
generateDefaultContent(e, s, t) {
|
|
2074
|
+
const o = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", i = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = e.relationship || "connected to";
|
|
2075
|
+
return `
|
|
2076
|
+
<style>
|
|
2077
|
+
.force-graph-edge-panel .panel-header {
|
|
2078
|
+
display: flex;
|
|
2079
|
+
align-items: center;
|
|
2080
|
+
justify-content: space-between;
|
|
2081
|
+
margin-bottom: 20px;
|
|
2082
|
+
}
|
|
2083
|
+
.force-graph-edge-panel .panel-title {
|
|
2084
|
+
font-size: 14px;
|
|
2085
|
+
text-transform: uppercase;
|
|
2086
|
+
letter-spacing: 1px;
|
|
2087
|
+
color: rgba(255, 255, 255, 0.6);
|
|
2088
|
+
margin: 0;
|
|
2089
|
+
}
|
|
2090
|
+
.force-graph-edge-panel .relationship-section {
|
|
2091
|
+
background: rgba(255, 255, 255, 0.05);
|
|
2092
|
+
border-radius: 12px;
|
|
2093
|
+
padding: 16px;
|
|
2094
|
+
text-align: center;
|
|
2095
|
+
margin-bottom: 20px;
|
|
2096
|
+
}
|
|
2097
|
+
.force-graph-edge-panel .relationship-label {
|
|
2098
|
+
font-size: 18px;
|
|
2099
|
+
font-weight: 600;
|
|
2100
|
+
color: #a78bfa;
|
|
2101
|
+
letter-spacing: 0.5px;
|
|
2102
|
+
}
|
|
2103
|
+
.force-graph-edge-panel .node-card {
|
|
2104
|
+
background: rgba(255, 255, 255, 0.05);
|
|
2105
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
2106
|
+
border-radius: 12px;
|
|
2107
|
+
padding: 14px;
|
|
2108
|
+
margin-bottom: 12px;
|
|
2109
|
+
cursor: pointer;
|
|
2110
|
+
transition: all 0.2s ease;
|
|
2111
|
+
}
|
|
2112
|
+
.force-graph-edge-panel .node-card:hover {
|
|
2113
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2114
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
2115
|
+
transform: translateX(4px);
|
|
2116
|
+
}
|
|
2117
|
+
.force-graph-edge-panel .node-card-header {
|
|
2118
|
+
display: flex;
|
|
2119
|
+
align-items: center;
|
|
2120
|
+
gap: 10px;
|
|
2121
|
+
margin-bottom: 6px;
|
|
2122
|
+
}
|
|
2123
|
+
.force-graph-edge-panel .node-type {
|
|
2124
|
+
font-size: 10px;
|
|
2125
|
+
text-transform: uppercase;
|
|
2126
|
+
letter-spacing: 1px;
|
|
2127
|
+
color: rgba(255, 255, 255, 0.5);
|
|
2128
|
+
}
|
|
2129
|
+
.force-graph-edge-panel .color-dot {
|
|
2130
|
+
width: 10px;
|
|
2131
|
+
height: 10px;
|
|
2132
|
+
border-radius: 50%;
|
|
2133
|
+
flex-shrink: 0;
|
|
2134
|
+
}
|
|
2135
|
+
.force-graph-edge-panel .node-label {
|
|
2136
|
+
font-size: 15px;
|
|
2137
|
+
font-weight: 500;
|
|
2138
|
+
color: rgba(255, 255, 255, 0.95);
|
|
2139
|
+
white-space: nowrap;
|
|
2140
|
+
overflow: hidden;
|
|
2141
|
+
text-overflow: ellipsis;
|
|
2142
|
+
}
|
|
2143
|
+
.force-graph-edge-panel .connection-arrow {
|
|
2144
|
+
text-align: center;
|
|
2145
|
+
color: rgba(255, 255, 255, 0.4);
|
|
2146
|
+
font-size: 18px;
|
|
2147
|
+
margin: 8px 0;
|
|
2148
|
+
}
|
|
2149
|
+
.force-graph-edge-panel .btn-close {
|
|
2150
|
+
width: 100%;
|
|
2151
|
+
padding: 12px 16px;
|
|
2152
|
+
border: none;
|
|
2153
|
+
border-radius: 8px;
|
|
2154
|
+
font-size: 13px;
|
|
2155
|
+
font-weight: 500;
|
|
2156
|
+
cursor: pointer;
|
|
2157
|
+
transition: all 0.2s ease;
|
|
2158
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2159
|
+
color: rgba(255, 255, 255, 0.8);
|
|
2160
|
+
margin-top: 16px;
|
|
2161
|
+
}
|
|
2162
|
+
.force-graph-edge-panel .btn-close:hover {
|
|
2163
|
+
background: rgba(255, 255, 255, 0.15);
|
|
2164
|
+
}
|
|
2165
|
+
.force-graph-edge-panel .hint-text {
|
|
2166
|
+
font-size: 11px;
|
|
2167
|
+
color: rgba(255, 255, 255, 0.4);
|
|
2168
|
+
text-align: center;
|
|
2169
|
+
margin-top: 8px;
|
|
2170
|
+
}
|
|
2171
|
+
</style>
|
|
2172
|
+
|
|
2173
|
+
<div class="panel-header">
|
|
2174
|
+
<h3 class="panel-title">Relationship</h3>
|
|
2175
|
+
</div>
|
|
2176
|
+
|
|
2177
|
+
<div class="relationship-section">
|
|
2178
|
+
<span class="relationship-label">${this.escapeHtml(a)}</span>
|
|
2179
|
+
</div>
|
|
2180
|
+
|
|
2181
|
+
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2182
|
+
<div class="node-type">Source</div>
|
|
2183
|
+
<div class="node-card-header">
|
|
2184
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2185
|
+
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2186
|
+
</div>
|
|
2187
|
+
</div>
|
|
2188
|
+
|
|
2189
|
+
<div class="connection-arrow">↓</div>
|
|
2190
|
+
|
|
2191
|
+
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2192
|
+
<div class="node-type">Target</div>
|
|
2193
|
+
<div class="node-card-header">
|
|
2194
|
+
<span class="color-dot" style="background: ${i}; box-shadow: 0 0 8px ${i}80;"></span>
|
|
2195
|
+
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2196
|
+
</div>
|
|
2197
|
+
</div>
|
|
2198
|
+
|
|
2199
|
+
<p class="hint-text">Click on a node to focus on it</p>
|
|
2200
|
+
|
|
2201
|
+
<button class="btn-close" data-action="close">Close</button>
|
|
2202
|
+
`;
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Escapes HTML to prevent XSS
|
|
2206
|
+
*/
|
|
2207
|
+
escapeHtml(e) {
|
|
2208
|
+
const s = document.createElement("div");
|
|
2209
|
+
return s.textContent = e, s.innerHTML;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Hides the panel
|
|
2213
|
+
*/
|
|
2214
|
+
hide() {
|
|
2215
|
+
this.panel && (this.panel.style.opacity = "0", this.panel.style.pointerEvents = "none", this.panel.style.transform = "translateY(-50%) translateX(20px)", this.visible = !1, this.currentEdgeKey = null);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Checks if the panel is visible
|
|
2219
|
+
*/
|
|
2220
|
+
isVisible() {
|
|
2221
|
+
return this.visible;
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Gets the currently displayed edge key
|
|
2225
|
+
*/
|
|
2226
|
+
getCurrentEdgeKey() {
|
|
2227
|
+
return this.currentEdgeKey;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Dispose the panel
|
|
2231
|
+
*/
|
|
2232
|
+
dispose() {
|
|
2233
|
+
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
class zt {
|
|
1978
2237
|
constructor() {
|
|
1979
|
-
|
|
1980
|
-
|
|
2238
|
+
l(this, "tooltip", null);
|
|
2239
|
+
l(this, "visible", !1);
|
|
1981
2240
|
this.createTooltip(), this.setupMouseTracking();
|
|
1982
2241
|
}
|
|
1983
2242
|
/**
|
|
@@ -2020,14 +2279,14 @@ class St {
|
|
|
2020
2279
|
*/
|
|
2021
2280
|
positionTooltip(e, s) {
|
|
2022
2281
|
if (!this.tooltip) return;
|
|
2023
|
-
const t = this.tooltip.getBoundingClientRect(),
|
|
2024
|
-
let a = e + 15,
|
|
2025
|
-
a + t.width >
|
|
2282
|
+
const t = this.tooltip.getBoundingClientRect(), o = window.innerWidth, i = window.innerHeight;
|
|
2283
|
+
let a = e + 15, r = s + 15;
|
|
2284
|
+
a + t.width > o - 10 && (a = e - t.width - 15), r + t.height > i - 10 && (r = s - t.height - 15), a < 10 && (a = 10), r < 10 && (r = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${r}px`;
|
|
2026
2285
|
}
|
|
2027
2286
|
/**
|
|
2028
2287
|
* Shows the tooltip with edge info
|
|
2029
2288
|
*/
|
|
2030
|
-
show(e, s, t,
|
|
2289
|
+
show(e, s, t, o, i) {
|
|
2031
2290
|
if (!this.tooltip) return;
|
|
2032
2291
|
const a = e.relationship || "connected to";
|
|
2033
2292
|
this.tooltip.innerHTML = `
|
|
@@ -2042,7 +2301,7 @@ class St {
|
|
|
2042
2301
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
2043
2302
|
</div>
|
|
2044
2303
|
</div>
|
|
2045
|
-
`, this.positionTooltip(
|
|
2304
|
+
`, this.positionTooltip(o, i), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2046
2305
|
}
|
|
2047
2306
|
/**
|
|
2048
2307
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2076,16 +2335,16 @@ class St {
|
|
|
2076
2335
|
this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
|
|
2077
2336
|
}
|
|
2078
2337
|
}
|
|
2079
|
-
class
|
|
2338
|
+
class kt {
|
|
2080
2339
|
constructor(e, s) {
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2340
|
+
l(this, "container");
|
|
2341
|
+
l(this, "searchContainer", null);
|
|
2342
|
+
l(this, "searchInput", null);
|
|
2343
|
+
l(this, "searchResults", null);
|
|
2344
|
+
l(this, "searchTimeout", null);
|
|
2345
|
+
l(this, "placeholder");
|
|
2346
|
+
l(this, "onSearch");
|
|
2347
|
+
l(this, "onResultClick");
|
|
2089
2348
|
this.container = e, this.placeholder = s.placeholder || "Search nodes or relationships...", this.onSearch = s.onSearch, this.onResultClick = s.onResultClick, this.init();
|
|
2090
2349
|
}
|
|
2091
2350
|
init() {
|
|
@@ -2095,7 +2354,8 @@ class zt {
|
|
|
2095
2354
|
this.searchContainer = document.createElement("div"), this.searchContainer.className = "f3d-search-container", Object.assign(this.searchContainer.style, {
|
|
2096
2355
|
position: "absolute",
|
|
2097
2356
|
top: "20px",
|
|
2098
|
-
left: "
|
|
2357
|
+
left: "175px",
|
|
2358
|
+
// Adjusted to make room for view toggle
|
|
2099
2359
|
zIndex: "100",
|
|
2100
2360
|
width: "320px"
|
|
2101
2361
|
});
|
|
@@ -2218,25 +2478,25 @@ class zt {
|
|
|
2218
2478
|
this.searchResults.innerHTML = '<div class="f3d-no-results">No results found</div>', this.searchResults.style.display = "block";
|
|
2219
2479
|
return;
|
|
2220
2480
|
}
|
|
2221
|
-
let
|
|
2222
|
-
s.length > 0 && (
|
|
2223
|
-
const a =
|
|
2224
|
-
|
|
2225
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2226
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2481
|
+
let o = "";
|
|
2482
|
+
s.length > 0 && (o += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((i) => {
|
|
2483
|
+
const a = i.type || "Node";
|
|
2484
|
+
o += `
|
|
2485
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(i.id)}">
|
|
2486
|
+
<div class="f3d-result-label">${this.escapeHtml(i.label)}</div>
|
|
2227
2487
|
<div class="f3d-result-type">${this.escapeHtml(a)}</div>
|
|
2228
2488
|
</div>
|
|
2229
2489
|
`;
|
|
2230
|
-
}), s.length > 10 && (
|
|
2231
|
-
|
|
2232
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2233
|
-
<div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(
|
|
2234
|
-
<div class="f3d-result-relationship">${this.escapeHtml(
|
|
2490
|
+
}), s.length > 10 && (o += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (o += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: i, sourceNode: a, targetNode: r }) => {
|
|
2491
|
+
o += `
|
|
2492
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(i.source)}">
|
|
2493
|
+
<div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(r.label)}</div>
|
|
2494
|
+
<div class="f3d-result-relationship">${this.escapeHtml(i.relationship || "connected")}</div>
|
|
2235
2495
|
</div>
|
|
2236
2496
|
`;
|
|
2237
|
-
}), t.length > 5 && (
|
|
2238
|
-
|
|
2239
|
-
const a =
|
|
2497
|
+
}), t.length > 5 && (o += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = o, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((i) => {
|
|
2498
|
+
i.addEventListener("click", () => {
|
|
2499
|
+
const a = i.dataset.nodeId;
|
|
2240
2500
|
a && (this.onResultClick(a), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2241
2501
|
});
|
|
2242
2502
|
});
|
|
@@ -2249,50 +2509,494 @@ class zt {
|
|
|
2249
2509
|
this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
|
|
2250
2510
|
}
|
|
2251
2511
|
}
|
|
2252
|
-
class
|
|
2512
|
+
class Pt {
|
|
2513
|
+
constructor(e, s) {
|
|
2514
|
+
l(this, "container");
|
|
2515
|
+
l(this, "toggleContainer", null);
|
|
2516
|
+
l(this, "currentMode", "3d");
|
|
2517
|
+
l(this, "onViewChange");
|
|
2518
|
+
this.container = e, this.currentMode = s.initialMode || "3d", this.onViewChange = s.onViewChange, this.init();
|
|
2519
|
+
}
|
|
2520
|
+
init() {
|
|
2521
|
+
this.createToggleUI();
|
|
2522
|
+
}
|
|
2523
|
+
createToggleUI() {
|
|
2524
|
+
this.toggleContainer = document.createElement("div"), this.toggleContainer.className = "f3d-view-toggle", Object.assign(this.toggleContainer.style, {
|
|
2525
|
+
position: "absolute",
|
|
2526
|
+
top: "20px",
|
|
2527
|
+
left: "20px",
|
|
2528
|
+
zIndex: "100",
|
|
2529
|
+
display: "flex",
|
|
2530
|
+
gap: "0",
|
|
2531
|
+
background: "rgba(255, 255, 255, 0.08)",
|
|
2532
|
+
backdropFilter: "blur(20px)",
|
|
2533
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
2534
|
+
border: "1px solid rgba(255, 255, 255, 0.12)",
|
|
2535
|
+
borderRadius: "12px",
|
|
2536
|
+
padding: "5px",
|
|
2537
|
+
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.3)"
|
|
2538
|
+
});
|
|
2539
|
+
const e = this.createButton("2D", "2d"), s = this.createButton("3D", "3d");
|
|
2540
|
+
this.toggleContainer.appendChild(e), this.toggleContainer.appendChild(s), this.container.appendChild(this.toggleContainer), this.updateButtonStates();
|
|
2541
|
+
const t = document.createElement("style");
|
|
2542
|
+
t.textContent = `
|
|
2543
|
+
.f3d-view-btn {
|
|
2544
|
+
padding: 8px 16px;
|
|
2545
|
+
background: transparent;
|
|
2546
|
+
border: none;
|
|
2547
|
+
color: rgba(255, 255, 255, 0.5);
|
|
2548
|
+
font-size: 12px;
|
|
2549
|
+
font-weight: 600;
|
|
2550
|
+
font-family: inherit;
|
|
2551
|
+
letter-spacing: 0.5px;
|
|
2552
|
+
cursor: pointer;
|
|
2553
|
+
transition: all 0.2s ease;
|
|
2554
|
+
border-radius: 8px;
|
|
2555
|
+
min-width: 44px;
|
|
2556
|
+
}
|
|
2557
|
+
.f3d-view-btn:hover {
|
|
2558
|
+
color: rgba(255, 255, 255, 0.8);
|
|
2559
|
+
}
|
|
2560
|
+
.f3d-view-btn.active {
|
|
2561
|
+
background: linear-gradient(135deg, rgba(255, 153, 102, 0.3), rgba(255, 102, 153, 0.2));
|
|
2562
|
+
color: white;
|
|
2563
|
+
box-shadow: 0 0 15px rgba(255, 153, 102, 0.2);
|
|
2564
|
+
}
|
|
2565
|
+
.f3d-view-btn:focus {
|
|
2566
|
+
outline: none;
|
|
2567
|
+
}
|
|
2568
|
+
`, document.head.appendChild(t);
|
|
2569
|
+
}
|
|
2570
|
+
createButton(e, s) {
|
|
2571
|
+
const t = document.createElement("button");
|
|
2572
|
+
return t.className = "f3d-view-btn", t.dataset.mode = s, t.innerHTML = this.getIcon(s) + `<span style="margin-left: 4px;">${e}</span>`, t.style.display = "flex", t.style.alignItems = "center", t.style.justifyContent = "center", t.addEventListener("click", () => {
|
|
2573
|
+
this.currentMode !== s && (this.currentMode = s, this.updateButtonStates(), this.onViewChange(s));
|
|
2574
|
+
}), t;
|
|
2575
|
+
}
|
|
2576
|
+
getIcon(e) {
|
|
2577
|
+
return e === "2d" ? `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2578
|
+
<rect x="3" y="3" width="7" height="7"></rect>
|
|
2579
|
+
<rect x="14" y="3" width="7" height="7"></rect>
|
|
2580
|
+
<rect x="14" y="14" width="7" height="7"></rect>
|
|
2581
|
+
<rect x="3" y="14" width="7" height="7"></rect>
|
|
2582
|
+
</svg>` : `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2583
|
+
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
|
|
2584
|
+
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
|
2585
|
+
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
|
2586
|
+
</svg>`;
|
|
2587
|
+
}
|
|
2588
|
+
updateButtonStates() {
|
|
2589
|
+
if (!this.toggleContainer) return;
|
|
2590
|
+
this.toggleContainer.querySelectorAll(".f3d-view-btn").forEach((s) => {
|
|
2591
|
+
s.dataset.mode === this.currentMode ? s.classList.add("active") : s.classList.remove("active");
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
setMode(e) {
|
|
2595
|
+
this.currentMode !== e && (this.currentMode = e, this.updateButtonStates());
|
|
2596
|
+
}
|
|
2597
|
+
getMode() {
|
|
2598
|
+
return this.currentMode;
|
|
2599
|
+
}
|
|
2600
|
+
dispose() {
|
|
2601
|
+
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
const Tt = {
|
|
2605
|
+
backgroundColor: "#0a0a0a",
|
|
2606
|
+
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
2607
|
+
nodeRadius: 24,
|
|
2608
|
+
edgeColor: "rgba(255, 255, 255, 0.15)",
|
|
2609
|
+
edgeOpacity: 0.4,
|
|
2610
|
+
repulsionStrength: 500,
|
|
2611
|
+
// Full repulsion
|
|
2612
|
+
attractionStrength: 1e-3,
|
|
2613
|
+
// Very low attraction
|
|
2614
|
+
damping: 0.85
|
|
2615
|
+
// Fast energy dissipation
|
|
2616
|
+
};
|
|
2617
|
+
class Lt {
|
|
2618
|
+
constructor(e, s = {}) {
|
|
2619
|
+
l(this, "container");
|
|
2620
|
+
l(this, "canvas");
|
|
2621
|
+
l(this, "ctx");
|
|
2622
|
+
l(this, "options");
|
|
2623
|
+
// Data
|
|
2624
|
+
l(this, "nodes", /* @__PURE__ */ new Map());
|
|
2625
|
+
l(this, "edges", []);
|
|
2626
|
+
l(this, "nodeIdToIndex", /* @__PURE__ */ new Map());
|
|
2627
|
+
// Transform state
|
|
2628
|
+
l(this, "transform", { x: 0, y: 0, scale: 1 });
|
|
2629
|
+
// Interaction state
|
|
2630
|
+
l(this, "isDragging", !1);
|
|
2631
|
+
l(this, "isPanning", !1);
|
|
2632
|
+
l(this, "draggedNode", null);
|
|
2633
|
+
l(this, "hoveredNode", null);
|
|
2634
|
+
l(this, "hoveredEdge", null);
|
|
2635
|
+
l(this, "lastMousePos", { x: 0, y: 0 });
|
|
2636
|
+
l(this, "dragStartPos", { x: 0, y: 0 });
|
|
2637
|
+
// Track initial click position
|
|
2638
|
+
l(this, "selectedNode", null);
|
|
2639
|
+
// Animation
|
|
2640
|
+
l(this, "animationId", null);
|
|
2641
|
+
l(this, "isSimulating", !0);
|
|
2642
|
+
// Resize handler
|
|
2643
|
+
l(this, "resizeHandler");
|
|
2644
|
+
this.container = e, this.options = { ...Tt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
|
|
2645
|
+
const t = this.canvas.getContext("2d");
|
|
2646
|
+
if (!t)
|
|
2647
|
+
throw new Error("Failed to get 2D context");
|
|
2648
|
+
this.ctx = t, this.resizeHandler = this.handleResize.bind(this), window.addEventListener("resize", this.resizeHandler), this.handleResize(), this.transform.x = this.canvas.width / 2, this.transform.y = this.canvas.height / 2, this.setupInteractions(), this.startAnimation();
|
|
2649
|
+
}
|
|
2650
|
+
handleResize() {
|
|
2651
|
+
const e = this.container.getBoundingClientRect(), s = window.devicePixelRatio || 1;
|
|
2652
|
+
this.canvas.width = e.width * s, this.canvas.height = e.height * s, this.ctx.scale(s, s);
|
|
2653
|
+
}
|
|
2654
|
+
setupInteractions() {
|
|
2655
|
+
this.canvas.addEventListener("wheel", (e) => {
|
|
2656
|
+
e.preventDefault();
|
|
2657
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = e.deltaY > 0 ? 0.9 : 1.1, a = Math.max(0.1, Math.min(5, this.transform.scale * i)), r = a / this.transform.scale;
|
|
2658
|
+
this.transform.x = t - (t - this.transform.x) * r, this.transform.y = o - (o - this.transform.y) * r, this.transform.scale = a;
|
|
2659
|
+
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2660
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = this.screenToWorld(t, o), a = this.findNodeAt(i.x, i.y);
|
|
2661
|
+
this.dragStartPos = { x: e.clientX, y: e.clientY }, a ? (this.isDragging = !0, this.draggedNode = a, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2662
|
+
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2663
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = this.screenToWorld(t, o);
|
|
2664
|
+
if (this.isDragging && this.draggedNode)
|
|
2665
|
+
this.draggedNode.x = i.x, this.draggedNode.y = i.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2666
|
+
else if (this.isPanning) {
|
|
2667
|
+
const a = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
|
|
2668
|
+
this.transform.x += a, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2669
|
+
} else {
|
|
2670
|
+
const a = this.findNodeAt(i.x, i.y);
|
|
2671
|
+
if (a !== this.hoveredNode && (this.hoveredNode = a, a ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(a.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !a) {
|
|
2672
|
+
const r = this.findEdgeAt(i.x, i.y);
|
|
2673
|
+
r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}), this.canvas.addEventListener("mouseup", (e) => {
|
|
2677
|
+
const s = Math.abs(e.clientX - this.dragStartPos.x), t = Math.abs(e.clientY - this.dragStartPos.y), o = s < 5 && t < 5;
|
|
2678
|
+
if (this.isDragging && this.draggedNode)
|
|
2679
|
+
o && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
2680
|
+
else if (o && !this.isPanning) {
|
|
2681
|
+
const i = this.canvas.getBoundingClientRect(), a = this.screenToWorld(e.clientX - i.left, e.clientY - i.top), r = this.findEdgeAt(a.x, a.y);
|
|
2682
|
+
r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
|
|
2683
|
+
}
|
|
2684
|
+
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
2685
|
+
}), this.canvas.addEventListener("mouseleave", () => {
|
|
2686
|
+
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.hoveredNode = null, this.canvas.style.cursor = "grab";
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
screenToWorld(e, s) {
|
|
2690
|
+
return {
|
|
2691
|
+
x: (e - this.transform.x) / this.transform.scale,
|
|
2692
|
+
y: (s - this.transform.y) / this.transform.scale
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
findNodeAt(e, s) {
|
|
2696
|
+
for (const t of this.nodes.values()) {
|
|
2697
|
+
const o = t.x - e, i = t.y - s;
|
|
2698
|
+
if (Math.sqrt(o * o + i * i) < t.radius)
|
|
2699
|
+
return t;
|
|
2700
|
+
}
|
|
2701
|
+
return null;
|
|
2702
|
+
}
|
|
2703
|
+
findEdgeAt(e, s) {
|
|
2704
|
+
for (const o of this.edges) {
|
|
2705
|
+
const i = this.nodes.get(o.source), a = this.nodes.get(o.target);
|
|
2706
|
+
if (!i || !a) continue;
|
|
2707
|
+
const r = a.x - i.x, c = a.y - i.y, d = r * r + c * c;
|
|
2708
|
+
if (d === 0) continue;
|
|
2709
|
+
const g = Math.max(0, Math.min(1, ((e - i.x) * r + (s - i.y) * c) / d)), u = i.x + g * r, y = i.y + g * c, f = e - u, x = s - y;
|
|
2710
|
+
if (Math.sqrt(f * f + x * x) < 8)
|
|
2711
|
+
return o;
|
|
2712
|
+
}
|
|
2713
|
+
return null;
|
|
2714
|
+
}
|
|
2715
|
+
startAnimation() {
|
|
2716
|
+
const e = () => {
|
|
2717
|
+
this.isSimulating && this.simulate(), this.render(), this.animationId = requestAnimationFrame(e);
|
|
2718
|
+
};
|
|
2719
|
+
e();
|
|
2720
|
+
}
|
|
2721
|
+
simulate() {
|
|
2722
|
+
const e = Array.from(this.nodes.values()), s = e.length;
|
|
2723
|
+
if (s === 0) return;
|
|
2724
|
+
const t = 60, o = 5;
|
|
2725
|
+
let i = 0;
|
|
2726
|
+
for (let r = 0; r < s; r++)
|
|
2727
|
+
for (let c = r + 1; c < s; c++) {
|
|
2728
|
+
const d = e[r], g = e[c];
|
|
2729
|
+
let u = g.x - d.x, y = g.y - d.y, f = Math.sqrt(u * u + y * y);
|
|
2730
|
+
if (f < t * 3) {
|
|
2731
|
+
f < 1 && (f = 1);
|
|
2732
|
+
const x = this.options.repulsionStrength / (f * f), v = u / f * x, M = y / f * x;
|
|
2733
|
+
d.vx -= v, d.vy -= M, g.vx += v, g.vy += M;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
const a = 80;
|
|
2737
|
+
for (const r of this.edges) {
|
|
2738
|
+
const c = this.nodes.get(r.source), d = this.nodes.get(r.target);
|
|
2739
|
+
if (!c || !d) continue;
|
|
2740
|
+
let g = d.x - c.x, u = d.y - c.y, y = Math.sqrt(g * g + u * u);
|
|
2741
|
+
y < 1 && (y = 1);
|
|
2742
|
+
const x = (y - a) * this.options.attractionStrength, v = g / y * x, M = u / y * x;
|
|
2743
|
+
c.vx += v, c.vy += M, d.vx -= v, d.vy -= M;
|
|
2744
|
+
}
|
|
2745
|
+
for (const r of e) {
|
|
2746
|
+
if (this.draggedNode === r) continue;
|
|
2747
|
+
r.vx *= this.options.damping, r.vy *= this.options.damping;
|
|
2748
|
+
const c = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
|
|
2749
|
+
c > o && (r.vx = r.vx / c * o, r.vy = r.vy / c * o), r.x += r.vx, r.y += r.vy, i += r.vx * r.vx + r.vy * r.vy;
|
|
2750
|
+
}
|
|
2751
|
+
i < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
2752
|
+
}
|
|
2753
|
+
render() {
|
|
2754
|
+
const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
|
|
2755
|
+
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();
|
|
2756
|
+
}
|
|
2757
|
+
renderGrid(e, s) {
|
|
2758
|
+
const t = this.ctx, o = 40 * this.transform.scale, i = 1.5, a = this.transform.x % o, r = this.transform.y % o;
|
|
2759
|
+
t.fillStyle = this.options.gridColor;
|
|
2760
|
+
for (let c = a; c < e; c += o)
|
|
2761
|
+
for (let d = r; d < s; d += o)
|
|
2762
|
+
t.beginPath(), t.arc(c, d, i, 0, Math.PI * 2), t.fill();
|
|
2763
|
+
}
|
|
2764
|
+
renderEdges() {
|
|
2765
|
+
const e = this.ctx;
|
|
2766
|
+
for (const s of this.edges) {
|
|
2767
|
+
const t = this.nodes.get(s.source), o = this.nodes.get(s.target);
|
|
2768
|
+
if (!t || !o) continue;
|
|
2769
|
+
const i = e.createLinearGradient(t.x, t.y, o.x, o.y);
|
|
2770
|
+
i.addColorStop(0, "rgba(255, 153, 102, 0.3)"), i.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), i.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(o.x, o.y), e.strokeStyle = i, e.lineWidth = 1.5, e.stroke();
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
renderNodes() {
|
|
2774
|
+
const e = this.ctx;
|
|
2775
|
+
for (const s of this.nodes.values()) {
|
|
2776
|
+
const t = s === this.hoveredNode, o = s === this.selectedNode, i = s.radius * (t ? 1.1 : 1);
|
|
2777
|
+
if (t || o) {
|
|
2778
|
+
const f = e.createRadialGradient(
|
|
2779
|
+
s.x,
|
|
2780
|
+
s.y,
|
|
2781
|
+
i * 0.5,
|
|
2782
|
+
s.x,
|
|
2783
|
+
s.y,
|
|
2784
|
+
i * 2
|
|
2785
|
+
);
|
|
2786
|
+
f.addColorStop(0, "rgba(255, 153, 102, 0.4)"), f.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = f, e.beginPath(), e.arc(s.x, s.y, i * 2, 0, Math.PI * 2), e.fill();
|
|
2787
|
+
}
|
|
2788
|
+
const a = e.createRadialGradient(
|
|
2789
|
+
s.x - i * 0.3,
|
|
2790
|
+
s.y - i * 0.3,
|
|
2791
|
+
0,
|
|
2792
|
+
s.x,
|
|
2793
|
+
s.y,
|
|
2794
|
+
i
|
|
2795
|
+
), r = s.color >> 16 & 255, c = s.color >> 8 & 255, d = s.color & 255;
|
|
2796
|
+
a.addColorStop(0, `rgba(${Math.min(255, r + 60)}, ${Math.min(255, c + 60)}, ${Math.min(255, d + 60)}, 0.95)`), a.addColorStop(0.7, `rgba(${r}, ${c}, ${d}, 0.9)`), a.addColorStop(1, `rgba(${Math.max(0, r - 40)}, ${Math.max(0, c - 40)}, ${Math.max(0, d - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, i, 0, Math.PI * 2), e.fillStyle = a, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - i * 0.25, s.y - i * 0.25, i * 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";
|
|
2797
|
+
const g = i * 1.6;
|
|
2798
|
+
let u = s.label, y = e.measureText(u).width;
|
|
2799
|
+
if (y > g) {
|
|
2800
|
+
for (; y > g && u.length > 3; )
|
|
2801
|
+
u = u.slice(0, -1), y = e.measureText(u + "...").width;
|
|
2802
|
+
u += "...";
|
|
2803
|
+
}
|
|
2804
|
+
e.shadowColor = "rgba(0, 0, 0, 0.5)", e.shadowBlur = 3, e.fillText(u, s.x, s.y), e.shadowBlur = 0;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
/**
|
|
2808
|
+
* Sets the graph data
|
|
2809
|
+
*/
|
|
2810
|
+
setData(e) {
|
|
2811
|
+
this.nodes.clear(), this.edges = [], this.nodeIdToIndex.clear(), e.nodes.forEach((s, t) => {
|
|
2812
|
+
const o = s.position || {
|
|
2813
|
+
x: (Math.random() - 0.5) * 300,
|
|
2814
|
+
y: (Math.random() - 0.5) * 300
|
|
2815
|
+
}, i = {
|
|
2816
|
+
id: s.id,
|
|
2817
|
+
label: s.label,
|
|
2818
|
+
x: o.x,
|
|
2819
|
+
y: o.y,
|
|
2820
|
+
vx: 0,
|
|
2821
|
+
vy: 0,
|
|
2822
|
+
color: s.color || 16750950,
|
|
2823
|
+
// Match 3D tangerine color
|
|
2824
|
+
radius: this.options.nodeRadius,
|
|
2825
|
+
data: s
|
|
2826
|
+
};
|
|
2827
|
+
this.nodes.set(s.id, i), this.nodeIdToIndex.set(s.id, t);
|
|
2828
|
+
}), this.edges = e.edges.map((s) => ({
|
|
2829
|
+
source: s.source,
|
|
2830
|
+
target: s.target,
|
|
2831
|
+
relationship: s.relationship,
|
|
2832
|
+
data: s
|
|
2833
|
+
}));
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* Adds a node
|
|
2837
|
+
*/
|
|
2838
|
+
addNode(e) {
|
|
2839
|
+
const s = e.position || {
|
|
2840
|
+
x: (Math.random() - 0.5) * 300,
|
|
2841
|
+
y: (Math.random() - 0.5) * 300
|
|
2842
|
+
}, t = {
|
|
2843
|
+
id: e.id,
|
|
2844
|
+
label: e.label,
|
|
2845
|
+
x: s.x,
|
|
2846
|
+
y: s.y,
|
|
2847
|
+
vx: 0,
|
|
2848
|
+
vy: 0,
|
|
2849
|
+
color: e.color || 16750950,
|
|
2850
|
+
// Match 3D tangerine color
|
|
2851
|
+
radius: this.options.nodeRadius,
|
|
2852
|
+
data: e
|
|
2853
|
+
};
|
|
2854
|
+
this.nodes.set(e.id, t);
|
|
2855
|
+
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Adds an edge
|
|
2858
|
+
*/
|
|
2859
|
+
addEdge(e) {
|
|
2860
|
+
this.edges.push({
|
|
2861
|
+
source: e.source,
|
|
2862
|
+
target: e.target,
|
|
2863
|
+
relationship: e.relationship,
|
|
2864
|
+
data: e
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Removes a node
|
|
2869
|
+
*/
|
|
2870
|
+
removeNode(e) {
|
|
2871
|
+
this.nodes.delete(e), this.edges = this.edges.filter((s) => s.source !== e && s.target !== e);
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Gets a node by ID
|
|
2875
|
+
*/
|
|
2876
|
+
getNode(e) {
|
|
2877
|
+
return this.nodes.get(e);
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Gets all nodes
|
|
2881
|
+
*/
|
|
2882
|
+
getAllNodes() {
|
|
2883
|
+
return Array.from(this.nodes.values()).map((e) => e.data);
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Gets node count
|
|
2887
|
+
*/
|
|
2888
|
+
getNodeCount() {
|
|
2889
|
+
return this.nodes.size;
|
|
2890
|
+
}
|
|
2891
|
+
/**
|
|
2892
|
+
* Gets edge count
|
|
2893
|
+
*/
|
|
2894
|
+
getEdgeCount() {
|
|
2895
|
+
return this.edges.length;
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Focuses on a node
|
|
2899
|
+
*/
|
|
2900
|
+
focusOnNode(e) {
|
|
2901
|
+
const s = this.nodes.get(e);
|
|
2902
|
+
if (!s) return;
|
|
2903
|
+
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, o = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, i = this.transform.x, a = this.transform.y, r = 500, c = performance.now(), d = () => {
|
|
2904
|
+
const g = performance.now() - c, u = Math.min(g / r, 1), y = 1 - Math.pow(1 - u, 3);
|
|
2905
|
+
this.transform.x = i + (t - i) * y, this.transform.y = a + (o - a) * y, u < 1 ? requestAnimationFrame(d) : this.selectedNode = s;
|
|
2906
|
+
};
|
|
2907
|
+
d();
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* Updates node positions from 3D data
|
|
2911
|
+
*/
|
|
2912
|
+
syncFrom3D(e) {
|
|
2913
|
+
e.forEach((s, t) => {
|
|
2914
|
+
const o = this.nodes.get(t);
|
|
2915
|
+
o && (o.x = s.position.x * 3, o.y = s.position.y * 3, o.vx = 0, o.vy = 0);
|
|
2916
|
+
}), this.isSimulating = !1;
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Gets 2D positions for syncing to 3D
|
|
2920
|
+
*/
|
|
2921
|
+
getPositions() {
|
|
2922
|
+
const e = /* @__PURE__ */ new Map();
|
|
2923
|
+
return this.nodes.forEach((s, t) => {
|
|
2924
|
+
e.set(t, { x: s.x, y: s.y });
|
|
2925
|
+
}), e;
|
|
2926
|
+
}
|
|
2927
|
+
/**
|
|
2928
|
+
* Show/hide the canvas
|
|
2929
|
+
*/
|
|
2930
|
+
show() {
|
|
2931
|
+
this.canvas.style.display = "block";
|
|
2932
|
+
}
|
|
2933
|
+
hide() {
|
|
2934
|
+
this.canvas.style.display = "none";
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Updates physics parameters in real-time
|
|
2938
|
+
*/
|
|
2939
|
+
setPhysicsParams(e) {
|
|
2940
|
+
e.repulsionStrength !== void 0 && (this.options.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.options.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.options.damping = e.damping), this.isSimulating = !0;
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Dispose resources
|
|
2944
|
+
*/
|
|
2945
|
+
dispose() {
|
|
2946
|
+
this.animationId && cancelAnimationFrame(this.animationId), window.removeEventListener("resize", this.resizeHandler), this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
class Ft {
|
|
2950
|
+
// Store graph data for view switching
|
|
2253
2951
|
constructor(e, s = {}) {
|
|
2254
2952
|
// Options
|
|
2255
|
-
|
|
2256
|
-
|
|
2953
|
+
l(this, "options");
|
|
2954
|
+
l(this, "container");
|
|
2257
2955
|
// Core components
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2956
|
+
l(this, "sceneManager");
|
|
2957
|
+
l(this, "nodeManager");
|
|
2958
|
+
l(this, "edgeManager");
|
|
2959
|
+
l(this, "graphEngine");
|
|
2960
|
+
l(this, "rendererManager");
|
|
2263
2961
|
// Factories
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2962
|
+
l(this, "materialFactory");
|
|
2963
|
+
l(this, "nodeFactory");
|
|
2964
|
+
l(this, "edgeFactory");
|
|
2267
2965
|
// Performance
|
|
2268
|
-
|
|
2269
|
-
|
|
2966
|
+
l(this, "lodManager");
|
|
2967
|
+
l(this, "frustumCuller");
|
|
2270
2968
|
// Interaction
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2969
|
+
l(this, "raycasterManager");
|
|
2970
|
+
l(this, "panelManager");
|
|
2971
|
+
l(this, "edgePanelManager");
|
|
2972
|
+
l(this, "edgeTooltipManager");
|
|
2973
|
+
l(this, "searchManager", null);
|
|
2974
|
+
l(this, "viewToggleManager", null);
|
|
2975
|
+
// 2D Renderer
|
|
2976
|
+
l(this, "forceGraph2D", null);
|
|
2275
2977
|
// Event system
|
|
2276
|
-
|
|
2978
|
+
l(this, "eventCallbacks", /* @__PURE__ */ new Map());
|
|
2277
2979
|
// State
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
this
|
|
2980
|
+
l(this, "initialized", !1);
|
|
2981
|
+
l(this, "devControls", null);
|
|
2982
|
+
l(this, "viewMode", "3d");
|
|
2983
|
+
l(this, "graphData", null);
|
|
2984
|
+
this.options = { ...R, ...s }, this.container = ct(e), this.materialFactory = new bt(), this.nodeFactory = new vt(
|
|
2281
2985
|
this.materialFactory,
|
|
2282
|
-
this.options.nodeRadius ??
|
|
2283
|
-
this.options.lodSegments ??
|
|
2986
|
+
this.options.nodeRadius ?? R.nodeRadius,
|
|
2987
|
+
this.options.lodSegments ?? R.lodSegments
|
|
2284
2988
|
), this.edgeFactory = new Mt(
|
|
2285
2989
|
this.materialFactory,
|
|
2286
|
-
this.options.edgeColor ??
|
|
2287
|
-
this.options.edgeOpacity ??
|
|
2288
|
-
), this.sceneManager = new
|
|
2990
|
+
this.options.edgeColor ?? R.edgeColor,
|
|
2991
|
+
this.options.edgeOpacity ?? R.edgeOpacity
|
|
2992
|
+
), this.sceneManager = new ut(this.container, this.options), this.lodManager = new wt(
|
|
2289
2993
|
this.sceneManager.camera,
|
|
2290
|
-
this.options.lodDistances ??
|
|
2291
|
-
this.options.enableLOD ??
|
|
2994
|
+
this.options.lodDistances ?? R.lodDistances,
|
|
2995
|
+
this.options.enableLOD ?? R.enableLOD
|
|
2292
2996
|
), this.frustumCuller = new Et(
|
|
2293
2997
|
this.sceneManager.camera,
|
|
2294
|
-
this.options.enableEdgeCulling ??
|
|
2295
|
-
), this.nodeManager = new
|
|
2998
|
+
this.options.enableEdgeCulling ?? R.enableEdgeCulling
|
|
2999
|
+
), this.nodeManager = new ft(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Le(
|
|
2296
3000
|
this.nodeManager.getAllNodes(),
|
|
2297
3001
|
this.edgeManager.getAllEdges(),
|
|
2298
3002
|
{
|
|
@@ -2302,12 +3006,16 @@ class Ot {
|
|
|
2302
3006
|
useBarnesHut: this.options.useBarnesHut,
|
|
2303
3007
|
barnesHutTheta: this.options.barnesHutTheta
|
|
2304
3008
|
}
|
|
2305
|
-
), this.rendererManager = new
|
|
3009
|
+
), this.rendererManager = new xt(
|
|
2306
3010
|
this.sceneManager,
|
|
2307
3011
|
() => this.onSimulate(),
|
|
2308
3012
|
() => this.onRender(),
|
|
2309
|
-
this.options.targetFPS ??
|
|
2310
|
-
), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.
|
|
3013
|
+
this.options.targetFPS ?? R.targetFPS
|
|
3014
|
+
), 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) => {
|
|
3015
|
+
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3016
|
+
this.showNodePanel(t);
|
|
3017
|
+
}, 400);
|
|
3018
|
+
}), this.options.showSearch !== !1 && (this.searchManager = new kt(this.container, {
|
|
2311
3019
|
placeholder: this.options.searchPlaceholder,
|
|
2312
3020
|
onSearch: (t) => ({
|
|
2313
3021
|
nodeResults: this.searchNodes(t),
|
|
@@ -2318,6 +3026,11 @@ class Ot {
|
|
|
2318
3026
|
this.showNodePanel(t);
|
|
2319
3027
|
}, 400);
|
|
2320
3028
|
}
|
|
3029
|
+
})), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Pt(this.container, {
|
|
3030
|
+
initialMode: this.viewMode,
|
|
3031
|
+
onViewChange: (t) => {
|
|
3032
|
+
this.switchView(t);
|
|
3033
|
+
}
|
|
2321
3034
|
})), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
2322
3035
|
}
|
|
2323
3036
|
/**
|
|
@@ -2330,6 +3043,8 @@ class Ot {
|
|
|
2330
3043
|
this.expandNode(e, s);
|
|
2331
3044
|
}), this.options.panelTemplate && this.panelManager.setPanelTemplate(this.options.panelTemplate), this.options.panelStyles && this.panelManager.setPanelStyles(this.options.panelStyles), this.raycasterManager.setEdgeHoverCallback((e) => {
|
|
2332
3045
|
this.onEdgeHover(e);
|
|
3046
|
+
}), this.raycasterManager.setEdgeClickCallback((e) => {
|
|
3047
|
+
this.onEdgeClick(e);
|
|
2333
3048
|
});
|
|
2334
3049
|
}
|
|
2335
3050
|
/**
|
|
@@ -2338,8 +3053,8 @@ class Ot {
|
|
|
2338
3053
|
onEdgeHover(e) {
|
|
2339
3054
|
if (e) {
|
|
2340
3055
|
this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
|
|
2341
|
-
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2,
|
|
2342
|
-
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t,
|
|
3056
|
+
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, o = s.top + s.height / 2;
|
|
3057
|
+
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, o), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
|
|
2343
3058
|
} else
|
|
2344
3059
|
this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
|
|
2345
3060
|
}
|
|
@@ -2347,9 +3062,16 @@ class Ot {
|
|
|
2347
3062
|
* Handles node click
|
|
2348
3063
|
*/
|
|
2349
3064
|
onNodeClick(e) {
|
|
2350
|
-
|
|
3065
|
+
this.edgePanelManager.hide();
|
|
3066
|
+
const t = this.edgeManager.getNeighborIds(e.id).map((o) => this.nodeManager.getNode(o)).filter((o) => o !== void 0);
|
|
2351
3067
|
this.options.showPanel !== !1 && this.panelManager.show(e, t), this.options.onNodeClick && this.options.onNodeClick(e), this.emit("nodeClick", e);
|
|
2352
3068
|
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Handles edge click
|
|
3071
|
+
*/
|
|
3072
|
+
onEdgeClick(e) {
|
|
3073
|
+
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);
|
|
3074
|
+
}
|
|
2353
3075
|
/**
|
|
2354
3076
|
* Called every simulation step
|
|
2355
3077
|
*/
|
|
@@ -2375,13 +3097,13 @@ class Ot {
|
|
|
2375
3097
|
* Sets the graph data
|
|
2376
3098
|
*/
|
|
2377
3099
|
setData(e) {
|
|
2378
|
-
if (this.edgeManager.clear(), this.nodeManager.clear(), e.nodes && Array.isArray(e.nodes))
|
|
3100
|
+
if (this.graphData = e, this.edgeManager.clear(), this.nodeManager.clear(), e.nodes && Array.isArray(e.nodes))
|
|
2379
3101
|
for (const s of e.nodes)
|
|
2380
3102
|
this.addNode(s);
|
|
2381
3103
|
if (e.edges && Array.isArray(e.edges))
|
|
2382
3104
|
for (const s of e.edges)
|
|
2383
3105
|
this.addEdge(s);
|
|
2384
|
-
this.graphEngine = new
|
|
3106
|
+
this.graphEngine = new Le(
|
|
2385
3107
|
this.nodeManager.getAllNodes(),
|
|
2386
3108
|
this.edgeManager.getAllEdges(),
|
|
2387
3109
|
{
|
|
@@ -2391,17 +3113,17 @@ class Ot {
|
|
|
2391
3113
|
useBarnesHut: this.options.useBarnesHut,
|
|
2392
3114
|
barnesHutTheta: this.options.barnesHutTheta
|
|
2393
3115
|
}
|
|
2394
|
-
), this.graphEngine.restart();
|
|
3116
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(e);
|
|
2395
3117
|
}
|
|
2396
3118
|
/**
|
|
2397
3119
|
* Adds a node to the graph
|
|
2398
3120
|
* @returns true if added, false if node already exists or invalid
|
|
2399
3121
|
*/
|
|
2400
3122
|
addNode(e) {
|
|
2401
|
-
if (!
|
|
3123
|
+
if (!Oe(e))
|
|
2402
3124
|
return !1;
|
|
2403
3125
|
const s = this.nodeManager.addNode(e);
|
|
2404
|
-
return s && (this.graphEngine.restart(), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
|
|
3126
|
+
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
|
|
2405
3127
|
}
|
|
2406
3128
|
/**
|
|
2407
3129
|
* Removes a node from the graph
|
|
@@ -2428,7 +3150,7 @@ class Ot {
|
|
|
2428
3150
|
if (!Fe(e))
|
|
2429
3151
|
return !1;
|
|
2430
3152
|
const s = this.edgeManager.addEdge(e);
|
|
2431
|
-
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), s;
|
|
3153
|
+
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;
|
|
2432
3154
|
}
|
|
2433
3155
|
/**
|
|
2434
3156
|
* Removes an edge from the graph
|
|
@@ -2445,20 +3167,20 @@ class Ot {
|
|
|
2445
3167
|
* @param fetchFn - Optional fetch function to override the default
|
|
2446
3168
|
*/
|
|
2447
3169
|
async expandNode(e, s = 1, t) {
|
|
2448
|
-
const
|
|
2449
|
-
if (!
|
|
3170
|
+
const o = t ?? this.options.onExpand;
|
|
3171
|
+
if (!o)
|
|
2450
3172
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
2451
3173
|
try {
|
|
2452
|
-
const
|
|
2453
|
-
if (
|
|
2454
|
-
for (const a of
|
|
3174
|
+
const i = await o(e, s);
|
|
3175
|
+
if (i.nodes && Array.isArray(i.nodes))
|
|
3176
|
+
for (const a of i.nodes)
|
|
2455
3177
|
this.addNode(a);
|
|
2456
|
-
if (
|
|
2457
|
-
for (const a of
|
|
3178
|
+
if (i.edges && Array.isArray(i.edges))
|
|
3179
|
+
for (const a of i.edges)
|
|
2458
3180
|
this.addEdge(a);
|
|
2459
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
2460
|
-
} catch (
|
|
2461
|
-
return console.error("[ForceGraph3D] Error expanding node:",
|
|
3181
|
+
return this.panelManager.hide(), this.emit("expand", e, i), !0;
|
|
3182
|
+
} catch (i) {
|
|
3183
|
+
return console.error("[ForceGraph3D] Error expanding node:", i), !1;
|
|
2462
3184
|
}
|
|
2463
3185
|
}
|
|
2464
3186
|
/**
|
|
@@ -2496,20 +3218,48 @@ class Ot {
|
|
|
2496
3218
|
* Focuses the camera on a specific node with smooth animation
|
|
2497
3219
|
*/
|
|
2498
3220
|
focusOnNode(e, s = 30) {
|
|
3221
|
+
if (this.viewMode === "2d" && this.forceGraph2D) {
|
|
3222
|
+
this.forceGraph2D.focusOnNode(e);
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
2499
3225
|
const t = this.nodeManager.getNode(e);
|
|
2500
3226
|
if (!t) {
|
|
2501
3227
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
2502
3228
|
return;
|
|
2503
3229
|
}
|
|
2504
|
-
const
|
|
2505
|
-
x:
|
|
2506
|
-
y:
|
|
2507
|
-
z:
|
|
2508
|
-
},
|
|
2509
|
-
const
|
|
2510
|
-
|
|
3230
|
+
const o = t.position, i = this.sceneManager.camera, a = this.sceneManager.controls, r = i.position.clone().sub(a.target).normalize(), c = {
|
|
3231
|
+
x: o.x + r.x * s,
|
|
3232
|
+
y: o.y + r.y * s,
|
|
3233
|
+
z: o.z + r.z * s
|
|
3234
|
+
}, d = { x: i.position.x, y: i.position.y, z: i.position.z }, g = { x: a.target.x, y: a.target.y, z: a.target.z }, u = 800, y = performance.now(), f = () => {
|
|
3235
|
+
const x = performance.now() - y, v = Math.min(x / u, 1), M = 1 - Math.pow(1 - v, 3);
|
|
3236
|
+
i.position.x = d.x + (c.x - d.x) * M, i.position.y = d.y + (c.y - d.y) * M, i.position.z = d.z + (c.z - d.z) * M, a.target.x = g.x + (o.x - g.x) * M, a.target.y = g.y + (o.y - g.y) * M, a.target.z = g.z + (o.z - g.z) * M, a.update(), v < 1 && requestAnimationFrame(f);
|
|
3237
|
+
};
|
|
3238
|
+
f();
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* Focuses the camera on an edge (both source and target nodes) with smooth animation
|
|
3242
|
+
* Camera targets the midpoint and zooms out enough to see both nodes
|
|
3243
|
+
*/
|
|
3244
|
+
focusOnEdge(e, s, t = 1.5) {
|
|
3245
|
+
const o = this.nodeManager.getNode(e), i = this.nodeManager.getNode(s);
|
|
3246
|
+
if (!o || !i) {
|
|
3247
|
+
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
const a = this.sceneManager.camera, r = this.sceneManager.controls, c = {
|
|
3251
|
+
x: (o.position.x + i.position.x) / 2,
|
|
3252
|
+
y: (o.position.y + i.position.y) / 2,
|
|
3253
|
+
z: (o.position.z + i.position.z) / 2
|
|
3254
|
+
}, d = i.position.x - o.position.x, g = i.position.y - o.position.y, u = i.position.z - o.position.z, y = Math.sqrt(d * d + g * g + u * u), f = Math.max(y * t, 40), x = a.position.clone().sub(r.target).normalize(), v = {
|
|
3255
|
+
x: c.x + x.x * f,
|
|
3256
|
+
y: c.y + x.y * f,
|
|
3257
|
+
z: c.z + x.z * f
|
|
3258
|
+
}, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, T = performance.now(), Y = () => {
|
|
3259
|
+
const P = performance.now() - T, H = Math.min(P / O, 1), E = 1 - Math.pow(1 - H, 3);
|
|
3260
|
+
a.position.x = M.x + (v.x - M.x) * E, a.position.y = M.y + (v.y - M.y) * E, a.position.z = M.z + (v.z - M.z) * E, r.target.x = N.x + (c.x - N.x) * E, r.target.y = N.y + (c.y - N.y) * E, r.target.z = N.z + (c.z - N.z) * E, r.update(), H < 1 && requestAnimationFrame(Y);
|
|
2511
3261
|
};
|
|
2512
|
-
|
|
3262
|
+
Y();
|
|
2513
3263
|
}
|
|
2514
3264
|
/**
|
|
2515
3265
|
* Shows the info panel for a specific node
|
|
@@ -2530,28 +3280,28 @@ class Ot {
|
|
|
2530
3280
|
searchNodes(e) {
|
|
2531
3281
|
if (!e || e.trim() === "")
|
|
2532
3282
|
return [];
|
|
2533
|
-
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(),
|
|
2534
|
-
return t.forEach((
|
|
2535
|
-
var
|
|
2536
|
-
const a = (
|
|
2537
|
-
(a ||
|
|
2538
|
-
}),
|
|
3283
|
+
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), o = [];
|
|
3284
|
+
return t.forEach((i) => {
|
|
3285
|
+
var d, g, u;
|
|
3286
|
+
const a = (d = i.label) == null ? void 0 : d.toLowerCase().includes(s), r = (g = i.id) == null ? void 0 : g.toLowerCase().includes(s), c = (u = i.type) == null ? void 0 : u.toLowerCase().includes(s);
|
|
3287
|
+
(a || r || c) && o.push(i);
|
|
3288
|
+
}), o;
|
|
2539
3289
|
}
|
|
2540
3290
|
/**
|
|
2541
3291
|
* Searches edges by relationship (case-insensitive)
|
|
2542
3292
|
* @returns Array of matching edges with source/target node info
|
|
2543
3293
|
*/
|
|
2544
3294
|
searchEdges(e) {
|
|
2545
|
-
var
|
|
3295
|
+
var i;
|
|
2546
3296
|
if (!e || e.trim() === "")
|
|
2547
3297
|
return [];
|
|
2548
|
-
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(),
|
|
3298
|
+
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), o = [];
|
|
2549
3299
|
for (const a of t)
|
|
2550
|
-
if ((
|
|
2551
|
-
const
|
|
2552
|
-
|
|
3300
|
+
if ((i = a.relationship) == null ? void 0 : i.toLowerCase().includes(s)) {
|
|
3301
|
+
const c = this.nodeManager.getNode(a.source), d = this.nodeManager.getNode(a.target);
|
|
3302
|
+
c && d && o.push({ edge: a, sourceNode: c, targetNode: d });
|
|
2553
3303
|
}
|
|
2554
|
-
return
|
|
3304
|
+
return o;
|
|
2555
3305
|
}
|
|
2556
3306
|
/**
|
|
2557
3307
|
* Gets all nodes as an array
|
|
@@ -2572,6 +3322,44 @@ class Ot {
|
|
|
2572
3322
|
isInitialized() {
|
|
2573
3323
|
return this.initialized;
|
|
2574
3324
|
}
|
|
3325
|
+
/**
|
|
3326
|
+
* Gets the current view mode
|
|
3327
|
+
*/
|
|
3328
|
+
getViewMode() {
|
|
3329
|
+
return this.viewMode;
|
|
3330
|
+
}
|
|
3331
|
+
/**
|
|
3332
|
+
* Switches between 2D and 3D view modes
|
|
3333
|
+
*/
|
|
3334
|
+
switchView(e) {
|
|
3335
|
+
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 Lt(this.container, {
|
|
3336
|
+
backgroundColor: "#0a0a0a",
|
|
3337
|
+
nodeRadius: 24,
|
|
3338
|
+
onNodeClick: (s) => {
|
|
3339
|
+
this.onNodeClick(s);
|
|
3340
|
+
},
|
|
3341
|
+
onNodeHover: (s) => {
|
|
3342
|
+
this.options.onNodeHover && this.options.onNodeHover(s);
|
|
3343
|
+
},
|
|
3344
|
+
onEdgeHover: (s, t) => {
|
|
3345
|
+
if (s && t) {
|
|
3346
|
+
const o = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3347
|
+
o && i && this.edgeTooltipManager.show(
|
|
3348
|
+
s,
|
|
3349
|
+
o,
|
|
3350
|
+
i,
|
|
3351
|
+
t.clientX,
|
|
3352
|
+
t.clientY
|
|
3353
|
+
);
|
|
3354
|
+
} else
|
|
3355
|
+
this.edgeTooltipManager.hide();
|
|
3356
|
+
},
|
|
3357
|
+
onEdgeClick: (s) => {
|
|
3358
|
+
const t = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
|
|
3359
|
+
t && o && this.edgePanelManager.show(s, t, o);
|
|
3360
|
+
}
|
|
3361
|
+
}), 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));
|
|
3362
|
+
}
|
|
2575
3363
|
/**
|
|
2576
3364
|
* Registers an event listener
|
|
2577
3365
|
*/
|
|
@@ -2583,13 +3371,13 @@ class Ot {
|
|
|
2583
3371
|
*/
|
|
2584
3372
|
emit(e, ...s) {
|
|
2585
3373
|
const t = this.eventCallbacks.get(e);
|
|
2586
|
-
t && t.forEach((
|
|
3374
|
+
t && t.forEach((o) => o(...s));
|
|
2587
3375
|
}
|
|
2588
3376
|
/**
|
|
2589
|
-
* Sets physics parameters
|
|
3377
|
+
* Sets physics parameters for both 3D and 2D views
|
|
2590
3378
|
*/
|
|
2591
3379
|
setPhysicsParams(e) {
|
|
2592
|
-
this.graphEngine.setPhysicsParams(e), this.graphEngine.restart();
|
|
3380
|
+
this.graphEngine.setPhysicsParams(e), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setPhysicsParams(e);
|
|
2593
3381
|
}
|
|
2594
3382
|
/**
|
|
2595
3383
|
* Creates dev mode controls (only in development)
|
|
@@ -2660,27 +3448,27 @@ class Ot {
|
|
|
2660
3448
|
`, this.container.appendChild(this.devControls);
|
|
2661
3449
|
const e = this.devControls.querySelector("#dev-repulsion"), s = this.devControls.querySelector("#dev-attraction"), t = this.devControls.querySelector("#dev-damping");
|
|
2662
3450
|
e == null || e.addEventListener("input", () => {
|
|
2663
|
-
const
|
|
2664
|
-
this.setPhysicsParams({ repulsionStrength:
|
|
3451
|
+
const o = parseFloat(e.value);
|
|
3452
|
+
this.setPhysicsParams({ repulsionStrength: o }), this.devControls.querySelector("#dev-repulsion-val").textContent = o.toString();
|
|
2665
3453
|
}), s == null || s.addEventListener("input", () => {
|
|
2666
|
-
const
|
|
2667
|
-
this.setPhysicsParams({ attractionStrength:
|
|
3454
|
+
const o = parseFloat(s.value) / 1e3;
|
|
3455
|
+
this.setPhysicsParams({ attractionStrength: o }), this.devControls.querySelector("#dev-attraction-val").textContent = o.toFixed(3);
|
|
2668
3456
|
}), t == null || t.addEventListener("input", () => {
|
|
2669
|
-
const
|
|
2670
|
-
this.setPhysicsParams({ damping:
|
|
3457
|
+
const o = parseFloat(t.value) / 100;
|
|
3458
|
+
this.setPhysicsParams({ damping: o }), this.devControls.querySelector("#dev-damping-val").textContent = o.toFixed(2);
|
|
2671
3459
|
}), setInterval(() => {
|
|
2672
|
-
const
|
|
2673
|
-
|
|
3460
|
+
const o = this.devControls.querySelector("#dev-node-count"), i = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
|
|
3461
|
+
o && (o.textContent = this.getNodeCount().toString()), i && (i.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
|
|
2674
3462
|
}, 500);
|
|
2675
3463
|
}
|
|
2676
3464
|
/**
|
|
2677
3465
|
* Destroys the graph and releases all resources
|
|
2678
3466
|
*/
|
|
2679
3467
|
destroy() {
|
|
2680
|
-
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
3468
|
+
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;
|
|
2681
3469
|
}
|
|
2682
3470
|
}
|
|
2683
|
-
const
|
|
3471
|
+
const Ie = [
|
|
2684
3472
|
"Alpha",
|
|
2685
3473
|
"Beta",
|
|
2686
3474
|
"Gamma",
|
|
@@ -2711,7 +3499,7 @@ const ke = [
|
|
|
2711
3499
|
"Link",
|
|
2712
3500
|
"Point",
|
|
2713
3501
|
"Vertex"
|
|
2714
|
-
],
|
|
3502
|
+
], J = [
|
|
2715
3503
|
"connects to",
|
|
2716
3504
|
"links with",
|
|
2717
3505
|
"relates to",
|
|
@@ -2722,7 +3510,7 @@ const ke = [
|
|
|
2722
3510
|
"partners with",
|
|
2723
3511
|
"collaborates with",
|
|
2724
3512
|
"supports"
|
|
2725
|
-
],
|
|
3513
|
+
], Re = [
|
|
2726
3514
|
16777215,
|
|
2727
3515
|
// White
|
|
2728
3516
|
16750950,
|
|
@@ -2734,14 +3522,14 @@ const ke = [
|
|
|
2734
3522
|
16746564
|
|
2735
3523
|
// Darker tangerine
|
|
2736
3524
|
];
|
|
2737
|
-
function
|
|
3525
|
+
function Ht(h = 30) {
|
|
2738
3526
|
const e = [], s = [];
|
|
2739
|
-
for (let
|
|
2740
|
-
const
|
|
3527
|
+
for (let o = 0; o < h; o++) {
|
|
3528
|
+
const i = o < Ie.length ? Ie[o] : `Node ${o + 1}`;
|
|
2741
3529
|
e.push({
|
|
2742
|
-
id: `node-${
|
|
2743
|
-
label:
|
|
2744
|
-
color:
|
|
3530
|
+
id: `node-${o}`,
|
|
3531
|
+
label: i,
|
|
3532
|
+
color: Re[o % Re.length],
|
|
2745
3533
|
position: {
|
|
2746
3534
|
x: (Math.random() - 0.5) * 60,
|
|
2747
3535
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -2749,43 +3537,43 @@ function kt(c = 30) {
|
|
|
2749
3537
|
}
|
|
2750
3538
|
});
|
|
2751
3539
|
}
|
|
2752
|
-
for (let
|
|
2753
|
-
const
|
|
3540
|
+
for (let o = 1; o < h; o++) {
|
|
3541
|
+
const i = Math.floor(Math.random() * o);
|
|
2754
3542
|
s.push({
|
|
2755
|
-
source: `node-${
|
|
2756
|
-
target: `node-${
|
|
2757
|
-
relationship:
|
|
3543
|
+
source: `node-${o}`,
|
|
3544
|
+
target: `node-${i}`,
|
|
3545
|
+
relationship: J[Math.floor(Math.random() * J.length)]
|
|
2758
3546
|
});
|
|
2759
3547
|
}
|
|
2760
|
-
const t = Math.floor(
|
|
2761
|
-
for (let
|
|
2762
|
-
const
|
|
2763
|
-
let a = Math.floor(Math.random() *
|
|
2764
|
-
|
|
2765
|
-
const
|
|
3548
|
+
const t = Math.floor(h * 0.5);
|
|
3549
|
+
for (let o = 0; o < t; o++) {
|
|
3550
|
+
const i = Math.floor(Math.random() * h);
|
|
3551
|
+
let a = Math.floor(Math.random() * h);
|
|
3552
|
+
i === a && (a = (a + 1) % h);
|
|
3553
|
+
const r = `node-${i}`, c = `node-${a}`;
|
|
2766
3554
|
s.some(
|
|
2767
|
-
(g) => g.source ===
|
|
3555
|
+
(g) => g.source === r && g.target === c || g.source === c && g.target === r
|
|
2768
3556
|
) || s.push({
|
|
2769
|
-
source:
|
|
2770
|
-
target:
|
|
2771
|
-
relationship:
|
|
3557
|
+
source: r,
|
|
3558
|
+
target: c,
|
|
3559
|
+
relationship: J[Math.floor(Math.random() * J.length)]
|
|
2772
3560
|
});
|
|
2773
3561
|
}
|
|
2774
3562
|
return { nodes: e, edges: s };
|
|
2775
3563
|
}
|
|
2776
|
-
function
|
|
2777
|
-
const e = [], s = [], t = Math.ceil(
|
|
2778
|
-
for (let
|
|
2779
|
-
|
|
3564
|
+
function Dt(h = 1e3) {
|
|
3565
|
+
const e = [], s = [], t = Math.ceil(h / 50), o = [];
|
|
3566
|
+
for (let i = 0; i < t; i++)
|
|
3567
|
+
o.push({
|
|
2780
3568
|
x: (Math.random() - 0.5) * 200,
|
|
2781
3569
|
y: (Math.random() - 0.5) * 200,
|
|
2782
3570
|
z: (Math.random() - 0.5) * 200
|
|
2783
3571
|
});
|
|
2784
|
-
for (let
|
|
2785
|
-
const a = i
|
|
3572
|
+
for (let i = 0; i < h; i++) {
|
|
3573
|
+
const a = o[i % t];
|
|
2786
3574
|
e.push({
|
|
2787
|
-
id: `node-${
|
|
2788
|
-
label: `N${
|
|
3575
|
+
id: `node-${i}`,
|
|
3576
|
+
label: `N${i}`,
|
|
2789
3577
|
position: {
|
|
2790
3578
|
x: a.x + (Math.random() - 0.5) * 40,
|
|
2791
3579
|
y: a.y + (Math.random() - 0.5) * 40,
|
|
@@ -2793,32 +3581,32 @@ function It(c = 1e3) {
|
|
|
2793
3581
|
}
|
|
2794
3582
|
});
|
|
2795
3583
|
}
|
|
2796
|
-
for (let
|
|
2797
|
-
const a = Math.floor(
|
|
3584
|
+
for (let i = 1; i < h; i++) {
|
|
3585
|
+
const a = Math.floor(i / 50) * 50, r = a === 0 ? Math.floor(Math.random() * i) : a + Math.floor(Math.random() * Math.min(i - a, 50));
|
|
2798
3586
|
s.push({
|
|
2799
|
-
source: `node-${
|
|
2800
|
-
target: `node-${Math.min(
|
|
3587
|
+
source: `node-${i}`,
|
|
3588
|
+
target: `node-${Math.min(r, i - 1)}`,
|
|
2801
3589
|
relationship: "links to"
|
|
2802
3590
|
});
|
|
2803
3591
|
}
|
|
2804
|
-
for (let
|
|
2805
|
-
const a =
|
|
3592
|
+
for (let i = 1; i < t; i++) {
|
|
3593
|
+
const a = i * 50, r = (i - 1) * 50 + Math.floor(Math.random() * 50);
|
|
2806
3594
|
s.push({
|
|
2807
3595
|
source: `node-${a}`,
|
|
2808
|
-
target: `node-${
|
|
3596
|
+
target: `node-${r}`,
|
|
2809
3597
|
relationship: "bridges to"
|
|
2810
3598
|
});
|
|
2811
3599
|
}
|
|
2812
3600
|
return { nodes: e, edges: s };
|
|
2813
3601
|
}
|
|
2814
3602
|
export {
|
|
2815
|
-
|
|
2816
|
-
|
|
3603
|
+
R as DEFAULT_OPTIONS,
|
|
3604
|
+
Ft as ForceGraph3D,
|
|
2817
3605
|
U as LODLevel,
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
3606
|
+
D as createEdgeKey,
|
|
3607
|
+
Dt as generateLargeSampleData,
|
|
3608
|
+
Ht as generateSampleData,
|
|
2821
3609
|
Fe as validateEdgeData,
|
|
2822
|
-
|
|
3610
|
+
Oe as validateNodeData
|
|
2823
3611
|
};
|
|
2824
3612
|
//# sourceMappingURL=force-3d-graph.js.map
|