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