force-3d-graph 1.2.4 → 1.2.5
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 +666 -606
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +21 -21
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var l = (
|
|
4
|
-
import * as
|
|
5
|
-
import { EventDispatcher as
|
|
6
|
-
const
|
|
1
|
+
var ot = Object.defineProperty;
|
|
2
|
+
var nt = (d, e, s) => e in d ? ot(d, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : d[e] = s;
|
|
3
|
+
var l = (d, e, s) => nt(d, typeof e != "symbol" ? e + "" : e, s);
|
|
4
|
+
import * as y from "three";
|
|
5
|
+
import { EventDispatcher as at, Vector3 as S, MOUSE as $, TOUCH as G, Spherical as ze, Quaternion as Te, Vector2 as T, Ray as rt, Plane as lt, MathUtils as ct } from "three";
|
|
6
|
+
const P = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
9
9
|
cameraFov: 75,
|
|
@@ -31,53 +31,53 @@ const T = {
|
|
|
31
31
|
targetFPS: 60,
|
|
32
32
|
maxVisibleNodes: 1e4
|
|
33
33
|
};
|
|
34
|
-
var
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
return
|
|
34
|
+
var X = /* @__PURE__ */ ((d) => (d[d.HIGH = 0] = "HIGH", d[d.MEDIUM = 1] = "MEDIUM", d[d.LOW = 2] = "LOW", d))(X || {});
|
|
35
|
+
function ht() {
|
|
36
|
+
const d = document.createElement("div");
|
|
37
|
+
return d.id = "force-graph-3d-container", d.style.cssText = `
|
|
38
38
|
width: 100%;
|
|
39
39
|
height: 100%;
|
|
40
40
|
position: absolute;
|
|
41
41
|
top: 0;
|
|
42
42
|
left: 0;
|
|
43
43
|
overflow: hidden;
|
|
44
|
-
`, document.body.appendChild(
|
|
44
|
+
`, document.body.appendChild(d), d;
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
return
|
|
46
|
+
function dt(d) {
|
|
47
|
+
return d && d instanceof HTMLElement ? d : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ht());
|
|
48
48
|
}
|
|
49
|
-
function
|
|
50
|
-
const e =
|
|
49
|
+
function ke(d) {
|
|
50
|
+
const e = d.getBoundingClientRect();
|
|
51
51
|
return {
|
|
52
52
|
width: e.width || window.innerWidth,
|
|
53
53
|
height: e.height || window.innerHeight
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
if (!
|
|
56
|
+
function He(d) {
|
|
57
|
+
if (!d || typeof d != "object")
|
|
58
58
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
59
|
-
const e =
|
|
60
|
-
return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !
|
|
59
|
+
const e = d;
|
|
60
|
+
return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !gt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
if (!
|
|
62
|
+
function De(d) {
|
|
63
|
+
if (!d || typeof d != "object")
|
|
64
64
|
return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
|
|
65
|
-
const e =
|
|
65
|
+
const e = d;
|
|
66
66
|
return typeof e.source != "string" || e.source.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: source must be a non-empty string"), !1) : typeof e.target != "string" || e.target.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: target must be a non-empty string"), !1) : !0;
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
return typeof
|
|
68
|
+
function pt(d) {
|
|
69
|
+
return typeof d != "string" || d.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
|
|
70
70
|
}
|
|
71
|
-
function
|
|
72
|
-
if (!
|
|
73
|
-
const e =
|
|
71
|
+
function gt(d) {
|
|
72
|
+
if (!d || typeof d != "object") return !1;
|
|
73
|
+
const e = d;
|
|
74
74
|
return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
|
|
75
75
|
}
|
|
76
|
-
function D(
|
|
77
|
-
return
|
|
76
|
+
function D(d, e) {
|
|
77
|
+
return d === e ? `${d}-${e}` : d < e ? `${d}-${e}` : `${e}-${d}`;
|
|
78
78
|
}
|
|
79
|
-
const
|
|
80
|
-
class
|
|
79
|
+
const Pe = { type: "change" }, ce = { type: "start" }, Re = { type: "end" }, J = new rt(), Ie = new lt(), ut = Math.cos(70 * ct.DEG2RAD);
|
|
80
|
+
class ft extends at {
|
|
81
81
|
constructor(e, s) {
|
|
82
82
|
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: $.ROTATE, MIDDLE: $.DOLLY, RIGHT: $.PAN }, this.touches = { ONE: G.ROTATE, TWO: G.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
83
83
|
return r.phi;
|
|
@@ -86,43 +86,43 @@ class gt extends ot {
|
|
|
86
86
|
}, this.getDistance = function() {
|
|
87
87
|
return this.object.position.distanceTo(this.target);
|
|
88
88
|
}, this.listenToKeyEvents = function(n) {
|
|
89
|
-
n.addEventListener("keydown",
|
|
89
|
+
n.addEventListener("keydown", re), this._domElementKeyEvents = n;
|
|
90
90
|
}, this.stopListenToKeyEvents = function() {
|
|
91
|
-
this._domElementKeyEvents.removeEventListener("keydown",
|
|
91
|
+
this._domElementKeyEvents.removeEventListener("keydown", re), this._domElementKeyEvents = null;
|
|
92
92
|
}, this.saveState = function() {
|
|
93
93
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
94
94
|
}, this.reset = function() {
|
|
95
|
-
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(
|
|
95
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(), i = o.NONE;
|
|
96
96
|
}, this.update = function() {
|
|
97
|
-
const n = new S(), g = new
|
|
98
|
-
return function(
|
|
99
|
-
const
|
|
100
|
-
n.copy(
|
|
101
|
-
let I = t.minAzimuthAngle,
|
|
102
|
-
isFinite(I) && isFinite(
|
|
103
|
-
let
|
|
97
|
+
const n = new S(), g = new Te().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), w = new S(), C = new Te(), F = new S(), z = 2 * Math.PI;
|
|
98
|
+
return function(it = null) {
|
|
99
|
+
const Se = t.object.position;
|
|
100
|
+
n.copy(Se).sub(t.target), n.applyQuaternion(g), r.setFromVector3(n), t.autoRotate && i === o.NONE && B(Ae(it)), t.enableDamping ? (r.theta += c.theta * t.dampingFactor, r.phi += c.phi * t.dampingFactor) : (r.theta += c.theta, r.phi += c.phi);
|
|
101
|
+
let I = t.minAzimuthAngle, L = t.maxAzimuthAngle;
|
|
102
|
+
isFinite(I) && isFinite(L) && (I < -Math.PI ? I += z : I > Math.PI && (I -= z), L < -Math.PI ? L += z : L > Math.PI && (L -= z), I <= L ? r.theta = Math.max(I, Math.min(L, r.theta)) : r.theta = r.theta > (I + L) / 2 ? Math.max(I, r.theta) : Math.min(L, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && H || t.object.isOrthographicCamera ? r.radius = ne(r.radius) : r.radius = ne(r.radius * h), n.setFromSpherical(r), n.applyQuaternion(v), Se.copy(t.target).add(n), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), p.set(0, 0, 0));
|
|
103
|
+
let le = !1;
|
|
104
104
|
if (t.zoomToCursor && H) {
|
|
105
|
-
let
|
|
105
|
+
let U = null;
|
|
106
106
|
if (t.object.isPerspectiveCamera) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
t.object.position.addScaledVector(Y,
|
|
107
|
+
const _ = n.length();
|
|
108
|
+
U = ne(_ * h);
|
|
109
|
+
const Q = _ - U;
|
|
110
|
+
t.object.position.addScaledVector(Y, Q), t.object.updateMatrixWorld();
|
|
111
111
|
} else if (t.object.isOrthographicCamera) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
112
|
+
const _ = new S(k.x, k.y, 0);
|
|
113
|
+
_.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), le = !0;
|
|
114
|
+
const Q = new S(k.x, k.y, 0);
|
|
115
|
+
Q.unproject(t.object), t.object.position.sub(Q).add(_), t.object.updateMatrixWorld(), U = n.length();
|
|
116
116
|
} else
|
|
117
117
|
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
|
|
118
|
-
|
|
119
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
120
|
-
return
|
|
118
|
+
U !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(U).add(t.object.position) : (J.origin.copy(t.object.position), J.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(J.direction)) < ut ? e.lookAt(t.target) : (Ie.setFromNormalAndCoplanarPoint(t.object.up, t.target), J.intersectPlane(Ie, t.target))));
|
|
119
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), le = !0);
|
|
120
|
+
return h = 1, H = !1, le || w.distanceToSquared(t.object.position) > a || 8 * (1 - C.dot(t.object.quaternion)) > a || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Pe), w.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
|
|
121
121
|
};
|
|
122
122
|
}(), this.dispose = function() {
|
|
123
|
-
t.domElement.removeEventListener("contextmenu",
|
|
123
|
+
t.domElement.removeEventListener("contextmenu", Ce), t.domElement.removeEventListener("pointerdown", Me), t.domElement.removeEventListener("pointercancel", K), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", re), t._domElementKeyEvents = null);
|
|
124
124
|
};
|
|
125
|
-
const t = this,
|
|
125
|
+
const t = this, o = {
|
|
126
126
|
NONE: -1,
|
|
127
127
|
ROTATE: 0,
|
|
128
128
|
DOLLY: 1,
|
|
@@ -132,32 +132,32 @@ class gt extends ot {
|
|
|
132
132
|
TOUCH_DOLLY_PAN: 5,
|
|
133
133
|
TOUCH_DOLLY_ROTATE: 6
|
|
134
134
|
};
|
|
135
|
-
let
|
|
136
|
-
const a = 1e-6, r = new
|
|
137
|
-
let
|
|
138
|
-
const p = new S(), x = new
|
|
135
|
+
let i = o.NONE;
|
|
136
|
+
const a = 1e-6, r = new ze(), c = new ze();
|
|
137
|
+
let h = 1;
|
|
138
|
+
const p = new S(), x = new T(), m = new T(), u = new T(), f = new T(), b = new T(), M = new T(), N = new T(), O = new T(), R = new T(), Y = new S(), k = new T();
|
|
139
139
|
let H = !1;
|
|
140
|
-
const E = [],
|
|
141
|
-
let
|
|
142
|
-
function
|
|
140
|
+
const E = [], q = {};
|
|
141
|
+
let se = !1;
|
|
142
|
+
function Ae(n) {
|
|
143
143
|
return n !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * n : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
|
|
144
144
|
}
|
|
145
|
-
function
|
|
145
|
+
function W(n) {
|
|
146
146
|
const g = Math.abs(n * 0.01);
|
|
147
147
|
return Math.pow(0.95, t.zoomSpeed * g);
|
|
148
148
|
}
|
|
149
149
|
function B(n) {
|
|
150
150
|
c.theta -= n;
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function Z(n) {
|
|
153
153
|
c.phi -= n;
|
|
154
154
|
}
|
|
155
|
-
const
|
|
155
|
+
const he = function() {
|
|
156
156
|
const n = new S();
|
|
157
157
|
return function(v, w) {
|
|
158
158
|
n.setFromMatrixColumn(w, 0), n.multiplyScalar(-v), p.add(n);
|
|
159
159
|
};
|
|
160
|
-
}(),
|
|
160
|
+
}(), de = function() {
|
|
161
161
|
const n = new S();
|
|
162
162
|
return function(v, w) {
|
|
163
163
|
t.screenSpacePanning === !0 ? n.setFromMatrixColumn(w, 1) : (n.setFromMatrixColumn(w, 0), n.crossVectors(t.object.up, n)), n.multiplyScalar(v), p.add(n);
|
|
@@ -170,57 +170,57 @@ class gt extends ot {
|
|
|
170
170
|
const F = t.object.position;
|
|
171
171
|
n.copy(F).sub(t.target);
|
|
172
172
|
let z = n.length();
|
|
173
|
-
z *= Math.tan(t.object.fov / 2 * Math.PI / 180),
|
|
174
|
-
} else t.object.isOrthographicCamera ? (
|
|
173
|
+
z *= Math.tan(t.object.fov / 2 * Math.PI / 180), he(2 * v * z / C.clientHeight, t.object.matrix), de(2 * w * z / C.clientHeight, t.object.matrix);
|
|
174
|
+
} else t.object.isOrthographicCamera ? (he(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), de(w * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
175
175
|
};
|
|
176
176
|
}();
|
|
177
|
-
function
|
|
178
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
177
|
+
function ie(n) {
|
|
178
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h /= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
179
179
|
}
|
|
180
|
-
function
|
|
181
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
180
|
+
function pe(n) {
|
|
181
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h *= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
182
182
|
}
|
|
183
|
-
function
|
|
183
|
+
function oe(n, g) {
|
|
184
184
|
if (!t.zoomToCursor)
|
|
185
185
|
return;
|
|
186
186
|
H = !0;
|
|
187
187
|
const v = t.domElement.getBoundingClientRect(), w = n - v.left, C = g - v.top, F = v.width, z = v.height;
|
|
188
|
-
|
|
188
|
+
k.x = w / F * 2 - 1, k.y = -(C / z) * 2 + 1, Y.set(k.x, k.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
189
189
|
}
|
|
190
|
-
function
|
|
190
|
+
function ne(n) {
|
|
191
191
|
return Math.max(t.minDistance, Math.min(t.maxDistance, n));
|
|
192
192
|
}
|
|
193
|
-
function
|
|
193
|
+
function ge(n) {
|
|
194
194
|
x.set(n.clientX, n.clientY);
|
|
195
195
|
}
|
|
196
|
-
function De(n) {
|
|
197
|
-
se(n.clientX, n.clientX), N.set(n.clientX, n.clientY);
|
|
198
|
-
}
|
|
199
|
-
function pe(n) {
|
|
200
|
-
y.set(n.clientX, n.clientY);
|
|
201
|
-
}
|
|
202
|
-
function Ae(n) {
|
|
203
|
-
f.set(n.clientX, n.clientY), u.subVectors(f, x).multiplyScalar(t.rotateSpeed);
|
|
204
|
-
const g = t.domElement;
|
|
205
|
-
B(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), x.copy(f), t.update();
|
|
206
|
-
}
|
|
207
196
|
function je(n) {
|
|
208
|
-
|
|
197
|
+
oe(n.clientX, n.clientX), N.set(n.clientX, n.clientY);
|
|
198
|
+
}
|
|
199
|
+
function ue(n) {
|
|
200
|
+
f.set(n.clientX, n.clientY);
|
|
209
201
|
}
|
|
210
202
|
function $e(n) {
|
|
211
|
-
|
|
203
|
+
m.set(n.clientX, n.clientY), u.subVectors(m, x).multiplyScalar(t.rotateSpeed);
|
|
204
|
+
const g = t.domElement;
|
|
205
|
+
B(2 * Math.PI * u.x / g.clientHeight), Z(2 * Math.PI * u.y / g.clientHeight), x.copy(m), t.update();
|
|
212
206
|
}
|
|
213
207
|
function Ge(n) {
|
|
214
|
-
|
|
208
|
+
O.set(n.clientX, n.clientY), R.subVectors(O, N), R.y > 0 ? ie(W(R.y)) : R.y < 0 && pe(W(R.y)), N.copy(O), t.update();
|
|
215
209
|
}
|
|
216
210
|
function Ye(n) {
|
|
211
|
+
b.set(n.clientX, n.clientY), M.subVectors(b, f).multiplyScalar(t.panSpeed), A(M.x, M.y), f.copy(b), t.update();
|
|
212
|
+
}
|
|
213
|
+
function Be(n) {
|
|
214
|
+
oe(n.clientX, n.clientY), n.deltaY < 0 ? pe(W(n.deltaY)) : n.deltaY > 0 && ie(W(n.deltaY)), t.update();
|
|
215
|
+
}
|
|
216
|
+
function Ke(n) {
|
|
217
217
|
let g = !1;
|
|
218
218
|
switch (n.code) {
|
|
219
219
|
case t.keys.UP:
|
|
220
|
-
n.ctrlKey || n.metaKey || n.shiftKey ?
|
|
220
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? Z(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), g = !0;
|
|
221
221
|
break;
|
|
222
222
|
case t.keys.BOTTOM:
|
|
223
|
-
n.ctrlKey || n.metaKey || n.shiftKey ?
|
|
223
|
+
n.ctrlKey || n.metaKey || n.shiftKey ? Z(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), g = !0;
|
|
224
224
|
break;
|
|
225
225
|
case t.keys.LEFT:
|
|
226
226
|
n.ctrlKey || n.metaKey || n.shiftKey ? B(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), g = !0;
|
|
@@ -231,7 +231,7 @@ class gt extends ot {
|
|
|
231
231
|
}
|
|
232
232
|
g && (n.preventDefault(), t.update());
|
|
233
233
|
}
|
|
234
|
-
function
|
|
234
|
+
function fe(n) {
|
|
235
235
|
if (E.length === 1)
|
|
236
236
|
x.set(n.pageX, n.pageY);
|
|
237
237
|
else {
|
|
@@ -239,66 +239,66 @@ class gt extends ot {
|
|
|
239
239
|
x.set(v, w);
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
-
function
|
|
242
|
+
function me(n) {
|
|
243
243
|
if (E.length === 1)
|
|
244
|
-
|
|
244
|
+
f.set(n.pageX, n.pageY);
|
|
245
245
|
else {
|
|
246
246
|
const g = j(n), v = 0.5 * (n.pageX + g.x), w = 0.5 * (n.pageY + g.y);
|
|
247
|
-
|
|
247
|
+
f.set(v, w);
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
|
-
function
|
|
250
|
+
function ye(n) {
|
|
251
251
|
const g = j(n), v = n.pageX - g.x, w = n.pageY - g.y, C = Math.sqrt(v * v + w * w);
|
|
252
252
|
N.set(0, C);
|
|
253
253
|
}
|
|
254
|
-
function
|
|
255
|
-
t.enableZoom &&
|
|
254
|
+
function Ue(n) {
|
|
255
|
+
t.enableZoom && ye(n), t.enablePan && me(n);
|
|
256
256
|
}
|
|
257
|
-
function
|
|
258
|
-
t.enableZoom &&
|
|
257
|
+
function _e(n) {
|
|
258
|
+
t.enableZoom && ye(n), t.enableRotate && fe(n);
|
|
259
259
|
}
|
|
260
|
-
function
|
|
260
|
+
function xe(n) {
|
|
261
261
|
if (E.length == 1)
|
|
262
|
-
|
|
262
|
+
m.set(n.pageX, n.pageY);
|
|
263
263
|
else {
|
|
264
264
|
const v = j(n), w = 0.5 * (n.pageX + v.x), C = 0.5 * (n.pageY + v.y);
|
|
265
|
-
|
|
265
|
+
m.set(w, C);
|
|
266
266
|
}
|
|
267
|
-
u.subVectors(
|
|
267
|
+
u.subVectors(m, x).multiplyScalar(t.rotateSpeed);
|
|
268
268
|
const g = t.domElement;
|
|
269
|
-
B(2 * Math.PI * u.x / g.clientHeight),
|
|
269
|
+
B(2 * Math.PI * u.x / g.clientHeight), Z(2 * Math.PI * u.y / g.clientHeight), x.copy(m);
|
|
270
270
|
}
|
|
271
|
-
function
|
|
271
|
+
function be(n) {
|
|
272
272
|
if (E.length === 1)
|
|
273
273
|
b.set(n.pageX, n.pageY);
|
|
274
274
|
else {
|
|
275
275
|
const g = j(n), v = 0.5 * (n.pageX + g.x), w = 0.5 * (n.pageY + g.y);
|
|
276
276
|
b.set(v, w);
|
|
277
277
|
}
|
|
278
|
-
M.subVectors(b,
|
|
278
|
+
M.subVectors(b, f).multiplyScalar(t.panSpeed), A(M.x, M.y), f.copy(b);
|
|
279
279
|
}
|
|
280
|
-
function
|
|
280
|
+
function ve(n) {
|
|
281
281
|
const g = j(n), v = n.pageX - g.x, w = n.pageY - g.y, C = Math.sqrt(v * v + w * w);
|
|
282
|
-
O.set(0, C),
|
|
282
|
+
O.set(0, C), R.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), ie(R.y), N.copy(O);
|
|
283
283
|
const F = (n.pageX + g.x) * 0.5, z = (n.pageY + g.y) * 0.5;
|
|
284
|
-
|
|
284
|
+
oe(F, z);
|
|
285
285
|
}
|
|
286
286
|
function Xe(n) {
|
|
287
|
-
t.enableZoom &&
|
|
287
|
+
t.enableZoom && ve(n), t.enablePan && be(n);
|
|
288
288
|
}
|
|
289
289
|
function Ve(n) {
|
|
290
|
-
t.enableZoom &&
|
|
290
|
+
t.enableZoom && ve(n), t.enableRotate && xe(n);
|
|
291
291
|
}
|
|
292
|
-
function
|
|
293
|
-
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(n.pointerId), t.domElement.addEventListener("pointermove",
|
|
292
|
+
function Me(n) {
|
|
293
|
+
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(n.pointerId), t.domElement.addEventListener("pointermove", ae), t.domElement.addEventListener("pointerup", K)), tt(n), n.pointerType === "touch" ? Je(n) : qe(n));
|
|
294
294
|
}
|
|
295
|
-
function
|
|
296
|
-
t.enabled !== !1 && (n.pointerType === "touch" ?
|
|
295
|
+
function ae(n) {
|
|
296
|
+
t.enabled !== !1 && (n.pointerType === "touch" ? et(n) : We(n));
|
|
297
297
|
}
|
|
298
298
|
function K(n) {
|
|
299
|
-
|
|
299
|
+
st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Re), i = o.NONE;
|
|
300
300
|
}
|
|
301
|
-
function
|
|
301
|
+
function qe(n) {
|
|
302
302
|
let g;
|
|
303
303
|
switch (n.button) {
|
|
304
304
|
case 0:
|
|
@@ -316,51 +316,51 @@ class gt extends ot {
|
|
|
316
316
|
switch (g) {
|
|
317
317
|
case $.DOLLY:
|
|
318
318
|
if (t.enableZoom === !1) return;
|
|
319
|
-
|
|
319
|
+
je(n), i = o.DOLLY;
|
|
320
320
|
break;
|
|
321
321
|
case $.ROTATE:
|
|
322
322
|
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
323
323
|
if (t.enablePan === !1) return;
|
|
324
|
-
|
|
324
|
+
ue(n), i = o.PAN;
|
|
325
325
|
} else {
|
|
326
326
|
if (t.enableRotate === !1) return;
|
|
327
|
-
|
|
327
|
+
ge(n), i = o.ROTATE;
|
|
328
328
|
}
|
|
329
329
|
break;
|
|
330
330
|
case $.PAN:
|
|
331
331
|
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
332
332
|
if (t.enableRotate === !1) return;
|
|
333
|
-
|
|
333
|
+
ge(n), i = o.ROTATE;
|
|
334
334
|
} else {
|
|
335
335
|
if (t.enablePan === !1) return;
|
|
336
|
-
|
|
336
|
+
ue(n), i = o.PAN;
|
|
337
337
|
}
|
|
338
338
|
break;
|
|
339
339
|
default:
|
|
340
|
-
|
|
340
|
+
i = o.NONE;
|
|
341
341
|
}
|
|
342
|
-
|
|
342
|
+
i !== o.NONE && t.dispatchEvent(ce);
|
|
343
343
|
}
|
|
344
|
-
function
|
|
345
|
-
switch (
|
|
346
|
-
case
|
|
344
|
+
function We(n) {
|
|
345
|
+
switch (i) {
|
|
346
|
+
case o.ROTATE:
|
|
347
347
|
if (t.enableRotate === !1) return;
|
|
348
|
-
|
|
348
|
+
$e(n);
|
|
349
349
|
break;
|
|
350
|
-
case
|
|
350
|
+
case o.DOLLY:
|
|
351
351
|
if (t.enableZoom === !1) return;
|
|
352
|
-
|
|
352
|
+
Ge(n);
|
|
353
353
|
break;
|
|
354
|
-
case
|
|
354
|
+
case o.PAN:
|
|
355
355
|
if (t.enablePan === !1) return;
|
|
356
|
-
|
|
356
|
+
Ye(n);
|
|
357
357
|
break;
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
|
-
function
|
|
361
|
-
t.enabled === !1 || t.enableZoom === !1 ||
|
|
360
|
+
function we(n) {
|
|
361
|
+
t.enabled === !1 || t.enableZoom === !1 || i !== o.NONE || (n.preventDefault(), t.dispatchEvent(ce), Be(Ze(n)), t.dispatchEvent(Re));
|
|
362
362
|
}
|
|
363
|
-
function
|
|
363
|
+
function Ze(n) {
|
|
364
364
|
const g = n.deltaMode, v = {
|
|
365
365
|
clientX: n.clientX,
|
|
366
366
|
clientY: n.clientY,
|
|
@@ -374,100 +374,100 @@ class gt extends ot {
|
|
|
374
374
|
v.deltaY *= 100;
|
|
375
375
|
break;
|
|
376
376
|
}
|
|
377
|
-
return n.ctrlKey && !
|
|
377
|
+
return n.ctrlKey && !se && (v.deltaY *= 10), v;
|
|
378
378
|
}
|
|
379
|
-
function
|
|
380
|
-
n.key === "Control" && (
|
|
379
|
+
function Qe(n) {
|
|
380
|
+
n.key === "Control" && (se = !0, document.addEventListener("keyup", Ee, { passive: !0, capture: !0 }));
|
|
381
381
|
}
|
|
382
|
-
function
|
|
383
|
-
n.key === "Control" && (
|
|
382
|
+
function Ee(n) {
|
|
383
|
+
n.key === "Control" && (se = !1, document.removeEventListener("keyup", Ee, { passive: !0, capture: !0 }));
|
|
384
384
|
}
|
|
385
|
-
function
|
|
386
|
-
t.enabled === !1 || t.enablePan === !1 ||
|
|
385
|
+
function re(n) {
|
|
386
|
+
t.enabled === !1 || t.enablePan === !1 || Ke(n);
|
|
387
387
|
}
|
|
388
|
-
function
|
|
389
|
-
switch (
|
|
388
|
+
function Je(n) {
|
|
389
|
+
switch (Ne(n), E.length) {
|
|
390
390
|
case 1:
|
|
391
391
|
switch (t.touches.ONE) {
|
|
392
392
|
case G.ROTATE:
|
|
393
393
|
if (t.enableRotate === !1) return;
|
|
394
|
-
|
|
394
|
+
fe(n), i = o.TOUCH_ROTATE;
|
|
395
395
|
break;
|
|
396
396
|
case G.PAN:
|
|
397
397
|
if (t.enablePan === !1) return;
|
|
398
|
-
|
|
398
|
+
me(n), i = o.TOUCH_PAN;
|
|
399
399
|
break;
|
|
400
400
|
default:
|
|
401
|
-
|
|
401
|
+
i = o.NONE;
|
|
402
402
|
}
|
|
403
403
|
break;
|
|
404
404
|
case 2:
|
|
405
405
|
switch (t.touches.TWO) {
|
|
406
406
|
case G.DOLLY_PAN:
|
|
407
407
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
408
|
-
|
|
408
|
+
Ue(n), i = o.TOUCH_DOLLY_PAN;
|
|
409
409
|
break;
|
|
410
410
|
case G.DOLLY_ROTATE:
|
|
411
411
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
412
|
-
|
|
412
|
+
_e(n), i = o.TOUCH_DOLLY_ROTATE;
|
|
413
413
|
break;
|
|
414
414
|
default:
|
|
415
|
-
|
|
415
|
+
i = o.NONE;
|
|
416
416
|
}
|
|
417
417
|
break;
|
|
418
418
|
default:
|
|
419
|
-
|
|
419
|
+
i = o.NONE;
|
|
420
420
|
}
|
|
421
|
-
|
|
421
|
+
i !== o.NONE && t.dispatchEvent(ce);
|
|
422
422
|
}
|
|
423
|
-
function
|
|
424
|
-
switch (
|
|
425
|
-
case
|
|
423
|
+
function et(n) {
|
|
424
|
+
switch (Ne(n), i) {
|
|
425
|
+
case o.TOUCH_ROTATE:
|
|
426
426
|
if (t.enableRotate === !1) return;
|
|
427
|
-
|
|
427
|
+
xe(n), t.update();
|
|
428
428
|
break;
|
|
429
|
-
case
|
|
429
|
+
case o.TOUCH_PAN:
|
|
430
430
|
if (t.enablePan === !1) return;
|
|
431
|
-
|
|
431
|
+
be(n), t.update();
|
|
432
432
|
break;
|
|
433
|
-
case
|
|
433
|
+
case o.TOUCH_DOLLY_PAN:
|
|
434
434
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
435
435
|
Xe(n), t.update();
|
|
436
436
|
break;
|
|
437
|
-
case
|
|
437
|
+
case o.TOUCH_DOLLY_ROTATE:
|
|
438
438
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
439
439
|
Ve(n), t.update();
|
|
440
440
|
break;
|
|
441
441
|
default:
|
|
442
|
-
|
|
442
|
+
i = o.NONE;
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
|
-
function
|
|
445
|
+
function Ce(n) {
|
|
446
446
|
t.enabled !== !1 && n.preventDefault();
|
|
447
447
|
}
|
|
448
|
-
function
|
|
448
|
+
function tt(n) {
|
|
449
449
|
E.push(n.pointerId);
|
|
450
450
|
}
|
|
451
|
-
function
|
|
452
|
-
delete
|
|
451
|
+
function st(n) {
|
|
452
|
+
delete q[n.pointerId];
|
|
453
453
|
for (let g = 0; g < E.length; g++)
|
|
454
454
|
if (E[g] == n.pointerId) {
|
|
455
455
|
E.splice(g, 1);
|
|
456
456
|
return;
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
|
-
function
|
|
460
|
-
let g =
|
|
461
|
-
g === void 0 && (g = new
|
|
459
|
+
function Ne(n) {
|
|
460
|
+
let g = q[n.pointerId];
|
|
461
|
+
g === void 0 && (g = new T(), q[n.pointerId] = g), g.set(n.pageX, n.pageY);
|
|
462
462
|
}
|
|
463
463
|
function j(n) {
|
|
464
464
|
const g = n.pointerId === E[0] ? E[1] : E[0];
|
|
465
|
-
return
|
|
465
|
+
return q[g];
|
|
466
466
|
}
|
|
467
|
-
t.domElement.addEventListener("contextmenu",
|
|
467
|
+
t.domElement.addEventListener("contextmenu", Ce), t.domElement.addEventListener("pointerdown", Me), t.domElement.addEventListener("pointercancel", K), t.domElement.addEventListener("wheel", we, { passive: !1 }), document.addEventListener("keydown", Qe, { passive: !0, capture: !0 }), this.update();
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
|
-
class
|
|
470
|
+
class mt {
|
|
471
471
|
constructor(e, s) {
|
|
472
472
|
l(this, "scene");
|
|
473
473
|
l(this, "camera");
|
|
@@ -475,42 +475,42 @@ class ut {
|
|
|
475
475
|
l(this, "controls");
|
|
476
476
|
l(this, "container");
|
|
477
477
|
l(this, "resizeHandler");
|
|
478
|
-
this.container = e, this.scene = new
|
|
478
|
+
this.container = e, this.scene = new y.Scene(), this.scene.background = new y.Color(
|
|
479
479
|
s.backgroundColor ?? 657930
|
|
480
480
|
);
|
|
481
|
-
const { width: t, height:
|
|
482
|
-
this.camera = new
|
|
481
|
+
const { width: t, height: o } = ke(e), i = s.cameraFov ?? 75;
|
|
482
|
+
this.camera = new y.PerspectiveCamera(i, t / o, 0.1, 2e3);
|
|
483
483
|
const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
484
|
-
this.camera.position.set(a.x, a.y, a.z), this.renderer = new
|
|
484
|
+
this.camera.position.set(a.x, a.y, a.z), this.renderer = new y.WebGLRenderer({
|
|
485
485
|
antialias: !0,
|
|
486
486
|
alpha: !0,
|
|
487
487
|
powerPreference: "high-performance"
|
|
488
|
-
}), this.renderer.setSize(t,
|
|
488
|
+
}), this.renderer.setSize(t, o), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = y.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = y.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ft(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
489
489
|
}
|
|
490
490
|
/**
|
|
491
491
|
* Sets up scene lighting for gradient glass on dark background
|
|
492
492
|
*/
|
|
493
493
|
setupLighting() {
|
|
494
|
-
const e = new
|
|
494
|
+
const e = new y.AmbientLight(16777215, 0.4);
|
|
495
495
|
this.scene.add(e);
|
|
496
|
-
const s = new
|
|
496
|
+
const s = new y.DirectionalLight(16777215, 0.9);
|
|
497
497
|
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
498
|
-
const t = new
|
|
498
|
+
const t = new y.DirectionalLight(16773344, 0.4);
|
|
499
499
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
const a = new
|
|
500
|
+
const o = new y.DirectionalLight(16777215, 0.3);
|
|
501
|
+
o.position.set(0, -30, -50), this.scene.add(o);
|
|
502
|
+
const i = new y.PointLight(16750950, 0.5, 150);
|
|
503
|
+
i.position.set(40, 20, 40), this.scene.add(i);
|
|
504
|
+
const a = new y.PointLight(16764057, 0.4, 150);
|
|
505
505
|
a.position.set(-40, -20, 40), this.scene.add(a);
|
|
506
|
-
const r = new
|
|
506
|
+
const r = new y.PointLight(6724095, 0.2, 100);
|
|
507
507
|
r.position.set(0, 40, -40), this.scene.add(r);
|
|
508
508
|
}
|
|
509
509
|
/**
|
|
510
510
|
* Handle window resize
|
|
511
511
|
*/
|
|
512
512
|
onWindowResize() {
|
|
513
|
-
const { width: e, height: s } =
|
|
513
|
+
const { width: e, height: s } = ke(this.container);
|
|
514
514
|
this.camera.aspect = e / s, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, s);
|
|
515
515
|
}
|
|
516
516
|
/**
|
|
@@ -545,7 +545,7 @@ class ut {
|
|
|
545
545
|
* Gets the camera's forward direction
|
|
546
546
|
*/
|
|
547
547
|
getCameraDirection() {
|
|
548
|
-
const e = new
|
|
548
|
+
const e = new y.Vector3();
|
|
549
549
|
return this.camera.getWorldDirection(e), e;
|
|
550
550
|
}
|
|
551
551
|
/**
|
|
@@ -558,7 +558,7 @@ class ut {
|
|
|
558
558
|
}
|
|
559
559
|
}
|
|
560
560
|
}
|
|
561
|
-
class
|
|
561
|
+
const V = class V {
|
|
562
562
|
constructor(e, s) {
|
|
563
563
|
l(this, "sceneManager");
|
|
564
564
|
l(this, "nodeFactory");
|
|
@@ -572,29 +572,36 @@ class ft {
|
|
|
572
572
|
hasNode(e) {
|
|
573
573
|
return this.nodes.has(e);
|
|
574
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Sets the expected total node count so spawn positions can be scaled.
|
|
577
|
+
* Call this before adding nodes in bulk.
|
|
578
|
+
*/
|
|
579
|
+
static setExpectedNodeCount(e) {
|
|
580
|
+
V.expectedNodeCount = e;
|
|
581
|
+
}
|
|
575
582
|
/**
|
|
576
583
|
* Adds a node to the graph
|
|
577
584
|
* @returns true if added, false if node already exists or invalid
|
|
578
585
|
*/
|
|
579
586
|
addNode(e, s = 0) {
|
|
580
|
-
if (!
|
|
587
|
+
if (!He(e))
|
|
581
588
|
return !1;
|
|
582
589
|
if (this.nodes.has(e.id))
|
|
583
590
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
584
|
-
const t = e.position ?? {
|
|
585
|
-
x: (Math.random() - 0.5) *
|
|
586
|
-
y: (Math.random() - 0.5) *
|
|
587
|
-
z: (Math.random() - 0.5) *
|
|
591
|
+
const t = Math.max(50, Math.cbrt(V.expectedNodeCount) * 10), o = e.position ?? {
|
|
592
|
+
x: (Math.random() - 0.5) * t,
|
|
593
|
+
y: (Math.random() - 0.5) * t,
|
|
594
|
+
z: (Math.random() - 0.5) * t
|
|
588
595
|
}, i = {
|
|
589
596
|
...e,
|
|
590
|
-
position:
|
|
597
|
+
position: o,
|
|
591
598
|
velocity: { x: 0, y: 0, z: 0 },
|
|
592
599
|
mass: 1
|
|
593
|
-
},
|
|
594
|
-
{ ...e, position:
|
|
600
|
+
}, a = this.nodeFactory.createNode(
|
|
601
|
+
{ ...e, position: o },
|
|
595
602
|
s
|
|
596
603
|
);
|
|
597
|
-
return this.sceneManager.add(
|
|
604
|
+
return this.sceneManager.add(a.group), this.nodes.set(e.id, i), this.nodeObjects.set(e.id, a), !0;
|
|
598
605
|
}
|
|
599
606
|
/**
|
|
600
607
|
* Removes a node from the graph
|
|
@@ -608,17 +615,17 @@ class ft {
|
|
|
608
615
|
* Updates a node's properties
|
|
609
616
|
*/
|
|
610
617
|
updateNode(e, s) {
|
|
611
|
-
const t = this.nodes.get(e),
|
|
612
|
-
return !t || !
|
|
613
|
-
|
|
618
|
+
const t = this.nodes.get(e), o = this.nodeObjects.get(e);
|
|
619
|
+
return !t || !o ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(o, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(o, s.color)), Object.keys(s).forEach((i) => {
|
|
620
|
+
i !== "id" && i !== "label" && i !== "color" && i !== "position" && (t[i] = s[i]);
|
|
614
621
|
}), !0);
|
|
615
622
|
}
|
|
616
623
|
/**
|
|
617
624
|
* Updates a node's position (called by physics engine)
|
|
618
625
|
*/
|
|
619
626
|
updateNodePosition(e, s) {
|
|
620
|
-
const t = this.nodes.get(e),
|
|
621
|
-
t &&
|
|
627
|
+
const t = this.nodes.get(e), o = this.nodeObjects.get(e);
|
|
628
|
+
t && o && (t.position = s, o.group.position.set(s.x, s.y, s.z));
|
|
622
629
|
}
|
|
623
630
|
/**
|
|
624
631
|
* Updates a node's LOD level
|
|
@@ -683,8 +690,11 @@ class ft {
|
|
|
683
690
|
dispose() {
|
|
684
691
|
this.clear();
|
|
685
692
|
}
|
|
686
|
-
}
|
|
687
|
-
|
|
693
|
+
};
|
|
694
|
+
// Scale spawn volume with expected graph size
|
|
695
|
+
l(V, "expectedNodeCount", 100);
|
|
696
|
+
let te = V;
|
|
697
|
+
class yt {
|
|
688
698
|
constructor(e, s, t) {
|
|
689
699
|
l(this, "sceneManager");
|
|
690
700
|
l(this, "nodeManager");
|
|
@@ -707,7 +717,7 @@ class mt {
|
|
|
707
717
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
708
718
|
*/
|
|
709
719
|
addEdge(e) {
|
|
710
|
-
if (!
|
|
720
|
+
if (!De(e))
|
|
711
721
|
return !1;
|
|
712
722
|
if (!this.nodeManager.hasNode(e.source))
|
|
713
723
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
@@ -716,14 +726,14 @@ class mt {
|
|
|
716
726
|
const s = D(e.source, e.target);
|
|
717
727
|
if (this.edgeKeySet.has(s))
|
|
718
728
|
return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
|
|
719
|
-
const t = this.nodeManager.getNode(e.source),
|
|
729
|
+
const t = this.nodeManager.getNode(e.source), o = this.nodeManager.getNode(e.target), i = this.edgeFactory.createEdge(
|
|
720
730
|
e,
|
|
721
731
|
t,
|
|
722
|
-
|
|
732
|
+
o,
|
|
723
733
|
t.position,
|
|
724
|
-
|
|
734
|
+
o.position
|
|
725
735
|
);
|
|
726
|
-
return this.sceneManager.add(
|
|
736
|
+
return this.sceneManager.add(i.line), this.edges.push(e), this.edgeObjects.push(i), this.edgeKeySet.add(s), !0;
|
|
727
737
|
}
|
|
728
738
|
/**
|
|
729
739
|
* Removes an edge from the graph
|
|
@@ -733,13 +743,13 @@ class mt {
|
|
|
733
743
|
const t = D(e, s);
|
|
734
744
|
if (!this.edgeKeySet.has(t))
|
|
735
745
|
return !1;
|
|
736
|
-
const
|
|
746
|
+
const o = this.edges.findIndex(
|
|
737
747
|
(a) => D(a.source, a.target) === t
|
|
738
748
|
);
|
|
739
|
-
if (
|
|
749
|
+
if (o === -1)
|
|
740
750
|
return !1;
|
|
741
|
-
const
|
|
742
|
-
return this.sceneManager.remove(
|
|
751
|
+
const i = this.edgeObjects[o];
|
|
752
|
+
return this.sceneManager.remove(i.line), this.edgeFactory.disposeEdge(i), this.edges.splice(o, 1), this.edgeObjects.splice(o, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
|
|
743
753
|
}
|
|
744
754
|
/**
|
|
745
755
|
* Highlights an edge
|
|
@@ -747,10 +757,10 @@ class mt {
|
|
|
747
757
|
highlightEdge(e, s) {
|
|
748
758
|
const t = D(e, s);
|
|
749
759
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
750
|
-
const
|
|
751
|
-
(
|
|
760
|
+
const o = this.edges.findIndex(
|
|
761
|
+
(i) => D(i.source, i.target) === t
|
|
752
762
|
);
|
|
753
|
-
|
|
763
|
+
o !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[o]), this.highlightedEdgeKey = t);
|
|
754
764
|
}
|
|
755
765
|
/**
|
|
756
766
|
* Unhighlights the currently highlighted edge
|
|
@@ -793,11 +803,11 @@ class mt {
|
|
|
793
803
|
*/
|
|
794
804
|
updateEdgePositions() {
|
|
795
805
|
this.edgeObjects.forEach((e, s) => {
|
|
796
|
-
const t = this.edges[s],
|
|
797
|
-
|
|
806
|
+
const t = this.edges[s], o = this.nodeManager.getNode(t.source), i = this.nodeManager.getNode(t.target);
|
|
807
|
+
o && i && this.edgeFactory.updateEdgePositions(
|
|
798
808
|
e,
|
|
799
|
-
|
|
800
|
-
|
|
809
|
+
o.position,
|
|
810
|
+
i.position
|
|
801
811
|
);
|
|
802
812
|
});
|
|
803
813
|
}
|
|
@@ -844,12 +854,29 @@ class Le {
|
|
|
844
854
|
l(this, "damping");
|
|
845
855
|
l(this, "useBarnesHut");
|
|
846
856
|
l(this, "barnesHutTheta");
|
|
857
|
+
// Scaling safeguards
|
|
858
|
+
l(this, "MAX_VELOCITY", 5);
|
|
859
|
+
// Max units/step
|
|
860
|
+
l(this, "REPULSION_CUTOFF", 200);
|
|
861
|
+
// Skip repulsion beyond this distance
|
|
862
|
+
l(this, "REPULSION_CUTOFF_SQ");
|
|
863
|
+
// Precomputed cutoff²
|
|
864
|
+
l(this, "GRAVITY_STRENGTH", 0.02);
|
|
865
|
+
// Centering force strength
|
|
847
866
|
// Simulation state
|
|
848
867
|
l(this, "alpha", 1);
|
|
849
868
|
l(this, "alphaDecay", 0.0228);
|
|
850
869
|
l(this, "alphaMin", 1e-3);
|
|
851
870
|
l(this, "alphaTarget", 0);
|
|
852
|
-
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;
|
|
871
|
+
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, this.REPULSION_CUTOFF_SQ = this.REPULSION_CUTOFF * this.REPULSION_CUTOFF;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Computes effective repulsion strength scaled by node count.
|
|
875
|
+
* This keeps total repulsion energy bounded as N grows.
|
|
876
|
+
*/
|
|
877
|
+
getEffectiveRepulsion() {
|
|
878
|
+
const e = this.nodes.size;
|
|
879
|
+
return e <= 500 ? this.repulsionStrength : this.repulsionStrength * Math.sqrt(500) / Math.sqrt(e);
|
|
853
880
|
}
|
|
854
881
|
/**
|
|
855
882
|
* Runs one simulation step
|
|
@@ -857,21 +884,22 @@ class Le {
|
|
|
857
884
|
simulate() {
|
|
858
885
|
if (this.alpha < this.alphaMin) return;
|
|
859
886
|
const e = this.nodes.size;
|
|
860
|
-
(this.useBarnesHut || e >= 1e3) && e >= 100 ? this.calculateRepulsionBarnesHut() : this.calculateRepulsionBruteForce(), this.calculateAttraction(), this.applyForces(), this.alpha += (this.alphaTarget - this.alpha) * this.alphaDecay;
|
|
887
|
+
(this.useBarnesHut || e >= 1e3) && e >= 100 ? this.calculateRepulsionBarnesHut() : this.calculateRepulsionBruteForce(), this.calculateAttraction(), this.applyCenteringForce(), this.applyForces(), this.alpha += (this.alphaTarget - this.alpha) * this.alphaDecay;
|
|
861
888
|
}
|
|
862
889
|
/**
|
|
863
890
|
* Brute force repulsion - O(n²)
|
|
864
891
|
*/
|
|
865
892
|
calculateRepulsionBruteForce() {
|
|
866
|
-
const e = Array.from(this.nodes.values()), s = e.length;
|
|
867
|
-
for (let
|
|
868
|
-
const i = e[
|
|
869
|
-
for (let
|
|
870
|
-
const
|
|
871
|
-
let
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
893
|
+
const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
|
|
894
|
+
for (let o = 0; o < s; o++) {
|
|
895
|
+
const i = e[o];
|
|
896
|
+
for (let a = o + 1; a < s; a++) {
|
|
897
|
+
const r = e[a], c = r.position.x - i.position.x, h = r.position.y - i.position.y, p = r.position.z - i.position.z;
|
|
898
|
+
let x = c * c + h * h + p * p;
|
|
899
|
+
if (x > this.REPULSION_CUTOFF_SQ) continue;
|
|
900
|
+
x < 0.01 && (x = 0.01);
|
|
901
|
+
const m = Math.sqrt(x), u = t * this.alpha / x, f = c / m * u, b = h / m * u, M = p / m * u;
|
|
902
|
+
i.velocity.x -= f / i.mass, i.velocity.y -= b / i.mass, i.velocity.z -= M / i.mass, r.velocity.x += f / r.mass, r.velocity.y += b / r.mass, r.velocity.z += M / r.mass;
|
|
875
903
|
}
|
|
876
904
|
}
|
|
877
905
|
}
|
|
@@ -879,7 +907,7 @@ class Le {
|
|
|
879
907
|
* Barnes-Hut approximation - O(n log n)
|
|
880
908
|
*/
|
|
881
909
|
calculateRepulsionBarnesHut() {
|
|
882
|
-
const e = Array.from(this.nodes.values()), s = new
|
|
910
|
+
const e = Array.from(this.nodes.values()), s = new xt(e);
|
|
883
911
|
for (const t of e)
|
|
884
912
|
this.calculateForceFromOctree(t, s.root);
|
|
885
913
|
}
|
|
@@ -892,43 +920,65 @@ class Le {
|
|
|
892
920
|
return;
|
|
893
921
|
}
|
|
894
922
|
if (s.mass === 0) return;
|
|
895
|
-
const t = s.centerOfMass.x - e.position.x,
|
|
896
|
-
if (
|
|
897
|
-
|
|
898
|
-
|
|
923
|
+
const t = s.centerOfMass.x - e.position.x, o = s.centerOfMass.y - e.position.y, i = s.centerOfMass.z - e.position.z, a = t * t + o * o + i * i;
|
|
924
|
+
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
925
|
+
const r = Math.sqrt(a), c = this.getEffectiveRepulsion();
|
|
926
|
+
if (r > 0 && s.size / r < this.barnesHutTheta) {
|
|
927
|
+
const h = Math.max(a, 0.01), p = c * this.alpha * s.mass / h;
|
|
928
|
+
e.velocity.x -= t / r * p / e.mass, e.velocity.y -= o / r * p / e.mass, e.velocity.z -= i / r * p / e.mass;
|
|
899
929
|
} else
|
|
900
|
-
for (const
|
|
901
|
-
|
|
930
|
+
for (const h of s.children)
|
|
931
|
+
h && this.calculateForceFromOctree(e, h);
|
|
902
932
|
}
|
|
903
933
|
/**
|
|
904
|
-
* Apply repulsion between two nodes
|
|
934
|
+
* Apply repulsion between two nodes (with cutoff)
|
|
905
935
|
*/
|
|
906
936
|
applyRepulsionBetween(e, s) {
|
|
907
|
-
const t = s.position.x - e.position.x,
|
|
908
|
-
let a = t * t +
|
|
937
|
+
const t = s.position.x - e.position.x, o = s.position.y - e.position.y, i = s.position.z - e.position.z;
|
|
938
|
+
let a = t * t + o * o + i * i;
|
|
939
|
+
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
909
940
|
a < 0.01 && (a = 0.01);
|
|
910
|
-
const r = Math.sqrt(a),
|
|
911
|
-
e.velocity.x -= t / r *
|
|
941
|
+
const r = Math.sqrt(a), h = this.getEffectiveRepulsion() * this.alpha / a;
|
|
942
|
+
e.velocity.x -= t / r * h / e.mass, e.velocity.y -= o / r * h / e.mass, e.velocity.z -= i / r * h / e.mass;
|
|
912
943
|
}
|
|
913
944
|
/**
|
|
914
945
|
* Calculate attraction forces along edges
|
|
915
946
|
*/
|
|
916
947
|
calculateAttraction() {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
948
|
+
const e = this.nodes.size, s = e > 500 ? 1 + Math.log10(e / 500) : 1;
|
|
949
|
+
for (const t of this.edges) {
|
|
950
|
+
const o = this.nodes.get(t.source), i = this.nodes.get(t.target);
|
|
951
|
+
if (!o || !i) continue;
|
|
952
|
+
const a = i.position.x - o.position.x, r = i.position.y - o.position.y, c = i.position.z - o.position.z, h = Math.sqrt(a * a + r * r + c * c);
|
|
953
|
+
if (h < 0.01) continue;
|
|
954
|
+
const x = (h - 15) * this.attractionStrength * s * this.alpha, m = a / h * x, u = r / h * x, f = c / h * x;
|
|
955
|
+
o.velocity.x += m / o.mass, o.velocity.y += u / o.mass, o.velocity.z += f / o.mass, i.velocity.x -= m / i.mass, i.velocity.y -= u / i.mass, i.velocity.z -= f / i.mass;
|
|
924
956
|
}
|
|
925
957
|
}
|
|
926
958
|
/**
|
|
927
|
-
*
|
|
959
|
+
* Applies a weak centering/gravity force pulling all nodes toward origin.
|
|
960
|
+
* Prevents the graph from drifting infinitely away from the center.
|
|
961
|
+
*/
|
|
962
|
+
applyCenteringForce() {
|
|
963
|
+
const e = this.GRAVITY_STRENGTH * this.alpha;
|
|
964
|
+
for (const s of this.nodes.values())
|
|
965
|
+
s.velocity.x -= s.position.x * e, s.velocity.y -= s.position.y * e, s.velocity.z -= s.position.z * e;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Apply velocities and damping to positions, with velocity capping
|
|
928
969
|
*/
|
|
929
970
|
applyForces() {
|
|
930
|
-
for (const e of this.nodes.values())
|
|
931
|
-
e.velocity.x *= this.damping, e.velocity.y *= this.damping, e.velocity.z *= this.damping
|
|
971
|
+
for (const e of this.nodes.values()) {
|
|
972
|
+
e.velocity.x *= this.damping, e.velocity.y *= this.damping, e.velocity.z *= this.damping;
|
|
973
|
+
const s = Math.sqrt(
|
|
974
|
+
e.velocity.x * e.velocity.x + e.velocity.y * e.velocity.y + e.velocity.z * e.velocity.z
|
|
975
|
+
);
|
|
976
|
+
if (s > this.MAX_VELOCITY) {
|
|
977
|
+
const t = this.MAX_VELOCITY / s;
|
|
978
|
+
e.velocity.x *= t, e.velocity.y *= t, e.velocity.z *= t;
|
|
979
|
+
}
|
|
980
|
+
e.position.x += e.velocity.x, e.position.y += e.velocity.y, e.position.z += e.velocity.z;
|
|
981
|
+
}
|
|
932
982
|
}
|
|
933
983
|
/**
|
|
934
984
|
* Updates the edges reference
|
|
@@ -955,7 +1005,7 @@ class Le {
|
|
|
955
1005
|
e.repulsionStrength !== void 0 && (this.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.damping = e.damping);
|
|
956
1006
|
}
|
|
957
1007
|
}
|
|
958
|
-
class
|
|
1008
|
+
class xt {
|
|
959
1009
|
constructor(e) {
|
|
960
1010
|
l(this, "root");
|
|
961
1011
|
const s = this.calculateBounds(e);
|
|
@@ -968,13 +1018,13 @@ class yt {
|
|
|
968
1018
|
max: { x: 100, y: 100, z: 100 }
|
|
969
1019
|
};
|
|
970
1020
|
const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
|
|
971
|
-
for (const
|
|
972
|
-
s.x = Math.min(s.x,
|
|
973
|
-
const
|
|
974
|
-
return s.x -=
|
|
1021
|
+
for (const i of e)
|
|
1022
|
+
s.x = Math.min(s.x, i.position.x), s.y = Math.min(s.y, i.position.y), s.z = Math.min(s.z, i.position.z), t.x = Math.max(t.x, i.position.x), t.y = Math.max(t.y, i.position.y), t.z = Math.max(t.z, i.position.z);
|
|
1023
|
+
const o = 10;
|
|
1024
|
+
return s.x -= o, s.y -= o, s.z -= o, t.x += o, t.y += o, t.z += o, { min: s, max: t };
|
|
975
1025
|
}
|
|
976
1026
|
buildTree(e, s, t = 0) {
|
|
977
|
-
const
|
|
1027
|
+
const o = Math.max(
|
|
978
1028
|
s.max.x - s.min.x,
|
|
979
1029
|
s.max.y - s.min.y,
|
|
980
1030
|
s.max.z - s.min.z
|
|
@@ -982,7 +1032,7 @@ class yt {
|
|
|
982
1032
|
if (e.length === 0)
|
|
983
1033
|
return {
|
|
984
1034
|
bounds: s,
|
|
985
|
-
size:
|
|
1035
|
+
size: o,
|
|
986
1036
|
centerOfMass: { x: 0, y: 0, z: 0 },
|
|
987
1037
|
mass: 0,
|
|
988
1038
|
isLeaf: !0,
|
|
@@ -991,46 +1041,46 @@ class yt {
|
|
|
991
1041
|
};
|
|
992
1042
|
if (e.length === 1 || t > 20) {
|
|
993
1043
|
let u = 0;
|
|
994
|
-
const
|
|
1044
|
+
const f = { x: 0, y: 0, z: 0 };
|
|
995
1045
|
for (const b of e)
|
|
996
|
-
u += b.mass,
|
|
997
|
-
return u > 0 && (
|
|
1046
|
+
u += b.mass, f.x += b.position.x * b.mass, f.y += b.position.y * b.mass, f.z += b.position.z * b.mass;
|
|
1047
|
+
return u > 0 && (f.x /= u, f.y /= u, f.z /= u), {
|
|
998
1048
|
bounds: s,
|
|
999
|
-
size:
|
|
1000
|
-
centerOfMass:
|
|
1049
|
+
size: o,
|
|
1050
|
+
centerOfMass: f,
|
|
1001
1051
|
mass: u,
|
|
1002
1052
|
isLeaf: !0,
|
|
1003
1053
|
node: e[0],
|
|
1004
1054
|
children: []
|
|
1005
1055
|
};
|
|
1006
1056
|
}
|
|
1007
|
-
const
|
|
1057
|
+
const i = (s.min.x + s.max.x) / 2, a = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
|
|
1008
1058
|
for (const u of e) {
|
|
1009
|
-
const
|
|
1010
|
-
c[
|
|
1011
|
-
}
|
|
1012
|
-
const
|
|
1013
|
-
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x:
|
|
1014
|
-
{ min: { x:
|
|
1015
|
-
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x:
|
|
1016
|
-
{ min: { x:
|
|
1017
|
-
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x:
|
|
1018
|
-
{ min: { x:
|
|
1019
|
-
{ min: { x: s.min.x, y: a, z: r }, max: { x:
|
|
1020
|
-
{ min: { x:
|
|
1059
|
+
const f = (u.position.x >= i ? 1 : 0) + (u.position.y >= a ? 2 : 0) + (u.position.z >= r ? 4 : 0);
|
|
1060
|
+
c[f].push(u);
|
|
1061
|
+
}
|
|
1062
|
+
const h = [
|
|
1063
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: i, y: a, z: r } },
|
|
1064
|
+
{ min: { x: i, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: r } },
|
|
1065
|
+
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x: i, y: s.max.y, z: r } },
|
|
1066
|
+
{ min: { x: i, y: a, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
|
|
1067
|
+
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x: i, y: a, z: s.max.z } },
|
|
1068
|
+
{ min: { x: i, y: s.min.y, z: r }, max: { x: s.max.x, y: a, z: s.max.z } },
|
|
1069
|
+
{ min: { x: s.min.x, y: a, z: r }, max: { x: i, y: s.max.y, z: s.max.z } },
|
|
1070
|
+
{ min: { x: i, y: a, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1021
1071
|
], p = [];
|
|
1022
1072
|
let x = 0;
|
|
1023
|
-
const
|
|
1073
|
+
const m = { x: 0, y: 0, z: 0 };
|
|
1024
1074
|
for (let u = 0; u < 8; u++)
|
|
1025
1075
|
if (c[u].length > 0) {
|
|
1026
|
-
const
|
|
1027
|
-
p.push(
|
|
1076
|
+
const f = this.buildTree(c[u], h[u], t + 1);
|
|
1077
|
+
p.push(f), x += f.mass, m.x += f.centerOfMass.x * f.mass, m.y += f.centerOfMass.y * f.mass, m.z += f.centerOfMass.z * f.mass;
|
|
1028
1078
|
} else
|
|
1029
1079
|
p.push(null);
|
|
1030
|
-
return x > 0 && (
|
|
1080
|
+
return x > 0 && (m.x /= x, m.y /= x, m.z /= x), {
|
|
1031
1081
|
bounds: s,
|
|
1032
|
-
size:
|
|
1033
|
-
centerOfMass:
|
|
1082
|
+
size: o,
|
|
1083
|
+
centerOfMass: m,
|
|
1034
1084
|
mass: x,
|
|
1035
1085
|
isLeaf: !1,
|
|
1036
1086
|
node: null,
|
|
@@ -1038,8 +1088,8 @@ class yt {
|
|
|
1038
1088
|
};
|
|
1039
1089
|
}
|
|
1040
1090
|
}
|
|
1041
|
-
class
|
|
1042
|
-
constructor(e, s, t,
|
|
1091
|
+
class bt {
|
|
1092
|
+
constructor(e, s, t, o = 60) {
|
|
1043
1093
|
l(this, "sceneManager");
|
|
1044
1094
|
l(this, "animationId", null);
|
|
1045
1095
|
l(this, "isRunning", !1);
|
|
@@ -1064,7 +1114,7 @@ class xt {
|
|
|
1064
1114
|
const t = e - this.fpsStartTime;
|
|
1065
1115
|
t >= 1e3 && (this.currentFPS = this.frameCount / (t / 1e3), this.frameCount = 0, this.fpsStartTime = e), this.onSimulate(), this.onRender(), this.sceneManager.render();
|
|
1066
1116
|
});
|
|
1067
|
-
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 /
|
|
1117
|
+
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 / o;
|
|
1068
1118
|
}
|
|
1069
1119
|
/**
|
|
1070
1120
|
* Starts the animation loop
|
|
@@ -1103,7 +1153,7 @@ class xt {
|
|
|
1103
1153
|
this.stop();
|
|
1104
1154
|
}
|
|
1105
1155
|
}
|
|
1106
|
-
class
|
|
1156
|
+
class vt {
|
|
1107
1157
|
constructor() {
|
|
1108
1158
|
l(this, "envMap", null);
|
|
1109
1159
|
l(this, "materialCache", /* @__PURE__ */ new Map());
|
|
@@ -1141,10 +1191,10 @@ class bt {
|
|
|
1141
1191
|
{ colors: ["#2d1a1a", "#1a0a0a", "#0f0505"] }
|
|
1142
1192
|
// -z
|
|
1143
1193
|
];
|
|
1144
|
-
for (const
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
const a =
|
|
1194
|
+
for (const o of t) {
|
|
1195
|
+
const i = document.createElement("canvas");
|
|
1196
|
+
i.width = 256, i.height = 256;
|
|
1197
|
+
const a = i.getContext("2d"), r = a.createRadialGradient(
|
|
1148
1198
|
256 / 2,
|
|
1149
1199
|
256 / 2,
|
|
1150
1200
|
0,
|
|
@@ -1152,17 +1202,17 @@ class bt {
|
|
|
1152
1202
|
256 / 2,
|
|
1153
1203
|
256 * 0.8
|
|
1154
1204
|
);
|
|
1155
|
-
r.addColorStop(0,
|
|
1205
|
+
r.addColorStop(0, o.colors[0]), r.addColorStop(0.5, o.colors[1]), r.addColorStop(1, o.colors[2]), a.fillStyle = r, a.fillRect(0, 0, 256, 256);
|
|
1156
1206
|
const c = a.getImageData(0, 0, 256, 256);
|
|
1157
|
-
for (let
|
|
1207
|
+
for (let h = 0; h < c.data.length; h += 4) {
|
|
1158
1208
|
const p = (Math.random() - 0.5) * 5;
|
|
1159
|
-
c.data[
|
|
1209
|
+
c.data[h] = Math.min(255, Math.max(0, c.data[h] + p)), c.data[h + 1] = Math.min(255, Math.max(0, c.data[h + 1] + p)), c.data[h + 2] = Math.min(255, Math.max(0, c.data[h + 2] + p));
|
|
1160
1210
|
}
|
|
1161
|
-
a.putImageData(c, 0, 0), s.push(
|
|
1211
|
+
a.putImageData(c, 0, 0), s.push(i);
|
|
1162
1212
|
}
|
|
1163
|
-
this.envMap = new
|
|
1164
|
-
const
|
|
1165
|
-
return
|
|
1213
|
+
this.envMap = new y.CubeTexture(s.map((o) => {
|
|
1214
|
+
const i = new Image();
|
|
1215
|
+
return i.src = o.toDataURL(), i;
|
|
1166
1216
|
})), this.envMap.needsUpdate = !0;
|
|
1167
1217
|
}
|
|
1168
1218
|
/**
|
|
@@ -1179,11 +1229,11 @@ class bt {
|
|
|
1179
1229
|
const t = "glass-single";
|
|
1180
1230
|
if (this.materialCache.has(t))
|
|
1181
1231
|
return this.materialCache.get(t).clone();
|
|
1182
|
-
const
|
|
1232
|
+
const o = new y.Color(16750950), i = new y.ShaderMaterial({
|
|
1183
1233
|
uniforms: {
|
|
1184
|
-
uColor: { value:
|
|
1234
|
+
uColor: { value: o },
|
|
1185
1235
|
uEnvMap: { value: this.envMap },
|
|
1186
|
-
uGlowColor: { value: new
|
|
1236
|
+
uGlowColor: { value: new y.Color(16777215) },
|
|
1187
1237
|
uGlowIntensity: { value: 0.8 },
|
|
1188
1238
|
uReflectivity: { value: 0.4 },
|
|
1189
1239
|
uFresnelPower: { value: 2.5 }
|
|
@@ -1249,17 +1299,17 @@ class bt {
|
|
|
1249
1299
|
}
|
|
1250
1300
|
`,
|
|
1251
1301
|
transparent: !0,
|
|
1252
|
-
side:
|
|
1302
|
+
side: y.FrontSide,
|
|
1253
1303
|
depthWrite: !0,
|
|
1254
|
-
blending:
|
|
1304
|
+
blending: y.NormalBlending
|
|
1255
1305
|
});
|
|
1256
|
-
return this.materialCache.set(t,
|
|
1306
|
+
return this.materialCache.set(t, i), i.clone();
|
|
1257
1307
|
}
|
|
1258
1308
|
/**
|
|
1259
1309
|
* Creates material for edges (light color for dark background)
|
|
1260
1310
|
*/
|
|
1261
1311
|
createEdgeMaterial(e = 6710886, s = 0.4) {
|
|
1262
|
-
return new
|
|
1312
|
+
return new y.LineBasicMaterial({
|
|
1263
1313
|
color: e,
|
|
1264
1314
|
transparent: !0,
|
|
1265
1315
|
opacity: s,
|
|
@@ -1270,7 +1320,7 @@ class bt {
|
|
|
1270
1320
|
* Creates highlighted edge material
|
|
1271
1321
|
*/
|
|
1272
1322
|
createHighlightedEdgeMaterial() {
|
|
1273
|
-
return new
|
|
1323
|
+
return new y.LineBasicMaterial({
|
|
1274
1324
|
color: 16750950,
|
|
1275
1325
|
// Tangerine highlight
|
|
1276
1326
|
transparent: !1,
|
|
@@ -1282,12 +1332,12 @@ class bt {
|
|
|
1282
1332
|
* Creates a sprite material for labels (light text for dark background)
|
|
1283
1333
|
*/
|
|
1284
1334
|
createLabelMaterial(e, s = 24) {
|
|
1285
|
-
const t = document.createElement("canvas"),
|
|
1286
|
-
|
|
1287
|
-
const a =
|
|
1288
|
-
t.width = Math.max(128, a + 24), t.height = s + 20,
|
|
1289
|
-
const r = new
|
|
1290
|
-
return r.needsUpdate = !0, new
|
|
1335
|
+
const t = document.createElement("canvas"), o = t.getContext("2d");
|
|
1336
|
+
o.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1337
|
+
const a = o.measureText(e).width;
|
|
1338
|
+
t.width = Math.max(128, a + 24), t.height = s + 20, o.clearRect(0, 0, t.width, t.height), o.font = `600 ${s}px Inter, -apple-system, sans-serif`, o.textAlign = "center", o.textBaseline = "middle", o.shadowColor = "rgba(0, 0, 0, 0.8)", o.shadowBlur = 4, o.shadowOffsetX = 1, o.shadowOffsetY = 1, o.fillStyle = "rgba(255, 255, 255, 0.95)", o.fillText(e, t.width / 2, t.height / 2);
|
|
1339
|
+
const r = new y.CanvasTexture(t);
|
|
1340
|
+
return r.needsUpdate = !0, new y.SpriteMaterial({
|
|
1291
1341
|
map: r,
|
|
1292
1342
|
transparent: !0,
|
|
1293
1343
|
depthTest: !1,
|
|
@@ -1307,14 +1357,14 @@ class bt {
|
|
|
1307
1357
|
this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
|
|
1308
1358
|
}
|
|
1309
1359
|
}
|
|
1310
|
-
class
|
|
1311
|
-
constructor(e, s = 2, t = [32, 16, 8],
|
|
1360
|
+
class Mt {
|
|
1361
|
+
constructor(e, s = 2, t = [32, 16, 8], o = 16750950) {
|
|
1312
1362
|
l(this, "materialFactory");
|
|
1313
1363
|
l(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
1314
1364
|
l(this, "nodeRadius");
|
|
1315
1365
|
l(this, "lodSegments");
|
|
1316
1366
|
l(this, "defaultNodeColor");
|
|
1317
|
-
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.defaultNodeColor =
|
|
1367
|
+
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.defaultNodeColor = o, this.initGeometryCache();
|
|
1318
1368
|
}
|
|
1319
1369
|
/**
|
|
1320
1370
|
* Pre-create geometries for each LOD level
|
|
@@ -1324,7 +1374,7 @@ class vt {
|
|
|
1324
1374
|
const t = `lod-${s}`;
|
|
1325
1375
|
this.geometryCache.set(
|
|
1326
1376
|
t,
|
|
1327
|
-
new
|
|
1377
|
+
new y.SphereGeometry(this.nodeRadius, e, e)
|
|
1328
1378
|
);
|
|
1329
1379
|
});
|
|
1330
1380
|
}
|
|
@@ -1339,13 +1389,13 @@ class vt {
|
|
|
1339
1389
|
* Creates a node visual (glass ball + label)
|
|
1340
1390
|
*/
|
|
1341
1391
|
createNode(e, s = 0) {
|
|
1342
|
-
const t = new
|
|
1392
|
+
const t = new y.Group();
|
|
1343
1393
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1344
|
-
const
|
|
1394
|
+
const o = this.getGeometry(s), i = this.materialFactory.createGlassMaterial(
|
|
1345
1395
|
e.color ?? this.defaultNodeColor
|
|
1346
|
-
), a = new
|
|
1396
|
+
), a = new y.Mesh(o, i);
|
|
1347
1397
|
a.castShadow = !0, a.receiveShadow = !0, t.add(a);
|
|
1348
|
-
const r = this.materialFactory.createLabelMaterial(e.label), c = new
|
|
1398
|
+
const r = this.materialFactory.createLabelMaterial(e.label), c = new y.Sprite(r);
|
|
1349
1399
|
return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
|
|
1350
1400
|
e.position.x,
|
|
1351
1401
|
e.position.y,
|
|
@@ -1369,19 +1419,19 @@ class vt {
|
|
|
1369
1419
|
* Updates the color of a node
|
|
1370
1420
|
*/
|
|
1371
1421
|
updateNodeColor(e, s) {
|
|
1372
|
-
e.sphere.material instanceof
|
|
1422
|
+
e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
|
|
1373
1423
|
}
|
|
1374
1424
|
/**
|
|
1375
1425
|
* Updates the label of a node
|
|
1376
1426
|
*/
|
|
1377
1427
|
updateNodeLabel(e, s) {
|
|
1378
|
-
e.label.material instanceof
|
|
1428
|
+
e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
|
|
1379
1429
|
}
|
|
1380
1430
|
/**
|
|
1381
1431
|
* Disposes a node's resources
|
|
1382
1432
|
*/
|
|
1383
1433
|
disposeNode(e) {
|
|
1384
|
-
e.sphere.material instanceof
|
|
1434
|
+
e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
|
|
1385
1435
|
}
|
|
1386
1436
|
/**
|
|
1387
1437
|
* Dispose factory resources
|
|
@@ -1390,7 +1440,7 @@ class vt {
|
|
|
1390
1440
|
this.geometryCache.forEach((e) => e.dispose()), this.geometryCache.clear();
|
|
1391
1441
|
}
|
|
1392
1442
|
}
|
|
1393
|
-
class
|
|
1443
|
+
class wt {
|
|
1394
1444
|
constructor(e, s = 10066329, t = 0.5) {
|
|
1395
1445
|
l(this, "materialFactory");
|
|
1396
1446
|
l(this, "edgeColor");
|
|
@@ -1417,25 +1467,25 @@ class Mt {
|
|
|
1417
1467
|
/**
|
|
1418
1468
|
* Creates an edge line between two positions
|
|
1419
1469
|
*/
|
|
1420
|
-
createEdge(e, s, t,
|
|
1421
|
-
const a = new
|
|
1422
|
-
i.x,
|
|
1423
|
-
i.y,
|
|
1424
|
-
i.z,
|
|
1470
|
+
createEdge(e, s, t, o, i) {
|
|
1471
|
+
const a = new y.BufferGeometry(), r = new Float32Array([
|
|
1425
1472
|
o.x,
|
|
1426
1473
|
o.y,
|
|
1427
|
-
o.z
|
|
1474
|
+
o.z,
|
|
1475
|
+
i.x,
|
|
1476
|
+
i.y,
|
|
1477
|
+
i.z
|
|
1428
1478
|
]);
|
|
1429
|
-
a.setAttribute("position", new
|
|
1430
|
-
const c = this.getDefaultMaterial().clone(),
|
|
1431
|
-
return
|
|
1479
|
+
a.setAttribute("position", new y.BufferAttribute(r, 3));
|
|
1480
|
+
const c = this.getDefaultMaterial().clone(), h = new y.Line(a, c);
|
|
1481
|
+
return h.name = `edge-${e.source}-${e.target}`, h.userData = {
|
|
1432
1482
|
source: e.source,
|
|
1433
1483
|
target: e.target,
|
|
1434
1484
|
edge: e,
|
|
1435
1485
|
sourceNode: s,
|
|
1436
1486
|
targetNode: t
|
|
1437
|
-
},
|
|
1438
|
-
line:
|
|
1487
|
+
}, h.frustumCulled = !0, {
|
|
1488
|
+
line: h,
|
|
1439
1489
|
source: e.source,
|
|
1440
1490
|
target: e.target
|
|
1441
1491
|
};
|
|
@@ -1444,26 +1494,26 @@ class Mt {
|
|
|
1444
1494
|
* Highlights an edge
|
|
1445
1495
|
*/
|
|
1446
1496
|
highlightEdge(e) {
|
|
1447
|
-
e.line.material instanceof
|
|
1497
|
+
e.line.material instanceof y.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
|
|
1448
1498
|
}
|
|
1449
1499
|
/**
|
|
1450
1500
|
* Resets an edge to default appearance
|
|
1451
1501
|
*/
|
|
1452
1502
|
unhighlightEdge(e) {
|
|
1453
|
-
e.line.material instanceof
|
|
1503
|
+
e.line.material instanceof y.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
|
|
1454
1504
|
}
|
|
1455
1505
|
/**
|
|
1456
1506
|
* Updates an edge's positions
|
|
1457
1507
|
*/
|
|
1458
1508
|
updateEdgePositions(e, s, t) {
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1509
|
+
const o = e.line.geometry.attributes.position, i = o.array;
|
|
1510
|
+
i[0] = s.x, i[1] = s.y, i[2] = s.z, i[3] = t.x, i[4] = t.y, i[5] = t.z, o.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
|
|
1461
1511
|
}
|
|
1462
1512
|
/**
|
|
1463
1513
|
* Disposes an edge's resources
|
|
1464
1514
|
*/
|
|
1465
1515
|
disposeEdge(e) {
|
|
1466
|
-
e.line.geometry.dispose(), e.line.material instanceof
|
|
1516
|
+
e.line.geometry.dispose(), e.line.material instanceof y.Material && e.line.material.dispose();
|
|
1467
1517
|
}
|
|
1468
1518
|
/**
|
|
1469
1519
|
* Dispose factory resources
|
|
@@ -1472,7 +1522,7 @@ class Mt {
|
|
|
1472
1522
|
this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
|
|
1473
1523
|
}
|
|
1474
1524
|
}
|
|
1475
|
-
class
|
|
1525
|
+
class Et {
|
|
1476
1526
|
constructor(e, s = [50, 100, 200], t = !0) {
|
|
1477
1527
|
l(this, "camera");
|
|
1478
1528
|
l(this, "lodDistances");
|
|
@@ -1484,16 +1534,16 @@ class wt {
|
|
|
1484
1534
|
*/
|
|
1485
1535
|
getLODLevel(e) {
|
|
1486
1536
|
if (!this.enabled)
|
|
1487
|
-
return
|
|
1488
|
-
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y,
|
|
1489
|
-
return
|
|
1537
|
+
return X.HIGH;
|
|
1538
|
+
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, o = e.z - this.camera.position.z, i = Math.sqrt(s * s + t * t + o * o);
|
|
1539
|
+
return i < this.lodDistances[0] ? X.HIGH : i < this.lodDistances[1] ? X.MEDIUM : X.LOW;
|
|
1490
1540
|
}
|
|
1491
1541
|
/**
|
|
1492
1542
|
* Checks if a node should be visible based on distance
|
|
1493
1543
|
*/
|
|
1494
1544
|
shouldRenderNode(e, s = 500) {
|
|
1495
|
-
const t = e.x - this.camera.position.x,
|
|
1496
|
-
return Math.sqrt(t * t +
|
|
1545
|
+
const t = e.x - this.camera.position.x, o = e.y - this.camera.position.y, i = e.z - this.camera.position.z;
|
|
1546
|
+
return Math.sqrt(t * t + o * o + i * i) < s;
|
|
1497
1547
|
}
|
|
1498
1548
|
/**
|
|
1499
1549
|
* Sets the LOD distances
|
|
@@ -1508,13 +1558,13 @@ class wt {
|
|
|
1508
1558
|
this.enabled = e;
|
|
1509
1559
|
}
|
|
1510
1560
|
}
|
|
1511
|
-
class
|
|
1561
|
+
class Ct {
|
|
1512
1562
|
constructor(e, s = !0) {
|
|
1513
1563
|
l(this, "camera");
|
|
1514
1564
|
l(this, "frustum");
|
|
1515
1565
|
l(this, "projScreenMatrix");
|
|
1516
1566
|
l(this, "enabled");
|
|
1517
|
-
this.camera = e, this.frustum = new
|
|
1567
|
+
this.camera = e, this.frustum = new y.Frustum(), this.projScreenMatrix = new y.Matrix4(), this.enabled = s;
|
|
1518
1568
|
}
|
|
1519
1569
|
/**
|
|
1520
1570
|
* Updates the frustum from the camera
|
|
@@ -1530,7 +1580,7 @@ class Et {
|
|
|
1530
1580
|
*/
|
|
1531
1581
|
isPointVisible(e) {
|
|
1532
1582
|
if (!this.enabled) return !0;
|
|
1533
|
-
const s = new
|
|
1583
|
+
const s = new y.Vector3(e.x, e.y, e.z);
|
|
1534
1584
|
return this.frustum.containsPoint(s);
|
|
1535
1585
|
}
|
|
1536
1586
|
/**
|
|
@@ -1538,8 +1588,8 @@ class Et {
|
|
|
1538
1588
|
*/
|
|
1539
1589
|
isSphereVisible(e, s) {
|
|
1540
1590
|
if (!this.enabled) return !0;
|
|
1541
|
-
const t = new
|
|
1542
|
-
new
|
|
1591
|
+
const t = new y.Sphere(
|
|
1592
|
+
new y.Vector3(e.x, e.y, e.z),
|
|
1543
1593
|
s
|
|
1544
1594
|
);
|
|
1545
1595
|
return this.frustum.intersectsSphere(t);
|
|
@@ -1549,14 +1599,14 @@ class Et {
|
|
|
1549
1599
|
*/
|
|
1550
1600
|
isLineVisible(e, s) {
|
|
1551
1601
|
if (!this.enabled) return !0;
|
|
1552
|
-
const t = new
|
|
1553
|
-
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(
|
|
1602
|
+
const t = new y.Vector3(e.x, e.y, e.z), o = new y.Vector3(s.x, s.y, s.z);
|
|
1603
|
+
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(o))
|
|
1554
1604
|
return !0;
|
|
1555
|
-
const
|
|
1605
|
+
const i = new y.Vector3(
|
|
1556
1606
|
(e.x + s.x) / 2,
|
|
1557
1607
|
(e.y + s.y) / 2,
|
|
1558
1608
|
(e.z + s.z) / 2
|
|
1559
|
-
), a =
|
|
1609
|
+
), a = i.distanceTo(t), r = new y.Sphere(i, a);
|
|
1560
1610
|
return this.frustum.intersectsSphere(r);
|
|
1561
1611
|
}
|
|
1562
1612
|
/**
|
|
@@ -1566,7 +1616,7 @@ class Et {
|
|
|
1566
1616
|
this.enabled = e;
|
|
1567
1617
|
}
|
|
1568
1618
|
}
|
|
1569
|
-
class
|
|
1619
|
+
class Nt {
|
|
1570
1620
|
constructor(e, s) {
|
|
1571
1621
|
l(this, "sceneManager");
|
|
1572
1622
|
l(this, "raycaster");
|
|
@@ -1580,7 +1630,7 @@ class Ct {
|
|
|
1580
1630
|
l(this, "hoveredEdgeKey", null);
|
|
1581
1631
|
l(this, "nodeObjects", []);
|
|
1582
1632
|
l(this, "edgeObjects", []);
|
|
1583
|
-
this.sceneManager = e, this.container = s, this.raycaster = new
|
|
1633
|
+
this.sceneManager = e, this.container = s, this.raycaster = new y.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new y.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
|
|
1584
1634
|
}
|
|
1585
1635
|
/**
|
|
1586
1636
|
* Updates the list of node objects to raycast against
|
|
@@ -1639,23 +1689,23 @@ class Ct {
|
|
|
1639
1689
|
this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
|
|
1640
1690
|
return;
|
|
1641
1691
|
}
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1692
|
+
const o = this.getIntersectedEdge(e), i = o ? `${o.edge.source}-${o.edge.target}` : null;
|
|
1693
|
+
i !== this.hoveredEdgeKey && (this.hoveredEdgeKey = i, this.onEdgeHover && this.onEdgeHover(o)), this.container.style.cursor = o ? "pointer" : "default";
|
|
1644
1694
|
}
|
|
1645
1695
|
/**
|
|
1646
1696
|
* Gets the intersected node from a mouse event
|
|
1647
1697
|
*/
|
|
1648
1698
|
getIntersectedNode(e) {
|
|
1649
|
-
var
|
|
1699
|
+
var o;
|
|
1650
1700
|
const s = this.container.getBoundingClientRect();
|
|
1651
1701
|
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);
|
|
1652
1702
|
const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1653
1703
|
if (t.length > 0) {
|
|
1654
|
-
let
|
|
1655
|
-
for (;
|
|
1656
|
-
if ((
|
|
1657
|
-
return
|
|
1658
|
-
|
|
1704
|
+
let i = t[0].object;
|
|
1705
|
+
for (; i; ) {
|
|
1706
|
+
if ((o = i.userData) != null && o.nodeData)
|
|
1707
|
+
return i.userData.nodeData;
|
|
1708
|
+
i = i.parent;
|
|
1659
1709
|
}
|
|
1660
1710
|
}
|
|
1661
1711
|
return null;
|
|
@@ -1668,13 +1718,13 @@ class Ct {
|
|
|
1668
1718
|
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);
|
|
1669
1719
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1670
1720
|
if (t.length > 0) {
|
|
1671
|
-
const
|
|
1672
|
-
if (
|
|
1721
|
+
const o = t[0].object, i = o.userData;
|
|
1722
|
+
if (i != null && i.edge && (i != null && i.sourceNode) && (i != null && i.targetNode))
|
|
1673
1723
|
return {
|
|
1674
|
-
edge:
|
|
1675
|
-
sourceNode:
|
|
1676
|
-
targetNode:
|
|
1677
|
-
edgeLine:
|
|
1724
|
+
edge: i.edge,
|
|
1725
|
+
sourceNode: i.sourceNode,
|
|
1726
|
+
targetNode: i.targetNode,
|
|
1727
|
+
edgeLine: o
|
|
1678
1728
|
};
|
|
1679
1729
|
}
|
|
1680
1730
|
return null;
|
|
@@ -1683,14 +1733,14 @@ class Ct {
|
|
|
1683
1733
|
* Performs a raycast and returns the intersected node ID
|
|
1684
1734
|
*/
|
|
1685
1735
|
getIntersectedNodeId(e, s) {
|
|
1686
|
-
var
|
|
1736
|
+
var i;
|
|
1687
1737
|
const t = this.container.getBoundingClientRect();
|
|
1688
1738
|
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);
|
|
1689
|
-
const
|
|
1690
|
-
if (
|
|
1691
|
-
let a =
|
|
1739
|
+
const o = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1740
|
+
if (o.length > 0) {
|
|
1741
|
+
let a = o[0].object;
|
|
1692
1742
|
for (; a; ) {
|
|
1693
|
-
if ((
|
|
1743
|
+
if ((i = a.userData) != null && i.nodeId)
|
|
1694
1744
|
return a.userData.nodeId;
|
|
1695
1745
|
a = a.parent;
|
|
1696
1746
|
}
|
|
@@ -1704,7 +1754,7 @@ class Ct {
|
|
|
1704
1754
|
this.container.removeEventListener("click", this.handleClick), this.container.removeEventListener("mousemove", this.handleMouseMove);
|
|
1705
1755
|
}
|
|
1706
1756
|
}
|
|
1707
|
-
class
|
|
1757
|
+
class St {
|
|
1708
1758
|
constructor(e) {
|
|
1709
1759
|
l(this, "container");
|
|
1710
1760
|
l(this, "panel", null);
|
|
@@ -1774,10 +1824,10 @@ class Nt {
|
|
|
1774
1824
|
this.currentNodeId = e.id;
|
|
1775
1825
|
let t;
|
|
1776
1826
|
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1827
|
+
const o = this.panel.querySelector('[data-action="expand"]'), i = this.panel.querySelector("[data-depth-select]");
|
|
1828
|
+
o && this.onExpand && o.addEventListener("click", () => {
|
|
1779
1829
|
if (this.currentNodeId) {
|
|
1780
|
-
const r =
|
|
1830
|
+
const r = i ? parseInt(i.value, 10) : 1;
|
|
1781
1831
|
this.onExpand(this.currentNodeId, r);
|
|
1782
1832
|
}
|
|
1783
1833
|
});
|
|
@@ -1936,7 +1986,7 @@ class Nt {
|
|
|
1936
1986
|
<div class="neighbors-section">
|
|
1937
1987
|
<div class="neighbors-title">Connected To</div>
|
|
1938
1988
|
${s.slice(0, 5).map(
|
|
1939
|
-
(
|
|
1989
|
+
(o) => `<span class="neighbor-chip">${this.escapeHtml(o.label)}</span>`
|
|
1940
1990
|
).join("")}
|
|
1941
1991
|
${s.length > 5 ? `<span class="neighbor-chip">+${s.length - 5} more</span>` : ""}
|
|
1942
1992
|
</div>
|
|
@@ -1990,7 +2040,7 @@ class Nt {
|
|
|
1990
2040
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
1991
2041
|
}
|
|
1992
2042
|
}
|
|
1993
|
-
class
|
|
2043
|
+
class zt {
|
|
1994
2044
|
constructor(e) {
|
|
1995
2045
|
l(this, "container");
|
|
1996
2046
|
l(this, "panel", null);
|
|
@@ -2054,10 +2104,10 @@ class St {
|
|
|
2054
2104
|
show(e, s, t) {
|
|
2055
2105
|
if (!this.panel) return;
|
|
2056
2106
|
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2057
|
-
let
|
|
2058
|
-
this.panelTemplate ?
|
|
2059
|
-
const
|
|
2060
|
-
|
|
2107
|
+
let o;
|
|
2108
|
+
this.panelTemplate ? o = this.panelTemplate(e, s, t) : o = this.generateDefaultContent(e, s, t), this.panel.innerHTML = o;
|
|
2109
|
+
const i = this.panel.querySelector('[data-action="close"]');
|
|
2110
|
+
i && i.addEventListener("click", () => {
|
|
2061
2111
|
this.hide(), this.onClose && this.onClose();
|
|
2062
2112
|
});
|
|
2063
2113
|
const a = this.panel.querySelector('[data-action="goto-source"]');
|
|
@@ -2073,7 +2123,7 @@ class St {
|
|
|
2073
2123
|
* Generates default panel content
|
|
2074
2124
|
*/
|
|
2075
2125
|
generateDefaultContent(e, s, t) {
|
|
2076
|
-
const
|
|
2126
|
+
const o = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", i = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = e.relationship || "connected to";
|
|
2077
2127
|
return `
|
|
2078
2128
|
<style>
|
|
2079
2129
|
.force-graph-edge-panel .panel-header {
|
|
@@ -2183,7 +2233,7 @@ class St {
|
|
|
2183
2233
|
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2184
2234
|
<div class="node-type">Source</div>
|
|
2185
2235
|
<div class="node-card-header">
|
|
2186
|
-
<span class="color-dot" style="background: ${
|
|
2236
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2187
2237
|
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2188
2238
|
</div>
|
|
2189
2239
|
</div>
|
|
@@ -2193,7 +2243,7 @@ class St {
|
|
|
2193
2243
|
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2194
2244
|
<div class="node-type">Target</div>
|
|
2195
2245
|
<div class="node-card-header">
|
|
2196
|
-
<span class="color-dot" style="background: ${
|
|
2246
|
+
<span class="color-dot" style="background: ${i}; box-shadow: 0 0 8px ${i}80;"></span>
|
|
2197
2247
|
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2198
2248
|
</div>
|
|
2199
2249
|
</div>
|
|
@@ -2235,7 +2285,7 @@ class St {
|
|
|
2235
2285
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2236
2286
|
}
|
|
2237
2287
|
}
|
|
2238
|
-
class
|
|
2288
|
+
class Tt {
|
|
2239
2289
|
constructor() {
|
|
2240
2290
|
l(this, "tooltip", null);
|
|
2241
2291
|
l(this, "visible", !1);
|
|
@@ -2281,14 +2331,14 @@ class zt {
|
|
|
2281
2331
|
*/
|
|
2282
2332
|
positionTooltip(e, s) {
|
|
2283
2333
|
if (!this.tooltip) return;
|
|
2284
|
-
const t = this.tooltip.getBoundingClientRect(),
|
|
2334
|
+
const t = this.tooltip.getBoundingClientRect(), o = window.innerWidth, i = window.innerHeight;
|
|
2285
2335
|
let a = e + 15, r = s + 15;
|
|
2286
|
-
a + t.width >
|
|
2336
|
+
a + t.width > o - 10 && (a = e - t.width - 15), r + t.height > i - 10 && (r = s - t.height - 15), a < 10 && (a = 10), r < 10 && (r = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${r}px`;
|
|
2287
2337
|
}
|
|
2288
2338
|
/**
|
|
2289
2339
|
* Shows the tooltip with edge info
|
|
2290
2340
|
*/
|
|
2291
|
-
show(e, s, t,
|
|
2341
|
+
show(e, s, t, o, i) {
|
|
2292
2342
|
if (!this.tooltip) return;
|
|
2293
2343
|
const a = e.relationship || "connected to";
|
|
2294
2344
|
this.tooltip.innerHTML = `
|
|
@@ -2303,7 +2353,7 @@ class zt {
|
|
|
2303
2353
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
2304
2354
|
</div>
|
|
2305
2355
|
</div>
|
|
2306
|
-
`, this.positionTooltip(
|
|
2356
|
+
`, this.positionTooltip(o, i), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2307
2357
|
}
|
|
2308
2358
|
/**
|
|
2309
2359
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2480,25 +2530,25 @@ class kt {
|
|
|
2480
2530
|
this.searchResults.innerHTML = '<div class="f3d-no-results">No results found</div>', this.searchResults.style.display = "block";
|
|
2481
2531
|
return;
|
|
2482
2532
|
}
|
|
2483
|
-
let
|
|
2484
|
-
s.length > 0 && (
|
|
2485
|
-
const a =
|
|
2486
|
-
|
|
2487
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2488
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2533
|
+
let o = "";
|
|
2534
|
+
s.length > 0 && (o += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((i) => {
|
|
2535
|
+
const a = i.type || "Node";
|
|
2536
|
+
o += `
|
|
2537
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(i.id)}">
|
|
2538
|
+
<div class="f3d-result-label">${this.escapeHtml(i.label)}</div>
|
|
2489
2539
|
<div class="f3d-result-type">${this.escapeHtml(a)}</div>
|
|
2490
2540
|
</div>
|
|
2491
2541
|
`;
|
|
2492
|
-
}), s.length > 10 && (
|
|
2493
|
-
|
|
2494
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2542
|
+
}), s.length > 10 && (o += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (o += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: i, sourceNode: a, targetNode: r }) => {
|
|
2543
|
+
o += `
|
|
2544
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(i.source)}">
|
|
2495
2545
|
<div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(r.label)}</div>
|
|
2496
|
-
<div class="f3d-result-relationship">${this.escapeHtml(
|
|
2546
|
+
<div class="f3d-result-relationship">${this.escapeHtml(i.relationship || "connected")}</div>
|
|
2497
2547
|
</div>
|
|
2498
2548
|
`;
|
|
2499
|
-
}), t.length > 5 && (
|
|
2500
|
-
|
|
2501
|
-
const a =
|
|
2549
|
+
}), t.length > 5 && (o += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = o, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((i) => {
|
|
2550
|
+
i.addEventListener("click", () => {
|
|
2551
|
+
const a = i.dataset.nodeId;
|
|
2502
2552
|
a && (this.onResultClick(a), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2503
2553
|
});
|
|
2504
2554
|
});
|
|
@@ -2603,7 +2653,7 @@ class Pt {
|
|
|
2603
2653
|
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2604
2654
|
}
|
|
2605
2655
|
}
|
|
2606
|
-
const
|
|
2656
|
+
const Rt = {
|
|
2607
2657
|
backgroundColor: "#0a0a0a",
|
|
2608
2658
|
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
2609
2659
|
nodeRadius: 24,
|
|
@@ -2616,7 +2666,7 @@ const Tt = {
|
|
|
2616
2666
|
damping: 0.85
|
|
2617
2667
|
// Fast energy dissipation
|
|
2618
2668
|
};
|
|
2619
|
-
class
|
|
2669
|
+
class It {
|
|
2620
2670
|
constructor(e, s = {}) {
|
|
2621
2671
|
l(this, "container");
|
|
2622
2672
|
l(this, "canvas");
|
|
@@ -2643,7 +2693,7 @@ class Lt {
|
|
|
2643
2693
|
l(this, "isSimulating", !0);
|
|
2644
2694
|
// Resize handler
|
|
2645
2695
|
l(this, "resizeHandler");
|
|
2646
|
-
this.container = e, this.options = { ...
|
|
2696
|
+
this.container = e, this.options = { ...Rt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
|
|
2647
2697
|
const t = this.canvas.getContext("2d");
|
|
2648
2698
|
if (!t)
|
|
2649
2699
|
throw new Error("Failed to get 2D context");
|
|
@@ -2656,31 +2706,31 @@ class Lt {
|
|
|
2656
2706
|
setupInteractions() {
|
|
2657
2707
|
this.canvas.addEventListener("wheel", (e) => {
|
|
2658
2708
|
e.preventDefault();
|
|
2659
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2660
|
-
this.transform.x = t - (t - this.transform.x) * r, this.transform.y =
|
|
2709
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = e.deltaY > 0 ? 0.9 : 1.1, a = Math.max(0.1, Math.min(5, this.transform.scale * i)), r = a / this.transform.scale;
|
|
2710
|
+
this.transform.x = t - (t - this.transform.x) * r, this.transform.y = o - (o - this.transform.y) * r, this.transform.scale = a;
|
|
2661
2711
|
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2662
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2712
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = this.screenToWorld(t, o), a = this.findNodeAt(i.x, i.y);
|
|
2663
2713
|
this.dragStartPos = { x: e.clientX, y: e.clientY }, a ? (this.isDragging = !0, this.draggedNode = a, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2664
2714
|
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2665
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2715
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, o = e.clientY - s.top, i = this.screenToWorld(t, o);
|
|
2666
2716
|
if (this.isDragging && this.draggedNode)
|
|
2667
|
-
this.draggedNode.x =
|
|
2717
|
+
this.draggedNode.x = i.x, this.draggedNode.y = i.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2668
2718
|
else if (this.isPanning) {
|
|
2669
2719
|
const a = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
|
|
2670
2720
|
this.transform.x += a, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2671
2721
|
} else {
|
|
2672
|
-
const a = this.findNodeAt(
|
|
2722
|
+
const a = this.findNodeAt(i.x, i.y);
|
|
2673
2723
|
if (a !== this.hoveredNode && (this.hoveredNode = a, a ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(a.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !a) {
|
|
2674
|
-
const r = this.findEdgeAt(
|
|
2724
|
+
const r = this.findEdgeAt(i.x, i.y);
|
|
2675
2725
|
r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
|
|
2676
2726
|
}
|
|
2677
2727
|
}
|
|
2678
2728
|
}), this.canvas.addEventListener("mouseup", (e) => {
|
|
2679
|
-
const s = Math.abs(e.clientX - this.dragStartPos.x), t = Math.abs(e.clientY - this.dragStartPos.y),
|
|
2729
|
+
const s = Math.abs(e.clientX - this.dragStartPos.x), t = Math.abs(e.clientY - this.dragStartPos.y), o = s < 5 && t < 5;
|
|
2680
2730
|
if (this.isDragging && this.draggedNode)
|
|
2681
|
-
|
|
2682
|
-
else if (
|
|
2683
|
-
const
|
|
2731
|
+
o && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
2732
|
+
else if (o) {
|
|
2733
|
+
const i = this.canvas.getBoundingClientRect(), a = this.screenToWorld(e.clientX - i.left, e.clientY - i.top), r = this.findEdgeAt(a.x, a.y);
|
|
2684
2734
|
r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
|
|
2685
2735
|
}
|
|
2686
2736
|
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
@@ -2696,21 +2746,21 @@ class Lt {
|
|
|
2696
2746
|
}
|
|
2697
2747
|
findNodeAt(e, s) {
|
|
2698
2748
|
for (const t of this.nodes.values()) {
|
|
2699
|
-
const
|
|
2700
|
-
if (Math.sqrt(
|
|
2749
|
+
const o = t.x - e, i = t.y - s;
|
|
2750
|
+
if (Math.sqrt(o * o + i * i) < t.radius)
|
|
2701
2751
|
return t;
|
|
2702
2752
|
}
|
|
2703
2753
|
return null;
|
|
2704
2754
|
}
|
|
2705
2755
|
findEdgeAt(e, s) {
|
|
2706
|
-
for (const
|
|
2707
|
-
const
|
|
2708
|
-
if (!
|
|
2709
|
-
const r = a.x -
|
|
2710
|
-
if (
|
|
2711
|
-
const p = Math.max(0, Math.min(1, ((e -
|
|
2712
|
-
if (Math.sqrt(u * u +
|
|
2713
|
-
return
|
|
2756
|
+
for (const o of this.edges) {
|
|
2757
|
+
const i = this.nodes.get(o.source), a = this.nodes.get(o.target);
|
|
2758
|
+
if (!i || !a) continue;
|
|
2759
|
+
const r = a.x - i.x, c = a.y - i.y, h = r * r + c * c;
|
|
2760
|
+
if (h === 0) continue;
|
|
2761
|
+
const p = Math.max(0, Math.min(1, ((e - i.x) * r + (s - i.y) * c) / h)), x = i.x + p * r, m = i.y + p * c, u = e - x, f = s - m;
|
|
2762
|
+
if (Math.sqrt(u * u + f * f) < 12)
|
|
2763
|
+
return o;
|
|
2714
2764
|
}
|
|
2715
2765
|
return null;
|
|
2716
2766
|
}
|
|
@@ -2723,69 +2773,69 @@ class Lt {
|
|
|
2723
2773
|
simulate() {
|
|
2724
2774
|
const e = Array.from(this.nodes.values()), s = e.length;
|
|
2725
2775
|
if (s === 0) return;
|
|
2726
|
-
const t = 60,
|
|
2727
|
-
let
|
|
2776
|
+
const t = 60, o = 5;
|
|
2777
|
+
let i = 0;
|
|
2728
2778
|
for (let r = 0; r < s; r++)
|
|
2729
2779
|
for (let c = r + 1; c < s; c++) {
|
|
2730
|
-
const
|
|
2731
|
-
let x = p.x -
|
|
2780
|
+
const h = e[r], p = e[c];
|
|
2781
|
+
let x = p.x - h.x, m = p.y - h.y, u = Math.sqrt(x * x + m * m);
|
|
2732
2782
|
if (u < t * 3) {
|
|
2733
2783
|
u < 1 && (u = 1);
|
|
2734
|
-
const
|
|
2735
|
-
|
|
2784
|
+
const f = this.options.repulsionStrength / (u * u), b = x / u * f, M = m / u * f;
|
|
2785
|
+
h.vx -= b, h.vy -= M, p.vx += b, p.vy += M;
|
|
2736
2786
|
}
|
|
2737
2787
|
}
|
|
2738
2788
|
const a = 80;
|
|
2739
2789
|
for (const r of this.edges) {
|
|
2740
|
-
const c = this.nodes.get(r.source),
|
|
2741
|
-
if (!c || !
|
|
2742
|
-
let p =
|
|
2743
|
-
|
|
2744
|
-
const
|
|
2745
|
-
c.vx += b, c.vy += M,
|
|
2790
|
+
const c = this.nodes.get(r.source), h = this.nodes.get(r.target);
|
|
2791
|
+
if (!c || !h) continue;
|
|
2792
|
+
let p = h.x - c.x, x = h.y - c.y, m = Math.sqrt(p * p + x * x);
|
|
2793
|
+
m < 1 && (m = 1);
|
|
2794
|
+
const f = (m - a) * this.options.attractionStrength, b = p / m * f, M = x / m * f;
|
|
2795
|
+
c.vx += b, c.vy += M, h.vx -= b, h.vy -= M;
|
|
2746
2796
|
}
|
|
2747
2797
|
for (const r of e) {
|
|
2748
2798
|
if (this.draggedNode === r) continue;
|
|
2749
2799
|
r.vx *= this.options.damping, r.vy *= this.options.damping;
|
|
2750
2800
|
const c = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
|
|
2751
|
-
c >
|
|
2801
|
+
c > o && (r.vx = r.vx / c * o, r.vy = r.vy / c * o), r.x += r.vx, r.y += r.vy, i += r.vx * r.vx + r.vy * r.vy;
|
|
2752
2802
|
}
|
|
2753
|
-
|
|
2803
|
+
i < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
2754
2804
|
}
|
|
2755
2805
|
render() {
|
|
2756
2806
|
const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
|
|
2757
2807
|
e.fillStyle = this.options.backgroundColor, e.fillRect(0, 0, s, t), this.renderGrid(s, t), e.save(), e.translate(this.transform.x, this.transform.y), e.scale(this.transform.scale, this.transform.scale), this.renderEdges(), this.renderNodes(), e.restore();
|
|
2758
2808
|
}
|
|
2759
2809
|
renderGrid(e, s) {
|
|
2760
|
-
const t = this.ctx,
|
|
2810
|
+
const t = this.ctx, o = 40 * this.transform.scale, i = 1.5, a = this.transform.x % o, r = this.transform.y % o;
|
|
2761
2811
|
t.fillStyle = this.options.gridColor;
|
|
2762
|
-
for (let c = a; c < e; c +=
|
|
2763
|
-
for (let
|
|
2764
|
-
t.beginPath(), t.arc(c,
|
|
2812
|
+
for (let c = a; c < e; c += o)
|
|
2813
|
+
for (let h = r; h < s; h += o)
|
|
2814
|
+
t.beginPath(), t.arc(c, h, i, 0, Math.PI * 2), t.fill();
|
|
2765
2815
|
}
|
|
2766
2816
|
renderEdges() {
|
|
2767
2817
|
const e = this.ctx;
|
|
2768
2818
|
for (const s of this.edges) {
|
|
2769
|
-
const t = this.nodes.get(s.source),
|
|
2770
|
-
if (!t || !
|
|
2771
|
-
const
|
|
2772
|
-
|
|
2819
|
+
const t = this.nodes.get(s.source), o = this.nodes.get(s.target);
|
|
2820
|
+
if (!t || !o) continue;
|
|
2821
|
+
const i = s === this.hoveredEdge, a = e.createLinearGradient(t.x, t.y, o.x, o.y);
|
|
2822
|
+
i ? (a.addColorStop(0, "rgba(255, 153, 102, 0.8)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), a.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (a.addColorStop(0, "rgba(255, 153, 102, 0.3)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), a.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(o.x, o.y), e.strokeStyle = a, e.stroke();
|
|
2773
2823
|
}
|
|
2774
2824
|
}
|
|
2775
2825
|
renderNodes() {
|
|
2776
2826
|
const e = this.ctx;
|
|
2777
2827
|
for (const s of this.nodes.values()) {
|
|
2778
|
-
const t = s === this.hoveredNode,
|
|
2779
|
-
if (t ||
|
|
2780
|
-
const
|
|
2828
|
+
const t = s === this.hoveredNode, o = s === this.selectedNode, i = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), a = s.radius * (t ? 1.1 : 1);
|
|
2829
|
+
if (t || o || i) {
|
|
2830
|
+
const f = e.createRadialGradient(
|
|
2781
2831
|
s.x,
|
|
2782
2832
|
s.y,
|
|
2783
2833
|
a * 0.5,
|
|
2784
2834
|
s.x,
|
|
2785
2835
|
s.y,
|
|
2786
2836
|
a * 2
|
|
2787
|
-
), b = t ||
|
|
2788
|
-
|
|
2837
|
+
), b = t || o ? 0.4 : 0.25;
|
|
2838
|
+
f.addColorStop(0, `rgba(255, 153, 102, ${b})`), f.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = f, e.beginPath(), e.arc(s.x, s.y, a * 2, 0, Math.PI * 2), e.fill();
|
|
2789
2839
|
}
|
|
2790
2840
|
const r = e.createRadialGradient(
|
|
2791
2841
|
s.x - a * 0.3,
|
|
@@ -2794,16 +2844,16 @@ class Lt {
|
|
|
2794
2844
|
s.x,
|
|
2795
2845
|
s.y,
|
|
2796
2846
|
a
|
|
2797
|
-
), c = s.color >> 16 & 255,
|
|
2798
|
-
r.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255,
|
|
2847
|
+
), c = s.color >> 16 & 255, h = s.color >> 8 & 255, p = s.color & 255;
|
|
2848
|
+
r.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255, h + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${c}, ${h}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, c - 40)}, ${Math.max(0, h - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, a, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - a * 0.25, s.y - a * 0.25, a * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
|
|
2799
2849
|
const x = a * 1.6;
|
|
2800
|
-
let
|
|
2850
|
+
let m = s.label, u = e.measureText(m).width;
|
|
2801
2851
|
if (u > x) {
|
|
2802
|
-
for (; u > x &&
|
|
2803
|
-
|
|
2804
|
-
|
|
2852
|
+
for (; u > x && m.length > 3; )
|
|
2853
|
+
m = m.slice(0, -1), u = e.measureText(m + "...").width;
|
|
2854
|
+
m += "...";
|
|
2805
2855
|
}
|
|
2806
|
-
e.shadowColor = "rgba(0, 0, 0, 0.5)", e.shadowBlur = 3, e.fillText(
|
|
2856
|
+
e.shadowColor = "rgba(0, 0, 0, 0.5)", e.shadowBlur = 3, e.fillText(m, s.x, s.y), e.shadowBlur = 0;
|
|
2807
2857
|
}
|
|
2808
2858
|
}
|
|
2809
2859
|
/**
|
|
@@ -2811,14 +2861,14 @@ class Lt {
|
|
|
2811
2861
|
*/
|
|
2812
2862
|
setData(e) {
|
|
2813
2863
|
this.nodes.clear(), this.edges = [], this.nodeIdToIndex.clear(), e.nodes.forEach((s, t) => {
|
|
2814
|
-
const
|
|
2864
|
+
const o = s.position || {
|
|
2815
2865
|
x: (Math.random() - 0.5) * 300,
|
|
2816
2866
|
y: (Math.random() - 0.5) * 300
|
|
2817
|
-
},
|
|
2867
|
+
}, i = {
|
|
2818
2868
|
id: s.id,
|
|
2819
2869
|
label: s.label,
|
|
2820
|
-
x:
|
|
2821
|
-
y:
|
|
2870
|
+
x: o.x,
|
|
2871
|
+
y: o.y,
|
|
2822
2872
|
vx: 0,
|
|
2823
2873
|
vy: 0,
|
|
2824
2874
|
color: s.color || 16750950,
|
|
@@ -2826,7 +2876,7 @@ class Lt {
|
|
|
2826
2876
|
radius: this.options.nodeRadius,
|
|
2827
2877
|
data: s
|
|
2828
2878
|
};
|
|
2829
|
-
this.nodes.set(s.id,
|
|
2879
|
+
this.nodes.set(s.id, i), this.nodeIdToIndex.set(s.id, t);
|
|
2830
2880
|
}), this.edges = e.edges.map((s) => ({
|
|
2831
2881
|
source: s.source,
|
|
2832
2882
|
target: s.target,
|
|
@@ -2902,19 +2952,19 @@ class Lt {
|
|
|
2902
2952
|
focusOnNode(e) {
|
|
2903
2953
|
const s = this.nodes.get(e);
|
|
2904
2954
|
if (!s) return;
|
|
2905
|
-
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale,
|
|
2906
|
-
const p = performance.now() - c, x = Math.min(p / r, 1),
|
|
2907
|
-
this.transform.x =
|
|
2955
|
+
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, o = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, i = this.transform.x, a = this.transform.y, r = 500, c = performance.now(), h = () => {
|
|
2956
|
+
const p = performance.now() - c, x = Math.min(p / r, 1), m = 1 - Math.pow(1 - x, 3);
|
|
2957
|
+
this.transform.x = i + (t - i) * m, this.transform.y = a + (o - a) * m, x < 1 ? requestAnimationFrame(h) : this.selectedNode = s;
|
|
2908
2958
|
};
|
|
2909
|
-
|
|
2959
|
+
h();
|
|
2910
2960
|
}
|
|
2911
2961
|
/**
|
|
2912
2962
|
* Updates node positions from 3D data
|
|
2913
2963
|
*/
|
|
2914
2964
|
syncFrom3D(e) {
|
|
2915
2965
|
e.forEach((s, t) => {
|
|
2916
|
-
const
|
|
2917
|
-
|
|
2966
|
+
const o = this.nodes.get(t);
|
|
2967
|
+
o && (o.x = s.position.x * 3, o.y = s.position.y * 3, o.vx = 0, o.vy = 0);
|
|
2918
2968
|
}), this.isSimulating = !1;
|
|
2919
2969
|
}
|
|
2920
2970
|
/**
|
|
@@ -2948,7 +2998,7 @@ class Lt {
|
|
|
2948
2998
|
this.animationId && cancelAnimationFrame(this.animationId), window.removeEventListener("resize", this.resizeHandler), this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
|
|
2949
2999
|
}
|
|
2950
3000
|
}
|
|
2951
|
-
class
|
|
3001
|
+
class Ht {
|
|
2952
3002
|
// Store graph data for view switching
|
|
2953
3003
|
constructor(e, s = {}) {
|
|
2954
3004
|
// Options
|
|
@@ -2983,23 +3033,23 @@ class Ft {
|
|
|
2983
3033
|
l(this, "devControls", null);
|
|
2984
3034
|
l(this, "viewMode", "3d");
|
|
2985
3035
|
l(this, "graphData", null);
|
|
2986
|
-
this.options = { ...
|
|
3036
|
+
this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new vt(), this.nodeFactory = new Mt(
|
|
2987
3037
|
this.materialFactory,
|
|
2988
|
-
this.options.nodeRadius ??
|
|
2989
|
-
this.options.lodSegments ??
|
|
2990
|
-
this.options.defaultNodeColor ??
|
|
2991
|
-
), this.edgeFactory = new
|
|
3038
|
+
this.options.nodeRadius ?? P.nodeRadius,
|
|
3039
|
+
this.options.lodSegments ?? P.lodSegments,
|
|
3040
|
+
this.options.defaultNodeColor ?? P.defaultNodeColor
|
|
3041
|
+
), this.edgeFactory = new wt(
|
|
2992
3042
|
this.materialFactory,
|
|
2993
|
-
this.options.edgeColor ??
|
|
2994
|
-
this.options.edgeOpacity ??
|
|
2995
|
-
), this.sceneManager = new
|
|
3043
|
+
this.options.edgeColor ?? P.edgeColor,
|
|
3044
|
+
this.options.edgeOpacity ?? P.edgeOpacity
|
|
3045
|
+
), this.sceneManager = new mt(this.container, this.options), this.lodManager = new Et(
|
|
2996
3046
|
this.sceneManager.camera,
|
|
2997
|
-
this.options.lodDistances ??
|
|
2998
|
-
this.options.enableLOD ??
|
|
2999
|
-
), this.frustumCuller = new
|
|
3047
|
+
this.options.lodDistances ?? P.lodDistances,
|
|
3048
|
+
this.options.enableLOD ?? P.enableLOD
|
|
3049
|
+
), this.frustumCuller = new Ct(
|
|
3000
3050
|
this.sceneManager.camera,
|
|
3001
|
-
this.options.enableEdgeCulling ??
|
|
3002
|
-
), this.nodeManager = new
|
|
3051
|
+
this.options.enableEdgeCulling ?? P.enableEdgeCulling
|
|
3052
|
+
), this.nodeManager = new te(this.sceneManager, this.nodeFactory), this.edgeManager = new yt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Le(
|
|
3003
3053
|
this.nodeManager.getAllNodes(),
|
|
3004
3054
|
this.edgeManager.getAllEdges(),
|
|
3005
3055
|
{
|
|
@@ -3009,12 +3059,12 @@ class Ft {
|
|
|
3009
3059
|
useBarnesHut: this.options.useBarnesHut,
|
|
3010
3060
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3011
3061
|
}
|
|
3012
|
-
), this.rendererManager = new
|
|
3062
|
+
), this.rendererManager = new bt(
|
|
3013
3063
|
this.sceneManager,
|
|
3014
3064
|
() => this.onSimulate(),
|
|
3015
3065
|
() => this.onRender(),
|
|
3016
|
-
this.options.targetFPS ??
|
|
3017
|
-
), this.raycasterManager = new
|
|
3066
|
+
this.options.targetFPS ?? P.targetFPS
|
|
3067
|
+
), this.raycasterManager = new Nt(this.sceneManager, this.container), this.panelManager = new St(this.container), this.edgePanelManager = new zt(this.container), this.edgeTooltipManager = new Tt(), this.edgePanelManager.setNodeClickCallback((t) => {
|
|
3018
3068
|
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3019
3069
|
this.showNodePanel(t);
|
|
3020
3070
|
}, 400);
|
|
@@ -3056,8 +3106,8 @@ class Ft {
|
|
|
3056
3106
|
onEdgeHover(e) {
|
|
3057
3107
|
if (e) {
|
|
3058
3108
|
this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
|
|
3059
|
-
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2,
|
|
3060
|
-
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t,
|
|
3109
|
+
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, o = s.top + s.height / 2;
|
|
3110
|
+
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, o), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
|
|
3061
3111
|
} else
|
|
3062
3112
|
this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
|
|
3063
3113
|
}
|
|
@@ -3066,7 +3116,7 @@ class Ft {
|
|
|
3066
3116
|
*/
|
|
3067
3117
|
onNodeClick(e) {
|
|
3068
3118
|
this.edgePanelManager.hide();
|
|
3069
|
-
const t = this.edgeManager.getNeighborIds(e.id).map((
|
|
3119
|
+
const t = this.edgeManager.getNeighborIds(e.id).map((o) => this.nodeManager.getNode(o)).filter((o) => o !== void 0);
|
|
3070
3120
|
this.options.showPanel !== !1 && this.panelManager.show(e, t), this.options.onNodeClick && this.options.onNodeClick(e), this.emit("nodeClick", e);
|
|
3071
3121
|
}
|
|
3072
3122
|
/**
|
|
@@ -3100,12 +3150,22 @@ class Ft {
|
|
|
3100
3150
|
* Sets the graph data
|
|
3101
3151
|
*/
|
|
3102
3152
|
setData(e) {
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3153
|
+
var a;
|
|
3154
|
+
const s = (a = e.data) != null && a.nodes ? e.data : e, o = (s.edges || s.links || []).map((r) => ({
|
|
3155
|
+
...r,
|
|
3156
|
+
relationship: r.relationship || r.label || "related_to"
|
|
3157
|
+
})), i = {
|
|
3158
|
+
nodes: s.nodes || [],
|
|
3159
|
+
edges: o
|
|
3160
|
+
};
|
|
3161
|
+
if (this.graphData = i, this.edgeManager.clear(), this.nodeManager.clear(), i.nodes && Array.isArray(i.nodes)) {
|
|
3162
|
+
te.setExpectedNodeCount(i.nodes.length);
|
|
3163
|
+
for (const r of i.nodes)
|
|
3164
|
+
this.addNode(r);
|
|
3165
|
+
}
|
|
3166
|
+
if (i.edges && Array.isArray(i.edges))
|
|
3167
|
+
for (const r of i.edges)
|
|
3168
|
+
this.addEdge(r);
|
|
3109
3169
|
this.graphEngine = new Le(
|
|
3110
3170
|
this.nodeManager.getAllNodes(),
|
|
3111
3171
|
this.edgeManager.getAllEdges(),
|
|
@@ -3116,14 +3176,14 @@ class Ft {
|
|
|
3116
3176
|
useBarnesHut: this.options.useBarnesHut,
|
|
3117
3177
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3118
3178
|
}
|
|
3119
|
-
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(
|
|
3179
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(i);
|
|
3120
3180
|
}
|
|
3121
3181
|
/**
|
|
3122
3182
|
* Adds a node to the graph
|
|
3123
3183
|
* @returns true if added, false if node already exists or invalid
|
|
3124
3184
|
*/
|
|
3125
3185
|
addNode(e) {
|
|
3126
|
-
if (!
|
|
3186
|
+
if (!He(e))
|
|
3127
3187
|
return !1;
|
|
3128
3188
|
const s = this.nodeManager.addNode(e);
|
|
3129
3189
|
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
|
|
@@ -3133,7 +3193,7 @@ class Ft {
|
|
|
3133
3193
|
* @returns true if removed, false if not found
|
|
3134
3194
|
*/
|
|
3135
3195
|
removeNode(e) {
|
|
3136
|
-
if (!
|
|
3196
|
+
if (!pt(e))
|
|
3137
3197
|
return !1;
|
|
3138
3198
|
this.edgeManager.removeEdgesForNode(e);
|
|
3139
3199
|
const s = this.nodeManager.removeNode(e);
|
|
@@ -3150,7 +3210,7 @@ class Ft {
|
|
|
3150
3210
|
* @returns true if added, false if edge already exists or nodes don't exist
|
|
3151
3211
|
*/
|
|
3152
3212
|
addEdge(e) {
|
|
3153
|
-
if (!
|
|
3213
|
+
if (!De(e))
|
|
3154
3214
|
return !1;
|
|
3155
3215
|
const s = this.edgeManager.addEdge(e);
|
|
3156
3216
|
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), s;
|
|
@@ -3170,20 +3230,20 @@ class Ft {
|
|
|
3170
3230
|
* @param fetchFn - Optional fetch function to override the default
|
|
3171
3231
|
*/
|
|
3172
3232
|
async expandNode(e, s = 1, t) {
|
|
3173
|
-
const
|
|
3174
|
-
if (!
|
|
3233
|
+
const o = t ?? this.options.onExpand;
|
|
3234
|
+
if (!o)
|
|
3175
3235
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
3176
3236
|
try {
|
|
3177
|
-
const
|
|
3178
|
-
if (
|
|
3179
|
-
for (const a of
|
|
3237
|
+
const i = await o(e, s);
|
|
3238
|
+
if (i.nodes && Array.isArray(i.nodes))
|
|
3239
|
+
for (const a of i.nodes)
|
|
3180
3240
|
this.addNode(a);
|
|
3181
|
-
if (
|
|
3182
|
-
for (const a of
|
|
3241
|
+
if (i.edges && Array.isArray(i.edges))
|
|
3242
|
+
for (const a of i.edges)
|
|
3183
3243
|
this.addEdge(a);
|
|
3184
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
3185
|
-
} catch (
|
|
3186
|
-
return console.error("[ForceGraph3D] Error expanding node:",
|
|
3244
|
+
return this.panelManager.hide(), this.emit("expand", e, i), !0;
|
|
3245
|
+
} catch (i) {
|
|
3246
|
+
return console.error("[ForceGraph3D] Error expanding node:", i), !1;
|
|
3187
3247
|
}
|
|
3188
3248
|
}
|
|
3189
3249
|
/**
|
|
@@ -3230,13 +3290,13 @@ class Ft {
|
|
|
3230
3290
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
3231
3291
|
return;
|
|
3232
3292
|
}
|
|
3233
|
-
const
|
|
3234
|
-
x:
|
|
3235
|
-
y:
|
|
3236
|
-
z:
|
|
3237
|
-
},
|
|
3238
|
-
const
|
|
3239
|
-
|
|
3293
|
+
const o = t.position, i = this.sceneManager.camera, a = this.sceneManager.controls, r = i.position.clone().sub(a.target).normalize(), c = {
|
|
3294
|
+
x: o.x + r.x * s,
|
|
3295
|
+
y: o.y + r.y * s,
|
|
3296
|
+
z: o.z + r.z * s
|
|
3297
|
+
}, h = { x: i.position.x, y: i.position.y, z: i.position.z }, p = { x: a.target.x, y: a.target.y, z: a.target.z }, x = 800, m = performance.now(), u = () => {
|
|
3298
|
+
const f = performance.now() - m, b = Math.min(f / x, 1), M = 1 - Math.pow(1 - b, 3);
|
|
3299
|
+
i.position.x = h.x + (c.x - h.x) * M, i.position.y = h.y + (c.y - h.y) * M, i.position.z = h.z + (c.z - h.z) * M, a.target.x = p.x + (o.x - p.x) * M, a.target.y = p.y + (o.y - p.y) * M, a.target.z = p.z + (o.z - p.z) * M, a.update(), b < 1 && requestAnimationFrame(u);
|
|
3240
3300
|
};
|
|
3241
3301
|
u();
|
|
3242
3302
|
}
|
|
@@ -3245,21 +3305,21 @@ class Ft {
|
|
|
3245
3305
|
* Camera targets the midpoint and zooms out enough to see both nodes
|
|
3246
3306
|
*/
|
|
3247
3307
|
focusOnEdge(e, s, t = 1.5) {
|
|
3248
|
-
const
|
|
3249
|
-
if (!
|
|
3308
|
+
const o = this.nodeManager.getNode(e), i = this.nodeManager.getNode(s);
|
|
3309
|
+
if (!o || !i) {
|
|
3250
3310
|
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3251
3311
|
return;
|
|
3252
3312
|
}
|
|
3253
3313
|
const a = this.sceneManager.camera, r = this.sceneManager.controls, c = {
|
|
3254
|
-
x: (
|
|
3255
|
-
y: (
|
|
3256
|
-
z: (
|
|
3257
|
-
},
|
|
3258
|
-
x: c.x +
|
|
3259
|
-
y: c.y +
|
|
3260
|
-
z: c.z +
|
|
3261
|
-
}, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800,
|
|
3262
|
-
const
|
|
3314
|
+
x: (o.position.x + i.position.x) / 2,
|
|
3315
|
+
y: (o.position.y + i.position.y) / 2,
|
|
3316
|
+
z: (o.position.z + i.position.z) / 2
|
|
3317
|
+
}, h = i.position.x - o.position.x, p = i.position.y - o.position.y, x = i.position.z - o.position.z, m = Math.sqrt(h * h + p * p + x * x), u = Math.max(m * t, 40), f = a.position.clone().sub(r.target).normalize(), b = {
|
|
3318
|
+
x: c.x + f.x * u,
|
|
3319
|
+
y: c.y + f.y * u,
|
|
3320
|
+
z: c.z + f.z * u
|
|
3321
|
+
}, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, R = performance.now(), Y = () => {
|
|
3322
|
+
const k = performance.now() - R, H = Math.min(k / O, 1), E = 1 - Math.pow(1 - H, 3);
|
|
3263
3323
|
a.position.x = M.x + (b.x - M.x) * E, a.position.y = M.y + (b.y - M.y) * E, a.position.z = M.z + (b.z - M.z) * E, r.target.x = N.x + (c.x - N.x) * E, r.target.y = N.y + (c.y - N.y) * E, r.target.z = N.z + (c.z - N.z) * E, r.update(), H < 1 && requestAnimationFrame(Y);
|
|
3264
3324
|
};
|
|
3265
3325
|
Y();
|
|
@@ -3283,28 +3343,28 @@ class Ft {
|
|
|
3283
3343
|
searchNodes(e) {
|
|
3284
3344
|
if (!e || e.trim() === "")
|
|
3285
3345
|
return [];
|
|
3286
|
-
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(),
|
|
3287
|
-
return t.forEach((
|
|
3288
|
-
var
|
|
3289
|
-
const a = (
|
|
3290
|
-
(a || r || c) &&
|
|
3291
|
-
}),
|
|
3346
|
+
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), o = [];
|
|
3347
|
+
return t.forEach((i) => {
|
|
3348
|
+
var h, p, x;
|
|
3349
|
+
const a = (h = i.label) == null ? void 0 : h.toLowerCase().includes(s), r = (p = i.id) == null ? void 0 : p.toLowerCase().includes(s), c = (x = i.type) == null ? void 0 : x.toLowerCase().includes(s);
|
|
3350
|
+
(a || r || c) && o.push(i);
|
|
3351
|
+
}), o;
|
|
3292
3352
|
}
|
|
3293
3353
|
/**
|
|
3294
3354
|
* Searches edges by relationship (case-insensitive)
|
|
3295
3355
|
* @returns Array of matching edges with source/target node info
|
|
3296
3356
|
*/
|
|
3297
3357
|
searchEdges(e) {
|
|
3298
|
-
var
|
|
3358
|
+
var i;
|
|
3299
3359
|
if (!e || e.trim() === "")
|
|
3300
3360
|
return [];
|
|
3301
|
-
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(),
|
|
3361
|
+
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), o = [];
|
|
3302
3362
|
for (const a of t)
|
|
3303
|
-
if ((
|
|
3304
|
-
const c = this.nodeManager.getNode(a.source),
|
|
3305
|
-
c &&
|
|
3363
|
+
if ((i = a.relationship) == null ? void 0 : i.toLowerCase().includes(s)) {
|
|
3364
|
+
const c = this.nodeManager.getNode(a.source), h = this.nodeManager.getNode(a.target);
|
|
3365
|
+
c && h && o.push({ edge: a, sourceNode: c, targetNode: h });
|
|
3306
3366
|
}
|
|
3307
|
-
return
|
|
3367
|
+
return o;
|
|
3308
3368
|
}
|
|
3309
3369
|
/**
|
|
3310
3370
|
* Gets all nodes as an array
|
|
@@ -3335,7 +3395,7 @@ class Ft {
|
|
|
3335
3395
|
* Switches between 2D and 3D view modes
|
|
3336
3396
|
*/
|
|
3337
3397
|
switchView(e) {
|
|
3338
|
-
this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new
|
|
3398
|
+
this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new It(this.container, {
|
|
3339
3399
|
backgroundColor: "#0a0a0a",
|
|
3340
3400
|
nodeRadius: 24,
|
|
3341
3401
|
onNodeClick: (s) => {
|
|
@@ -3346,11 +3406,11 @@ class Ft {
|
|
|
3346
3406
|
},
|
|
3347
3407
|
onEdgeHover: (s, t) => {
|
|
3348
3408
|
if (s && t) {
|
|
3349
|
-
const
|
|
3350
|
-
|
|
3409
|
+
const o = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3410
|
+
o && i && this.edgeTooltipManager.show(
|
|
3351
3411
|
s,
|
|
3352
|
-
i,
|
|
3353
3412
|
o,
|
|
3413
|
+
i,
|
|
3354
3414
|
t.clientX,
|
|
3355
3415
|
t.clientY
|
|
3356
3416
|
);
|
|
@@ -3358,8 +3418,8 @@ class Ft {
|
|
|
3358
3418
|
this.edgeTooltipManager.hide();
|
|
3359
3419
|
},
|
|
3360
3420
|
onEdgeClick: (s) => {
|
|
3361
|
-
const t = this.nodeManager.getNode(s.source),
|
|
3362
|
-
t &&
|
|
3421
|
+
const t = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
|
|
3422
|
+
t && o && this.edgePanelManager.show(s, t, o);
|
|
3363
3423
|
}
|
|
3364
3424
|
}), this.graphData && (this.forceGraph2D.setData(this.graphData), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())))) : (this.forceGraph2D && this.forceGraph2D.hide(), this.sceneManager.renderer.domElement.style.display = "block", this.rendererManager.start()), this.emit("viewChange", e));
|
|
3365
3425
|
}
|
|
@@ -3374,7 +3434,7 @@ class Ft {
|
|
|
3374
3434
|
*/
|
|
3375
3435
|
emit(e, ...s) {
|
|
3376
3436
|
const t = this.eventCallbacks.get(e);
|
|
3377
|
-
t && t.forEach((
|
|
3437
|
+
t && t.forEach((o) => o(...s));
|
|
3378
3438
|
}
|
|
3379
3439
|
/**
|
|
3380
3440
|
* Sets physics parameters for both 3D and 2D views
|
|
@@ -3451,17 +3511,17 @@ class Ft {
|
|
|
3451
3511
|
`, this.container.appendChild(this.devControls);
|
|
3452
3512
|
const e = this.devControls.querySelector("#dev-repulsion"), s = this.devControls.querySelector("#dev-attraction"), t = this.devControls.querySelector("#dev-damping");
|
|
3453
3513
|
e == null || e.addEventListener("input", () => {
|
|
3454
|
-
const
|
|
3455
|
-
this.setPhysicsParams({ repulsionStrength:
|
|
3514
|
+
const o = parseFloat(e.value);
|
|
3515
|
+
this.setPhysicsParams({ repulsionStrength: o }), this.devControls.querySelector("#dev-repulsion-val").textContent = o.toString();
|
|
3456
3516
|
}), s == null || s.addEventListener("input", () => {
|
|
3457
|
-
const
|
|
3458
|
-
this.setPhysicsParams({ attractionStrength:
|
|
3517
|
+
const o = parseFloat(s.value) / 1e3;
|
|
3518
|
+
this.setPhysicsParams({ attractionStrength: o }), this.devControls.querySelector("#dev-attraction-val").textContent = o.toFixed(3);
|
|
3459
3519
|
}), t == null || t.addEventListener("input", () => {
|
|
3460
|
-
const
|
|
3461
|
-
this.setPhysicsParams({ damping:
|
|
3520
|
+
const o = parseFloat(t.value) / 100;
|
|
3521
|
+
this.setPhysicsParams({ damping: o }), this.devControls.querySelector("#dev-damping-val").textContent = o.toFixed(2);
|
|
3462
3522
|
}), setInterval(() => {
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3523
|
+
const o = this.devControls.querySelector("#dev-node-count"), i = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
|
|
3524
|
+
o && (o.textContent = this.getNodeCount().toString()), i && (i.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
|
|
3465
3525
|
}, 500);
|
|
3466
3526
|
}
|
|
3467
3527
|
/**
|
|
@@ -3471,7 +3531,7 @@ class Ft {
|
|
|
3471
3531
|
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
3472
3532
|
}
|
|
3473
3533
|
}
|
|
3474
|
-
const
|
|
3534
|
+
const Oe = [
|
|
3475
3535
|
"Alpha",
|
|
3476
3536
|
"Beta",
|
|
3477
3537
|
"Gamma",
|
|
@@ -3502,7 +3562,7 @@ const Ie = [
|
|
|
3502
3562
|
"Link",
|
|
3503
3563
|
"Point",
|
|
3504
3564
|
"Vertex"
|
|
3505
|
-
],
|
|
3565
|
+
], ee = [
|
|
3506
3566
|
"connects to",
|
|
3507
3567
|
"links with",
|
|
3508
3568
|
"relates to",
|
|
@@ -3513,7 +3573,7 @@ const Ie = [
|
|
|
3513
3573
|
"partners with",
|
|
3514
3574
|
"collaborates with",
|
|
3515
3575
|
"supports"
|
|
3516
|
-
],
|
|
3576
|
+
], Fe = [
|
|
3517
3577
|
16777215,
|
|
3518
3578
|
// White
|
|
3519
3579
|
16750950,
|
|
@@ -3525,14 +3585,14 @@ const Ie = [
|
|
|
3525
3585
|
16746564
|
|
3526
3586
|
// Darker tangerine
|
|
3527
3587
|
];
|
|
3528
|
-
function
|
|
3588
|
+
function Dt(d = 30) {
|
|
3529
3589
|
const e = [], s = [];
|
|
3530
|
-
for (let
|
|
3531
|
-
const
|
|
3590
|
+
for (let o = 0; o < d; o++) {
|
|
3591
|
+
const i = o < Oe.length ? Oe[o] : `Node ${o + 1}`;
|
|
3532
3592
|
e.push({
|
|
3533
|
-
id: `node-${
|
|
3534
|
-
label:
|
|
3535
|
-
color:
|
|
3593
|
+
id: `node-${o}`,
|
|
3594
|
+
label: i,
|
|
3595
|
+
color: Fe[o % Fe.length],
|
|
3536
3596
|
position: {
|
|
3537
3597
|
x: (Math.random() - 0.5) * 60,
|
|
3538
3598
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -3540,43 +3600,43 @@ function Ht(h = 30) {
|
|
|
3540
3600
|
}
|
|
3541
3601
|
});
|
|
3542
3602
|
}
|
|
3543
|
-
for (let
|
|
3544
|
-
const
|
|
3603
|
+
for (let o = 1; o < d; o++) {
|
|
3604
|
+
const i = Math.floor(Math.random() * o);
|
|
3545
3605
|
s.push({
|
|
3546
|
-
source: `node-${
|
|
3547
|
-
target: `node-${
|
|
3548
|
-
relationship:
|
|
3606
|
+
source: `node-${o}`,
|
|
3607
|
+
target: `node-${i}`,
|
|
3608
|
+
relationship: ee[Math.floor(Math.random() * ee.length)]
|
|
3549
3609
|
});
|
|
3550
3610
|
}
|
|
3551
|
-
const t = Math.floor(
|
|
3552
|
-
for (let
|
|
3553
|
-
const
|
|
3554
|
-
let a = Math.floor(Math.random() *
|
|
3555
|
-
|
|
3556
|
-
const r = `node-${
|
|
3611
|
+
const t = Math.floor(d * 0.5);
|
|
3612
|
+
for (let o = 0; o < t; o++) {
|
|
3613
|
+
const i = Math.floor(Math.random() * d);
|
|
3614
|
+
let a = Math.floor(Math.random() * d);
|
|
3615
|
+
i === a && (a = (a + 1) % d);
|
|
3616
|
+
const r = `node-${i}`, c = `node-${a}`;
|
|
3557
3617
|
s.some(
|
|
3558
3618
|
(p) => p.source === r && p.target === c || p.source === c && p.target === r
|
|
3559
3619
|
) || s.push({
|
|
3560
3620
|
source: r,
|
|
3561
3621
|
target: c,
|
|
3562
|
-
relationship:
|
|
3622
|
+
relationship: ee[Math.floor(Math.random() * ee.length)]
|
|
3563
3623
|
});
|
|
3564
3624
|
}
|
|
3565
3625
|
return { nodes: e, edges: s };
|
|
3566
3626
|
}
|
|
3567
|
-
function
|
|
3568
|
-
const e = [], s = [], t = Math.ceil(
|
|
3569
|
-
for (let
|
|
3570
|
-
|
|
3627
|
+
function At(d = 1e3) {
|
|
3628
|
+
const e = [], s = [], t = Math.ceil(d / 50), o = [];
|
|
3629
|
+
for (let i = 0; i < t; i++)
|
|
3630
|
+
o.push({
|
|
3571
3631
|
x: (Math.random() - 0.5) * 200,
|
|
3572
3632
|
y: (Math.random() - 0.5) * 200,
|
|
3573
3633
|
z: (Math.random() - 0.5) * 200
|
|
3574
3634
|
});
|
|
3575
|
-
for (let
|
|
3576
|
-
const a = i
|
|
3635
|
+
for (let i = 0; i < d; i++) {
|
|
3636
|
+
const a = o[i % t];
|
|
3577
3637
|
e.push({
|
|
3578
|
-
id: `node-${
|
|
3579
|
-
label: `N${
|
|
3638
|
+
id: `node-${i}`,
|
|
3639
|
+
label: `N${i}`,
|
|
3580
3640
|
position: {
|
|
3581
3641
|
x: a.x + (Math.random() - 0.5) * 40,
|
|
3582
3642
|
y: a.y + (Math.random() - 0.5) * 40,
|
|
@@ -3584,16 +3644,16 @@ function Dt(h = 1e3) {
|
|
|
3584
3644
|
}
|
|
3585
3645
|
});
|
|
3586
3646
|
}
|
|
3587
|
-
for (let
|
|
3588
|
-
const a = Math.floor(
|
|
3647
|
+
for (let i = 1; i < d; i++) {
|
|
3648
|
+
const a = Math.floor(i / 50) * 50, r = a === 0 ? Math.floor(Math.random() * i) : a + Math.floor(Math.random() * Math.min(i - a, 50));
|
|
3589
3649
|
s.push({
|
|
3590
|
-
source: `node-${
|
|
3591
|
-
target: `node-${Math.min(r,
|
|
3650
|
+
source: `node-${i}`,
|
|
3651
|
+
target: `node-${Math.min(r, i - 1)}`,
|
|
3592
3652
|
relationship: "links to"
|
|
3593
3653
|
});
|
|
3594
3654
|
}
|
|
3595
|
-
for (let
|
|
3596
|
-
const a =
|
|
3655
|
+
for (let i = 1; i < t; i++) {
|
|
3656
|
+
const a = i * 50, r = (i - 1) * 50 + Math.floor(Math.random() * 50);
|
|
3597
3657
|
s.push({
|
|
3598
3658
|
source: `node-${a}`,
|
|
3599
3659
|
target: `node-${r}`,
|
|
@@ -3603,13 +3663,13 @@ function Dt(h = 1e3) {
|
|
|
3603
3663
|
return { nodes: e, edges: s };
|
|
3604
3664
|
}
|
|
3605
3665
|
export {
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3666
|
+
P as DEFAULT_OPTIONS,
|
|
3667
|
+
Ht as ForceGraph3D,
|
|
3668
|
+
X as LODLevel,
|
|
3609
3669
|
D as createEdgeKey,
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3670
|
+
At as generateLargeSampleData,
|
|
3671
|
+
Dt as generateSampleData,
|
|
3672
|
+
De as validateEdgeData,
|
|
3673
|
+
He as validateNodeData
|
|
3614
3674
|
};
|
|
3615
3675
|
//# sourceMappingURL=force-3d-graph.js.map
|