force-3d-graph 1.3.8 → 1.3.9
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 +1149 -686
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +184 -30
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/dist/index.d.ts +37 -0
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var it = Object.defineProperty;
|
|
2
|
-
var
|
|
3
|
-
var l = (
|
|
4
|
-
import * as
|
|
5
|
-
import { EventDispatcher as
|
|
6
|
-
const
|
|
2
|
+
var nt = (p, e, s) => e in p ? it(p, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : p[e] = s;
|
|
3
|
+
var l = (p, e, s) => nt(p, typeof e != "symbol" ? e + "" : e, s);
|
|
4
|
+
import * as y from "three";
|
|
5
|
+
import { EventDispatcher as ot, Vector3 as k, MOUSE as j, TOUCH as B, Spherical as ze, Quaternion as Pe, Vector2 as P, Ray as at, Plane as rt, MathUtils as lt } from "three";
|
|
6
|
+
const C = {
|
|
7
7
|
backgroundColor: 657930,
|
|
8
8
|
cameraPosition: { x: 0, y: 0, z: 80 },
|
|
9
9
|
cameraFov: 75,
|
|
@@ -30,98 +30,108 @@ const P = {
|
|
|
30
30
|
showViewToggle: !0,
|
|
31
31
|
showLegend: !0,
|
|
32
32
|
targetFPS: 60,
|
|
33
|
-
maxVisibleNodes: 1e4
|
|
33
|
+
maxVisibleNodes: 1e4,
|
|
34
|
+
performanceMode: "balanced",
|
|
35
|
+
enableAdaptivePerformance: !0,
|
|
36
|
+
minPixelRatio: 0.6,
|
|
37
|
+
maxPixelRatio: 2,
|
|
38
|
+
labelRenderMode: "adaptive",
|
|
39
|
+
labelDistance: 140,
|
|
40
|
+
disableEdgeInteractionsInLowMode: !0,
|
|
41
|
+
edgeBatchThreshold: 1200,
|
|
42
|
+
enableWorkerPhysics: !0,
|
|
43
|
+
workerPhysicsNodeThreshold: 1500
|
|
34
44
|
};
|
|
35
|
-
var
|
|
45
|
+
var K = /* @__PURE__ */ ((p) => (p[p.HIGH = 0] = "HIGH", p[p.MEDIUM = 1] = "MEDIUM", p[p.LOW = 2] = "LOW", p))(K || {});
|
|
36
46
|
function ct() {
|
|
37
|
-
const
|
|
38
|
-
return
|
|
47
|
+
const p = document.createElement("div");
|
|
48
|
+
return p.id = "force-graph-3d-container", p.style.cssText = `
|
|
39
49
|
width: 100%;
|
|
40
50
|
height: 100%;
|
|
41
51
|
position: absolute;
|
|
42
52
|
top: 0;
|
|
43
53
|
left: 0;
|
|
44
54
|
overflow: hidden;
|
|
45
|
-
`, document.body.appendChild(
|
|
55
|
+
`, document.body.appendChild(p), p;
|
|
46
56
|
}
|
|
47
|
-
function ht(
|
|
48
|
-
return
|
|
57
|
+
function ht(p) {
|
|
58
|
+
return p && p instanceof HTMLElement ? p : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ct());
|
|
49
59
|
}
|
|
50
|
-
function
|
|
51
|
-
const e =
|
|
60
|
+
function Re(p) {
|
|
61
|
+
const e = p.getBoundingClientRect();
|
|
52
62
|
return {
|
|
53
63
|
width: e.width || window.innerWidth,
|
|
54
64
|
height: e.height || window.innerHeight
|
|
55
65
|
};
|
|
56
66
|
}
|
|
57
|
-
function
|
|
58
|
-
if (!
|
|
67
|
+
function ce(p) {
|
|
68
|
+
if (!p || typeof p != "object")
|
|
59
69
|
return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
|
|
60
|
-
const e =
|
|
70
|
+
const e = p;
|
|
61
71
|
return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !pt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
|
|
62
72
|
}
|
|
63
|
-
function
|
|
64
|
-
if (!
|
|
73
|
+
function he(p) {
|
|
74
|
+
if (!p || typeof p != "object")
|
|
65
75
|
return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
|
|
66
|
-
const e =
|
|
76
|
+
const e = p;
|
|
67
77
|
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;
|
|
68
78
|
}
|
|
69
|
-
function dt(
|
|
70
|
-
return typeof
|
|
79
|
+
function dt(p) {
|
|
80
|
+
return typeof p != "string" || p.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
|
|
71
81
|
}
|
|
72
|
-
function pt(
|
|
73
|
-
if (!
|
|
74
|
-
const e =
|
|
82
|
+
function pt(p) {
|
|
83
|
+
if (!p || typeof p != "object") return !1;
|
|
84
|
+
const e = p;
|
|
75
85
|
return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
|
|
76
86
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
87
|
+
function At(p, e) {
|
|
88
|
+
return p === e ? `${p}-${e}` : p < e ? `${p}-${e}` : `${e}-${p}`;
|
|
79
89
|
}
|
|
80
|
-
const
|
|
81
|
-
class ut extends
|
|
90
|
+
const Le = { type: "change" }, le = { type: "start" }, Te = { type: "end" }, Q = new at(), Ie = new rt(), gt = Math.cos(70 * lt.DEG2RAD);
|
|
91
|
+
class ut extends ot {
|
|
82
92
|
constructor(e, s) {
|
|
83
|
-
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new
|
|
84
|
-
return
|
|
93
|
+
super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new k(), this.cursor = new k(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: j.ROTATE, MIDDLE: j.DOLLY, RIGHT: j.PAN }, this.touches = { ONE: B.ROTATE, TWO: B.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
|
|
94
|
+
return a.phi;
|
|
85
95
|
}, this.getAzimuthalAngle = function() {
|
|
86
|
-
return
|
|
96
|
+
return a.theta;
|
|
87
97
|
}, this.getDistance = function() {
|
|
88
98
|
return this.object.position.distanceTo(this.target);
|
|
89
|
-
}, this.listenToKeyEvents = function(
|
|
90
|
-
|
|
99
|
+
}, this.listenToKeyEvents = function(r) {
|
|
100
|
+
r.addEventListener("keydown", ae), this._domElementKeyEvents = r;
|
|
91
101
|
}, this.stopListenToKeyEvents = function() {
|
|
92
102
|
this._domElementKeyEvents.removeEventListener("keydown", ae), this._domElementKeyEvents = null;
|
|
93
103
|
}, this.saveState = function() {
|
|
94
104
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
95
105
|
}, this.reset = function() {
|
|
96
|
-
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(
|
|
106
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Le), t.update(), n = i.NONE;
|
|
97
107
|
}, this.update = function() {
|
|
98
|
-
const
|
|
108
|
+
const r = new k(), g = new Pe().setFromUnitVectors(e.up, new k(0, 1, 0)), v = g.clone().invert(), w = new k(), S = new Pe(), D = new k(), z = 2 * Math.PI;
|
|
99
109
|
return function(st = null) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
let
|
|
103
|
-
isFinite(
|
|
110
|
+
const ke = t.object.position;
|
|
111
|
+
r.copy(ke).sub(t.target), r.applyQuaternion(g), a.setFromVector3(r), t.autoRotate && n === i.NONE && $(Ae(st)), t.enableDamping ? (a.theta += c.theta * t.dampingFactor, a.phi += c.phi * t.dampingFactor) : (a.theta += c.theta, a.phi += c.phi);
|
|
112
|
+
let T = t.minAzimuthAngle, I = t.maxAzimuthAngle;
|
|
113
|
+
isFinite(T) && isFinite(I) && (T < -Math.PI ? T += z : T > Math.PI && (T -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), T <= I ? a.theta = Math.max(T, Math.min(I, a.theta)) : a.theta = a.theta > (T + I) / 2 ? Math.max(T, a.theta) : Math.min(I, a.theta)), a.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, a.phi)), a.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(d, t.dampingFactor) : t.target.add(d), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && F || t.object.isOrthographicCamera ? a.radius = ne(a.radius) : a.radius = ne(a.radius * h), r.setFromSpherical(a), r.applyQuaternion(v), ke.copy(t.target).add(r), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, d.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), d.set(0, 0, 0));
|
|
104
114
|
let re = !1;
|
|
105
|
-
if (t.zoomToCursor &&
|
|
106
|
-
let
|
|
115
|
+
if (t.zoomToCursor && F) {
|
|
116
|
+
let W = null;
|
|
107
117
|
if (t.object.isPerspectiveCamera) {
|
|
108
|
-
const U =
|
|
109
|
-
|
|
110
|
-
const Z = U -
|
|
118
|
+
const U = r.length();
|
|
119
|
+
W = ne(U * h);
|
|
120
|
+
const Z = U - W;
|
|
111
121
|
t.object.position.addScaledVector(G, Z), t.object.updateMatrixWorld();
|
|
112
122
|
} else if (t.object.isOrthographicCamera) {
|
|
113
|
-
const U = new
|
|
114
|
-
U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
115
|
-
const Z = new
|
|
116
|
-
Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(),
|
|
123
|
+
const U = new k(R.x, R.y, 0);
|
|
124
|
+
U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), re = !0;
|
|
125
|
+
const Z = new k(R.x, R.y, 0);
|
|
126
|
+
Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(), W = r.length();
|
|
117
127
|
} else
|
|
118
128
|
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
|
|
119
|
-
|
|
120
|
-
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom /
|
|
121
|
-
return
|
|
129
|
+
W !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(W).add(t.object.position) : (Q.origin.copy(t.object.position), Q.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Q.direction)) < gt ? e.lookAt(t.target) : (Ie.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Ie, t.target))));
|
|
130
|
+
} else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), re = !0);
|
|
131
|
+
return h = 1, F = !1, re || w.distanceToSquared(t.object.position) > o || 8 * (1 - S.dot(t.object.quaternion)) > o || D.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Le), w.copy(t.object.position), S.copy(t.object.quaternion), D.copy(t.target), !0) : !1;
|
|
122
132
|
};
|
|
123
133
|
}(), this.dispose = function() {
|
|
124
|
-
t.domElement.removeEventListener("contextmenu",
|
|
134
|
+
t.domElement.removeEventListener("contextmenu", Se), t.domElement.removeEventListener("pointerdown", we), t.domElement.removeEventListener("pointercancel", Y), t.domElement.removeEventListener("wheel", Ee), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", Y), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ae), t._domElementKeyEvents = null);
|
|
125
135
|
};
|
|
126
136
|
const t = this, i = {
|
|
127
137
|
NONE: -1,
|
|
@@ -133,175 +143,175 @@ class ut extends nt {
|
|
|
133
143
|
TOUCH_DOLLY_PAN: 5,
|
|
134
144
|
TOUCH_DOLLY_ROTATE: 6
|
|
135
145
|
};
|
|
136
|
-
let
|
|
137
|
-
const
|
|
138
|
-
let
|
|
139
|
-
const
|
|
140
|
-
let
|
|
141
|
-
const E = [],
|
|
146
|
+
let n = i.NONE;
|
|
147
|
+
const o = 1e-6, a = new ze(), c = new ze();
|
|
148
|
+
let h = 1;
|
|
149
|
+
const d = new k(), f = new P(), m = new P(), u = new P(), b = new P(), x = new P(), M = new P(), N = new P(), O = new P(), L = new P(), G = new k(), R = new P();
|
|
150
|
+
let F = !1;
|
|
151
|
+
const E = [], _ = {};
|
|
142
152
|
let te = !1;
|
|
143
|
-
function
|
|
144
|
-
return
|
|
153
|
+
function Ae(r) {
|
|
154
|
+
return r !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * r : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
|
|
145
155
|
}
|
|
146
|
-
function
|
|
147
|
-
const g = Math.abs(
|
|
156
|
+
function V(r) {
|
|
157
|
+
const g = Math.abs(r * 0.01);
|
|
148
158
|
return Math.pow(0.95, t.zoomSpeed * g);
|
|
149
159
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
160
|
+
function $(r) {
|
|
161
|
+
c.theta -= r;
|
|
152
162
|
}
|
|
153
|
-
function
|
|
154
|
-
|
|
163
|
+
function X(r) {
|
|
164
|
+
c.phi -= r;
|
|
155
165
|
}
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
return function(v,
|
|
159
|
-
|
|
166
|
+
const de = function() {
|
|
167
|
+
const r = new k();
|
|
168
|
+
return function(v, w) {
|
|
169
|
+
r.setFromMatrixColumn(w, 0), r.multiplyScalar(-v), d.add(r);
|
|
160
170
|
};
|
|
161
|
-
}(),
|
|
162
|
-
const
|
|
163
|
-
return function(v,
|
|
164
|
-
t.screenSpacePanning === !0 ?
|
|
171
|
+
}(), pe = function() {
|
|
172
|
+
const r = new k();
|
|
173
|
+
return function(v, w) {
|
|
174
|
+
t.screenSpacePanning === !0 ? r.setFromMatrixColumn(w, 1) : (r.setFromMatrixColumn(w, 0), r.crossVectors(t.object.up, r)), r.multiplyScalar(v), d.add(r);
|
|
165
175
|
};
|
|
166
|
-
}(),
|
|
167
|
-
const
|
|
168
|
-
return function(v,
|
|
169
|
-
const
|
|
176
|
+
}(), A = function() {
|
|
177
|
+
const r = new k();
|
|
178
|
+
return function(v, w) {
|
|
179
|
+
const S = t.domElement;
|
|
170
180
|
if (t.object.isPerspectiveCamera) {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
let z =
|
|
174
|
-
z *= Math.tan(t.object.fov / 2 * Math.PI / 180),
|
|
175
|
-
} else t.object.isOrthographicCamera ? (
|
|
181
|
+
const D = t.object.position;
|
|
182
|
+
r.copy(D).sub(t.target);
|
|
183
|
+
let z = r.length();
|
|
184
|
+
z *= Math.tan(t.object.fov / 2 * Math.PI / 180), de(2 * v * z / S.clientHeight, t.object.matrix), pe(2 * w * z / S.clientHeight, t.object.matrix);
|
|
185
|
+
} else t.object.isOrthographicCamera ? (de(v * (t.object.right - t.object.left) / t.object.zoom / S.clientWidth, t.object.matrix), pe(w * (t.object.top - t.object.bottom) / t.object.zoom / S.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
|
|
176
186
|
};
|
|
177
187
|
}();
|
|
178
|
-
function se(
|
|
179
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
188
|
+
function se(r) {
|
|
189
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h /= r : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
180
190
|
}
|
|
181
|
-
function
|
|
182
|
-
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ?
|
|
191
|
+
function ge(r) {
|
|
192
|
+
t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h *= r : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
|
|
183
193
|
}
|
|
184
|
-
function ie(
|
|
194
|
+
function ie(r, g) {
|
|
185
195
|
if (!t.zoomToCursor)
|
|
186
196
|
return;
|
|
187
|
-
|
|
188
|
-
const v = t.domElement.getBoundingClientRect(),
|
|
189
|
-
|
|
197
|
+
F = !0;
|
|
198
|
+
const v = t.domElement.getBoundingClientRect(), w = r - v.left, S = g - v.top, D = v.width, z = v.height;
|
|
199
|
+
R.x = w / D * 2 - 1, R.y = -(S / z) * 2 + 1, G.set(R.x, R.y, 1).unproject(t.object).sub(t.object.position).normalize();
|
|
190
200
|
}
|
|
191
|
-
function
|
|
192
|
-
return Math.max(t.minDistance, Math.min(t.maxDistance,
|
|
201
|
+
function ne(r) {
|
|
202
|
+
return Math.max(t.minDistance, Math.min(t.maxDistance, r));
|
|
193
203
|
}
|
|
194
|
-
function
|
|
195
|
-
f.set(
|
|
204
|
+
function ue(r) {
|
|
205
|
+
f.set(r.clientX, r.clientY);
|
|
196
206
|
}
|
|
197
|
-
function
|
|
198
|
-
ie(
|
|
207
|
+
function He(r) {
|
|
208
|
+
ie(r.clientX, r.clientX), N.set(r.clientX, r.clientY);
|
|
199
209
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
210
|
+
function fe(r) {
|
|
211
|
+
b.set(r.clientX, r.clientY);
|
|
202
212
|
}
|
|
203
|
-
function je(
|
|
204
|
-
m.set(
|
|
213
|
+
function je(r) {
|
|
214
|
+
m.set(r.clientX, r.clientY), u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
|
|
205
215
|
const g = t.domElement;
|
|
206
|
-
|
|
216
|
+
$(2 * Math.PI * u.x / g.clientHeight), X(2 * Math.PI * u.y / g.clientHeight), f.copy(m), t.update();
|
|
207
217
|
}
|
|
208
|
-
function
|
|
209
|
-
O.set(
|
|
218
|
+
function Be(r) {
|
|
219
|
+
O.set(r.clientX, r.clientY), L.subVectors(O, N), L.y > 0 ? se(V(L.y)) : L.y < 0 && ge(V(L.y)), N.copy(O), t.update();
|
|
210
220
|
}
|
|
211
|
-
function Ge(
|
|
212
|
-
|
|
221
|
+
function Ge(r) {
|
|
222
|
+
x.set(r.clientX, r.clientY), M.subVectors(x, b).multiplyScalar(t.panSpeed), A(M.x, M.y), b.copy(x), t.update();
|
|
213
223
|
}
|
|
214
|
-
function
|
|
215
|
-
ie(
|
|
224
|
+
function $e(r) {
|
|
225
|
+
ie(r.clientX, r.clientY), r.deltaY < 0 ? ge(V(r.deltaY)) : r.deltaY > 0 && se(V(r.deltaY)), t.update();
|
|
216
226
|
}
|
|
217
|
-
function
|
|
227
|
+
function Ye(r) {
|
|
218
228
|
let g = !1;
|
|
219
|
-
switch (
|
|
229
|
+
switch (r.code) {
|
|
220
230
|
case t.keys.UP:
|
|
221
|
-
|
|
231
|
+
r.ctrlKey || r.metaKey || r.shiftKey ? X(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), g = !0;
|
|
222
232
|
break;
|
|
223
233
|
case t.keys.BOTTOM:
|
|
224
|
-
|
|
234
|
+
r.ctrlKey || r.metaKey || r.shiftKey ? X(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), g = !0;
|
|
225
235
|
break;
|
|
226
236
|
case t.keys.LEFT:
|
|
227
|
-
|
|
237
|
+
r.ctrlKey || r.metaKey || r.shiftKey ? $(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), g = !0;
|
|
228
238
|
break;
|
|
229
239
|
case t.keys.RIGHT:
|
|
230
|
-
|
|
240
|
+
r.ctrlKey || r.metaKey || r.shiftKey ? $(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(-t.keyPanSpeed, 0), g = !0;
|
|
231
241
|
break;
|
|
232
242
|
}
|
|
233
|
-
g && (
|
|
243
|
+
g && (r.preventDefault(), t.update());
|
|
234
244
|
}
|
|
235
|
-
function
|
|
245
|
+
function me(r) {
|
|
236
246
|
if (E.length === 1)
|
|
237
|
-
f.set(
|
|
247
|
+
f.set(r.pageX, r.pageY);
|
|
238
248
|
else {
|
|
239
|
-
const g =
|
|
240
|
-
f.set(v,
|
|
249
|
+
const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
|
|
250
|
+
f.set(v, w);
|
|
241
251
|
}
|
|
242
252
|
}
|
|
243
|
-
function
|
|
253
|
+
function ye(r) {
|
|
244
254
|
if (E.length === 1)
|
|
245
|
-
|
|
255
|
+
b.set(r.pageX, r.pageY);
|
|
246
256
|
else {
|
|
247
|
-
const g =
|
|
248
|
-
|
|
257
|
+
const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
|
|
258
|
+
b.set(v, w);
|
|
249
259
|
}
|
|
250
260
|
}
|
|
251
|
-
function
|
|
252
|
-
const g =
|
|
253
|
-
N.set(0,
|
|
261
|
+
function be(r) {
|
|
262
|
+
const g = H(r), v = r.pageX - g.x, w = r.pageY - g.y, S = Math.sqrt(v * v + w * w);
|
|
263
|
+
N.set(0, S);
|
|
254
264
|
}
|
|
255
|
-
function
|
|
256
|
-
t.enableZoom &&
|
|
265
|
+
function We(r) {
|
|
266
|
+
t.enableZoom && be(r), t.enablePan && ye(r);
|
|
257
267
|
}
|
|
258
|
-
function Ue(
|
|
259
|
-
t.enableZoom &&
|
|
268
|
+
function Ue(r) {
|
|
269
|
+
t.enableZoom && be(r), t.enableRotate && me(r);
|
|
260
270
|
}
|
|
261
|
-
function
|
|
271
|
+
function xe(r) {
|
|
262
272
|
if (E.length == 1)
|
|
263
|
-
m.set(
|
|
273
|
+
m.set(r.pageX, r.pageY);
|
|
264
274
|
else {
|
|
265
|
-
const v =
|
|
266
|
-
m.set(
|
|
275
|
+
const v = H(r), w = 0.5 * (r.pageX + v.x), S = 0.5 * (r.pageY + v.y);
|
|
276
|
+
m.set(w, S);
|
|
267
277
|
}
|
|
268
278
|
u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
|
|
269
279
|
const g = t.domElement;
|
|
270
|
-
|
|
280
|
+
$(2 * Math.PI * u.x / g.clientHeight), X(2 * Math.PI * u.y / g.clientHeight), f.copy(m);
|
|
271
281
|
}
|
|
272
|
-
function
|
|
282
|
+
function ve(r) {
|
|
273
283
|
if (E.length === 1)
|
|
274
|
-
|
|
284
|
+
x.set(r.pageX, r.pageY);
|
|
275
285
|
else {
|
|
276
|
-
const g =
|
|
277
|
-
|
|
286
|
+
const g = H(r), v = 0.5 * (r.pageX + g.x), w = 0.5 * (r.pageY + g.y);
|
|
287
|
+
x.set(v, w);
|
|
278
288
|
}
|
|
279
|
-
|
|
289
|
+
M.subVectors(x, b).multiplyScalar(t.panSpeed), A(M.x, M.y), b.copy(x);
|
|
280
290
|
}
|
|
281
|
-
function
|
|
282
|
-
const g =
|
|
283
|
-
O.set(0,
|
|
284
|
-
const
|
|
285
|
-
ie(
|
|
291
|
+
function Me(r) {
|
|
292
|
+
const g = H(r), v = r.pageX - g.x, w = r.pageY - g.y, S = Math.sqrt(v * v + w * w);
|
|
293
|
+
O.set(0, S), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), se(L.y), N.copy(O);
|
|
294
|
+
const D = (r.pageX + g.x) * 0.5, z = (r.pageY + g.y) * 0.5;
|
|
295
|
+
ie(D, z);
|
|
286
296
|
}
|
|
287
|
-
function
|
|
288
|
-
t.enableZoom &&
|
|
297
|
+
function Ke(r) {
|
|
298
|
+
t.enableZoom && Me(r), t.enablePan && ve(r);
|
|
289
299
|
}
|
|
290
|
-
function
|
|
291
|
-
t.enableZoom &&
|
|
300
|
+
function qe(r) {
|
|
301
|
+
t.enableZoom && Me(r), t.enableRotate && xe(r);
|
|
292
302
|
}
|
|
293
|
-
function
|
|
294
|
-
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(
|
|
303
|
+
function we(r) {
|
|
304
|
+
t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(r.pointerId), t.domElement.addEventListener("pointermove", oe), t.domElement.addEventListener("pointerup", Y)), et(r), r.pointerType === "touch" ? Qe(r) : _e(r));
|
|
295
305
|
}
|
|
296
|
-
function
|
|
297
|
-
t.enabled !== !1 && (
|
|
306
|
+
function oe(r) {
|
|
307
|
+
t.enabled !== !1 && (r.pointerType === "touch" ? Je(r) : Ve(r));
|
|
298
308
|
}
|
|
299
|
-
function
|
|
300
|
-
tt(
|
|
309
|
+
function Y(r) {
|
|
310
|
+
tt(r), E.length === 0 && (t.domElement.releasePointerCapture(r.pointerId), t.domElement.removeEventListener("pointermove", oe), t.domElement.removeEventListener("pointerup", Y)), t.dispatchEvent(Te), n = i.NONE;
|
|
301
311
|
}
|
|
302
|
-
function
|
|
312
|
+
function _e(r) {
|
|
303
313
|
let g;
|
|
304
|
-
switch (
|
|
314
|
+
switch (r.button) {
|
|
305
315
|
case 0:
|
|
306
316
|
g = t.mouseButtons.LEFT;
|
|
307
317
|
break;
|
|
@@ -317,55 +327,55 @@ class ut extends nt {
|
|
|
317
327
|
switch (g) {
|
|
318
328
|
case j.DOLLY:
|
|
319
329
|
if (t.enableZoom === !1) return;
|
|
320
|
-
|
|
330
|
+
He(r), n = i.DOLLY;
|
|
321
331
|
break;
|
|
322
332
|
case j.ROTATE:
|
|
323
|
-
if (
|
|
333
|
+
if (r.ctrlKey || r.metaKey || r.shiftKey) {
|
|
324
334
|
if (t.enablePan === !1) return;
|
|
325
|
-
|
|
335
|
+
fe(r), n = i.PAN;
|
|
326
336
|
} else {
|
|
327
337
|
if (t.enableRotate === !1) return;
|
|
328
|
-
|
|
338
|
+
ue(r), n = i.ROTATE;
|
|
329
339
|
}
|
|
330
340
|
break;
|
|
331
341
|
case j.PAN:
|
|
332
|
-
if (
|
|
342
|
+
if (r.ctrlKey || r.metaKey || r.shiftKey) {
|
|
333
343
|
if (t.enableRotate === !1) return;
|
|
334
|
-
|
|
344
|
+
ue(r), n = i.ROTATE;
|
|
335
345
|
} else {
|
|
336
346
|
if (t.enablePan === !1) return;
|
|
337
|
-
|
|
347
|
+
fe(r), n = i.PAN;
|
|
338
348
|
}
|
|
339
349
|
break;
|
|
340
350
|
default:
|
|
341
|
-
|
|
351
|
+
n = i.NONE;
|
|
342
352
|
}
|
|
343
|
-
|
|
353
|
+
n !== i.NONE && t.dispatchEvent(le);
|
|
344
354
|
}
|
|
345
|
-
function
|
|
346
|
-
switch (
|
|
355
|
+
function Ve(r) {
|
|
356
|
+
switch (n) {
|
|
347
357
|
case i.ROTATE:
|
|
348
358
|
if (t.enableRotate === !1) return;
|
|
349
|
-
je(
|
|
359
|
+
je(r);
|
|
350
360
|
break;
|
|
351
361
|
case i.DOLLY:
|
|
352
362
|
if (t.enableZoom === !1) return;
|
|
353
|
-
|
|
363
|
+
Be(r);
|
|
354
364
|
break;
|
|
355
365
|
case i.PAN:
|
|
356
366
|
if (t.enablePan === !1) return;
|
|
357
|
-
Ge(
|
|
367
|
+
Ge(r);
|
|
358
368
|
break;
|
|
359
369
|
}
|
|
360
370
|
}
|
|
361
|
-
function
|
|
362
|
-
t.enabled === !1 || t.enableZoom === !1 ||
|
|
371
|
+
function Ee(r) {
|
|
372
|
+
t.enabled === !1 || t.enableZoom === !1 || n !== i.NONE || (r.preventDefault(), t.dispatchEvent(le), $e(Xe(r)), t.dispatchEvent(Te));
|
|
363
373
|
}
|
|
364
|
-
function
|
|
365
|
-
const g =
|
|
366
|
-
clientX:
|
|
367
|
-
clientY:
|
|
368
|
-
deltaY:
|
|
374
|
+
function Xe(r) {
|
|
375
|
+
const g = r.deltaMode, v = {
|
|
376
|
+
clientX: r.clientX,
|
|
377
|
+
clientY: r.clientY,
|
|
378
|
+
deltaY: r.deltaY
|
|
369
379
|
};
|
|
370
380
|
switch (g) {
|
|
371
381
|
case 1:
|
|
@@ -375,97 +385,97 @@ class ut extends nt {
|
|
|
375
385
|
v.deltaY *= 100;
|
|
376
386
|
break;
|
|
377
387
|
}
|
|
378
|
-
return
|
|
388
|
+
return r.ctrlKey && !te && (v.deltaY *= 10), v;
|
|
379
389
|
}
|
|
380
|
-
function Ze(
|
|
381
|
-
|
|
390
|
+
function Ze(r) {
|
|
391
|
+
r.key === "Control" && (te = !0, document.addEventListener("keyup", Ce, { passive: !0, capture: !0 }));
|
|
382
392
|
}
|
|
383
|
-
function
|
|
384
|
-
|
|
393
|
+
function Ce(r) {
|
|
394
|
+
r.key === "Control" && (te = !1, document.removeEventListener("keyup", Ce, { passive: !0, capture: !0 }));
|
|
385
395
|
}
|
|
386
|
-
function ae(
|
|
387
|
-
t.enabled === !1 || t.enablePan === !1 ||
|
|
396
|
+
function ae(r) {
|
|
397
|
+
t.enabled === !1 || t.enablePan === !1 || Ye(r);
|
|
388
398
|
}
|
|
389
|
-
function Qe(
|
|
390
|
-
switch (
|
|
399
|
+
function Qe(r) {
|
|
400
|
+
switch (Ne(r), E.length) {
|
|
391
401
|
case 1:
|
|
392
402
|
switch (t.touches.ONE) {
|
|
393
|
-
case
|
|
403
|
+
case B.ROTATE:
|
|
394
404
|
if (t.enableRotate === !1) return;
|
|
395
|
-
|
|
405
|
+
me(r), n = i.TOUCH_ROTATE;
|
|
396
406
|
break;
|
|
397
|
-
case
|
|
407
|
+
case B.PAN:
|
|
398
408
|
if (t.enablePan === !1) return;
|
|
399
|
-
|
|
409
|
+
ye(r), n = i.TOUCH_PAN;
|
|
400
410
|
break;
|
|
401
411
|
default:
|
|
402
|
-
|
|
412
|
+
n = i.NONE;
|
|
403
413
|
}
|
|
404
414
|
break;
|
|
405
415
|
case 2:
|
|
406
416
|
switch (t.touches.TWO) {
|
|
407
|
-
case
|
|
417
|
+
case B.DOLLY_PAN:
|
|
408
418
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
409
|
-
|
|
419
|
+
We(r), n = i.TOUCH_DOLLY_PAN;
|
|
410
420
|
break;
|
|
411
|
-
case
|
|
421
|
+
case B.DOLLY_ROTATE:
|
|
412
422
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
413
|
-
Ue(
|
|
423
|
+
Ue(r), n = i.TOUCH_DOLLY_ROTATE;
|
|
414
424
|
break;
|
|
415
425
|
default:
|
|
416
|
-
|
|
426
|
+
n = i.NONE;
|
|
417
427
|
}
|
|
418
428
|
break;
|
|
419
429
|
default:
|
|
420
|
-
|
|
430
|
+
n = i.NONE;
|
|
421
431
|
}
|
|
422
|
-
|
|
432
|
+
n !== i.NONE && t.dispatchEvent(le);
|
|
423
433
|
}
|
|
424
|
-
function Je(
|
|
425
|
-
switch (
|
|
434
|
+
function Je(r) {
|
|
435
|
+
switch (Ne(r), n) {
|
|
426
436
|
case i.TOUCH_ROTATE:
|
|
427
437
|
if (t.enableRotate === !1) return;
|
|
428
|
-
|
|
438
|
+
xe(r), t.update();
|
|
429
439
|
break;
|
|
430
440
|
case i.TOUCH_PAN:
|
|
431
441
|
if (t.enablePan === !1) return;
|
|
432
|
-
|
|
442
|
+
ve(r), t.update();
|
|
433
443
|
break;
|
|
434
444
|
case i.TOUCH_DOLLY_PAN:
|
|
435
445
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
436
|
-
|
|
446
|
+
Ke(r), t.update();
|
|
437
447
|
break;
|
|
438
448
|
case i.TOUCH_DOLLY_ROTATE:
|
|
439
449
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
440
|
-
|
|
450
|
+
qe(r), t.update();
|
|
441
451
|
break;
|
|
442
452
|
default:
|
|
443
|
-
|
|
453
|
+
n = i.NONE;
|
|
444
454
|
}
|
|
445
455
|
}
|
|
446
|
-
function
|
|
447
|
-
t.enabled !== !1 &&
|
|
456
|
+
function Se(r) {
|
|
457
|
+
t.enabled !== !1 && r.preventDefault();
|
|
448
458
|
}
|
|
449
|
-
function et(
|
|
450
|
-
E.push(
|
|
459
|
+
function et(r) {
|
|
460
|
+
E.push(r.pointerId);
|
|
451
461
|
}
|
|
452
|
-
function tt(
|
|
453
|
-
delete
|
|
462
|
+
function tt(r) {
|
|
463
|
+
delete _[r.pointerId];
|
|
454
464
|
for (let g = 0; g < E.length; g++)
|
|
455
|
-
if (E[g] ==
|
|
465
|
+
if (E[g] == r.pointerId) {
|
|
456
466
|
E.splice(g, 1);
|
|
457
467
|
return;
|
|
458
468
|
}
|
|
459
469
|
}
|
|
460
|
-
function
|
|
461
|
-
let g =
|
|
462
|
-
g === void 0 && (g = new
|
|
470
|
+
function Ne(r) {
|
|
471
|
+
let g = _[r.pointerId];
|
|
472
|
+
g === void 0 && (g = new P(), _[r.pointerId] = g), g.set(r.pageX, r.pageY);
|
|
463
473
|
}
|
|
464
|
-
function
|
|
465
|
-
const g =
|
|
466
|
-
return
|
|
474
|
+
function H(r) {
|
|
475
|
+
const g = r.pointerId === E[0] ? E[1] : E[0];
|
|
476
|
+
return _[g];
|
|
467
477
|
}
|
|
468
|
-
t.domElement.addEventListener("contextmenu",
|
|
478
|
+
t.domElement.addEventListener("contextmenu", Se), t.domElement.addEventListener("pointerdown", we), t.domElement.addEventListener("pointercancel", Y), t.domElement.addEventListener("wheel", Ee, { passive: !1 }), document.addEventListener("keydown", Ze, { passive: !0, capture: !0 }), this.update();
|
|
469
479
|
}
|
|
470
480
|
}
|
|
471
481
|
class ft {
|
|
@@ -476,42 +486,43 @@ class ft {
|
|
|
476
486
|
l(this, "controls");
|
|
477
487
|
l(this, "container");
|
|
478
488
|
l(this, "resizeHandler");
|
|
479
|
-
this
|
|
489
|
+
l(this, "maxPixelRatio");
|
|
490
|
+
this.container = e, this.maxPixelRatio = s.maxPixelRatio ?? 2, this.scene = new y.Scene(), this.scene.background = new y.Color(
|
|
480
491
|
s.backgroundColor ?? 657930
|
|
481
492
|
);
|
|
482
|
-
const { width: t, height: i } =
|
|
483
|
-
this.camera = new
|
|
484
|
-
const
|
|
485
|
-
this.camera.position.set(
|
|
493
|
+
const { width: t, height: i } = Re(e), n = s.cameraFov ?? 75;
|
|
494
|
+
this.camera = new y.PerspectiveCamera(n, t / i, 0.1, 2e3);
|
|
495
|
+
const o = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
496
|
+
this.camera.position.set(o.x, o.y, o.z), this.renderer = new y.WebGLRenderer({
|
|
486
497
|
antialias: !0,
|
|
487
498
|
alpha: !0,
|
|
488
499
|
powerPreference: "high-performance"
|
|
489
|
-
}), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,
|
|
500
|
+
}), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, this.maxPixelRatio)), 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 ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
490
501
|
}
|
|
491
502
|
/**
|
|
492
503
|
* Sets up scene lighting for gradient glass on dark background
|
|
493
504
|
*/
|
|
494
505
|
setupLighting() {
|
|
495
|
-
const e = new
|
|
506
|
+
const e = new y.AmbientLight(16777215, 0.4);
|
|
496
507
|
this.scene.add(e);
|
|
497
|
-
const s = new
|
|
508
|
+
const s = new y.DirectionalLight(16777215, 0.9);
|
|
498
509
|
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
499
|
-
const t = new
|
|
510
|
+
const t = new y.DirectionalLight(16773344, 0.4);
|
|
500
511
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
501
|
-
const i = new
|
|
512
|
+
const i = new y.DirectionalLight(16777215, 0.3);
|
|
502
513
|
i.position.set(0, -30, -50), this.scene.add(i);
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
514
|
+
const n = new y.PointLight(16750950, 0.5, 150);
|
|
515
|
+
n.position.set(40, 20, 40), this.scene.add(n);
|
|
516
|
+
const o = new y.PointLight(16764057, 0.4, 150);
|
|
517
|
+
o.position.set(-40, -20, 40), this.scene.add(o);
|
|
518
|
+
const a = new y.PointLight(6724095, 0.2, 100);
|
|
519
|
+
a.position.set(0, 40, -40), this.scene.add(a);
|
|
509
520
|
}
|
|
510
521
|
/**
|
|
511
522
|
* Handle window resize
|
|
512
523
|
*/
|
|
513
524
|
onWindowResize() {
|
|
514
|
-
const { width: e, height: s } =
|
|
525
|
+
const { width: e, height: s } = Re(this.container);
|
|
515
526
|
this.camera.aspect = e / s, this.camera.updateProjectionMatrix(), this.renderer.setSize(e, s);
|
|
516
527
|
}
|
|
517
528
|
/**
|
|
@@ -532,6 +543,25 @@ class ft {
|
|
|
532
543
|
render() {
|
|
533
544
|
this.controls.update(), this.renderer.render(this.scene, this.camera);
|
|
534
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* Updates renderer pixel ratio with bounds checking
|
|
548
|
+
*/
|
|
549
|
+
setPixelRatio(e) {
|
|
550
|
+
const s = Math.max(0.3, Math.min(e, this.maxPixelRatio));
|
|
551
|
+
this.renderer.setPixelRatio(s);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Gets current renderer pixel ratio
|
|
555
|
+
*/
|
|
556
|
+
getPixelRatio() {
|
|
557
|
+
return this.renderer.getPixelRatio();
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Enables/disables expensive renderer features
|
|
561
|
+
*/
|
|
562
|
+
setHighQualityRendering(e) {
|
|
563
|
+
this.renderer.shadowMap.enabled = e, this.renderer.toneMapping = e ? y.ACESFilmicToneMapping : y.NoToneMapping, this.renderer.toneMappingExposure = 1;
|
|
564
|
+
}
|
|
535
565
|
/**
|
|
536
566
|
* Gets the camera position
|
|
537
567
|
*/
|
|
@@ -546,7 +576,7 @@ class ft {
|
|
|
546
576
|
* Gets the camera's forward direction
|
|
547
577
|
*/
|
|
548
578
|
getCameraDirection() {
|
|
549
|
-
const e = new
|
|
579
|
+
const e = new y.Vector3();
|
|
550
580
|
return this.camera.getWorldDirection(e), e;
|
|
551
581
|
}
|
|
552
582
|
/**
|
|
@@ -559,12 +589,16 @@ class ft {
|
|
|
559
589
|
}
|
|
560
590
|
}
|
|
561
591
|
}
|
|
562
|
-
const
|
|
592
|
+
const q = class q {
|
|
563
593
|
constructor(e, s) {
|
|
564
594
|
l(this, "sceneManager");
|
|
565
595
|
l(this, "nodeFactory");
|
|
566
596
|
l(this, "nodes", /* @__PURE__ */ new Map());
|
|
567
597
|
l(this, "nodeObjects", /* @__PURE__ */ new Map());
|
|
598
|
+
l(this, "nodeObjectCache", []);
|
|
599
|
+
l(this, "nodeObjectCacheDirty", !0);
|
|
600
|
+
l(this, "labelRenderMode", "adaptive");
|
|
601
|
+
l(this, "labelDistance", 140);
|
|
568
602
|
this.sceneManager = e, this.nodeFactory = s;
|
|
569
603
|
}
|
|
570
604
|
/**
|
|
@@ -578,31 +612,31 @@ const X = class X {
|
|
|
578
612
|
* Call this before adding nodes in bulk.
|
|
579
613
|
*/
|
|
580
614
|
static setExpectedNodeCount(e) {
|
|
581
|
-
|
|
615
|
+
q.expectedNodeCount = e;
|
|
582
616
|
}
|
|
583
617
|
/**
|
|
584
618
|
* Adds a node to the graph
|
|
585
619
|
* @returns true if added, false if node already exists or invalid
|
|
586
620
|
*/
|
|
587
621
|
addNode(e, s = 0) {
|
|
588
|
-
if (!
|
|
622
|
+
if (!ce(e))
|
|
589
623
|
return !1;
|
|
590
624
|
if (this.nodes.has(e.id))
|
|
591
625
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
592
|
-
const t = Math.max(50, Math.cbrt(
|
|
626
|
+
const t = Math.max(50, Math.cbrt(q.expectedNodeCount) * 10), i = e.position ?? {
|
|
593
627
|
x: (Math.random() - 0.5) * t,
|
|
594
628
|
y: (Math.random() - 0.5) * t,
|
|
595
629
|
z: (Math.random() - 0.5) * t
|
|
596
|
-
},
|
|
630
|
+
}, n = {
|
|
597
631
|
...e,
|
|
598
632
|
position: i,
|
|
599
633
|
velocity: { x: 0, y: 0, z: 0 },
|
|
600
634
|
mass: 1
|
|
601
|
-
},
|
|
635
|
+
}, o = this.nodeFactory.createNode(
|
|
602
636
|
{ ...e, position: i },
|
|
603
637
|
s
|
|
604
638
|
);
|
|
605
|
-
return this.sceneManager.add(
|
|
639
|
+
return this.sceneManager.add(o.group), this.nodes.set(e.id, n), this.nodeObjects.set(e.id, o), this.nodeObjectCacheDirty = !0, !0;
|
|
606
640
|
}
|
|
607
641
|
/**
|
|
608
642
|
* Removes a node from the graph
|
|
@@ -610,15 +644,15 @@ const X = class X {
|
|
|
610
644
|
*/
|
|
611
645
|
removeNode(e) {
|
|
612
646
|
const s = this.nodes.get(e), t = this.nodeObjects.get(e);
|
|
613
|
-
return !s || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), !0);
|
|
647
|
+
return !s || !t ? !1 : (this.sceneManager.remove(t.group), this.nodeFactory.disposeNode(t), this.nodes.delete(e), this.nodeObjects.delete(e), this.nodeObjectCacheDirty = !0, !0);
|
|
614
648
|
}
|
|
615
649
|
/**
|
|
616
650
|
* Updates a node's properties
|
|
617
651
|
*/
|
|
618
652
|
updateNode(e, s) {
|
|
619
653
|
const t = this.nodes.get(e), i = this.nodeObjects.get(e);
|
|
620
|
-
return !t || !i ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(i, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(i, s.color)), Object.keys(s).forEach((
|
|
621
|
-
|
|
654
|
+
return !t || !i ? (console.warn(`[ForceGraph3D] Node "${e}" not found`), !1) : (s.label !== void 0 && (t.label = s.label, this.nodeFactory.updateNodeLabel(i, s.label)), s.color !== void 0 && (t.color = s.color, this.nodeFactory.updateNodeColor(i, s.color)), Object.keys(s).forEach((n) => {
|
|
655
|
+
n !== "id" && n !== "label" && n !== "color" && n !== "position" && (t[n] = s[n]);
|
|
622
656
|
}), !0);
|
|
623
657
|
}
|
|
624
658
|
/**
|
|
@@ -657,10 +691,12 @@ const X = class X {
|
|
|
657
691
|
* Gets all node objects (for raycasting)
|
|
658
692
|
*/
|
|
659
693
|
getAllNodeObjects() {
|
|
694
|
+
if (!this.nodeObjectCacheDirty)
|
|
695
|
+
return this.nodeObjectCache;
|
|
660
696
|
const e = [];
|
|
661
697
|
return this.nodeObjects.forEach((s) => {
|
|
662
698
|
e.push(s.group);
|
|
663
|
-
}), e;
|
|
699
|
+
}), this.nodeObjectCache = e, this.nodeObjectCacheDirty = !1, this.nodeObjectCache;
|
|
664
700
|
}
|
|
665
701
|
/**
|
|
666
702
|
* Gets all spheres (for raycasting)
|
|
@@ -683,6 +719,41 @@ const X = class X {
|
|
|
683
719
|
clear() {
|
|
684
720
|
this.nodes.forEach((e, s) => {
|
|
685
721
|
this.removeNode(s);
|
|
722
|
+
}), this.nodeObjectCacheDirty = !0;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Controls label rendering strategy for performance
|
|
726
|
+
*/
|
|
727
|
+
setLabelRenderMode(e) {
|
|
728
|
+
this.labelRenderMode = e;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Sets camera distance threshold used in adaptive label mode
|
|
732
|
+
*/
|
|
733
|
+
setLabelDistance(e) {
|
|
734
|
+
this.labelDistance = Math.max(20, e);
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Updates label visibility for all nodes
|
|
738
|
+
*/
|
|
739
|
+
updateLabelVisibility(e, s = null, t = null) {
|
|
740
|
+
this.nodeObjects.forEach((i, n) => {
|
|
741
|
+
const o = n === s || n === t;
|
|
742
|
+
if (this.labelRenderMode === "none") {
|
|
743
|
+
i.label.visible = o;
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (this.labelRenderMode === "all") {
|
|
747
|
+
i.label.visible = !0;
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const a = this.nodes.get(n);
|
|
751
|
+
if (!a) {
|
|
752
|
+
i.label.visible = !1;
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const c = a.position.x - e.x, h = a.position.y - e.y, d = a.position.z - e.z, f = c * c + h * h + d * d, m = o || f <= this.labelDistance * this.labelDistance;
|
|
756
|
+
i.label.visible = m;
|
|
686
757
|
});
|
|
687
758
|
}
|
|
688
759
|
/**
|
|
@@ -693,8 +764,8 @@ const X = class X {
|
|
|
693
764
|
}
|
|
694
765
|
};
|
|
695
766
|
// Scale spawn volume with expected graph size
|
|
696
|
-
l(
|
|
697
|
-
let ee =
|
|
767
|
+
l(q, "expectedNodeCount", 100);
|
|
768
|
+
let ee = q;
|
|
698
769
|
class mt {
|
|
699
770
|
constructor(e, s, t) {
|
|
700
771
|
l(this, "sceneManager");
|
|
@@ -704,6 +775,8 @@ class mt {
|
|
|
704
775
|
l(this, "edges", []);
|
|
705
776
|
// One visual line per directed pair (source -> target)
|
|
706
777
|
l(this, "edgeObjects", []);
|
|
778
|
+
l(this, "edgeLineCache", []);
|
|
779
|
+
l(this, "edgeLineCacheDirty", !0);
|
|
707
780
|
// Tracks which directed pairs currently have at least one relationship
|
|
708
781
|
l(this, "edgeKeySet", /* @__PURE__ */ new Set());
|
|
709
782
|
// Maps directed pair key to the relationship list for that pair
|
|
@@ -711,6 +784,9 @@ class mt {
|
|
|
711
784
|
// Maps directed pair key to the corresponding visual line
|
|
712
785
|
l(this, "edgeObjectMap", /* @__PURE__ */ new Map());
|
|
713
786
|
l(this, "highlightedEdgeKey", null);
|
|
787
|
+
l(this, "batchRenderingEnabled", !1);
|
|
788
|
+
l(this, "batchLineSegments", null);
|
|
789
|
+
l(this, "batchGeometryDirty", !1);
|
|
714
790
|
this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
|
|
715
791
|
}
|
|
716
792
|
/**
|
|
@@ -725,7 +801,7 @@ class mt {
|
|
|
725
801
|
* @returns true if added, false if invalid or nodes don't exist
|
|
726
802
|
*/
|
|
727
803
|
addEdge(e) {
|
|
728
|
-
if (!
|
|
804
|
+
if (!he(e))
|
|
729
805
|
return !1;
|
|
730
806
|
if (!this.nodeManager.hasNode(e.source))
|
|
731
807
|
return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
|
|
@@ -733,20 +809,20 @@ class mt {
|
|
|
733
809
|
return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
|
|
734
810
|
const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target), i = this.createDirectedEdgeKey(e.source, e.target);
|
|
735
811
|
this.edges.push(e);
|
|
736
|
-
const
|
|
737
|
-
if (
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
return
|
|
812
|
+
const n = this.edgeGroups.get(i);
|
|
813
|
+
if (n) {
|
|
814
|
+
n.push(e);
|
|
815
|
+
const a = this.edgeObjectMap.get(i);
|
|
816
|
+
return a && this.updateEdgeUserData(a, n, s, t), !0;
|
|
741
817
|
}
|
|
742
|
-
const
|
|
818
|
+
const o = this.edgeFactory.createEdge(
|
|
743
819
|
e,
|
|
744
820
|
s,
|
|
745
821
|
t,
|
|
746
822
|
s.position,
|
|
747
823
|
t.position
|
|
748
824
|
);
|
|
749
|
-
return this.sceneManager.add(
|
|
825
|
+
return this.batchRenderingEnabled || this.sceneManager.add(o.line), this.edgeGroups.set(i, [e]), this.edgeObjectMap.set(i, o), this.edgeObjects.push(o), this.edgeLineCacheDirty = !0, this.edgeKeySet.add(i), this.updateEdgeUserData(o, [e], s, t), this.batchRenderingEnabled && (this.batchGeometryDirty = !0), !0;
|
|
750
826
|
}
|
|
751
827
|
/**
|
|
752
828
|
* Removes an edge from the graph
|
|
@@ -758,32 +834,32 @@ class mt {
|
|
|
758
834
|
if (!this.edgeKeySet.has(t))
|
|
759
835
|
return !1;
|
|
760
836
|
const i = this.edges.findIndex(
|
|
761
|
-
(
|
|
837
|
+
(h) => h.source === e && h.target === s
|
|
762
838
|
);
|
|
763
839
|
if (i === -1)
|
|
764
840
|
return !1;
|
|
765
|
-
const
|
|
841
|
+
const n = this.edges[i];
|
|
766
842
|
this.edges.splice(i, 1);
|
|
767
|
-
const
|
|
768
|
-
if (!
|
|
843
|
+
const o = this.edgeGroups.get(t);
|
|
844
|
+
if (!o)
|
|
769
845
|
return !0;
|
|
770
|
-
const
|
|
771
|
-
if (
|
|
772
|
-
|
|
846
|
+
const a = o.findIndex((h) => h === n);
|
|
847
|
+
if (a !== -1)
|
|
848
|
+
o.splice(a, 1);
|
|
773
849
|
else {
|
|
774
|
-
const
|
|
775
|
-
|
|
850
|
+
const h = o.findIndex((d) => d.source === e && d.target === s);
|
|
851
|
+
h !== -1 && o.splice(h, 1);
|
|
776
852
|
}
|
|
777
|
-
const
|
|
778
|
-
if (!
|
|
853
|
+
const c = this.edgeObjectMap.get(t);
|
|
854
|
+
if (!c)
|
|
779
855
|
return !0;
|
|
780
|
-
if (
|
|
781
|
-
this.sceneManager.remove(
|
|
782
|
-
const
|
|
783
|
-
|
|
856
|
+
if (o.length === 0) {
|
|
857
|
+
this.sceneManager.remove(c.line), this.edgeFactory.disposeEdge(c);
|
|
858
|
+
const h = this.edgeObjects.findIndex((d) => d === c);
|
|
859
|
+
h !== -1 && (this.edgeObjects.splice(h, 1), this.edgeLineCacheDirty = !0), this.edgeGroups.delete(t), this.edgeObjectMap.delete(t), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), this.batchRenderingEnabled && (this.batchGeometryDirty = !0);
|
|
784
860
|
} else {
|
|
785
|
-
const
|
|
786
|
-
|
|
861
|
+
const h = this.nodeManager.getNode(e), d = this.nodeManager.getNode(s);
|
|
862
|
+
h && d && this.updateEdgeUserData(c, o, h, d), this.batchRenderingEnabled && (this.batchGeometryDirty = !0);
|
|
787
863
|
}
|
|
788
864
|
return !0;
|
|
789
865
|
}
|
|
@@ -834,6 +910,14 @@ class mt {
|
|
|
834
910
|
* Updates all edge positions based on current node positions
|
|
835
911
|
*/
|
|
836
912
|
updateEdgePositions() {
|
|
913
|
+
if (this.batchRenderingEnabled && this.batchGeometryDirty) {
|
|
914
|
+
this.rebuildBatchGeometry(), this.batchGeometryDirty = !1;
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (this.batchRenderingEnabled && this.batchLineSegments) {
|
|
918
|
+
this.updateBatchEdgePositions();
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
837
921
|
this.edgeObjects.forEach((e) => {
|
|
838
922
|
const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target);
|
|
839
923
|
s && t && this.edgeFactory.updateEdgePositions(
|
|
@@ -853,7 +937,7 @@ class mt {
|
|
|
853
937
|
* Gets all edge line objects (for raycasting)
|
|
854
938
|
*/
|
|
855
939
|
getAllEdgeLines() {
|
|
856
|
-
return this.edgeObjects.map((e) => e.line);
|
|
940
|
+
return this.edgeLineCacheDirty ? (this.edgeLineCache = this.edgeObjects.map((e) => e.line), this.edgeLineCacheDirty = !1, this.edgeLineCache) : this.edgeLineCache;
|
|
857
941
|
}
|
|
858
942
|
/**
|
|
859
943
|
* Gets edge count
|
|
@@ -867,7 +951,23 @@ class mt {
|
|
|
867
951
|
clear() {
|
|
868
952
|
this.edgeObjects.forEach((e) => {
|
|
869
953
|
this.sceneManager.remove(e.line), this.edgeFactory.disposeEdge(e);
|
|
870
|
-
}), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null;
|
|
954
|
+
}), this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edges = [], this.edgeObjects = [], this.edgeLineCache = [], this.edgeLineCacheDirty = !0, this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null, this.batchGeometryDirty = !1;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Enables batched edge rendering (single LineSegments draw call)
|
|
958
|
+
*/
|
|
959
|
+
setBatchRenderingEnabled(e) {
|
|
960
|
+
this.batchRenderingEnabled !== e && (this.batchRenderingEnabled = e, e ? (this.edgeObjects.forEach((s) => {
|
|
961
|
+
this.sceneManager.remove(s.line);
|
|
962
|
+
}), this.batchGeometryDirty = !0, this.rebuildBatchGeometry()) : (this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edgeObjects.forEach((s) => {
|
|
963
|
+
this.sceneManager.add(s.line);
|
|
964
|
+
}), this.batchGeometryDirty = !1));
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Checks whether batch rendering is active
|
|
968
|
+
*/
|
|
969
|
+
isBatchRenderingEnabled() {
|
|
970
|
+
return this.batchRenderingEnabled;
|
|
871
971
|
}
|
|
872
972
|
/**
|
|
873
973
|
* Creates a directed key (source -> target)
|
|
@@ -879,18 +979,46 @@ class mt {
|
|
|
879
979
|
* Syncs relationship metadata to the line for hover/click UI
|
|
880
980
|
*/
|
|
881
981
|
updateEdgeUserData(e, s, t, i) {
|
|
882
|
-
const
|
|
982
|
+
const n = s[0];
|
|
883
983
|
e.line.userData = {
|
|
884
984
|
...e.line.userData,
|
|
885
985
|
source: e.source,
|
|
886
986
|
target: e.target,
|
|
887
|
-
edge:
|
|
987
|
+
edge: n,
|
|
888
988
|
relationships: [...s],
|
|
889
989
|
relationshipCount: s.length,
|
|
890
990
|
sourceNode: t,
|
|
891
991
|
targetNode: i
|
|
892
992
|
};
|
|
893
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* Rebuilds merged edge geometry used for low-end rendering mode
|
|
996
|
+
*/
|
|
997
|
+
rebuildBatchGeometry() {
|
|
998
|
+
if (!this.batchRenderingEnabled || (this.batchLineSegments && (this.sceneManager.remove(this.batchLineSegments), this.batchLineSegments.geometry.dispose(), this.batchLineSegments = null), this.edgeObjects.length === 0)) return;
|
|
999
|
+
const e = new Float32Array(this.edgeObjects.length * 6), s = new y.BufferGeometry();
|
|
1000
|
+
s.setAttribute("position", new y.BufferAttribute(e, 3)), this.batchLineSegments = new y.LineSegments(
|
|
1001
|
+
s,
|
|
1002
|
+
this.edgeFactory.getSharedEdgeMaterial()
|
|
1003
|
+
), this.batchLineSegments.name = "f3d-edge-batch", this.sceneManager.add(this.batchLineSegments), this.updateBatchEdgePositions();
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Updates merged edge geometry positions from node coordinates
|
|
1007
|
+
*/
|
|
1008
|
+
updateBatchEdgePositions() {
|
|
1009
|
+
if (!this.batchLineSegments) return;
|
|
1010
|
+
const e = this.batchLineSegments.geometry.getAttribute("position"), s = e.array;
|
|
1011
|
+
let t = 0;
|
|
1012
|
+
for (const i of this.edgeObjects) {
|
|
1013
|
+
const n = this.nodeManager.getNode(i.source), o = this.nodeManager.getNode(i.target);
|
|
1014
|
+
if (!n || !o) {
|
|
1015
|
+
t += 6;
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
s[t++] = n.position.x, s[t++] = n.position.y, s[t++] = n.position.z, s[t++] = o.position.x, s[t++] = o.position.y, s[t++] = o.position.z;
|
|
1019
|
+
}
|
|
1020
|
+
e.needsUpdate = !0, this.batchLineSegments.geometry.computeBoundingSphere();
|
|
1021
|
+
}
|
|
894
1022
|
/**
|
|
895
1023
|
* Dispose resources
|
|
896
1024
|
*/
|
|
@@ -898,7 +1026,7 @@ class mt {
|
|
|
898
1026
|
this.clear();
|
|
899
1027
|
}
|
|
900
1028
|
}
|
|
901
|
-
class
|
|
1029
|
+
class Oe {
|
|
902
1030
|
constructor(e, s, t = {}) {
|
|
903
1031
|
l(this, "nodes");
|
|
904
1032
|
l(this, "edges");
|
|
@@ -936,10 +1064,10 @@ class Le {
|
|
|
936
1064
|
e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
|
|
937
1065
|
console.log(`[ForceGraph3D] initializeNodeMassAndPin: ${this.edges.length} edges, ${this.nodes.size} nodes, ${e.size} unique endpoints`);
|
|
938
1066
|
let s = 0, t = null;
|
|
939
|
-
for (const [i,
|
|
940
|
-
|
|
941
|
-
const
|
|
942
|
-
|
|
1067
|
+
for (const [i, n] of e) {
|
|
1068
|
+
n > s && (s = n, t = i);
|
|
1069
|
+
const o = this.nodes.get(i);
|
|
1070
|
+
o && (o.mass = 1 + Math.log2(1 + n));
|
|
943
1071
|
}
|
|
944
1072
|
if (console.log(`[ForceGraph3D] Hub detected: id="${t}", degree=${s}, threshold=10`), t && s > 10) {
|
|
945
1073
|
this.pinnedNodeId = t;
|
|
@@ -970,14 +1098,14 @@ class Le {
|
|
|
970
1098
|
calculateRepulsionBruteForce() {
|
|
971
1099
|
const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
|
|
972
1100
|
for (let i = 0; i < s; i++) {
|
|
973
|
-
const
|
|
974
|
-
for (let
|
|
975
|
-
const
|
|
976
|
-
let f =
|
|
1101
|
+
const n = e[i];
|
|
1102
|
+
for (let o = i + 1; o < s; o++) {
|
|
1103
|
+
const a = e[o], c = a.position.x - n.position.x, h = a.position.y - n.position.y, d = a.position.z - n.position.z;
|
|
1104
|
+
let f = c * c + h * h + d * d;
|
|
977
1105
|
if (f > this.REPULSION_CUTOFF_SQ) continue;
|
|
978
1106
|
f < 0.01 && (f = 0.01);
|
|
979
|
-
const m = Math.sqrt(f), u = t * this.alpha / f,
|
|
980
|
-
|
|
1107
|
+
const m = Math.sqrt(f), u = t * this.alpha / f, b = c / m * u, x = h / m * u, M = d / m * u;
|
|
1108
|
+
n.velocity.x -= b / n.mass, n.velocity.y -= x / n.mass, n.velocity.z -= M / n.mass, a.velocity.x += b / a.mass, a.velocity.y += x / a.mass, a.velocity.z += M / a.mass;
|
|
981
1109
|
}
|
|
982
1110
|
}
|
|
983
1111
|
}
|
|
@@ -998,26 +1126,26 @@ class Le {
|
|
|
998
1126
|
return;
|
|
999
1127
|
}
|
|
1000
1128
|
if (s.mass === 0) return;
|
|
1001
|
-
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y,
|
|
1002
|
-
if (
|
|
1003
|
-
const
|
|
1004
|
-
if (
|
|
1005
|
-
const
|
|
1006
|
-
e.velocity.x -= t /
|
|
1129
|
+
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, n = s.centerOfMass.z - e.position.z, o = t * t + i * i + n * n;
|
|
1130
|
+
if (o > this.REPULSION_CUTOFF_SQ) return;
|
|
1131
|
+
const a = Math.sqrt(o), c = this.getEffectiveRepulsion();
|
|
1132
|
+
if (a > 0 && s.size / a < this.barnesHutTheta) {
|
|
1133
|
+
const h = Math.max(o, 0.01), d = c * this.alpha * s.mass / h;
|
|
1134
|
+
e.velocity.x -= t / a * d / e.mass, e.velocity.y -= i / a * d / e.mass, e.velocity.z -= n / a * d / e.mass;
|
|
1007
1135
|
} else
|
|
1008
|
-
for (const
|
|
1009
|
-
|
|
1136
|
+
for (const h of s.children)
|
|
1137
|
+
h && this.calculateForceFromOctree(e, h);
|
|
1010
1138
|
}
|
|
1011
1139
|
/**
|
|
1012
1140
|
* Apply repulsion between two nodes (with cutoff)
|
|
1013
1141
|
*/
|
|
1014
1142
|
applyRepulsionBetween(e, s) {
|
|
1015
|
-
const t = s.position.x - e.position.x, i = s.position.y - e.position.y,
|
|
1016
|
-
let
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
const
|
|
1020
|
-
e.velocity.x -= t /
|
|
1143
|
+
const t = s.position.x - e.position.x, i = s.position.y - e.position.y, n = s.position.z - e.position.z;
|
|
1144
|
+
let o = t * t + i * i + n * n;
|
|
1145
|
+
if (o > this.REPULSION_CUTOFF_SQ) return;
|
|
1146
|
+
o < 0.01 && (o = 0.01);
|
|
1147
|
+
const a = Math.sqrt(o), h = this.getEffectiveRepulsion() * this.alpha / o;
|
|
1148
|
+
e.velocity.x -= t / a * h / e.mass, e.velocity.y -= i / a * h / e.mass, e.velocity.z -= n / a * h / e.mass;
|
|
1021
1149
|
}
|
|
1022
1150
|
/**
|
|
1023
1151
|
* Calculate attraction forces along edges
|
|
@@ -1025,12 +1153,12 @@ class Le {
|
|
|
1025
1153
|
calculateAttraction() {
|
|
1026
1154
|
const e = this.nodes.size, s = e > 500 ? 1 + Math.log10(e / 500) : 1;
|
|
1027
1155
|
for (const t of this.edges) {
|
|
1028
|
-
const i = this.nodes.get(t.source),
|
|
1029
|
-
if (!i || !
|
|
1030
|
-
const
|
|
1031
|
-
if (
|
|
1032
|
-
const f = (
|
|
1033
|
-
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z +=
|
|
1156
|
+
const i = this.nodes.get(t.source), n = this.nodes.get(t.target);
|
|
1157
|
+
if (!i || !n) continue;
|
|
1158
|
+
const o = n.position.x - i.position.x, a = n.position.y - i.position.y, c = n.position.z - i.position.z, h = Math.sqrt(o * o + a * a + c * c);
|
|
1159
|
+
if (h < 0.01) continue;
|
|
1160
|
+
const f = (h - 15) * this.attractionStrength * s * this.alpha, m = o / h * f, u = a / h * f, b = c / h * f;
|
|
1161
|
+
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += b / i.mass, n.velocity.x -= m / n.mass, n.velocity.y -= u / n.mass, n.velocity.z -= b / n.mass;
|
|
1034
1162
|
}
|
|
1035
1163
|
}
|
|
1036
1164
|
/**
|
|
@@ -1100,8 +1228,8 @@ class yt {
|
|
|
1100
1228
|
max: { x: 100, y: 100, z: 100 }
|
|
1101
1229
|
};
|
|
1102
1230
|
const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
|
|
1103
|
-
for (const
|
|
1104
|
-
s.x = Math.min(s.x,
|
|
1231
|
+
for (const n of e)
|
|
1232
|
+
s.x = Math.min(s.x, n.position.x), s.y = Math.min(s.y, n.position.y), s.z = Math.min(s.z, n.position.z), t.x = Math.max(t.x, n.position.x), t.y = Math.max(t.y, n.position.y), t.z = Math.max(t.z, n.position.z);
|
|
1105
1233
|
const i = 10;
|
|
1106
1234
|
return s.x -= i, s.y -= i, s.z -= i, t.x += i, t.y += i, t.z += i, { min: s, max: t };
|
|
1107
1235
|
}
|
|
@@ -1123,42 +1251,42 @@ class yt {
|
|
|
1123
1251
|
};
|
|
1124
1252
|
if (e.length === 1 || t > 20) {
|
|
1125
1253
|
let u = 0;
|
|
1126
|
-
const
|
|
1127
|
-
for (const
|
|
1128
|
-
u +=
|
|
1129
|
-
return u > 0 && (
|
|
1254
|
+
const b = { x: 0, y: 0, z: 0 };
|
|
1255
|
+
for (const x of e)
|
|
1256
|
+
u += x.mass, b.x += x.position.x * x.mass, b.y += x.position.y * x.mass, b.z += x.position.z * x.mass;
|
|
1257
|
+
return u > 0 && (b.x /= u, b.y /= u, b.z /= u), {
|
|
1130
1258
|
bounds: s,
|
|
1131
1259
|
size: i,
|
|
1132
|
-
centerOfMass:
|
|
1260
|
+
centerOfMass: b,
|
|
1133
1261
|
mass: u,
|
|
1134
1262
|
isLeaf: !0,
|
|
1135
1263
|
node: e[0],
|
|
1136
1264
|
children: []
|
|
1137
1265
|
};
|
|
1138
1266
|
}
|
|
1139
|
-
const
|
|
1267
|
+
const n = (s.min.x + s.max.x) / 2, o = (s.min.y + s.max.y) / 2, a = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
|
|
1140
1268
|
for (const u of e) {
|
|
1141
|
-
const
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
const
|
|
1145
|
-
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x:
|
|
1146
|
-
{ min: { x:
|
|
1147
|
-
{ min: { x: s.min.x, y:
|
|
1148
|
-
{ min: { x:
|
|
1149
|
-
{ min: { x: s.min.x, y: s.min.y, z:
|
|
1150
|
-
{ min: { x:
|
|
1151
|
-
{ min: { x: s.min.x, y:
|
|
1152
|
-
{ min: { x:
|
|
1153
|
-
],
|
|
1269
|
+
const b = (u.position.x >= n ? 1 : 0) + (u.position.y >= o ? 2 : 0) + (u.position.z >= a ? 4 : 0);
|
|
1270
|
+
c[b].push(u);
|
|
1271
|
+
}
|
|
1272
|
+
const h = [
|
|
1273
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: n, y: o, z: a } },
|
|
1274
|
+
{ min: { x: n, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: o, z: a } },
|
|
1275
|
+
{ min: { x: s.min.x, y: o, z: s.min.z }, max: { x: n, y: s.max.y, z: a } },
|
|
1276
|
+
{ min: { x: n, y: o, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: a } },
|
|
1277
|
+
{ min: { x: s.min.x, y: s.min.y, z: a }, max: { x: n, y: o, z: s.max.z } },
|
|
1278
|
+
{ min: { x: n, y: s.min.y, z: a }, max: { x: s.max.x, y: o, z: s.max.z } },
|
|
1279
|
+
{ min: { x: s.min.x, y: o, z: a }, max: { x: n, y: s.max.y, z: s.max.z } },
|
|
1280
|
+
{ min: { x: n, y: o, z: a }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1281
|
+
], d = [];
|
|
1154
1282
|
let f = 0;
|
|
1155
1283
|
const m = { x: 0, y: 0, z: 0 };
|
|
1156
1284
|
for (let u = 0; u < 8; u++)
|
|
1157
|
-
if (
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1285
|
+
if (c[u].length > 0) {
|
|
1286
|
+
const b = this.buildTree(c[u], h[u], t + 1);
|
|
1287
|
+
d.push(b), f += b.mass, m.x += b.centerOfMass.x * b.mass, m.y += b.centerOfMass.y * b.mass, m.z += b.centerOfMass.z * b.mass;
|
|
1160
1288
|
} else
|
|
1161
|
-
|
|
1289
|
+
d.push(null);
|
|
1162
1290
|
return f > 0 && (m.x /= f, m.y /= f, m.z /= f), {
|
|
1163
1291
|
bounds: s,
|
|
1164
1292
|
size: i,
|
|
@@ -1166,11 +1294,11 @@ class yt {
|
|
|
1166
1294
|
mass: f,
|
|
1167
1295
|
isLeaf: !1,
|
|
1168
1296
|
node: null,
|
|
1169
|
-
children:
|
|
1297
|
+
children: d
|
|
1170
1298
|
};
|
|
1171
1299
|
}
|
|
1172
1300
|
}
|
|
1173
|
-
class
|
|
1301
|
+
class bt {
|
|
1174
1302
|
constructor(e, s, t, i = 60) {
|
|
1175
1303
|
l(this, "sceneManager");
|
|
1176
1304
|
l(this, "animationId", null);
|
|
@@ -1235,7 +1363,222 @@ class xt {
|
|
|
1235
1363
|
this.stop();
|
|
1236
1364
|
}
|
|
1237
1365
|
}
|
|
1238
|
-
class
|
|
1366
|
+
class xt {
|
|
1367
|
+
constructor() {
|
|
1368
|
+
l(this, "worker", null);
|
|
1369
|
+
l(this, "nodeIds", []);
|
|
1370
|
+
l(this, "pendingStep", !1);
|
|
1371
|
+
l(this, "latestFrame", null);
|
|
1372
|
+
typeof Worker > "u" || typeof URL > "u" || typeof Blob > "u" || (this.worker = this.createWorker(), this.worker.onmessage = (e) => {
|
|
1373
|
+
const s = e.data;
|
|
1374
|
+
s.type === "positions" && s.positions && (this.latestFrame = {
|
|
1375
|
+
ids: this.nodeIds,
|
|
1376
|
+
positions: s.positions
|
|
1377
|
+
}, this.pendingStep = !1);
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
isAvailable() {
|
|
1381
|
+
return this.worker !== null;
|
|
1382
|
+
}
|
|
1383
|
+
init(e, s, t) {
|
|
1384
|
+
if (!this.worker) return;
|
|
1385
|
+
this.nodeIds = Array.from(e.keys());
|
|
1386
|
+
const i = /* @__PURE__ */ new Map(), n = [];
|
|
1387
|
+
let o = 0;
|
|
1388
|
+
e.forEach((c, h) => {
|
|
1389
|
+
i.set(h, o++), n.push({
|
|
1390
|
+
id: h,
|
|
1391
|
+
x: c.position.x,
|
|
1392
|
+
y: c.position.y,
|
|
1393
|
+
z: c.position.z,
|
|
1394
|
+
mass: c.mass
|
|
1395
|
+
});
|
|
1396
|
+
});
|
|
1397
|
+
const a = [];
|
|
1398
|
+
s.forEach((c) => {
|
|
1399
|
+
const h = i.get(c.source), d = i.get(c.target);
|
|
1400
|
+
h !== void 0 && d !== void 0 && a.push([h, d]);
|
|
1401
|
+
}), this.latestFrame = null, this.pendingStep = !1, this.worker.postMessage({
|
|
1402
|
+
type: "init",
|
|
1403
|
+
nodes: n,
|
|
1404
|
+
edges: a,
|
|
1405
|
+
params: t
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
setPhysicsParams(e) {
|
|
1409
|
+
this.worker && this.worker.postMessage({ type: "params", params: e });
|
|
1410
|
+
}
|
|
1411
|
+
requestStep() {
|
|
1412
|
+
!this.worker || this.pendingStep || (this.pendingStep = !0, this.worker.postMessage({ type: "step" }));
|
|
1413
|
+
}
|
|
1414
|
+
consumeLatestFrame() {
|
|
1415
|
+
if (!this.latestFrame) return null;
|
|
1416
|
+
const e = this.latestFrame;
|
|
1417
|
+
return this.latestFrame = null, e;
|
|
1418
|
+
}
|
|
1419
|
+
dispose() {
|
|
1420
|
+
this.worker && (this.worker.terminate(), this.worker = null), this.latestFrame = null, this.pendingStep = !1, this.nodeIds = [];
|
|
1421
|
+
}
|
|
1422
|
+
createWorker() {
|
|
1423
|
+
const e = `
|
|
1424
|
+
let nodes = [];
|
|
1425
|
+
let edges = [];
|
|
1426
|
+
let alpha = 1;
|
|
1427
|
+
let repulsion = 180;
|
|
1428
|
+
let attraction = 0.008;
|
|
1429
|
+
let damping = 0.95;
|
|
1430
|
+
let randState = 123456789;
|
|
1431
|
+
|
|
1432
|
+
function random() {
|
|
1433
|
+
randState = (1664525 * randState + 1013904223) >>> 0;
|
|
1434
|
+
return randState / 4294967296;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function applySampledRepulsion() {
|
|
1438
|
+
const n = nodes.length;
|
|
1439
|
+
if (n < 2) return;
|
|
1440
|
+
const sampleCount = n > 2500 ? 18 : n > 1200 ? 24 : 40;
|
|
1441
|
+
const nScale = n > 0 ? Math.sqrt(n / 500) : 1;
|
|
1442
|
+
|
|
1443
|
+
for (let i = 0; i < n; i++) {
|
|
1444
|
+
const nodeA = nodes[i];
|
|
1445
|
+
for (let s = 0; s < sampleCount; s++) {
|
|
1446
|
+
const j = (i + 1 + Math.floor(random() * (n - 1))) % n;
|
|
1447
|
+
const nodeB = nodes[j];
|
|
1448
|
+
const dx = nodeB.x - nodeA.x;
|
|
1449
|
+
const dy = nodeB.y - nodeA.y;
|
|
1450
|
+
const dz = nodeB.z - nodeA.z;
|
|
1451
|
+
let distSq = dx * dx + dy * dy + dz * dz;
|
|
1452
|
+
if (distSq > 40000) continue;
|
|
1453
|
+
if (distSq < 0.01) distSq = 0.01;
|
|
1454
|
+
const dist = Math.sqrt(distSq);
|
|
1455
|
+
const force = (repulsion * alpha) / (distSq * nScale);
|
|
1456
|
+
const fx = (dx / dist) * force;
|
|
1457
|
+
const fy = (dy / dist) * force;
|
|
1458
|
+
const fz = (dz / dist) * force;
|
|
1459
|
+
nodeA.vx -= fx / nodeA.mass;
|
|
1460
|
+
nodeA.vy -= fy / nodeA.mass;
|
|
1461
|
+
nodeA.vz -= fz / nodeA.mass;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function applyAttraction() {
|
|
1467
|
+
const targetDist = 15;
|
|
1468
|
+
const attrMul = nodes.length > 500 ? 1 + Math.log10(nodes.length / 500) : 1;
|
|
1469
|
+
for (let i = 0; i < edges.length; i++) {
|
|
1470
|
+
const e = edges[i];
|
|
1471
|
+
const source = nodes[e[0]];
|
|
1472
|
+
const target = nodes[e[1]];
|
|
1473
|
+
if (!source || !target) continue;
|
|
1474
|
+
const dx = target.x - source.x;
|
|
1475
|
+
const dy = target.y - source.y;
|
|
1476
|
+
const dz = target.z - source.z;
|
|
1477
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1478
|
+
if (dist < 0.01) continue;
|
|
1479
|
+
const force = (dist - targetDist) * attraction * attrMul * alpha;
|
|
1480
|
+
const fx = (dx / dist) * force;
|
|
1481
|
+
const fy = (dy / dist) * force;
|
|
1482
|
+
const fz = (dz / dist) * force;
|
|
1483
|
+
source.vx += fx / source.mass;
|
|
1484
|
+
source.vy += fy / source.mass;
|
|
1485
|
+
source.vz += fz / source.mass;
|
|
1486
|
+
target.vx -= fx / target.mass;
|
|
1487
|
+
target.vy -= fy / target.mass;
|
|
1488
|
+
target.vz -= fz / target.mass;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
function integrate() {
|
|
1493
|
+
const gravity = 0.05 * alpha;
|
|
1494
|
+
const maxVelocity = 5;
|
|
1495
|
+
|
|
1496
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1497
|
+
const node = nodes[i];
|
|
1498
|
+
node.vx -= node.x * gravity;
|
|
1499
|
+
node.vy -= node.y * gravity;
|
|
1500
|
+
node.vz -= node.z * gravity;
|
|
1501
|
+
|
|
1502
|
+
node.vx *= damping;
|
|
1503
|
+
node.vy *= damping;
|
|
1504
|
+
node.vz *= damping;
|
|
1505
|
+
|
|
1506
|
+
const speed = Math.sqrt(node.vx * node.vx + node.vy * node.vy + node.vz * node.vz);
|
|
1507
|
+
if (speed > maxVelocity) {
|
|
1508
|
+
const scale = maxVelocity / speed;
|
|
1509
|
+
node.vx *= scale;
|
|
1510
|
+
node.vy *= scale;
|
|
1511
|
+
node.vz *= scale;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
node.x += node.vx;
|
|
1515
|
+
node.y += node.vy;
|
|
1516
|
+
node.z += node.vz;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
alpha += (0 - alpha) * 0.0228;
|
|
1520
|
+
if (alpha < 0.001) {
|
|
1521
|
+
alpha = 0.001;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function step() {
|
|
1526
|
+
if (nodes.length === 0) return;
|
|
1527
|
+
applySampledRepulsion();
|
|
1528
|
+
applyAttraction();
|
|
1529
|
+
integrate();
|
|
1530
|
+
|
|
1531
|
+
const positions = new Float32Array(nodes.length * 3);
|
|
1532
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1533
|
+
const node = nodes[i];
|
|
1534
|
+
const base = i * 3;
|
|
1535
|
+
positions[base] = node.x;
|
|
1536
|
+
positions[base + 1] = node.y;
|
|
1537
|
+
positions[base + 2] = node.z;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
postMessage({ type: 'positions', positions }, [positions.buffer]);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
self.onmessage = (event) => {
|
|
1544
|
+
const msg = event.data || {};
|
|
1545
|
+
if (msg.type === 'init') {
|
|
1546
|
+
nodes = (msg.nodes || []).map((node) => ({
|
|
1547
|
+
id: node.id,
|
|
1548
|
+
x: node.x,
|
|
1549
|
+
y: node.y,
|
|
1550
|
+
z: node.z,
|
|
1551
|
+
vx: 0,
|
|
1552
|
+
vy: 0,
|
|
1553
|
+
vz: 0,
|
|
1554
|
+
mass: node.mass || 1,
|
|
1555
|
+
}));
|
|
1556
|
+
edges = msg.edges || [];
|
|
1557
|
+
alpha = 1;
|
|
1558
|
+
if (msg.params) {
|
|
1559
|
+
if (typeof msg.params.repulsionStrength === 'number') repulsion = msg.params.repulsionStrength;
|
|
1560
|
+
if (typeof msg.params.attractionStrength === 'number') attraction = msg.params.attractionStrength;
|
|
1561
|
+
if (typeof msg.params.damping === 'number') damping = msg.params.damping;
|
|
1562
|
+
}
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
if (msg.type === 'params') {
|
|
1567
|
+
if (typeof msg.params?.repulsionStrength === 'number') repulsion = msg.params.repulsionStrength;
|
|
1568
|
+
if (typeof msg.params?.attractionStrength === 'number') attraction = msg.params.attractionStrength;
|
|
1569
|
+
if (typeof msg.params?.damping === 'number') damping = msg.params.damping;
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (msg.type === 'step') {
|
|
1574
|
+
step();
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
`, s = new Blob([e], { type: "application/javascript" }), t = URL.createObjectURL(s), i = new Worker(t);
|
|
1578
|
+
return URL.revokeObjectURL(t), i;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
class vt {
|
|
1239
1582
|
constructor() {
|
|
1240
1583
|
l(this, "envMap", null);
|
|
1241
1584
|
l(this, "materialCache", /* @__PURE__ */ new Map());
|
|
@@ -1274,9 +1617,9 @@ class bt {
|
|
|
1274
1617
|
// -z
|
|
1275
1618
|
];
|
|
1276
1619
|
for (const i of t) {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1620
|
+
const n = document.createElement("canvas");
|
|
1621
|
+
n.width = 256, n.height = 256;
|
|
1622
|
+
const o = n.getContext("2d"), a = o.createRadialGradient(
|
|
1280
1623
|
256 / 2,
|
|
1281
1624
|
256 / 2,
|
|
1282
1625
|
0,
|
|
@@ -1284,17 +1627,17 @@ class bt {
|
|
|
1284
1627
|
256 / 2,
|
|
1285
1628
|
256 * 0.8
|
|
1286
1629
|
);
|
|
1287
|
-
|
|
1288
|
-
const
|
|
1289
|
-
for (let
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1630
|
+
a.addColorStop(0, i.colors[0]), a.addColorStop(0.5, i.colors[1]), a.addColorStop(1, i.colors[2]), o.fillStyle = a, o.fillRect(0, 0, 256, 256);
|
|
1631
|
+
const c = o.getImageData(0, 0, 256, 256);
|
|
1632
|
+
for (let h = 0; h < c.data.length; h += 4) {
|
|
1633
|
+
const d = (Math.random() - 0.5) * 5;
|
|
1634
|
+
c.data[h] = Math.min(255, Math.max(0, c.data[h] + d)), c.data[h + 1] = Math.min(255, Math.max(0, c.data[h + 1] + d)), c.data[h + 2] = Math.min(255, Math.max(0, c.data[h + 2] + d));
|
|
1292
1635
|
}
|
|
1293
|
-
|
|
1636
|
+
o.putImageData(c, 0, 0), s.push(n);
|
|
1294
1637
|
}
|
|
1295
|
-
this.envMap = new
|
|
1296
|
-
const
|
|
1297
|
-
return
|
|
1638
|
+
this.envMap = new y.CubeTexture(s.map((i) => {
|
|
1639
|
+
const n = new Image();
|
|
1640
|
+
return n.src = i.toDataURL(), n;
|
|
1298
1641
|
})), this.envMap.needsUpdate = !0;
|
|
1299
1642
|
}
|
|
1300
1643
|
/**
|
|
@@ -1311,11 +1654,11 @@ class bt {
|
|
|
1311
1654
|
const t = "glass-single";
|
|
1312
1655
|
if (this.materialCache.has(t))
|
|
1313
1656
|
return this.materialCache.get(t).clone();
|
|
1314
|
-
const i = new
|
|
1657
|
+
const i = new y.Color(16750950), n = new y.ShaderMaterial({
|
|
1315
1658
|
uniforms: {
|
|
1316
1659
|
uColor: { value: i },
|
|
1317
1660
|
uEnvMap: { value: this.envMap },
|
|
1318
|
-
uGlowColor: { value: new
|
|
1661
|
+
uGlowColor: { value: new y.Color(16777215) },
|
|
1319
1662
|
uGlowIntensity: { value: 0.8 },
|
|
1320
1663
|
uReflectivity: { value: 0.4 },
|
|
1321
1664
|
uFresnelPower: { value: 2.5 }
|
|
@@ -1381,17 +1724,17 @@ class bt {
|
|
|
1381
1724
|
}
|
|
1382
1725
|
`,
|
|
1383
1726
|
transparent: !0,
|
|
1384
|
-
side:
|
|
1727
|
+
side: y.FrontSide,
|
|
1385
1728
|
depthWrite: !0,
|
|
1386
|
-
blending:
|
|
1729
|
+
blending: y.NormalBlending
|
|
1387
1730
|
});
|
|
1388
|
-
return this.materialCache.set(t,
|
|
1731
|
+
return this.materialCache.set(t, n), n.clone();
|
|
1389
1732
|
}
|
|
1390
1733
|
/**
|
|
1391
1734
|
* Creates material for edges (light color for dark background)
|
|
1392
1735
|
*/
|
|
1393
1736
|
createEdgeMaterial(e = 6710886, s = 0.4) {
|
|
1394
|
-
return new
|
|
1737
|
+
return new y.LineBasicMaterial({
|
|
1395
1738
|
color: e,
|
|
1396
1739
|
transparent: !0,
|
|
1397
1740
|
opacity: s,
|
|
@@ -1402,7 +1745,7 @@ class bt {
|
|
|
1402
1745
|
* Creates highlighted edge material
|
|
1403
1746
|
*/
|
|
1404
1747
|
createHighlightedEdgeMaterial() {
|
|
1405
|
-
return new
|
|
1748
|
+
return new y.LineBasicMaterial({
|
|
1406
1749
|
color: 16750950,
|
|
1407
1750
|
// Tangerine highlight
|
|
1408
1751
|
transparent: !1,
|
|
@@ -1416,11 +1759,11 @@ class bt {
|
|
|
1416
1759
|
createLabelMaterial(e, s = 24) {
|
|
1417
1760
|
const t = document.createElement("canvas"), i = t.getContext("2d");
|
|
1418
1761
|
i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1419
|
-
const
|
|
1420
|
-
t.width = Math.max(128,
|
|
1421
|
-
const
|
|
1422
|
-
return
|
|
1423
|
-
map:
|
|
1762
|
+
const o = i.measureText(e).width;
|
|
1763
|
+
t.width = Math.max(128, o + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
|
|
1764
|
+
const a = new y.CanvasTexture(t);
|
|
1765
|
+
return a.needsUpdate = !0, new y.SpriteMaterial({
|
|
1766
|
+
map: a,
|
|
1424
1767
|
transparent: !0,
|
|
1425
1768
|
depthTest: !1,
|
|
1426
1769
|
depthWrite: !1
|
|
@@ -1439,7 +1782,7 @@ class bt {
|
|
|
1439
1782
|
this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
|
|
1440
1783
|
}
|
|
1441
1784
|
}
|
|
1442
|
-
class
|
|
1785
|
+
class Mt {
|
|
1443
1786
|
constructor(e, s = 2, t = [32, 16, 8], i = 16750950) {
|
|
1444
1787
|
l(this, "materialFactory");
|
|
1445
1788
|
l(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
@@ -1456,7 +1799,7 @@ class vt {
|
|
|
1456
1799
|
const t = `lod-${s}`;
|
|
1457
1800
|
this.geometryCache.set(
|
|
1458
1801
|
t,
|
|
1459
|
-
new
|
|
1802
|
+
new y.SphereGeometry(this.nodeRadius, e, e)
|
|
1460
1803
|
);
|
|
1461
1804
|
});
|
|
1462
1805
|
}
|
|
@@ -1471,21 +1814,21 @@ class vt {
|
|
|
1471
1814
|
* Creates a node visual (glass ball + label)
|
|
1472
1815
|
*/
|
|
1473
1816
|
createNode(e, s = 0) {
|
|
1474
|
-
const t = new
|
|
1817
|
+
const t = new y.Group();
|
|
1475
1818
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1476
|
-
const i = this.getGeometry(s),
|
|
1819
|
+
const i = this.getGeometry(s), n = this.materialFactory.createGlassMaterial(
|
|
1477
1820
|
e.color ?? this.defaultNodeColor
|
|
1478
|
-
),
|
|
1479
|
-
|
|
1480
|
-
const
|
|
1481
|
-
return
|
|
1821
|
+
), o = new y.Mesh(i, n);
|
|
1822
|
+
o.castShadow = !0, o.receiveShadow = !0, t.add(o);
|
|
1823
|
+
const a = this.materialFactory.createLabelMaterial(e.label), c = new y.Sprite(a);
|
|
1824
|
+
return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
|
|
1482
1825
|
e.position.x,
|
|
1483
1826
|
e.position.y,
|
|
1484
1827
|
e.position.z
|
|
1485
1828
|
), {
|
|
1486
1829
|
group: t,
|
|
1487
|
-
sphere:
|
|
1488
|
-
label:
|
|
1830
|
+
sphere: o,
|
|
1831
|
+
label: c,
|
|
1489
1832
|
lodLevel: s
|
|
1490
1833
|
};
|
|
1491
1834
|
}
|
|
@@ -1501,19 +1844,19 @@ class vt {
|
|
|
1501
1844
|
* Updates the color of a node
|
|
1502
1845
|
*/
|
|
1503
1846
|
updateNodeColor(e, s) {
|
|
1504
|
-
e.sphere.material instanceof
|
|
1847
|
+
e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
|
|
1505
1848
|
}
|
|
1506
1849
|
/**
|
|
1507
1850
|
* Updates the label of a node
|
|
1508
1851
|
*/
|
|
1509
1852
|
updateNodeLabel(e, s) {
|
|
1510
|
-
e.label.material instanceof
|
|
1853
|
+
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);
|
|
1511
1854
|
}
|
|
1512
1855
|
/**
|
|
1513
1856
|
* Disposes a node's resources
|
|
1514
1857
|
*/
|
|
1515
1858
|
disposeNode(e) {
|
|
1516
|
-
e.sphere.material instanceof
|
|
1859
|
+
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());
|
|
1517
1860
|
}
|
|
1518
1861
|
/**
|
|
1519
1862
|
* Dispose factory resources
|
|
@@ -1549,17 +1892,17 @@ class wt {
|
|
|
1549
1892
|
/**
|
|
1550
1893
|
* Creates an edge line between two positions
|
|
1551
1894
|
*/
|
|
1552
|
-
createEdge(e, s, t, i,
|
|
1553
|
-
const
|
|
1895
|
+
createEdge(e, s, t, i, n) {
|
|
1896
|
+
const o = new y.BufferGeometry(), a = new Float32Array([
|
|
1554
1897
|
i.x,
|
|
1555
1898
|
i.y,
|
|
1556
1899
|
i.z,
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1900
|
+
n.x,
|
|
1901
|
+
n.y,
|
|
1902
|
+
n.z
|
|
1560
1903
|
]);
|
|
1561
|
-
|
|
1562
|
-
const
|
|
1904
|
+
o.setAttribute("position", new y.BufferAttribute(a, 3));
|
|
1905
|
+
const c = new y.Line(o, this.getDefaultMaterial());
|
|
1563
1906
|
return c.name = `edge-${e.source}-${e.target}`, c.userData = {
|
|
1564
1907
|
source: e.source,
|
|
1565
1908
|
target: e.target,
|
|
@@ -1576,26 +1919,35 @@ class wt {
|
|
|
1576
1919
|
* Highlights an edge
|
|
1577
1920
|
*/
|
|
1578
1921
|
highlightEdge(e) {
|
|
1579
|
-
e.line.material
|
|
1922
|
+
e.line.material = this.getHighlightMaterial();
|
|
1580
1923
|
}
|
|
1581
1924
|
/**
|
|
1582
1925
|
* Resets an edge to default appearance
|
|
1583
1926
|
*/
|
|
1584
1927
|
unhighlightEdge(e) {
|
|
1585
|
-
e.line.material
|
|
1928
|
+
e.line.material = this.getDefaultMaterial();
|
|
1586
1929
|
}
|
|
1587
1930
|
/**
|
|
1588
1931
|
* Updates an edge's positions
|
|
1589
1932
|
*/
|
|
1590
1933
|
updateEdgePositions(e, s, t) {
|
|
1591
|
-
const i = e.line.geometry.attributes.position,
|
|
1592
|
-
|
|
1934
|
+
const i = e.line.geometry.attributes.position, n = i.array;
|
|
1935
|
+
n[0] = s.x, n[1] = s.y, n[2] = s.z, n[3] = t.x, n[4] = t.y, n[5] = t.z, i.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
|
|
1593
1936
|
}
|
|
1594
1937
|
/**
|
|
1595
1938
|
* Disposes an edge's resources
|
|
1596
1939
|
*/
|
|
1597
1940
|
disposeEdge(e) {
|
|
1598
|
-
e.line.geometry.dispose(), e.line.material instanceof
|
|
1941
|
+
if (e.line.geometry.dispose(), e.line.material instanceof y.Material) {
|
|
1942
|
+
const s = e.line.material;
|
|
1943
|
+
s !== this.defaultMaterial && s !== this.highlightMaterial && s.dispose();
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Returns the shared edge material for batch rendering
|
|
1948
|
+
*/
|
|
1949
|
+
getSharedEdgeMaterial() {
|
|
1950
|
+
return this.getDefaultMaterial();
|
|
1599
1951
|
}
|
|
1600
1952
|
/**
|
|
1601
1953
|
* Dispose factory resources
|
|
@@ -1604,7 +1956,7 @@ class wt {
|
|
|
1604
1956
|
this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
|
|
1605
1957
|
}
|
|
1606
1958
|
}
|
|
1607
|
-
class
|
|
1959
|
+
class Et {
|
|
1608
1960
|
constructor(e, s = [50, 100, 200], t = !0) {
|
|
1609
1961
|
l(this, "camera");
|
|
1610
1962
|
l(this, "lodDistances");
|
|
@@ -1616,16 +1968,16 @@ class Mt {
|
|
|
1616
1968
|
*/
|
|
1617
1969
|
getLODLevel(e) {
|
|
1618
1970
|
if (!this.enabled)
|
|
1619
|
-
return
|
|
1620
|
-
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z,
|
|
1621
|
-
return
|
|
1971
|
+
return K.HIGH;
|
|
1972
|
+
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, n = Math.sqrt(s * s + t * t + i * i);
|
|
1973
|
+
return n < this.lodDistances[0] ? K.HIGH : n < this.lodDistances[1] ? K.MEDIUM : K.LOW;
|
|
1622
1974
|
}
|
|
1623
1975
|
/**
|
|
1624
1976
|
* Checks if a node should be visible based on distance
|
|
1625
1977
|
*/
|
|
1626
1978
|
shouldRenderNode(e, s = 500) {
|
|
1627
|
-
const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y,
|
|
1628
|
-
return Math.sqrt(t * t + i * i +
|
|
1979
|
+
const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y, n = e.z - this.camera.position.z;
|
|
1980
|
+
return Math.sqrt(t * t + i * i + n * n) < s;
|
|
1629
1981
|
}
|
|
1630
1982
|
/**
|
|
1631
1983
|
* Sets the LOD distances
|
|
@@ -1640,13 +1992,13 @@ class Mt {
|
|
|
1640
1992
|
this.enabled = e;
|
|
1641
1993
|
}
|
|
1642
1994
|
}
|
|
1643
|
-
class
|
|
1995
|
+
class Ct {
|
|
1644
1996
|
constructor(e, s = !0) {
|
|
1645
1997
|
l(this, "camera");
|
|
1646
1998
|
l(this, "frustum");
|
|
1647
1999
|
l(this, "projScreenMatrix");
|
|
1648
2000
|
l(this, "enabled");
|
|
1649
|
-
this.camera = e, this.frustum = new
|
|
2001
|
+
this.camera = e, this.frustum = new y.Frustum(), this.projScreenMatrix = new y.Matrix4(), this.enabled = s;
|
|
1650
2002
|
}
|
|
1651
2003
|
/**
|
|
1652
2004
|
* Updates the frustum from the camera
|
|
@@ -1662,7 +2014,7 @@ class Et {
|
|
|
1662
2014
|
*/
|
|
1663
2015
|
isPointVisible(e) {
|
|
1664
2016
|
if (!this.enabled) return !0;
|
|
1665
|
-
const s = new
|
|
2017
|
+
const s = new y.Vector3(e.x, e.y, e.z);
|
|
1666
2018
|
return this.frustum.containsPoint(s);
|
|
1667
2019
|
}
|
|
1668
2020
|
/**
|
|
@@ -1670,8 +2022,8 @@ class Et {
|
|
|
1670
2022
|
*/
|
|
1671
2023
|
isSphereVisible(e, s) {
|
|
1672
2024
|
if (!this.enabled) return !0;
|
|
1673
|
-
const t = new
|
|
1674
|
-
new
|
|
2025
|
+
const t = new y.Sphere(
|
|
2026
|
+
new y.Vector3(e.x, e.y, e.z),
|
|
1675
2027
|
s
|
|
1676
2028
|
);
|
|
1677
2029
|
return this.frustum.intersectsSphere(t);
|
|
@@ -1681,15 +2033,15 @@ class Et {
|
|
|
1681
2033
|
*/
|
|
1682
2034
|
isLineVisible(e, s) {
|
|
1683
2035
|
if (!this.enabled) return !0;
|
|
1684
|
-
const t = new
|
|
2036
|
+
const t = new y.Vector3(e.x, e.y, e.z), i = new y.Vector3(s.x, s.y, s.z);
|
|
1685
2037
|
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
|
|
1686
2038
|
return !0;
|
|
1687
|
-
const
|
|
2039
|
+
const n = new y.Vector3(
|
|
1688
2040
|
(e.x + s.x) / 2,
|
|
1689
2041
|
(e.y + s.y) / 2,
|
|
1690
2042
|
(e.z + s.z) / 2
|
|
1691
|
-
),
|
|
1692
|
-
return this.frustum.intersectsSphere(
|
|
2043
|
+
), o = n.distanceTo(t), a = new y.Sphere(n, o);
|
|
2044
|
+
return this.frustum.intersectsSphere(a);
|
|
1693
2045
|
}
|
|
1694
2046
|
/**
|
|
1695
2047
|
* Enables/disables frustum culling
|
|
@@ -1698,7 +2050,7 @@ class Et {
|
|
|
1698
2050
|
this.enabled = e;
|
|
1699
2051
|
}
|
|
1700
2052
|
}
|
|
1701
|
-
class
|
|
2053
|
+
class St {
|
|
1702
2054
|
constructor(e, s) {
|
|
1703
2055
|
l(this, "sceneManager");
|
|
1704
2056
|
l(this, "raycaster");
|
|
@@ -1710,9 +2062,10 @@ class Ct {
|
|
|
1710
2062
|
l(this, "onEdgeClick", null);
|
|
1711
2063
|
l(this, "hoveredNodeId", null);
|
|
1712
2064
|
l(this, "hoveredEdgeKey", null);
|
|
2065
|
+
l(this, "edgeInteractionEnabled", !0);
|
|
1713
2066
|
l(this, "nodeObjects", []);
|
|
1714
2067
|
l(this, "edgeObjects", []);
|
|
1715
|
-
this.sceneManager = e, this.container = s, this.raycaster = new
|
|
2068
|
+
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);
|
|
1716
2069
|
}
|
|
1717
2070
|
/**
|
|
1718
2071
|
* Updates the list of node objects to raycast against
|
|
@@ -1726,6 +2079,12 @@ class Ct {
|
|
|
1726
2079
|
setEdgeObjects(e) {
|
|
1727
2080
|
this.edgeObjects = e;
|
|
1728
2081
|
}
|
|
2082
|
+
/**
|
|
2083
|
+
* Enables/disables edge hover and click raycasting
|
|
2084
|
+
*/
|
|
2085
|
+
setEdgeInteractionEnabled(e) {
|
|
2086
|
+
this.edgeInteractionEnabled = e, e || (this.hoveredEdgeKey = null, this.onEdgeHover && this.onEdgeHover(null));
|
|
2087
|
+
}
|
|
1729
2088
|
/**
|
|
1730
2089
|
* Sets the click callback
|
|
1731
2090
|
*/
|
|
@@ -1759,7 +2118,7 @@ class Ct {
|
|
|
1759
2118
|
this.onNodeClick(s);
|
|
1760
2119
|
return;
|
|
1761
2120
|
}
|
|
1762
|
-
const t = this.getIntersectedEdge(e);
|
|
2121
|
+
const t = this.edgeInteractionEnabled ? this.getIntersectedEdge(e) : null;
|
|
1763
2122
|
t && this.onEdgeClick && this.onEdgeClick(t);
|
|
1764
2123
|
}
|
|
1765
2124
|
/**
|
|
@@ -1771,8 +2130,14 @@ class Ct {
|
|
|
1771
2130
|
this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
|
|
1772
2131
|
return;
|
|
1773
2132
|
}
|
|
1774
|
-
const i = this.getIntersectedEdge(e),
|
|
1775
|
-
|
|
2133
|
+
const i = this.edgeInteractionEnabled ? this.getIntersectedEdge(e) : null, n = i ? `${i.edge.source}-${i.edge.target}` : null;
|
|
2134
|
+
n !== this.hoveredEdgeKey && (this.hoveredEdgeKey = n, this.onEdgeHover && this.onEdgeHover(i)), this.container.style.cursor = i ? "pointer" : "default";
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Gets currently hovered node ID
|
|
2138
|
+
*/
|
|
2139
|
+
getHoveredNodeId() {
|
|
2140
|
+
return this.hoveredNodeId;
|
|
1776
2141
|
}
|
|
1777
2142
|
/**
|
|
1778
2143
|
* Gets the intersected node from a mouse event
|
|
@@ -1783,11 +2148,11 @@ class Ct {
|
|
|
1783
2148
|
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);
|
|
1784
2149
|
const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1785
2150
|
if (t.length > 0) {
|
|
1786
|
-
let
|
|
1787
|
-
for (;
|
|
1788
|
-
if ((i =
|
|
1789
|
-
return
|
|
1790
|
-
|
|
2151
|
+
let n = t[0].object;
|
|
2152
|
+
for (; n; ) {
|
|
2153
|
+
if ((i = n.userData) != null && i.nodeData)
|
|
2154
|
+
return n.userData.nodeData;
|
|
2155
|
+
n = n.parent;
|
|
1791
2156
|
}
|
|
1792
2157
|
}
|
|
1793
2158
|
return null;
|
|
@@ -1800,15 +2165,15 @@ class Ct {
|
|
|
1800
2165
|
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);
|
|
1801
2166
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1802
2167
|
if (t.length > 0) {
|
|
1803
|
-
const i = t[0].object,
|
|
1804
|
-
if (
|
|
1805
|
-
const
|
|
2168
|
+
const i = t[0].object, n = i.userData;
|
|
2169
|
+
if (n != null && n.edge && (n != null && n.sourceNode) && (n != null && n.targetNode)) {
|
|
2170
|
+
const o = Array.isArray(n.relationships) ? n.relationships : [n.edge];
|
|
1806
2171
|
return {
|
|
1807
|
-
edge:
|
|
1808
|
-
relationships:
|
|
1809
|
-
relationshipCount: typeof
|
|
1810
|
-
sourceNode:
|
|
1811
|
-
targetNode:
|
|
2172
|
+
edge: n.edge,
|
|
2173
|
+
relationships: o,
|
|
2174
|
+
relationshipCount: typeof n.relationshipCount == "number" ? n.relationshipCount : o.length,
|
|
2175
|
+
sourceNode: n.sourceNode,
|
|
2176
|
+
targetNode: n.targetNode,
|
|
1812
2177
|
edgeLine: i
|
|
1813
2178
|
};
|
|
1814
2179
|
}
|
|
@@ -1819,16 +2184,16 @@ class Ct {
|
|
|
1819
2184
|
* Performs a raycast and returns the intersected node ID
|
|
1820
2185
|
*/
|
|
1821
2186
|
getIntersectedNodeId(e, s) {
|
|
1822
|
-
var
|
|
2187
|
+
var n;
|
|
1823
2188
|
const t = this.container.getBoundingClientRect();
|
|
1824
2189
|
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);
|
|
1825
2190
|
const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1826
2191
|
if (i.length > 0) {
|
|
1827
|
-
let
|
|
1828
|
-
for (;
|
|
1829
|
-
if ((
|
|
1830
|
-
return
|
|
1831
|
-
|
|
2192
|
+
let o = i[0].object;
|
|
2193
|
+
for (; o; ) {
|
|
2194
|
+
if ((n = o.userData) != null && n.nodeId)
|
|
2195
|
+
return o.userData.nodeId;
|
|
2196
|
+
o = o.parent;
|
|
1832
2197
|
}
|
|
1833
2198
|
}
|
|
1834
2199
|
return null;
|
|
@@ -1907,9 +2272,16 @@ class Nt {
|
|
|
1907
2272
|
if (!this.panel) return;
|
|
1908
2273
|
this.currentNodeId = e.id;
|
|
1909
2274
|
let t;
|
|
1910
|
-
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t
|
|
1911
|
-
const i = this.panel.querySelector('[data-action="
|
|
1912
|
-
i && i.addEventListener("click", () => {
|
|
2275
|
+
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
2276
|
+
const i = this.panel.querySelector('[data-action="expand"]'), n = this.panel.querySelector("[data-depth-select]");
|
|
2277
|
+
i && this.onExpand && i.addEventListener("click", () => {
|
|
2278
|
+
if (this.currentNodeId) {
|
|
2279
|
+
const a = n ? parseInt(n.value, 10) : 1;
|
|
2280
|
+
this.onExpand(this.currentNodeId, a);
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
const o = this.panel.querySelector('[data-action="close"]');
|
|
2284
|
+
o && o.addEventListener("click", () => {
|
|
1913
2285
|
this.hide();
|
|
1914
2286
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
1915
2287
|
}
|
|
@@ -2078,7 +2450,7 @@ class Nt {
|
|
|
2078
2450
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2079
2451
|
}
|
|
2080
2452
|
}
|
|
2081
|
-
class
|
|
2453
|
+
class kt {
|
|
2082
2454
|
constructor(e) {
|
|
2083
2455
|
l(this, "container");
|
|
2084
2456
|
l(this, "panel", null);
|
|
@@ -2140,18 +2512,18 @@ class St {
|
|
|
2140
2512
|
show(e, s, t, i = [e]) {
|
|
2141
2513
|
if (!this.panel) return;
|
|
2142
2514
|
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2143
|
-
let
|
|
2144
|
-
this.panelTemplate ?
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2515
|
+
let n;
|
|
2516
|
+
this.panelTemplate ? n = this.panelTemplate(e, s, t) : n = this.generateDefaultContent(e, s, t, i), this.panel.innerHTML = n;
|
|
2517
|
+
const o = this.panel.querySelector('[data-action="close"]');
|
|
2518
|
+
o && o.addEventListener("click", () => {
|
|
2147
2519
|
this.hide(), this.onClose && this.onClose();
|
|
2148
2520
|
});
|
|
2149
|
-
const
|
|
2150
|
-
|
|
2521
|
+
const a = this.panel.querySelector('[data-action="goto-source"]');
|
|
2522
|
+
a && this.onNodeClick && a.addEventListener("click", () => {
|
|
2151
2523
|
this.onNodeClick && this.onNodeClick(e.source);
|
|
2152
2524
|
});
|
|
2153
|
-
const
|
|
2154
|
-
|
|
2525
|
+
const c = this.panel.querySelector('[data-action="goto-target"]');
|
|
2526
|
+
c && this.onNodeClick && c.addEventListener("click", () => {
|
|
2155
2527
|
this.onNodeClick && this.onNodeClick(e.target);
|
|
2156
2528
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
2157
2529
|
}
|
|
@@ -2159,12 +2531,12 @@ class St {
|
|
|
2159
2531
|
* Generates default panel content
|
|
2160
2532
|
*/
|
|
2161
2533
|
generateDefaultContent(e, s, t, i = [e]) {
|
|
2162
|
-
const
|
|
2534
|
+
const n = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", o = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = this.getRelationshipLabel(e), c = i.length > 0 ? i : [e], h = this.summarizeRelationships(c), d = h.slice(0, 10).map((m) => `
|
|
2163
2535
|
<div class="relationship-item">
|
|
2164
2536
|
<span class="relationship-item-label">${this.escapeHtml(m.label)}</span>
|
|
2165
2537
|
<span class="relationship-item-count">${m.count}</span>
|
|
2166
2538
|
</div>
|
|
2167
|
-
`).join(""), f =
|
|
2539
|
+
`).join(""), f = h.length - Math.min(h.length, 10);
|
|
2168
2540
|
return `
|
|
2169
2541
|
<style>
|
|
2170
2542
|
.force-graph-edge-panel .panel-header {
|
|
@@ -2317,19 +2689,19 @@ class St {
|
|
|
2317
2689
|
</div>
|
|
2318
2690
|
|
|
2319
2691
|
<div class="relationship-section">
|
|
2320
|
-
<span class="relationship-label">${this.escapeHtml(
|
|
2321
|
-
<div class="relationship-count">${
|
|
2692
|
+
<span class="relationship-label">${this.escapeHtml(a)}</span>
|
|
2693
|
+
<div class="relationship-count">${c.length} relationships</div>
|
|
2322
2694
|
</div>
|
|
2323
2695
|
|
|
2324
2696
|
<div class="relationship-list">
|
|
2325
|
-
${
|
|
2697
|
+
${d}
|
|
2326
2698
|
${f > 0 ? `<div class="relationship-more">+ ${f} more</div>` : ""}
|
|
2327
2699
|
</div>
|
|
2328
2700
|
|
|
2329
2701
|
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2330
2702
|
<div class="node-type">Source</div>
|
|
2331
2703
|
<div class="node-card-header">
|
|
2332
|
-
<span class="color-dot" style="background: ${
|
|
2704
|
+
<span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
|
|
2333
2705
|
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2334
2706
|
</div>
|
|
2335
2707
|
</div>
|
|
@@ -2339,7 +2711,7 @@ class St {
|
|
|
2339
2711
|
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2340
2712
|
<div class="node-type">Target</div>
|
|
2341
2713
|
<div class="node-card-header">
|
|
2342
|
-
<span class="color-dot" style="background: ${
|
|
2714
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2343
2715
|
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2344
2716
|
</div>
|
|
2345
2717
|
</div>
|
|
@@ -2438,25 +2810,25 @@ class zt {
|
|
|
2438
2810
|
*/
|
|
2439
2811
|
positionTooltip(e, s) {
|
|
2440
2812
|
if (!this.tooltip) return;
|
|
2441
|
-
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth,
|
|
2442
|
-
let
|
|
2443
|
-
|
|
2813
|
+
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, n = window.innerHeight;
|
|
2814
|
+
let o = e + 15, a = s + 15;
|
|
2815
|
+
o + t.width > i - 10 && (o = e - t.width - 15), a + t.height > n - 10 && (a = s - t.height - 15), o < 10 && (o = 10), a < 10 && (a = 10), this.tooltip.style.left = `${o}px`, this.tooltip.style.top = `${a}px`;
|
|
2444
2816
|
}
|
|
2445
2817
|
/**
|
|
2446
2818
|
* Shows the tooltip with edge info
|
|
2447
2819
|
*/
|
|
2448
|
-
show(e, s, t, i,
|
|
2820
|
+
show(e, s, t, i, n, o = [e]) {
|
|
2449
2821
|
if (!this.tooltip) return;
|
|
2450
|
-
const
|
|
2451
|
-
if (
|
|
2452
|
-
const
|
|
2822
|
+
const a = o.length > 0 ? o : [e], c = this.summarizeRelationships(a), h = a.length;
|
|
2823
|
+
if (h <= 1) {
|
|
2824
|
+
const d = this.getRelationshipLabel(a[0]);
|
|
2453
2825
|
this.tooltip.innerHTML = `
|
|
2454
2826
|
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
2455
2827
|
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2456
2828
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
|
|
2457
2829
|
</div>
|
|
2458
2830
|
<div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
|
|
2459
|
-
↳ ${this.escapeHtml(
|
|
2831
|
+
↳ ${this.escapeHtml(d)}
|
|
2460
2832
|
</div>
|
|
2461
2833
|
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2462
2834
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
@@ -2464,7 +2836,7 @@ class zt {
|
|
|
2464
2836
|
</div>
|
|
2465
2837
|
`;
|
|
2466
2838
|
} else {
|
|
2467
|
-
const
|
|
2839
|
+
const d = c.slice(0, 4), f = c.length - d.length, m = d.map((u) => `<div style="display:flex; justify-content:space-between; gap:10px; font-size:12px; color: rgba(255,255,255,0.8);">
|
|
2468
2840
|
<span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${this.escapeHtml(u.label)}</span>
|
|
2469
2841
|
<span style="color: rgba(255,153,102,0.9); font-weight:600;">${u.count}</span>
|
|
2470
2842
|
</div>`).join("");
|
|
@@ -2474,7 +2846,7 @@ class zt {
|
|
|
2474
2846
|
${this.escapeHtml(s.label)} → ${this.escapeHtml(t.label)}
|
|
2475
2847
|
</div>
|
|
2476
2848
|
<div style="font-size:11px; letter-spacing:0.4px; text-transform:uppercase; color: rgba(255,255,255,0.6);">
|
|
2477
|
-
${
|
|
2849
|
+
${h} relationships
|
|
2478
2850
|
</div>
|
|
2479
2851
|
<div style="display:flex; flex-direction:column; gap:4px;">
|
|
2480
2852
|
${m}
|
|
@@ -2483,7 +2855,7 @@ class zt {
|
|
|
2483
2855
|
</div>
|
|
2484
2856
|
`;
|
|
2485
2857
|
}
|
|
2486
|
-
this.positionTooltip(i,
|
|
2858
|
+
this.positionTooltip(i, n), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2487
2859
|
}
|
|
2488
2860
|
/**
|
|
2489
2861
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2528,7 +2900,7 @@ class zt {
|
|
|
2528
2900
|
this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
|
|
2529
2901
|
}
|
|
2530
2902
|
}
|
|
2531
|
-
class
|
|
2903
|
+
class Pt {
|
|
2532
2904
|
constructor(e, s) {
|
|
2533
2905
|
l(this, "container");
|
|
2534
2906
|
l(this, "searchContainer", null);
|
|
@@ -2672,25 +3044,25 @@ class kt {
|
|
|
2672
3044
|
return;
|
|
2673
3045
|
}
|
|
2674
3046
|
let i = "";
|
|
2675
|
-
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((
|
|
2676
|
-
const
|
|
3047
|
+
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((n) => {
|
|
3048
|
+
const o = n.type || "Node";
|
|
2677
3049
|
i += `
|
|
2678
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2679
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2680
|
-
<div class="f3d-result-type">${this.escapeHtml(
|
|
3050
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.id)}">
|
|
3051
|
+
<div class="f3d-result-label">${this.escapeHtml(n.label)}</div>
|
|
3052
|
+
<div class="f3d-result-type">${this.escapeHtml(o)}</div>
|
|
2681
3053
|
</div>
|
|
2682
3054
|
`;
|
|
2683
|
-
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge:
|
|
3055
|
+
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: n, sourceNode: o, targetNode: a }) => {
|
|
2684
3056
|
i += `
|
|
2685
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2686
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2687
|
-
<div class="f3d-result-relationship">${this.escapeHtml(
|
|
3057
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(n.source)}">
|
|
3058
|
+
<div class="f3d-result-label">${this.escapeHtml(o.label)} → ${this.escapeHtml(a.label)}</div>
|
|
3059
|
+
<div class="f3d-result-relationship">${this.escapeHtml(n.relationship || "connected")}</div>
|
|
2688
3060
|
</div>
|
|
2689
3061
|
`;
|
|
2690
|
-
}), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((
|
|
2691
|
-
|
|
2692
|
-
const
|
|
2693
|
-
|
|
3062
|
+
}), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((n) => {
|
|
3063
|
+
n.addEventListener("click", () => {
|
|
3064
|
+
const o = n.dataset.nodeId;
|
|
3065
|
+
o && (this.onResultClick(o), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2694
3066
|
});
|
|
2695
3067
|
});
|
|
2696
3068
|
}
|
|
@@ -2702,7 +3074,7 @@ class kt {
|
|
|
2702
3074
|
this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
|
|
2703
3075
|
}
|
|
2704
3076
|
}
|
|
2705
|
-
class
|
|
3077
|
+
class Rt {
|
|
2706
3078
|
constructor(e, s) {
|
|
2707
3079
|
l(this, "container");
|
|
2708
3080
|
l(this, "toggleContainer", null);
|
|
@@ -2794,7 +3166,7 @@ class Tt {
|
|
|
2794
3166
|
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2795
3167
|
}
|
|
2796
3168
|
}
|
|
2797
|
-
class
|
|
3169
|
+
class Lt {
|
|
2798
3170
|
constructor(e) {
|
|
2799
3171
|
l(this, "container");
|
|
2800
3172
|
l(this, "legendContainer", null);
|
|
@@ -2877,7 +3249,7 @@ class Pt {
|
|
|
2877
3249
|
this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
|
|
2878
3250
|
}
|
|
2879
3251
|
}
|
|
2880
|
-
const
|
|
3252
|
+
const Tt = {
|
|
2881
3253
|
backgroundColor: "#0a0a0a",
|
|
2882
3254
|
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
2883
3255
|
nodeRadius: 24,
|
|
@@ -2890,7 +3262,7 @@ const Rt = {
|
|
|
2890
3262
|
damping: 0.85
|
|
2891
3263
|
// Fast energy dissipation
|
|
2892
3264
|
};
|
|
2893
|
-
class
|
|
3265
|
+
class It {
|
|
2894
3266
|
constructor(e, s = {}) {
|
|
2895
3267
|
l(this, "container");
|
|
2896
3268
|
l(this, "canvas");
|
|
@@ -2917,7 +3289,7 @@ class Lt {
|
|
|
2917
3289
|
l(this, "isSimulating", !0);
|
|
2918
3290
|
// Resize handler
|
|
2919
3291
|
l(this, "resizeHandler");
|
|
2920
|
-
this.container = e, this.options = { ...
|
|
3292
|
+
this.container = e, this.options = { ...Tt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
|
|
2921
3293
|
const t = this.canvas.getContext("2d");
|
|
2922
3294
|
if (!t)
|
|
2923
3295
|
throw new Error("Failed to get 2D context");
|
|
@@ -2930,23 +3302,23 @@ class Lt {
|
|
|
2930
3302
|
setupInteractions() {
|
|
2931
3303
|
this.canvas.addEventListener("wheel", (e) => {
|
|
2932
3304
|
e.preventDefault();
|
|
2933
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top,
|
|
2934
|
-
this.transform.x = t - (t - this.transform.x) *
|
|
3305
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = e.deltaY > 0 ? 0.9 : 1.1, o = Math.max(0.1, Math.min(5, this.transform.scale * n)), a = o / this.transform.scale;
|
|
3306
|
+
this.transform.x = t - (t - this.transform.x) * a, this.transform.y = i - (i - this.transform.y) * a, this.transform.scale = o;
|
|
2935
3307
|
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2936
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top,
|
|
2937
|
-
this.dragStartPos = { x: e.clientX, y: e.clientY },
|
|
3308
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = this.screenToWorld(t, i), o = this.findNodeAt(n.x, n.y);
|
|
3309
|
+
this.dragStartPos = { x: e.clientX, y: e.clientY }, o ? (this.isDragging = !0, this.draggedNode = o, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2938
3310
|
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2939
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top,
|
|
3311
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, n = this.screenToWorld(t, i);
|
|
2940
3312
|
if (this.isDragging && this.draggedNode)
|
|
2941
|
-
this.draggedNode.x =
|
|
3313
|
+
this.draggedNode.x = n.x, this.draggedNode.y = n.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2942
3314
|
else if (this.isPanning) {
|
|
2943
|
-
const
|
|
2944
|
-
this.transform.x +=
|
|
3315
|
+
const o = e.clientX - this.lastMousePos.x, a = e.clientY - this.lastMousePos.y;
|
|
3316
|
+
this.transform.x += o, this.transform.y += a, this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2945
3317
|
} else {
|
|
2946
|
-
const
|
|
2947
|
-
if (
|
|
2948
|
-
const
|
|
2949
|
-
|
|
3318
|
+
const o = this.findNodeAt(n.x, n.y);
|
|
3319
|
+
if (o !== this.hoveredNode && (this.hoveredNode = o, o ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(o.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !o) {
|
|
3320
|
+
const a = this.findEdgeAt(n.x, n.y);
|
|
3321
|
+
a !== this.hoveredEdge && (this.hoveredEdge = a, this.canvas.style.cursor = a ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(a ? a.data : null, e));
|
|
2950
3322
|
}
|
|
2951
3323
|
}
|
|
2952
3324
|
}), this.canvas.addEventListener("mouseup", (e) => {
|
|
@@ -2954,8 +3326,8 @@ class Lt {
|
|
|
2954
3326
|
if (this.isDragging && this.draggedNode)
|
|
2955
3327
|
i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
2956
3328
|
else if (i) {
|
|
2957
|
-
const
|
|
2958
|
-
|
|
3329
|
+
const n = this.canvas.getBoundingClientRect(), o = this.screenToWorld(e.clientX - n.left, e.clientY - n.top), a = this.findEdgeAt(o.x, o.y);
|
|
3330
|
+
a && this.options.onEdgeClick && this.options.onEdgeClick(a.data);
|
|
2959
3331
|
}
|
|
2960
3332
|
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
2961
3333
|
}), this.canvas.addEventListener("mouseleave", () => {
|
|
@@ -2970,20 +3342,20 @@ class Lt {
|
|
|
2970
3342
|
}
|
|
2971
3343
|
findNodeAt(e, s) {
|
|
2972
3344
|
for (const t of this.nodes.values()) {
|
|
2973
|
-
const i = t.x - e,
|
|
2974
|
-
if (Math.sqrt(i * i +
|
|
3345
|
+
const i = t.x - e, n = t.y - s;
|
|
3346
|
+
if (Math.sqrt(i * i + n * n) < t.radius)
|
|
2975
3347
|
return t;
|
|
2976
3348
|
}
|
|
2977
3349
|
return null;
|
|
2978
3350
|
}
|
|
2979
3351
|
findEdgeAt(e, s) {
|
|
2980
3352
|
for (const i of this.edges) {
|
|
2981
|
-
const
|
|
2982
|
-
if (!
|
|
2983
|
-
const
|
|
2984
|
-
if (
|
|
2985
|
-
const
|
|
2986
|
-
if (Math.sqrt(u * u +
|
|
3353
|
+
const n = this.nodes.get(i.source), o = this.nodes.get(i.target);
|
|
3354
|
+
if (!n || !o) continue;
|
|
3355
|
+
const a = o.x - n.x, c = o.y - n.y, h = a * a + c * c;
|
|
3356
|
+
if (h === 0) continue;
|
|
3357
|
+
const d = Math.max(0, Math.min(1, ((e - n.x) * a + (s - n.y) * c) / h)), f = n.x + d * a, m = n.y + d * c, u = e - f, b = s - m;
|
|
3358
|
+
if (Math.sqrt(u * u + b * b) < 12)
|
|
2987
3359
|
return i;
|
|
2988
3360
|
}
|
|
2989
3361
|
return null;
|
|
@@ -2998,79 +3370,79 @@ class Lt {
|
|
|
2998
3370
|
const e = Array.from(this.nodes.values()), s = e.length;
|
|
2999
3371
|
if (s === 0) return;
|
|
3000
3372
|
const t = 60, i = 5;
|
|
3001
|
-
let
|
|
3002
|
-
for (let
|
|
3003
|
-
for (let
|
|
3004
|
-
const
|
|
3005
|
-
let f =
|
|
3373
|
+
let n = 0;
|
|
3374
|
+
for (let a = 0; a < s; a++)
|
|
3375
|
+
for (let c = a + 1; c < s; c++) {
|
|
3376
|
+
const h = e[a], d = e[c];
|
|
3377
|
+
let f = d.x - h.x, m = d.y - h.y, u = Math.sqrt(f * f + m * m);
|
|
3006
3378
|
if (u < t * 3) {
|
|
3007
3379
|
u < 1 && (u = 1);
|
|
3008
|
-
const
|
|
3009
|
-
|
|
3380
|
+
const b = this.options.repulsionStrength / (u * u), x = f / u * b, M = m / u * b;
|
|
3381
|
+
h.vx -= x, h.vy -= M, d.vx += x, d.vy += M;
|
|
3010
3382
|
}
|
|
3011
3383
|
}
|
|
3012
|
-
const
|
|
3013
|
-
for (const
|
|
3014
|
-
const
|
|
3015
|
-
if (!
|
|
3016
|
-
let
|
|
3384
|
+
const o = 80;
|
|
3385
|
+
for (const a of this.edges) {
|
|
3386
|
+
const c = this.nodes.get(a.source), h = this.nodes.get(a.target);
|
|
3387
|
+
if (!c || !h) continue;
|
|
3388
|
+
let d = h.x - c.x, f = h.y - c.y, m = Math.sqrt(d * d + f * f);
|
|
3017
3389
|
m < 1 && (m = 1);
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3390
|
+
const b = (m - o) * this.options.attractionStrength, x = d / m * b, M = f / m * b;
|
|
3391
|
+
c.vx += x, c.vy += M, h.vx -= x, h.vy -= M;
|
|
3020
3392
|
}
|
|
3021
|
-
for (const
|
|
3022
|
-
if (this.draggedNode ===
|
|
3023
|
-
|
|
3024
|
-
const
|
|
3025
|
-
|
|
3393
|
+
for (const a of e) {
|
|
3394
|
+
if (this.draggedNode === a) continue;
|
|
3395
|
+
a.vx *= this.options.damping, a.vy *= this.options.damping;
|
|
3396
|
+
const c = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
|
|
3397
|
+
c > i && (a.vx = a.vx / c * i, a.vy = a.vy / c * i), a.x += a.vx, a.y += a.vy, n += a.vx * a.vx + a.vy * a.vy;
|
|
3026
3398
|
}
|
|
3027
|
-
|
|
3399
|
+
n < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
3028
3400
|
}
|
|
3029
3401
|
render() {
|
|
3030
3402
|
const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
|
|
3031
3403
|
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();
|
|
3032
3404
|
}
|
|
3033
3405
|
renderGrid(e, s) {
|
|
3034
|
-
const t = this.ctx, i = 40 * this.transform.scale,
|
|
3406
|
+
const t = this.ctx, i = 40 * this.transform.scale, n = 1.5, o = this.transform.x % i, a = this.transform.y % i;
|
|
3035
3407
|
t.fillStyle = this.options.gridColor;
|
|
3036
|
-
for (let
|
|
3037
|
-
for (let
|
|
3038
|
-
t.beginPath(), t.arc(
|
|
3408
|
+
for (let c = o; c < e; c += i)
|
|
3409
|
+
for (let h = a; h < s; h += i)
|
|
3410
|
+
t.beginPath(), t.arc(c, h, n, 0, Math.PI * 2), t.fill();
|
|
3039
3411
|
}
|
|
3040
3412
|
renderEdges() {
|
|
3041
3413
|
const e = this.ctx;
|
|
3042
3414
|
for (const s of this.edges) {
|
|
3043
3415
|
const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
|
|
3044
3416
|
if (!t || !i) continue;
|
|
3045
|
-
const
|
|
3046
|
-
|
|
3417
|
+
const n = s === this.hoveredEdge, o = e.createLinearGradient(t.x, t.y, i.x, i.y);
|
|
3418
|
+
n ? (o.addColorStop(0, "rgba(255, 153, 102, 0.8)"), o.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), o.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (o.addColorStop(0, "rgba(255, 153, 102, 0.3)"), o.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), o.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = o, e.stroke();
|
|
3047
3419
|
}
|
|
3048
3420
|
}
|
|
3049
3421
|
renderNodes() {
|
|
3050
3422
|
const e = this.ctx;
|
|
3051
3423
|
for (const s of this.nodes.values()) {
|
|
3052
|
-
const t = s === this.hoveredNode, i = s === this.selectedNode,
|
|
3053
|
-
if (t || i ||
|
|
3054
|
-
const
|
|
3424
|
+
const t = s === this.hoveredNode, i = s === this.selectedNode, n = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), o = s.radius * (t ? 1.1 : 1);
|
|
3425
|
+
if (t || i || n) {
|
|
3426
|
+
const b = e.createRadialGradient(
|
|
3055
3427
|
s.x,
|
|
3056
3428
|
s.y,
|
|
3057
|
-
|
|
3429
|
+
o * 0.5,
|
|
3058
3430
|
s.x,
|
|
3059
3431
|
s.y,
|
|
3060
|
-
|
|
3061
|
-
),
|
|
3062
|
-
|
|
3432
|
+
o * 2
|
|
3433
|
+
), x = t || i ? 0.4 : 0.25;
|
|
3434
|
+
b.addColorStop(0, `rgba(255, 153, 102, ${x})`), b.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = b, e.beginPath(), e.arc(s.x, s.y, o * 2, 0, Math.PI * 2), e.fill();
|
|
3063
3435
|
}
|
|
3064
|
-
const
|
|
3065
|
-
s.x -
|
|
3066
|
-
s.y -
|
|
3436
|
+
const a = e.createRadialGradient(
|
|
3437
|
+
s.x - o * 0.3,
|
|
3438
|
+
s.y - o * 0.3,
|
|
3067
3439
|
0,
|
|
3068
3440
|
s.x,
|
|
3069
3441
|
s.y,
|
|
3070
|
-
|
|
3071
|
-
),
|
|
3072
|
-
|
|
3073
|
-
const f =
|
|
3442
|
+
o
|
|
3443
|
+
), c = s.color >> 16 & 255, h = s.color >> 8 & 255, d = s.color & 255;
|
|
3444
|
+
a.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255, h + 60)}, ${Math.min(255, d + 60)}, 0.95)`), a.addColorStop(0.7, `rgba(${c}, ${h}, ${d}, 0.9)`), a.addColorStop(1, `rgba(${Math.max(0, c - 40)}, ${Math.max(0, h - 40)}, ${Math.max(0, d - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, o, 0, Math.PI * 2), e.fillStyle = a, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - o * 0.25, s.y - o * 0.25, o * 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";
|
|
3445
|
+
const f = o * 1.6;
|
|
3074
3446
|
let m = s.label, u = e.measureText(m).width;
|
|
3075
3447
|
if (u > f) {
|
|
3076
3448
|
for (; u > f && m.length > 3; )
|
|
@@ -3088,7 +3460,7 @@ class Lt {
|
|
|
3088
3460
|
const i = s.position || {
|
|
3089
3461
|
x: (Math.random() - 0.5) * 300,
|
|
3090
3462
|
y: (Math.random() - 0.5) * 300
|
|
3091
|
-
},
|
|
3463
|
+
}, n = {
|
|
3092
3464
|
id: s.id,
|
|
3093
3465
|
label: s.label,
|
|
3094
3466
|
x: i.x,
|
|
@@ -3100,7 +3472,7 @@ class Lt {
|
|
|
3100
3472
|
radius: this.options.nodeRadius,
|
|
3101
3473
|
data: s
|
|
3102
3474
|
};
|
|
3103
|
-
this.nodes.set(s.id,
|
|
3475
|
+
this.nodes.set(s.id, n), this.nodeIdToIndex.set(s.id, t);
|
|
3104
3476
|
}), this.edges = e.edges.map((s) => ({
|
|
3105
3477
|
source: s.source,
|
|
3106
3478
|
target: s.target,
|
|
@@ -3176,11 +3548,11 @@ class Lt {
|
|
|
3176
3548
|
focusOnNode(e) {
|
|
3177
3549
|
const s = this.nodes.get(e);
|
|
3178
3550
|
if (!s) return;
|
|
3179
|
-
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale,
|
|
3180
|
-
const
|
|
3181
|
-
this.transform.x =
|
|
3551
|
+
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, n = this.transform.x, o = this.transform.y, a = 500, c = performance.now(), h = () => {
|
|
3552
|
+
const d = performance.now() - c, f = Math.min(d / a, 1), m = 1 - Math.pow(1 - f, 3);
|
|
3553
|
+
this.transform.x = n + (t - n) * m, this.transform.y = o + (i - o) * m, f < 1 ? requestAnimationFrame(h) : this.selectedNode = s;
|
|
3182
3554
|
};
|
|
3183
|
-
|
|
3555
|
+
h();
|
|
3184
3556
|
}
|
|
3185
3557
|
/**
|
|
3186
3558
|
* Updates node positions from 3D data
|
|
@@ -3223,7 +3595,6 @@ class Lt {
|
|
|
3223
3595
|
}
|
|
3224
3596
|
}
|
|
3225
3597
|
class Ht {
|
|
3226
|
-
// Store graph data for view switching
|
|
3227
3598
|
constructor(e, s = {}) {
|
|
3228
3599
|
// Options
|
|
3229
3600
|
l(this, "options");
|
|
@@ -3234,6 +3605,8 @@ class Ht {
|
|
|
3234
3605
|
l(this, "edgeManager");
|
|
3235
3606
|
l(this, "graphEngine");
|
|
3236
3607
|
l(this, "rendererManager");
|
|
3608
|
+
l(this, "physicsWorkerBridge", null);
|
|
3609
|
+
l(this, "useWorkerPhysics", !1);
|
|
3237
3610
|
// Factories
|
|
3238
3611
|
l(this, "materialFactory");
|
|
3239
3612
|
l(this, "nodeFactory");
|
|
@@ -3258,23 +3631,33 @@ class Ht {
|
|
|
3258
3631
|
l(this, "devControls", null);
|
|
3259
3632
|
l(this, "viewMode", "3d");
|
|
3260
3633
|
l(this, "graphData", null);
|
|
3261
|
-
|
|
3634
|
+
// Store graph data for view switching
|
|
3635
|
+
l(this, "simulationStride", 1);
|
|
3636
|
+
l(this, "simulationTick", 0);
|
|
3637
|
+
l(this, "labelUpdateIntervalMs", 140);
|
|
3638
|
+
l(this, "lastLabelUpdateAt", 0);
|
|
3639
|
+
l(this, "performanceCheckIntervalMs", 1e3);
|
|
3640
|
+
l(this, "lastPerformanceCheckAt", 0);
|
|
3641
|
+
l(this, "currentPixelRatio", 1);
|
|
3642
|
+
l(this, "edgeInteractionsEnabled", !0);
|
|
3643
|
+
l(this, "physicsWorkerNeedsSync", !0);
|
|
3644
|
+
this.options = { ...C, ...s }, this.container = ht(e), this.materialFactory = new vt(), this.nodeFactory = new Mt(
|
|
3262
3645
|
this.materialFactory,
|
|
3263
|
-
this.options.nodeRadius ??
|
|
3264
|
-
this.options.lodSegments ??
|
|
3265
|
-
this.options.defaultNodeColor ??
|
|
3646
|
+
this.options.nodeRadius ?? C.nodeRadius,
|
|
3647
|
+
this.options.lodSegments ?? C.lodSegments,
|
|
3648
|
+
this.options.defaultNodeColor ?? C.defaultNodeColor
|
|
3266
3649
|
), this.edgeFactory = new wt(
|
|
3267
3650
|
this.materialFactory,
|
|
3268
|
-
this.options.edgeColor ??
|
|
3269
|
-
this.options.edgeOpacity ??
|
|
3270
|
-
), this.sceneManager = new ft(this.container, this.options), this.lodManager = new
|
|
3651
|
+
this.options.edgeColor ?? C.edgeColor,
|
|
3652
|
+
this.options.edgeOpacity ?? C.edgeOpacity
|
|
3653
|
+
), this.sceneManager = new ft(this.container, this.options), this.currentPixelRatio = this.sceneManager.getPixelRatio(), this.lodManager = new Et(
|
|
3271
3654
|
this.sceneManager.camera,
|
|
3272
|
-
this.options.lodDistances ??
|
|
3273
|
-
this.options.enableLOD ??
|
|
3274
|
-
), this.frustumCuller = new
|
|
3655
|
+
this.options.lodDistances ?? C.lodDistances,
|
|
3656
|
+
this.options.enableLOD ?? C.enableLOD
|
|
3657
|
+
), this.frustumCuller = new Ct(
|
|
3275
3658
|
this.sceneManager.camera,
|
|
3276
|
-
this.options.enableEdgeCulling ??
|
|
3277
|
-
), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new
|
|
3659
|
+
this.options.enableEdgeCulling ?? C.enableEdgeCulling
|
|
3660
|
+
), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Oe(
|
|
3278
3661
|
this.nodeManager.getAllNodes(),
|
|
3279
3662
|
this.edgeManager.getAllEdges(),
|
|
3280
3663
|
{
|
|
@@ -3284,16 +3667,16 @@ class Ht {
|
|
|
3284
3667
|
useBarnesHut: this.options.useBarnesHut,
|
|
3285
3668
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3286
3669
|
}
|
|
3287
|
-
), this.rendererManager = new
|
|
3670
|
+
), this.rendererManager = new bt(
|
|
3288
3671
|
this.sceneManager,
|
|
3289
3672
|
() => this.onSimulate(),
|
|
3290
3673
|
() => this.onRender(),
|
|
3291
|
-
this.options.targetFPS ??
|
|
3292
|
-
), this.raycasterManager = new
|
|
3674
|
+
this.options.targetFPS ?? C.targetFPS
|
|
3675
|
+
), this.raycasterManager = new St(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgePanelManager = new kt(this.container), this.edgeTooltipManager = new zt(), this.edgePanelManager.setNodeClickCallback((t) => {
|
|
3293
3676
|
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3294
3677
|
this.showNodePanel(t);
|
|
3295
3678
|
}, 400);
|
|
3296
|
-
}), this.options.showSearch !== !1 && (this.searchManager = new
|
|
3679
|
+
}), this.options.showSearch !== !1 && (this.searchManager = new Pt(this.container, {
|
|
3297
3680
|
placeholder: this.options.searchPlaceholder,
|
|
3298
3681
|
onSearch: (t) => ({
|
|
3299
3682
|
nodeResults: this.searchNodes(t),
|
|
@@ -3304,12 +3687,12 @@ class Ht {
|
|
|
3304
3687
|
this.showNodePanel(t);
|
|
3305
3688
|
}, 400);
|
|
3306
3689
|
}
|
|
3307
|
-
})), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new
|
|
3690
|
+
})), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Rt(this.container, {
|
|
3308
3691
|
initialMode: this.viewMode,
|
|
3309
3692
|
onViewChange: (t) => {
|
|
3310
3693
|
this.switchView(t);
|
|
3311
3694
|
}
|
|
3312
|
-
})), this.options.showLegend !== !1 && (this.legendManager = new
|
|
3695
|
+
})), this.options.showLegend !== !1 && (this.legendManager = new Lt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.applyPerformanceModeSettings(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
|
|
3313
3696
|
}
|
|
3314
3697
|
/**
|
|
3315
3698
|
* Sets up internal callbacks
|
|
@@ -3361,19 +3744,50 @@ class Ht {
|
|
|
3361
3744
|
* Called every simulation step
|
|
3362
3745
|
*/
|
|
3363
3746
|
onSimulate() {
|
|
3364
|
-
this.
|
|
3365
|
-
|
|
3366
|
-
if (
|
|
3367
|
-
const t =
|
|
3368
|
-
|
|
3747
|
+
if (this.useWorkerPhysics && this.physicsWorkerBridge) {
|
|
3748
|
+
const e = this.physicsWorkerBridge.consumeLatestFrame();
|
|
3749
|
+
if (e) {
|
|
3750
|
+
const { ids: s, positions: t } = e;
|
|
3751
|
+
for (let i = 0; i < s.length; i++) {
|
|
3752
|
+
const n = i * 3, o = s[i];
|
|
3753
|
+
if (this.nodeManager.updateNodePosition(o, {
|
|
3754
|
+
x: t[n],
|
|
3755
|
+
y: t[n + 1],
|
|
3756
|
+
z: t[n + 2]
|
|
3757
|
+
}), this.options.enableLOD) {
|
|
3758
|
+
const a = this.nodeManager.getNode(o);
|
|
3759
|
+
if (a) {
|
|
3760
|
+
const c = this.lodManager.getLODLevel(a.position);
|
|
3761
|
+
this.nodeManager.updateNodeLOD(o, c);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
this.edgeManager.updateEdgePositions();
|
|
3369
3766
|
}
|
|
3370
|
-
|
|
3767
|
+
this.physicsWorkerBridge.requestStep();
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3770
|
+
if (this.simulationTick = (this.simulationTick + 1) % this.simulationStride, this.simulationTick === 0) {
|
|
3771
|
+
this.graphEngine.simulate();
|
|
3772
|
+
for (const [e, s] of this.nodeManager.getAllNodes())
|
|
3773
|
+
if (this.nodeManager.updateNodePosition(e, s.position), this.options.enableLOD) {
|
|
3774
|
+
const t = this.lodManager.getLODLevel(s.position);
|
|
3775
|
+
this.nodeManager.updateNodeLOD(e, t);
|
|
3776
|
+
}
|
|
3777
|
+
this.edgeManager.updateEdgePositions();
|
|
3778
|
+
}
|
|
3371
3779
|
}
|
|
3372
3780
|
/**
|
|
3373
3781
|
* Called every render frame
|
|
3374
3782
|
*/
|
|
3375
3783
|
onRender() {
|
|
3376
|
-
this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
|
|
3784
|
+
this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeInteractionEnabled(this.edgeInteractionsEnabled), this.edgeInteractionsEnabled ? this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines()) : this.raycasterManager.setEdgeObjects([]);
|
|
3785
|
+
const e = performance.now();
|
|
3786
|
+
e - this.lastLabelUpdateAt >= this.labelUpdateIntervalMs && (this.nodeManager.updateLabelVisibility(
|
|
3787
|
+
this.sceneManager.getCameraPosition(),
|
|
3788
|
+
this.panelManager.getCurrentNodeId(),
|
|
3789
|
+
this.raycasterManager.getHoveredNodeId()
|
|
3790
|
+
), this.lastLabelUpdateAt = e), (this.options.enableAdaptivePerformance ?? !0) && e - this.lastPerformanceCheckAt >= this.performanceCheckIntervalMs && (this.tunePerformanceForCurrentFPS(), this.lastPerformanceCheckAt = e);
|
|
3377
3791
|
}
|
|
3378
3792
|
/**
|
|
3379
3793
|
* Updates bottom-right legend counts
|
|
@@ -3381,6 +3795,55 @@ class Ht {
|
|
|
3381
3795
|
updateLegendCounts() {
|
|
3382
3796
|
this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
|
|
3383
3797
|
}
|
|
3798
|
+
/**
|
|
3799
|
+
* Applies baseline performance preset from constructor options
|
|
3800
|
+
*/
|
|
3801
|
+
applyPerformanceModeSettings() {
|
|
3802
|
+
const e = this.options.performanceMode ?? "balanced";
|
|
3803
|
+
this.nodeManager.setLabelDistance(this.options.labelDistance ?? C.labelDistance), e === "quality" ? (this.sceneManager.setHighQualityRendering(!0), this.rendererManager.setTargetFPS(this.options.targetFPS ?? 60), this.simulationStride = 1, this.nodeManager.setLabelRenderMode("all"), this.edgeInteractionsEnabled = !0) : e === "low" ? (this.sceneManager.setHighQualityRendering(!1), this.rendererManager.setTargetFPS(Math.min(this.options.targetFPS ?? 60, 45)), this.simulationStride = 2, this.nodeManager.setLabelRenderMode(
|
|
3804
|
+
this.options.labelRenderMode === "all" ? "adaptive" : this.options.labelRenderMode ?? "none"
|
|
3805
|
+
), this.edgeInteractionsEnabled = !(this.options.disableEdgeInteractionsInLowMode ?? !0), this.currentPixelRatio = Math.min(this.currentPixelRatio, 1), this.sceneManager.setPixelRatio(this.currentPixelRatio)) : (this.sceneManager.setHighQualityRendering(!0), this.rendererManager.setTargetFPS(this.options.targetFPS ?? 60), this.simulationStride = 1, this.nodeManager.setLabelRenderMode(this.options.labelRenderMode ?? "adaptive"), this.edgeInteractionsEnabled = !0), this.refreshPhysicsBackend();
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* Applies adaptive quality tuning based on runtime FPS
|
|
3809
|
+
*/
|
|
3810
|
+
tunePerformanceForCurrentFPS() {
|
|
3811
|
+
const e = this.rendererManager.getFPS(), s = this.options.minPixelRatio ?? C.minPixelRatio, t = Math.min(
|
|
3812
|
+
this.options.maxPixelRatio ?? C.maxPixelRatio,
|
|
3813
|
+
window.devicePixelRatio || 1
|
|
3814
|
+
);
|
|
3815
|
+
e < 24 ? (this.simulationStride = 3, this.currentPixelRatio > s && (this.currentPixelRatio = Math.max(s, this.currentPixelRatio - 0.1), this.sceneManager.setPixelRatio(this.currentPixelRatio)), this.options.performanceMode !== "quality" && this.nodeManager.setLabelRenderMode("none")) : e < 35 ? (this.simulationStride = Math.max(this.simulationStride, 2), this.currentPixelRatio > s && (this.currentPixelRatio = Math.max(s, this.currentPixelRatio - 0.06), this.sceneManager.setPixelRatio(this.currentPixelRatio)), this.options.performanceMode === "balanced" && this.nodeManager.setLabelRenderMode("adaptive")) : e > 52 ? (this.simulationStride = 1, this.options.performanceMode !== "low" && this.nodeManager.setLabelRenderMode(this.options.labelRenderMode ?? "adaptive"), this.currentPixelRatio < t && (this.currentPixelRatio = Math.min(t, this.currentPixelRatio + 0.05), this.sceneManager.setPixelRatio(this.currentPixelRatio))) : this.simulationStride = Math.min(this.simulationStride, 2), this.refreshPerformanceRenderingMode(), this.refreshPhysicsBackend();
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Chooses edge rendering strategy based on graph size and mode
|
|
3819
|
+
*/
|
|
3820
|
+
refreshPerformanceRenderingMode() {
|
|
3821
|
+
const e = this.getEdgeCount(), s = this.options.edgeBatchThreshold ?? C.edgeBatchThreshold, t = this.options.performanceMode === "low" && e >= s;
|
|
3822
|
+
this.edgeManager.setBatchRenderingEnabled(t), t && (this.options.disableEdgeInteractionsInLowMode ?? !0) ? this.edgeInteractionsEnabled = !1 : this.edgeInteractionsEnabled = !0;
|
|
3823
|
+
}
|
|
3824
|
+
/**
|
|
3825
|
+
* Enables worker-based physics on large graphs when requested
|
|
3826
|
+
*/
|
|
3827
|
+
refreshPhysicsBackend() {
|
|
3828
|
+
const e = this.options.workerPhysicsNodeThreshold ?? C.workerPhysicsNodeThreshold;
|
|
3829
|
+
if (!((this.options.enableWorkerPhysics ?? C.enableWorkerPhysics) && this.options.performanceMode !== "quality" && this.getNodeCount() >= e)) {
|
|
3830
|
+
this.useWorkerPhysics = !1, this.physicsWorkerBridge && (this.physicsWorkerBridge.dispose(), this.physicsWorkerBridge = null), this.physicsWorkerNeedsSync = !0;
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3833
|
+
if (this.physicsWorkerBridge || (this.physicsWorkerBridge = new xt()), !this.physicsWorkerBridge.isAvailable()) {
|
|
3834
|
+
this.useWorkerPhysics = !1;
|
|
3835
|
+
return;
|
|
3836
|
+
}
|
|
3837
|
+
(!this.useWorkerPhysics || this.physicsWorkerNeedsSync) && (this.physicsWorkerBridge.init(
|
|
3838
|
+
this.nodeManager.getAllNodes(),
|
|
3839
|
+
this.edgeManager.getAllEdges(),
|
|
3840
|
+
{
|
|
3841
|
+
repulsionStrength: this.options.repulsionStrength,
|
|
3842
|
+
attractionStrength: this.options.attractionStrength,
|
|
3843
|
+
damping: this.options.damping
|
|
3844
|
+
}
|
|
3845
|
+
), this.physicsWorkerBridge.requestStep(), this.physicsWorkerNeedsSync = !1), this.useWorkerPhysics = !0, this.simulationStride = 1;
|
|
3846
|
+
}
|
|
3384
3847
|
// ==========================================================================
|
|
3385
3848
|
// Public API
|
|
3386
3849
|
// ==========================================================================
|
|
@@ -3388,23 +3851,23 @@ class Ht {
|
|
|
3388
3851
|
* Sets the graph data
|
|
3389
3852
|
*/
|
|
3390
3853
|
setData(e) {
|
|
3391
|
-
var
|
|
3392
|
-
const s = (
|
|
3393
|
-
...
|
|
3394
|
-
relationship:
|
|
3395
|
-
})),
|
|
3854
|
+
var o;
|
|
3855
|
+
const s = (o = e.data) != null && o.nodes ? e.data : e, i = (s.edges || s.links || []).map((a) => ({
|
|
3856
|
+
...a,
|
|
3857
|
+
relationship: a.relationship || a.label || "related_to"
|
|
3858
|
+
})), n = {
|
|
3396
3859
|
nodes: s.nodes || [],
|
|
3397
3860
|
edges: i
|
|
3398
3861
|
};
|
|
3399
|
-
if (this.graphData =
|
|
3400
|
-
ee.setExpectedNodeCount(
|
|
3401
|
-
for (const
|
|
3402
|
-
this.addNode(
|
|
3403
|
-
}
|
|
3404
|
-
if (
|
|
3405
|
-
for (const
|
|
3406
|
-
this.addEdge(
|
|
3407
|
-
this.graphEngine = new
|
|
3862
|
+
if (this.graphData = n, this.edgeManager.clear(), this.nodeManager.clear(), n.nodes && Array.isArray(n.nodes)) {
|
|
3863
|
+
ee.setExpectedNodeCount(n.nodes.length);
|
|
3864
|
+
for (const a of n.nodes)
|
|
3865
|
+
ce(a) && this.nodeManager.addNode(a);
|
|
3866
|
+
}
|
|
3867
|
+
if (n.edges && Array.isArray(n.edges))
|
|
3868
|
+
for (const a of n.edges)
|
|
3869
|
+
he(a) && this.edgeManager.addEdge(a);
|
|
3870
|
+
this.graphEngine = new Oe(
|
|
3408
3871
|
this.nodeManager.getAllNodes(),
|
|
3409
3872
|
this.edgeManager.getAllEdges(),
|
|
3410
3873
|
{
|
|
@@ -3414,17 +3877,17 @@ class Ht {
|
|
|
3414
3877
|
useBarnesHut: this.options.useBarnesHut,
|
|
3415
3878
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3416
3879
|
}
|
|
3417
|
-
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(
|
|
3880
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(n), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend();
|
|
3418
3881
|
}
|
|
3419
3882
|
/**
|
|
3420
3883
|
* Adds a node to the graph
|
|
3421
3884
|
* @returns true if added, false if node already exists or invalid
|
|
3422
3885
|
*/
|
|
3423
3886
|
addNode(e) {
|
|
3424
|
-
if (!
|
|
3887
|
+
if (!ce(e))
|
|
3425
3888
|
return !1;
|
|
3426
3889
|
const s = this.nodeManager.addNode(e);
|
|
3427
|
-
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
|
|
3890
|
+
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
|
|
3428
3891
|
}
|
|
3429
3892
|
/**
|
|
3430
3893
|
* Removes a node from the graph
|
|
@@ -3435,7 +3898,7 @@ class Ht {
|
|
|
3435
3898
|
return !1;
|
|
3436
3899
|
this.edgeManager.removeEdgesForNode(e);
|
|
3437
3900
|
const s = this.nodeManager.removeNode(e);
|
|
3438
|
-
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
|
|
3901
|
+
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
|
|
3439
3902
|
}
|
|
3440
3903
|
/**
|
|
3441
3904
|
* Updates a node's properties
|
|
@@ -3448,10 +3911,10 @@ class Ht {
|
|
|
3448
3911
|
* @returns true if added, false if invalid or nodes don't exist
|
|
3449
3912
|
*/
|
|
3450
3913
|
addEdge(e) {
|
|
3451
|
-
if (!
|
|
3914
|
+
if (!he(e))
|
|
3452
3915
|
return !1;
|
|
3453
3916
|
const s = this.edgeManager.addEdge(e);
|
|
3454
|
-
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
|
|
3917
|
+
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), s;
|
|
3455
3918
|
}
|
|
3456
3919
|
/**
|
|
3457
3920
|
* Removes an edge from the graph
|
|
@@ -3459,7 +3922,7 @@ class Ht {
|
|
|
3459
3922
|
*/
|
|
3460
3923
|
removeEdge(e, s) {
|
|
3461
3924
|
const t = this.edgeManager.removeEdge(e, s);
|
|
3462
|
-
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
|
|
3925
|
+
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts(), this.refreshPerformanceRenderingMode(), this.physicsWorkerNeedsSync = !0, this.refreshPhysicsBackend()), t;
|
|
3463
3926
|
}
|
|
3464
3927
|
/**
|
|
3465
3928
|
* Expands a node by fetching more data
|
|
@@ -3472,16 +3935,16 @@ class Ht {
|
|
|
3472
3935
|
if (!i)
|
|
3473
3936
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
3474
3937
|
try {
|
|
3475
|
-
const
|
|
3476
|
-
if (
|
|
3477
|
-
for (const
|
|
3478
|
-
this.addNode(
|
|
3479
|
-
if (
|
|
3480
|
-
for (const
|
|
3481
|
-
this.addEdge(
|
|
3482
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
3483
|
-
} catch (
|
|
3484
|
-
return console.error("[ForceGraph3D] Error expanding node:",
|
|
3938
|
+
const n = await i(e, s);
|
|
3939
|
+
if (n.nodes && Array.isArray(n.nodes))
|
|
3940
|
+
for (const o of n.nodes)
|
|
3941
|
+
this.addNode(o);
|
|
3942
|
+
if (n.edges && Array.isArray(n.edges))
|
|
3943
|
+
for (const o of n.edges)
|
|
3944
|
+
this.addEdge(o);
|
|
3945
|
+
return this.panelManager.hide(), this.emit("expand", e, n), !0;
|
|
3946
|
+
} catch (n) {
|
|
3947
|
+
return console.error("[ForceGraph3D] Error expanding node:", n), !1;
|
|
3485
3948
|
}
|
|
3486
3949
|
}
|
|
3487
3950
|
/**
|
|
@@ -3528,13 +3991,13 @@ class Ht {
|
|
|
3528
3991
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
3529
3992
|
return;
|
|
3530
3993
|
}
|
|
3531
|
-
const i = t.position,
|
|
3532
|
-
x: i.x +
|
|
3533
|
-
y: i.y +
|
|
3534
|
-
z: i.z +
|
|
3535
|
-
},
|
|
3536
|
-
const
|
|
3537
|
-
|
|
3994
|
+
const i = t.position, n = this.sceneManager.camera, o = this.sceneManager.controls, a = n.position.clone().sub(o.target).normalize(), c = {
|
|
3995
|
+
x: i.x + a.x * s,
|
|
3996
|
+
y: i.y + a.y * s,
|
|
3997
|
+
z: i.z + a.z * s
|
|
3998
|
+
}, h = { x: n.position.x, y: n.position.y, z: n.position.z }, d = { x: o.target.x, y: o.target.y, z: o.target.z }, f = 800, m = performance.now(), u = () => {
|
|
3999
|
+
const b = performance.now() - m, x = Math.min(b / f, 1), M = 1 - Math.pow(1 - x, 3);
|
|
4000
|
+
n.position.x = h.x + (c.x - h.x) * M, n.position.y = h.y + (c.y - h.y) * M, n.position.z = h.z + (c.z - h.z) * M, o.target.x = d.x + (i.x - d.x) * M, o.target.y = d.y + (i.y - d.y) * M, o.target.z = d.z + (i.z - d.z) * M, o.update(), x < 1 && requestAnimationFrame(u);
|
|
3538
4001
|
};
|
|
3539
4002
|
u();
|
|
3540
4003
|
}
|
|
@@ -3543,22 +4006,22 @@ class Ht {
|
|
|
3543
4006
|
* Camera targets the midpoint and zooms out enough to see both nodes
|
|
3544
4007
|
*/
|
|
3545
4008
|
focusOnEdge(e, s, t = 1.5) {
|
|
3546
|
-
const i = this.nodeManager.getNode(e),
|
|
3547
|
-
if (!i || !
|
|
4009
|
+
const i = this.nodeManager.getNode(e), n = this.nodeManager.getNode(s);
|
|
4010
|
+
if (!i || !n) {
|
|
3548
4011
|
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3549
4012
|
return;
|
|
3550
4013
|
}
|
|
3551
|
-
const
|
|
3552
|
-
x: (i.position.x +
|
|
3553
|
-
y: (i.position.y +
|
|
3554
|
-
z: (i.position.z +
|
|
3555
|
-
},
|
|
3556
|
-
x:
|
|
3557
|
-
y:
|
|
3558
|
-
z:
|
|
3559
|
-
},
|
|
3560
|
-
const
|
|
3561
|
-
|
|
4014
|
+
const o = this.sceneManager.camera, a = this.sceneManager.controls, c = {
|
|
4015
|
+
x: (i.position.x + n.position.x) / 2,
|
|
4016
|
+
y: (i.position.y + n.position.y) / 2,
|
|
4017
|
+
z: (i.position.z + n.position.z) / 2
|
|
4018
|
+
}, h = n.position.x - i.position.x, d = n.position.y - i.position.y, f = n.position.z - i.position.z, m = Math.sqrt(h * h + d * d + f * f), u = Math.max(m * t, 40), b = o.position.clone().sub(a.target).normalize(), x = {
|
|
4019
|
+
x: c.x + b.x * u,
|
|
4020
|
+
y: c.y + b.y * u,
|
|
4021
|
+
z: c.z + b.z * u
|
|
4022
|
+
}, M = { x: o.position.x, y: o.position.y, z: o.position.z }, N = { x: a.target.x, y: a.target.y, z: a.target.z }, O = 800, L = performance.now(), G = () => {
|
|
4023
|
+
const R = performance.now() - L, F = Math.min(R / O, 1), E = 1 - Math.pow(1 - F, 3);
|
|
4024
|
+
o.position.x = M.x + (x.x - M.x) * E, o.position.y = M.y + (x.y - M.y) * E, o.position.z = M.z + (x.z - M.z) * E, a.target.x = N.x + (c.x - N.x) * E, a.target.y = N.y + (c.y - N.y) * E, a.target.z = N.z + (c.z - N.z) * E, a.update(), F < 1 && requestAnimationFrame(G);
|
|
3562
4025
|
};
|
|
3563
4026
|
G();
|
|
3564
4027
|
}
|
|
@@ -3582,10 +4045,10 @@ class Ht {
|
|
|
3582
4045
|
if (!e || e.trim() === "")
|
|
3583
4046
|
return [];
|
|
3584
4047
|
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
3585
|
-
return t.forEach((
|
|
3586
|
-
var
|
|
3587
|
-
const
|
|
3588
|
-
(
|
|
4048
|
+
return t.forEach((n) => {
|
|
4049
|
+
var h, d, f;
|
|
4050
|
+
const o = (h = n.label) == null ? void 0 : h.toLowerCase().includes(s), a = (d = n.id) == null ? void 0 : d.toLowerCase().includes(s), c = (f = n.type) == null ? void 0 : f.toLowerCase().includes(s);
|
|
4051
|
+
(o || a || c) && i.push(n);
|
|
3589
4052
|
}), i;
|
|
3590
4053
|
}
|
|
3591
4054
|
/**
|
|
@@ -3593,14 +4056,14 @@ class Ht {
|
|
|
3593
4056
|
* @returns Array of matching edges with source/target node info
|
|
3594
4057
|
*/
|
|
3595
4058
|
searchEdges(e) {
|
|
3596
|
-
var
|
|
4059
|
+
var n;
|
|
3597
4060
|
if (!e || e.trim() === "")
|
|
3598
4061
|
return [];
|
|
3599
4062
|
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
3600
|
-
for (const
|
|
3601
|
-
if ((
|
|
3602
|
-
const
|
|
3603
|
-
|
|
4063
|
+
for (const o of t)
|
|
4064
|
+
if ((n = o.relationship) == null ? void 0 : n.toLowerCase().includes(s)) {
|
|
4065
|
+
const c = this.nodeManager.getNode(o.source), h = this.nodeManager.getNode(o.target);
|
|
4066
|
+
c && h && i.push({ edge: o, sourceNode: c, targetNode: h });
|
|
3604
4067
|
}
|
|
3605
4068
|
return i;
|
|
3606
4069
|
}
|
|
@@ -3633,7 +4096,7 @@ class Ht {
|
|
|
3633
4096
|
* Switches between 2D and 3D view modes
|
|
3634
4097
|
*/
|
|
3635
4098
|
switchView(e) {
|
|
3636
|
-
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
|
|
4099
|
+
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, {
|
|
3637
4100
|
backgroundColor: "#0a0a0a",
|
|
3638
4101
|
nodeRadius: 24,
|
|
3639
4102
|
onNodeClick: (s) => {
|
|
@@ -3644,16 +4107,16 @@ class Ht {
|
|
|
3644
4107
|
},
|
|
3645
4108
|
onEdgeHover: (s, t) => {
|
|
3646
4109
|
if (s && t) {
|
|
3647
|
-
const i = this.nodeManager.getNode(s.source),
|
|
3648
|
-
if (i &&
|
|
3649
|
-
const
|
|
4110
|
+
const i = this.nodeManager.getNode(s.source), n = this.nodeManager.getNode(s.target);
|
|
4111
|
+
if (i && n) {
|
|
4112
|
+
const o = this.edgeManager.getAllEdges().filter((a) => a.source === s.source && a.target === s.target);
|
|
3650
4113
|
this.edgeTooltipManager.show(
|
|
3651
4114
|
s,
|
|
3652
4115
|
i,
|
|
3653
|
-
|
|
4116
|
+
n,
|
|
3654
4117
|
t.clientX,
|
|
3655
4118
|
t.clientY,
|
|
3656
|
-
|
|
4119
|
+
o.length > 0 ? o : [s]
|
|
3657
4120
|
);
|
|
3658
4121
|
}
|
|
3659
4122
|
} else
|
|
@@ -3662,12 +4125,12 @@ class Ht {
|
|
|
3662
4125
|
onEdgeClick: (s) => {
|
|
3663
4126
|
const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3664
4127
|
if (t && i) {
|
|
3665
|
-
const
|
|
4128
|
+
const n = this.edgeManager.getAllEdges().filter((o) => o.source === s.source && o.target === s.target);
|
|
3666
4129
|
this.edgePanelManager.show(
|
|
3667
4130
|
s,
|
|
3668
4131
|
t,
|
|
3669
4132
|
i,
|
|
3670
|
-
|
|
4133
|
+
n.length > 0 ? n : [s]
|
|
3671
4134
|
);
|
|
3672
4135
|
}
|
|
3673
4136
|
}
|
|
@@ -3690,7 +4153,7 @@ class Ht {
|
|
|
3690
4153
|
* Sets physics parameters for both 3D and 2D views
|
|
3691
4154
|
*/
|
|
3692
4155
|
setPhysicsParams(e) {
|
|
3693
|
-
this.graphEngine.setPhysicsParams(e), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setPhysicsParams(e);
|
|
4156
|
+
e.repulsionStrength !== void 0 && (this.options.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.options.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.options.damping = e.damping), this.graphEngine.setPhysicsParams(e), this.graphEngine.restart(), this.physicsWorkerBridge && this.physicsWorkerBridge.setPhysicsParams(e), this.forceGraph2D && this.forceGraph2D.setPhysicsParams(e);
|
|
3694
4157
|
}
|
|
3695
4158
|
/**
|
|
3696
4159
|
* Creates dev mode controls (only in development)
|
|
@@ -3770,18 +4233,18 @@ class Ht {
|
|
|
3770
4233
|
const i = parseFloat(t.value) / 100;
|
|
3771
4234
|
this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
|
|
3772
4235
|
}), setInterval(() => {
|
|
3773
|
-
const i = this.devControls.querySelector("#dev-node-count"),
|
|
3774
|
-
i && (i.textContent = this.getNodeCount().toString()),
|
|
4236
|
+
const i = this.devControls.querySelector("#dev-node-count"), n = this.devControls.querySelector("#dev-edge-count"), o = this.devControls.querySelector("#dev-fps");
|
|
4237
|
+
i && (i.textContent = this.getNodeCount().toString()), n && (n.textContent = this.getEdgeCount().toString()), o && (o.textContent = this.rendererManager.getFPS().toString());
|
|
3775
4238
|
}, 500);
|
|
3776
4239
|
}
|
|
3777
4240
|
/**
|
|
3778
4241
|
* Destroys the graph and releases all resources
|
|
3779
4242
|
*/
|
|
3780
4243
|
destroy() {
|
|
3781
|
-
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
4244
|
+
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.physicsWorkerBridge && (this.physicsWorkerBridge.dispose(), this.physicsWorkerBridge = null), 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;
|
|
3782
4245
|
}
|
|
3783
4246
|
}
|
|
3784
|
-
const
|
|
4247
|
+
const De = [
|
|
3785
4248
|
"Alpha",
|
|
3786
4249
|
"Beta",
|
|
3787
4250
|
"Gamma",
|
|
@@ -3823,7 +4286,7 @@ const Ie = [
|
|
|
3823
4286
|
"partners with",
|
|
3824
4287
|
"collaborates with",
|
|
3825
4288
|
"supports"
|
|
3826
|
-
],
|
|
4289
|
+
], Fe = [
|
|
3827
4290
|
16777215,
|
|
3828
4291
|
// White
|
|
3829
4292
|
16750950,
|
|
@@ -3835,14 +4298,14 @@ const Ie = [
|
|
|
3835
4298
|
16746564
|
|
3836
4299
|
// Darker tangerine
|
|
3837
4300
|
];
|
|
3838
|
-
function
|
|
4301
|
+
function jt(p = 30) {
|
|
3839
4302
|
const e = [], s = [];
|
|
3840
|
-
for (let i = 0; i <
|
|
3841
|
-
const
|
|
4303
|
+
for (let i = 0; i < p; i++) {
|
|
4304
|
+
const n = i < De.length ? De[i] : `Node ${i + 1}`;
|
|
3842
4305
|
e.push({
|
|
3843
4306
|
id: `node-${i}`,
|
|
3844
|
-
label:
|
|
3845
|
-
color:
|
|
4307
|
+
label: n,
|
|
4308
|
+
color: Fe[i % Fe.length],
|
|
3846
4309
|
position: {
|
|
3847
4310
|
x: (Math.random() - 0.5) * 60,
|
|
3848
4311
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -3850,76 +4313,76 @@ function At(d = 30) {
|
|
|
3850
4313
|
}
|
|
3851
4314
|
});
|
|
3852
4315
|
}
|
|
3853
|
-
for (let i = 1; i <
|
|
3854
|
-
const
|
|
4316
|
+
for (let i = 1; i < p; i++) {
|
|
4317
|
+
const n = Math.floor(Math.random() * i);
|
|
3855
4318
|
s.push({
|
|
3856
4319
|
source: `node-${i}`,
|
|
3857
|
-
target: `node-${
|
|
4320
|
+
target: `node-${n}`,
|
|
3858
4321
|
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3859
4322
|
});
|
|
3860
4323
|
}
|
|
3861
|
-
const t = Math.floor(
|
|
4324
|
+
const t = Math.floor(p * 0.5);
|
|
3862
4325
|
for (let i = 0; i < t; i++) {
|
|
3863
|
-
const
|
|
3864
|
-
let
|
|
3865
|
-
|
|
3866
|
-
const
|
|
4326
|
+
const n = Math.floor(Math.random() * p);
|
|
4327
|
+
let o = Math.floor(Math.random() * p);
|
|
4328
|
+
n === o && (o = (o + 1) % p);
|
|
4329
|
+
const a = `node-${n}`, c = `node-${o}`;
|
|
3867
4330
|
s.some(
|
|
3868
|
-
(
|
|
4331
|
+
(d) => d.source === a && d.target === c || d.source === c && d.target === a
|
|
3869
4332
|
) || s.push({
|
|
3870
|
-
source:
|
|
3871
|
-
target:
|
|
4333
|
+
source: a,
|
|
4334
|
+
target: c,
|
|
3872
4335
|
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3873
4336
|
});
|
|
3874
4337
|
}
|
|
3875
4338
|
return { nodes: e, edges: s };
|
|
3876
4339
|
}
|
|
3877
|
-
function
|
|
3878
|
-
const e = [], s = [], t = Math.ceil(
|
|
3879
|
-
for (let
|
|
4340
|
+
function Bt(p = 1e3) {
|
|
4341
|
+
const e = [], s = [], t = Math.ceil(p / 50), i = [];
|
|
4342
|
+
for (let n = 0; n < t; n++)
|
|
3880
4343
|
i.push({
|
|
3881
4344
|
x: (Math.random() - 0.5) * 200,
|
|
3882
4345
|
y: (Math.random() - 0.5) * 200,
|
|
3883
4346
|
z: (Math.random() - 0.5) * 200
|
|
3884
4347
|
});
|
|
3885
|
-
for (let
|
|
3886
|
-
const
|
|
4348
|
+
for (let n = 0; n < p; n++) {
|
|
4349
|
+
const o = i[n % t];
|
|
3887
4350
|
e.push({
|
|
3888
|
-
id: `node-${
|
|
3889
|
-
label: `N${
|
|
4351
|
+
id: `node-${n}`,
|
|
4352
|
+
label: `N${n}`,
|
|
3890
4353
|
position: {
|
|
3891
|
-
x:
|
|
3892
|
-
y:
|
|
3893
|
-
z:
|
|
4354
|
+
x: o.x + (Math.random() - 0.5) * 40,
|
|
4355
|
+
y: o.y + (Math.random() - 0.5) * 40,
|
|
4356
|
+
z: o.z + (Math.random() - 0.5) * 40
|
|
3894
4357
|
}
|
|
3895
4358
|
});
|
|
3896
4359
|
}
|
|
3897
|
-
for (let
|
|
3898
|
-
const
|
|
4360
|
+
for (let n = 1; n < p; n++) {
|
|
4361
|
+
const o = Math.floor(n / 50) * 50, a = o === 0 ? Math.floor(Math.random() * n) : o + Math.floor(Math.random() * Math.min(n - o, 50));
|
|
3899
4362
|
s.push({
|
|
3900
|
-
source: `node-${
|
|
3901
|
-
target: `node-${Math.min(
|
|
4363
|
+
source: `node-${n}`,
|
|
4364
|
+
target: `node-${Math.min(a, n - 1)}`,
|
|
3902
4365
|
relationship: "links to"
|
|
3903
4366
|
});
|
|
3904
4367
|
}
|
|
3905
|
-
for (let
|
|
3906
|
-
const
|
|
4368
|
+
for (let n = 1; n < t; n++) {
|
|
4369
|
+
const o = n * 50, a = (n - 1) * 50 + Math.floor(Math.random() * 50);
|
|
3907
4370
|
s.push({
|
|
3908
|
-
source: `node-${
|
|
3909
|
-
target: `node-${
|
|
4371
|
+
source: `node-${o}`,
|
|
4372
|
+
target: `node-${a}`,
|
|
3910
4373
|
relationship: "bridges to"
|
|
3911
4374
|
});
|
|
3912
4375
|
}
|
|
3913
4376
|
return { nodes: e, edges: s };
|
|
3914
4377
|
}
|
|
3915
4378
|
export {
|
|
3916
|
-
|
|
4379
|
+
C as DEFAULT_OPTIONS,
|
|
3917
4380
|
Ht as ForceGraph3D,
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
4381
|
+
K as LODLevel,
|
|
4382
|
+
At as createEdgeKey,
|
|
4383
|
+
Bt as generateLargeSampleData,
|
|
4384
|
+
jt as generateSampleData,
|
|
4385
|
+
he as validateEdgeData,
|
|
4386
|
+
ce as validateNodeData
|
|
3924
4387
|
};
|
|
3925
4388
|
//# sourceMappingURL=force-3d-graph.js.map
|