force-3d-graph 1.3.7 → 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 +1145 -739
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +184 -80
- 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
|
|
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
|
|
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 Re {
|
|
|
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 Re {
|
|
|
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 Re {
|
|
|
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 Re {
|
|
|
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;
|
|
@@ -1908,15 +2273,15 @@ class Nt {
|
|
|
1908
2273
|
this.currentNodeId = e.id;
|
|
1909
2274
|
let t;
|
|
1910
2275
|
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
1911
|
-
const i = this.panel.querySelector('[data-action="expand"]'),
|
|
2276
|
+
const i = this.panel.querySelector('[data-action="expand"]'), n = this.panel.querySelector("[data-depth-select]");
|
|
1912
2277
|
i && this.onExpand && i.addEventListener("click", () => {
|
|
1913
2278
|
if (this.currentNodeId) {
|
|
1914
|
-
const
|
|
1915
|
-
this.onExpand(this.currentNodeId,
|
|
2279
|
+
const a = n ? parseInt(n.value, 10) : 1;
|
|
2280
|
+
this.onExpand(this.currentNodeId, a);
|
|
1916
2281
|
}
|
|
1917
2282
|
});
|
|
1918
|
-
const
|
|
1919
|
-
|
|
2283
|
+
const o = this.panel.querySelector('[data-action="close"]');
|
|
2284
|
+
o && o.addEventListener("click", () => {
|
|
1920
2285
|
this.hide();
|
|
1921
2286
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
1922
2287
|
}
|
|
@@ -1995,36 +2360,6 @@ class Nt {
|
|
|
1995
2360
|
border-radius: 12px;
|
|
1996
2361
|
font-size: 12px;
|
|
1997
2362
|
}
|
|
1998
|
-
.force-graph-panel .depth-selector {
|
|
1999
|
-
margin-top: 16px;
|
|
2000
|
-
margin-bottom: 8px;
|
|
2001
|
-
}
|
|
2002
|
-
.force-graph-panel .depth-label {
|
|
2003
|
-
font-size: 12px;
|
|
2004
|
-
color: rgba(255, 255, 255, 0.65);
|
|
2005
|
-
margin-bottom: 6px;
|
|
2006
|
-
text-transform: uppercase;
|
|
2007
|
-
letter-spacing: 0.5px;
|
|
2008
|
-
}
|
|
2009
|
-
.force-graph-panel select {
|
|
2010
|
-
width: 100%;
|
|
2011
|
-
padding: 8px 12px;
|
|
2012
|
-
background: rgba(255, 255, 255, 0.08);
|
|
2013
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2014
|
-
border-radius: 8px;
|
|
2015
|
-
color: white;
|
|
2016
|
-
font-size: 13px;
|
|
2017
|
-
cursor: pointer;
|
|
2018
|
-
outline: none;
|
|
2019
|
-
transition: all 0.2s ease;
|
|
2020
|
-
}
|
|
2021
|
-
.force-graph-panel select:hover {
|
|
2022
|
-
background: rgba(255, 255, 255, 0.12);
|
|
2023
|
-
}
|
|
2024
|
-
.force-graph-panel select:focus {
|
|
2025
|
-
border-color: rgba(255, 153, 102, 0.5);
|
|
2026
|
-
box-shadow: 0 0 20px rgba(255, 153, 102, 0.15);
|
|
2027
|
-
}
|
|
2028
2363
|
.force-graph-panel .btn-row {
|
|
2029
2364
|
display: flex;
|
|
2030
2365
|
gap: 8px;
|
|
@@ -2040,15 +2375,6 @@ class Nt {
|
|
|
2040
2375
|
cursor: pointer;
|
|
2041
2376
|
transition: all 0.2s ease;
|
|
2042
2377
|
}
|
|
2043
|
-
.force-graph-panel .btn-expand {
|
|
2044
|
-
background: linear-gradient(135deg, rgba(255, 153, 102, 0.3), rgba(255, 102, 153, 0.2));
|
|
2045
|
-
border-color: rgba(255, 153, 102, 0.35);
|
|
2046
|
-
color: white;
|
|
2047
|
-
}
|
|
2048
|
-
.force-graph-panel .btn-expand:hover {
|
|
2049
|
-
transform: translateY(-1px);
|
|
2050
|
-
box-shadow: 0 0 15px rgba(255, 153, 102, 0.2);
|
|
2051
|
-
}
|
|
2052
2378
|
.force-graph-panel .btn-close {
|
|
2053
2379
|
background: rgba(255, 255, 255, 0.08);
|
|
2054
2380
|
color: rgba(255, 255, 255, 0.8);
|
|
@@ -2087,18 +2413,7 @@ class Nt {
|
|
|
2087
2413
|
</div>
|
|
2088
2414
|
` : ""}
|
|
2089
2415
|
|
|
2090
|
-
<div class="depth-selector">
|
|
2091
|
-
<div class="depth-label">Expansion Depth</div>
|
|
2092
|
-
<select data-depth-select>
|
|
2093
|
-
<option value="1">1 Level</option>
|
|
2094
|
-
<option value="2">2 Levels</option>
|
|
2095
|
-
<option value="3" selected>3 Levels</option>
|
|
2096
|
-
</select>
|
|
2097
|
-
</div>
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
2416
|
<div class="btn-row">
|
|
2101
|
-
<button class="btn-expand" data-action="expand">Expand</button>
|
|
2102
2417
|
<button class="btn-close" data-action="close">Close</button>
|
|
2103
2418
|
</div>
|
|
2104
2419
|
`;
|
|
@@ -2135,7 +2450,7 @@ class Nt {
|
|
|
2135
2450
|
this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
|
|
2136
2451
|
}
|
|
2137
2452
|
}
|
|
2138
|
-
class
|
|
2453
|
+
class kt {
|
|
2139
2454
|
constructor(e) {
|
|
2140
2455
|
l(this, "container");
|
|
2141
2456
|
l(this, "panel", null);
|
|
@@ -2197,18 +2512,18 @@ class St {
|
|
|
2197
2512
|
show(e, s, t, i = [e]) {
|
|
2198
2513
|
if (!this.panel) return;
|
|
2199
2514
|
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2200
|
-
let
|
|
2201
|
-
this.panelTemplate ?
|
|
2202
|
-
const
|
|
2203
|
-
|
|
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", () => {
|
|
2204
2519
|
this.hide(), this.onClose && this.onClose();
|
|
2205
2520
|
});
|
|
2206
|
-
const
|
|
2207
|
-
|
|
2521
|
+
const a = this.panel.querySelector('[data-action="goto-source"]');
|
|
2522
|
+
a && this.onNodeClick && a.addEventListener("click", () => {
|
|
2208
2523
|
this.onNodeClick && this.onNodeClick(e.source);
|
|
2209
2524
|
});
|
|
2210
|
-
const
|
|
2211
|
-
|
|
2525
|
+
const c = this.panel.querySelector('[data-action="goto-target"]');
|
|
2526
|
+
c && this.onNodeClick && c.addEventListener("click", () => {
|
|
2212
2527
|
this.onNodeClick && this.onNodeClick(e.target);
|
|
2213
2528
|
}), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
|
|
2214
2529
|
}
|
|
@@ -2216,12 +2531,12 @@ class St {
|
|
|
2216
2531
|
* Generates default panel content
|
|
2217
2532
|
*/
|
|
2218
2533
|
generateDefaultContent(e, s, t, i = [e]) {
|
|
2219
|
-
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) => `
|
|
2220
2535
|
<div class="relationship-item">
|
|
2221
2536
|
<span class="relationship-item-label">${this.escapeHtml(m.label)}</span>
|
|
2222
2537
|
<span class="relationship-item-count">${m.count}</span>
|
|
2223
2538
|
</div>
|
|
2224
|
-
`).join(""), f =
|
|
2539
|
+
`).join(""), f = h.length - Math.min(h.length, 10);
|
|
2225
2540
|
return `
|
|
2226
2541
|
<style>
|
|
2227
2542
|
.force-graph-edge-panel .panel-header {
|
|
@@ -2374,19 +2689,19 @@ class St {
|
|
|
2374
2689
|
</div>
|
|
2375
2690
|
|
|
2376
2691
|
<div class="relationship-section">
|
|
2377
|
-
<span class="relationship-label">${this.escapeHtml(
|
|
2378
|
-
<div class="relationship-count">${
|
|
2692
|
+
<span class="relationship-label">${this.escapeHtml(a)}</span>
|
|
2693
|
+
<div class="relationship-count">${c.length} relationships</div>
|
|
2379
2694
|
</div>
|
|
2380
2695
|
|
|
2381
2696
|
<div class="relationship-list">
|
|
2382
|
-
${
|
|
2697
|
+
${d}
|
|
2383
2698
|
${f > 0 ? `<div class="relationship-more">+ ${f} more</div>` : ""}
|
|
2384
2699
|
</div>
|
|
2385
2700
|
|
|
2386
2701
|
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2387
2702
|
<div class="node-type">Source</div>
|
|
2388
2703
|
<div class="node-card-header">
|
|
2389
|
-
<span class="color-dot" style="background: ${
|
|
2704
|
+
<span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
|
|
2390
2705
|
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2391
2706
|
</div>
|
|
2392
2707
|
</div>
|
|
@@ -2396,7 +2711,7 @@ class St {
|
|
|
2396
2711
|
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2397
2712
|
<div class="node-type">Target</div>
|
|
2398
2713
|
<div class="node-card-header">
|
|
2399
|
-
<span class="color-dot" style="background: ${
|
|
2714
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2400
2715
|
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2401
2716
|
</div>
|
|
2402
2717
|
</div>
|
|
@@ -2495,25 +2810,25 @@ class zt {
|
|
|
2495
2810
|
*/
|
|
2496
2811
|
positionTooltip(e, s) {
|
|
2497
2812
|
if (!this.tooltip) return;
|
|
2498
|
-
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth,
|
|
2499
|
-
let
|
|
2500
|
-
|
|
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`;
|
|
2501
2816
|
}
|
|
2502
2817
|
/**
|
|
2503
2818
|
* Shows the tooltip with edge info
|
|
2504
2819
|
*/
|
|
2505
|
-
show(e, s, t, i,
|
|
2820
|
+
show(e, s, t, i, n, o = [e]) {
|
|
2506
2821
|
if (!this.tooltip) return;
|
|
2507
|
-
const
|
|
2508
|
-
if (
|
|
2509
|
-
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]);
|
|
2510
2825
|
this.tooltip.innerHTML = `
|
|
2511
2826
|
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
2512
2827
|
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2513
2828
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
|
|
2514
2829
|
</div>
|
|
2515
2830
|
<div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
|
|
2516
|
-
↳ ${this.escapeHtml(
|
|
2831
|
+
↳ ${this.escapeHtml(d)}
|
|
2517
2832
|
</div>
|
|
2518
2833
|
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
|
|
2519
2834
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
@@ -2521,7 +2836,7 @@ class zt {
|
|
|
2521
2836
|
</div>
|
|
2522
2837
|
`;
|
|
2523
2838
|
} else {
|
|
2524
|
-
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);">
|
|
2525
2840
|
<span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${this.escapeHtml(u.label)}</span>
|
|
2526
2841
|
<span style="color: rgba(255,153,102,0.9); font-weight:600;">${u.count}</span>
|
|
2527
2842
|
</div>`).join("");
|
|
@@ -2531,7 +2846,7 @@ class zt {
|
|
|
2531
2846
|
${this.escapeHtml(s.label)} → ${this.escapeHtml(t.label)}
|
|
2532
2847
|
</div>
|
|
2533
2848
|
<div style="font-size:11px; letter-spacing:0.4px; text-transform:uppercase; color: rgba(255,255,255,0.6);">
|
|
2534
|
-
${
|
|
2849
|
+
${h} relationships
|
|
2535
2850
|
</div>
|
|
2536
2851
|
<div style="display:flex; flex-direction:column; gap:4px;">
|
|
2537
2852
|
${m}
|
|
@@ -2540,7 +2855,7 @@ class zt {
|
|
|
2540
2855
|
</div>
|
|
2541
2856
|
`;
|
|
2542
2857
|
}
|
|
2543
|
-
this.positionTooltip(i,
|
|
2858
|
+
this.positionTooltip(i, n), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2544
2859
|
}
|
|
2545
2860
|
/**
|
|
2546
2861
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2585,7 +2900,7 @@ class zt {
|
|
|
2585
2900
|
this.tooltip && this.tooltip.parentNode && this.tooltip.parentNode.removeChild(this.tooltip), this.tooltip = null;
|
|
2586
2901
|
}
|
|
2587
2902
|
}
|
|
2588
|
-
class
|
|
2903
|
+
class Pt {
|
|
2589
2904
|
constructor(e, s) {
|
|
2590
2905
|
l(this, "container");
|
|
2591
2906
|
l(this, "searchContainer", null);
|
|
@@ -2729,25 +3044,25 @@ class kt {
|
|
|
2729
3044
|
return;
|
|
2730
3045
|
}
|
|
2731
3046
|
let i = "";
|
|
2732
|
-
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((
|
|
2733
|
-
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";
|
|
2734
3049
|
i += `
|
|
2735
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2736
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2737
|
-
<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>
|
|
2738
3053
|
</div>
|
|
2739
3054
|
`;
|
|
2740
|
-
}), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge:
|
|
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 }) => {
|
|
2741
3056
|
i += `
|
|
2742
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2743
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2744
|
-
<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>
|
|
2745
3060
|
</div>
|
|
2746
3061
|
`;
|
|
2747
|
-
}), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((
|
|
2748
|
-
|
|
2749
|
-
const
|
|
2750
|
-
|
|
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());
|
|
2751
3066
|
});
|
|
2752
3067
|
});
|
|
2753
3068
|
}
|
|
@@ -2759,7 +3074,7 @@ class kt {
|
|
|
2759
3074
|
this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
|
|
2760
3075
|
}
|
|
2761
3076
|
}
|
|
2762
|
-
class
|
|
3077
|
+
class Rt {
|
|
2763
3078
|
constructor(e, s) {
|
|
2764
3079
|
l(this, "container");
|
|
2765
3080
|
l(this, "toggleContainer", null);
|
|
@@ -2851,7 +3166,7 @@ class Tt {
|
|
|
2851
3166
|
this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
|
|
2852
3167
|
}
|
|
2853
3168
|
}
|
|
2854
|
-
class
|
|
3169
|
+
class Lt {
|
|
2855
3170
|
constructor(e) {
|
|
2856
3171
|
l(this, "container");
|
|
2857
3172
|
l(this, "legendContainer", null);
|
|
@@ -2934,7 +3249,7 @@ class Pt {
|
|
|
2934
3249
|
this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
|
|
2935
3250
|
}
|
|
2936
3251
|
}
|
|
2937
|
-
const
|
|
3252
|
+
const Tt = {
|
|
2938
3253
|
backgroundColor: "#0a0a0a",
|
|
2939
3254
|
gridColor: "rgba(255, 255, 255, 0.03)",
|
|
2940
3255
|
nodeRadius: 24,
|
|
@@ -2947,7 +3262,7 @@ const Lt = {
|
|
|
2947
3262
|
damping: 0.85
|
|
2948
3263
|
// Fast energy dissipation
|
|
2949
3264
|
};
|
|
2950
|
-
class
|
|
3265
|
+
class It {
|
|
2951
3266
|
constructor(e, s = {}) {
|
|
2952
3267
|
l(this, "container");
|
|
2953
3268
|
l(this, "canvas");
|
|
@@ -2974,7 +3289,7 @@ class Rt {
|
|
|
2974
3289
|
l(this, "isSimulating", !0);
|
|
2975
3290
|
// Resize handler
|
|
2976
3291
|
l(this, "resizeHandler");
|
|
2977
|
-
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);
|
|
2978
3293
|
const t = this.canvas.getContext("2d");
|
|
2979
3294
|
if (!t)
|
|
2980
3295
|
throw new Error("Failed to get 2D context");
|
|
@@ -2987,23 +3302,23 @@ class Rt {
|
|
|
2987
3302
|
setupInteractions() {
|
|
2988
3303
|
this.canvas.addEventListener("wheel", (e) => {
|
|
2989
3304
|
e.preventDefault();
|
|
2990
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top,
|
|
2991
|
-
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;
|
|
2992
3307
|
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2993
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top,
|
|
2994
|
-
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 };
|
|
2995
3310
|
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2996
|
-
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);
|
|
2997
3312
|
if (this.isDragging && this.draggedNode)
|
|
2998
|
-
this.draggedNode.x =
|
|
3313
|
+
this.draggedNode.x = n.x, this.draggedNode.y = n.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2999
3314
|
else if (this.isPanning) {
|
|
3000
|
-
const
|
|
3001
|
-
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 };
|
|
3002
3317
|
} else {
|
|
3003
|
-
const
|
|
3004
|
-
if (
|
|
3005
|
-
const
|
|
3006
|
-
|
|
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));
|
|
3007
3322
|
}
|
|
3008
3323
|
}
|
|
3009
3324
|
}), this.canvas.addEventListener("mouseup", (e) => {
|
|
@@ -3011,8 +3326,8 @@ class Rt {
|
|
|
3011
3326
|
if (this.isDragging && this.draggedNode)
|
|
3012
3327
|
i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
3013
3328
|
else if (i) {
|
|
3014
|
-
const
|
|
3015
|
-
|
|
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);
|
|
3016
3331
|
}
|
|
3017
3332
|
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
3018
3333
|
}), this.canvas.addEventListener("mouseleave", () => {
|
|
@@ -3027,20 +3342,20 @@ class Rt {
|
|
|
3027
3342
|
}
|
|
3028
3343
|
findNodeAt(e, s) {
|
|
3029
3344
|
for (const t of this.nodes.values()) {
|
|
3030
|
-
const i = t.x - e,
|
|
3031
|
-
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)
|
|
3032
3347
|
return t;
|
|
3033
3348
|
}
|
|
3034
3349
|
return null;
|
|
3035
3350
|
}
|
|
3036
3351
|
findEdgeAt(e, s) {
|
|
3037
3352
|
for (const i of this.edges) {
|
|
3038
|
-
const
|
|
3039
|
-
if (!
|
|
3040
|
-
const
|
|
3041
|
-
if (
|
|
3042
|
-
const
|
|
3043
|
-
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)
|
|
3044
3359
|
return i;
|
|
3045
3360
|
}
|
|
3046
3361
|
return null;
|
|
@@ -3055,79 +3370,79 @@ class Rt {
|
|
|
3055
3370
|
const e = Array.from(this.nodes.values()), s = e.length;
|
|
3056
3371
|
if (s === 0) return;
|
|
3057
3372
|
const t = 60, i = 5;
|
|
3058
|
-
let
|
|
3059
|
-
for (let
|
|
3060
|
-
for (let
|
|
3061
|
-
const
|
|
3062
|
-
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);
|
|
3063
3378
|
if (u < t * 3) {
|
|
3064
3379
|
u < 1 && (u = 1);
|
|
3065
|
-
const
|
|
3066
|
-
|
|
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;
|
|
3067
3382
|
}
|
|
3068
3383
|
}
|
|
3069
|
-
const
|
|
3070
|
-
for (const
|
|
3071
|
-
const
|
|
3072
|
-
if (!
|
|
3073
|
-
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);
|
|
3074
3389
|
m < 1 && (m = 1);
|
|
3075
|
-
const
|
|
3076
|
-
|
|
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;
|
|
3077
3392
|
}
|
|
3078
|
-
for (const
|
|
3079
|
-
if (this.draggedNode ===
|
|
3080
|
-
|
|
3081
|
-
const
|
|
3082
|
-
|
|
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;
|
|
3083
3398
|
}
|
|
3084
|
-
|
|
3399
|
+
n < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
3085
3400
|
}
|
|
3086
3401
|
render() {
|
|
3087
3402
|
const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
|
|
3088
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();
|
|
3089
3404
|
}
|
|
3090
3405
|
renderGrid(e, s) {
|
|
3091
|
-
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;
|
|
3092
3407
|
t.fillStyle = this.options.gridColor;
|
|
3093
|
-
for (let
|
|
3094
|
-
for (let
|
|
3095
|
-
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();
|
|
3096
3411
|
}
|
|
3097
3412
|
renderEdges() {
|
|
3098
3413
|
const e = this.ctx;
|
|
3099
3414
|
for (const s of this.edges) {
|
|
3100
3415
|
const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
|
|
3101
3416
|
if (!t || !i) continue;
|
|
3102
|
-
const
|
|
3103
|
-
|
|
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();
|
|
3104
3419
|
}
|
|
3105
3420
|
}
|
|
3106
3421
|
renderNodes() {
|
|
3107
3422
|
const e = this.ctx;
|
|
3108
3423
|
for (const s of this.nodes.values()) {
|
|
3109
|
-
const t = s === this.hoveredNode, i = s === this.selectedNode,
|
|
3110
|
-
if (t || i ||
|
|
3111
|
-
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(
|
|
3112
3427
|
s.x,
|
|
3113
3428
|
s.y,
|
|
3114
|
-
|
|
3429
|
+
o * 0.5,
|
|
3115
3430
|
s.x,
|
|
3116
3431
|
s.y,
|
|
3117
|
-
|
|
3118
|
-
),
|
|
3119
|
-
|
|
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();
|
|
3120
3435
|
}
|
|
3121
|
-
const
|
|
3122
|
-
s.x -
|
|
3123
|
-
s.y -
|
|
3436
|
+
const a = e.createRadialGradient(
|
|
3437
|
+
s.x - o * 0.3,
|
|
3438
|
+
s.y - o * 0.3,
|
|
3124
3439
|
0,
|
|
3125
3440
|
s.x,
|
|
3126
3441
|
s.y,
|
|
3127
|
-
|
|
3128
|
-
),
|
|
3129
|
-
|
|
3130
|
-
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;
|
|
3131
3446
|
let m = s.label, u = e.measureText(m).width;
|
|
3132
3447
|
if (u > f) {
|
|
3133
3448
|
for (; u > f && m.length > 3; )
|
|
@@ -3145,7 +3460,7 @@ class Rt {
|
|
|
3145
3460
|
const i = s.position || {
|
|
3146
3461
|
x: (Math.random() - 0.5) * 300,
|
|
3147
3462
|
y: (Math.random() - 0.5) * 300
|
|
3148
|
-
},
|
|
3463
|
+
}, n = {
|
|
3149
3464
|
id: s.id,
|
|
3150
3465
|
label: s.label,
|
|
3151
3466
|
x: i.x,
|
|
@@ -3157,7 +3472,7 @@ class Rt {
|
|
|
3157
3472
|
radius: this.options.nodeRadius,
|
|
3158
3473
|
data: s
|
|
3159
3474
|
};
|
|
3160
|
-
this.nodes.set(s.id,
|
|
3475
|
+
this.nodes.set(s.id, n), this.nodeIdToIndex.set(s.id, t);
|
|
3161
3476
|
}), this.edges = e.edges.map((s) => ({
|
|
3162
3477
|
source: s.source,
|
|
3163
3478
|
target: s.target,
|
|
@@ -3233,11 +3548,11 @@ class Rt {
|
|
|
3233
3548
|
focusOnNode(e) {
|
|
3234
3549
|
const s = this.nodes.get(e);
|
|
3235
3550
|
if (!s) return;
|
|
3236
|
-
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale,
|
|
3237
|
-
const
|
|
3238
|
-
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;
|
|
3239
3554
|
};
|
|
3240
|
-
|
|
3555
|
+
h();
|
|
3241
3556
|
}
|
|
3242
3557
|
/**
|
|
3243
3558
|
* Updates node positions from 3D data
|
|
@@ -3280,7 +3595,6 @@ class Rt {
|
|
|
3280
3595
|
}
|
|
3281
3596
|
}
|
|
3282
3597
|
class Ht {
|
|
3283
|
-
// Store graph data for view switching
|
|
3284
3598
|
constructor(e, s = {}) {
|
|
3285
3599
|
// Options
|
|
3286
3600
|
l(this, "options");
|
|
@@ -3291,6 +3605,8 @@ class Ht {
|
|
|
3291
3605
|
l(this, "edgeManager");
|
|
3292
3606
|
l(this, "graphEngine");
|
|
3293
3607
|
l(this, "rendererManager");
|
|
3608
|
+
l(this, "physicsWorkerBridge", null);
|
|
3609
|
+
l(this, "useWorkerPhysics", !1);
|
|
3294
3610
|
// Factories
|
|
3295
3611
|
l(this, "materialFactory");
|
|
3296
3612
|
l(this, "nodeFactory");
|
|
@@ -3315,23 +3631,33 @@ class Ht {
|
|
|
3315
3631
|
l(this, "devControls", null);
|
|
3316
3632
|
l(this, "viewMode", "3d");
|
|
3317
3633
|
l(this, "graphData", null);
|
|
3318
|
-
|
|
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(
|
|
3319
3645
|
this.materialFactory,
|
|
3320
|
-
this.options.nodeRadius ??
|
|
3321
|
-
this.options.lodSegments ??
|
|
3322
|
-
this.options.defaultNodeColor ??
|
|
3646
|
+
this.options.nodeRadius ?? C.nodeRadius,
|
|
3647
|
+
this.options.lodSegments ?? C.lodSegments,
|
|
3648
|
+
this.options.defaultNodeColor ?? C.defaultNodeColor
|
|
3323
3649
|
), this.edgeFactory = new wt(
|
|
3324
3650
|
this.materialFactory,
|
|
3325
|
-
this.options.edgeColor ??
|
|
3326
|
-
this.options.edgeOpacity ??
|
|
3327
|
-
), 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(
|
|
3328
3654
|
this.sceneManager.camera,
|
|
3329
|
-
this.options.lodDistances ??
|
|
3330
|
-
this.options.enableLOD ??
|
|
3331
|
-
), this.frustumCuller = new
|
|
3655
|
+
this.options.lodDistances ?? C.lodDistances,
|
|
3656
|
+
this.options.enableLOD ?? C.enableLOD
|
|
3657
|
+
), this.frustumCuller = new Ct(
|
|
3332
3658
|
this.sceneManager.camera,
|
|
3333
|
-
this.options.enableEdgeCulling ??
|
|
3334
|
-
), 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(
|
|
3335
3661
|
this.nodeManager.getAllNodes(),
|
|
3336
3662
|
this.edgeManager.getAllEdges(),
|
|
3337
3663
|
{
|
|
@@ -3341,16 +3667,16 @@ class Ht {
|
|
|
3341
3667
|
useBarnesHut: this.options.useBarnesHut,
|
|
3342
3668
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3343
3669
|
}
|
|
3344
|
-
), this.rendererManager = new
|
|
3670
|
+
), this.rendererManager = new bt(
|
|
3345
3671
|
this.sceneManager,
|
|
3346
3672
|
() => this.onSimulate(),
|
|
3347
3673
|
() => this.onRender(),
|
|
3348
|
-
this.options.targetFPS ??
|
|
3349
|
-
), 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) => {
|
|
3350
3676
|
this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
|
|
3351
3677
|
this.showNodePanel(t);
|
|
3352
3678
|
}, 400);
|
|
3353
|
-
}), this.options.showSearch !== !1 && (this.searchManager = new
|
|
3679
|
+
}), this.options.showSearch !== !1 && (this.searchManager = new Pt(this.container, {
|
|
3354
3680
|
placeholder: this.options.searchPlaceholder,
|
|
3355
3681
|
onSearch: (t) => ({
|
|
3356
3682
|
nodeResults: this.searchNodes(t),
|
|
@@ -3361,12 +3687,12 @@ class Ht {
|
|
|
3361
3687
|
this.showNodePanel(t);
|
|
3362
3688
|
}, 400);
|
|
3363
3689
|
}
|
|
3364
|
-
})), 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, {
|
|
3365
3691
|
initialMode: this.viewMode,
|
|
3366
3692
|
onViewChange: (t) => {
|
|
3367
3693
|
this.switchView(t);
|
|
3368
3694
|
}
|
|
3369
|
-
})), 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");
|
|
3370
3696
|
}
|
|
3371
3697
|
/**
|
|
3372
3698
|
* Sets up internal callbacks
|
|
@@ -3418,19 +3744,50 @@ class Ht {
|
|
|
3418
3744
|
* Called every simulation step
|
|
3419
3745
|
*/
|
|
3420
3746
|
onSimulate() {
|
|
3421
|
-
this.
|
|
3422
|
-
|
|
3423
|
-
if (
|
|
3424
|
-
const t =
|
|
3425
|
-
|
|
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();
|
|
3426
3766
|
}
|
|
3427
|
-
|
|
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
|
+
}
|
|
3428
3779
|
}
|
|
3429
3780
|
/**
|
|
3430
3781
|
* Called every render frame
|
|
3431
3782
|
*/
|
|
3432
3783
|
onRender() {
|
|
3433
|
-
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);
|
|
3434
3791
|
}
|
|
3435
3792
|
/**
|
|
3436
3793
|
* Updates bottom-right legend counts
|
|
@@ -3438,6 +3795,55 @@ class Ht {
|
|
|
3438
3795
|
updateLegendCounts() {
|
|
3439
3796
|
this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
|
|
3440
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
|
+
}
|
|
3441
3847
|
// ==========================================================================
|
|
3442
3848
|
// Public API
|
|
3443
3849
|
// ==========================================================================
|
|
@@ -3445,23 +3851,23 @@ class Ht {
|
|
|
3445
3851
|
* Sets the graph data
|
|
3446
3852
|
*/
|
|
3447
3853
|
setData(e) {
|
|
3448
|
-
var
|
|
3449
|
-
const s = (
|
|
3450
|
-
...
|
|
3451
|
-
relationship:
|
|
3452
|
-
})),
|
|
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 = {
|
|
3453
3859
|
nodes: s.nodes || [],
|
|
3454
3860
|
edges: i
|
|
3455
3861
|
};
|
|
3456
|
-
if (this.graphData =
|
|
3457
|
-
ee.setExpectedNodeCount(
|
|
3458
|
-
for (const
|
|
3459
|
-
this.addNode(
|
|
3460
|
-
}
|
|
3461
|
-
if (
|
|
3462
|
-
for (const
|
|
3463
|
-
this.addEdge(
|
|
3464
|
-
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(
|
|
3465
3871
|
this.nodeManager.getAllNodes(),
|
|
3466
3872
|
this.edgeManager.getAllEdges(),
|
|
3467
3873
|
{
|
|
@@ -3471,28 +3877,28 @@ class Ht {
|
|
|
3471
3877
|
useBarnesHut: this.options.useBarnesHut,
|
|
3472
3878
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3473
3879
|
}
|
|
3474
|
-
), 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();
|
|
3475
3881
|
}
|
|
3476
3882
|
/**
|
|
3477
3883
|
* Adds a node to the graph
|
|
3478
3884
|
* @returns true if added, false if node already exists or invalid
|
|
3479
3885
|
*/
|
|
3480
3886
|
addNode(e) {
|
|
3481
|
-
if (!
|
|
3887
|
+
if (!ce(e))
|
|
3482
3888
|
return !1;
|
|
3483
3889
|
const s = this.nodeManager.addNode(e);
|
|
3484
|
-
return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
|
|
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;
|
|
3485
3891
|
}
|
|
3486
3892
|
/**
|
|
3487
3893
|
* Removes a node from the graph
|
|
3488
3894
|
* @returns true if removed, false if not found
|
|
3489
3895
|
*/
|
|
3490
3896
|
removeNode(e) {
|
|
3491
|
-
if (!
|
|
3897
|
+
if (!dt(e))
|
|
3492
3898
|
return !1;
|
|
3493
3899
|
this.edgeManager.removeEdgesForNode(e);
|
|
3494
3900
|
const s = this.nodeManager.removeNode(e);
|
|
3495
|
-
return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
|
|
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;
|
|
3496
3902
|
}
|
|
3497
3903
|
/**
|
|
3498
3904
|
* Updates a node's properties
|
|
@@ -3505,10 +3911,10 @@ class Ht {
|
|
|
3505
3911
|
* @returns true if added, false if invalid or nodes don't exist
|
|
3506
3912
|
*/
|
|
3507
3913
|
addEdge(e) {
|
|
3508
|
-
if (!
|
|
3914
|
+
if (!he(e))
|
|
3509
3915
|
return !1;
|
|
3510
3916
|
const s = this.edgeManager.addEdge(e);
|
|
3511
|
-
return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
|
|
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;
|
|
3512
3918
|
}
|
|
3513
3919
|
/**
|
|
3514
3920
|
* Removes an edge from the graph
|
|
@@ -3516,7 +3922,7 @@ class Ht {
|
|
|
3516
3922
|
*/
|
|
3517
3923
|
removeEdge(e, s) {
|
|
3518
3924
|
const t = this.edgeManager.removeEdge(e, s);
|
|
3519
|
-
return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
|
|
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;
|
|
3520
3926
|
}
|
|
3521
3927
|
/**
|
|
3522
3928
|
* Expands a node by fetching more data
|
|
@@ -3529,16 +3935,16 @@ class Ht {
|
|
|
3529
3935
|
if (!i)
|
|
3530
3936
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
3531
3937
|
try {
|
|
3532
|
-
const
|
|
3533
|
-
if (
|
|
3534
|
-
for (const
|
|
3535
|
-
this.addNode(
|
|
3536
|
-
if (
|
|
3537
|
-
for (const
|
|
3538
|
-
this.addEdge(
|
|
3539
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
3540
|
-
} catch (
|
|
3541
|
-
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;
|
|
3542
3948
|
}
|
|
3543
3949
|
}
|
|
3544
3950
|
/**
|
|
@@ -3585,13 +3991,13 @@ class Ht {
|
|
|
3585
3991
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
3586
3992
|
return;
|
|
3587
3993
|
}
|
|
3588
|
-
const i = t.position,
|
|
3589
|
-
x: i.x +
|
|
3590
|
-
y: i.y +
|
|
3591
|
-
z: i.z +
|
|
3592
|
-
},
|
|
3593
|
-
const
|
|
3594
|
-
|
|
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);
|
|
3595
4001
|
};
|
|
3596
4002
|
u();
|
|
3597
4003
|
}
|
|
@@ -3600,22 +4006,22 @@ class Ht {
|
|
|
3600
4006
|
* Camera targets the midpoint and zooms out enough to see both nodes
|
|
3601
4007
|
*/
|
|
3602
4008
|
focusOnEdge(e, s, t = 1.5) {
|
|
3603
|
-
const i = this.nodeManager.getNode(e),
|
|
3604
|
-
if (!i || !
|
|
4009
|
+
const i = this.nodeManager.getNode(e), n = this.nodeManager.getNode(s);
|
|
4010
|
+
if (!i || !n) {
|
|
3605
4011
|
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3606
4012
|
return;
|
|
3607
4013
|
}
|
|
3608
|
-
const
|
|
3609
|
-
x: (i.position.x +
|
|
3610
|
-
y: (i.position.y +
|
|
3611
|
-
z: (i.position.z +
|
|
3612
|
-
},
|
|
3613
|
-
x:
|
|
3614
|
-
y:
|
|
3615
|
-
z:
|
|
3616
|
-
},
|
|
3617
|
-
const
|
|
3618
|
-
|
|
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);
|
|
3619
4025
|
};
|
|
3620
4026
|
G();
|
|
3621
4027
|
}
|
|
@@ -3639,10 +4045,10 @@ class Ht {
|
|
|
3639
4045
|
if (!e || e.trim() === "")
|
|
3640
4046
|
return [];
|
|
3641
4047
|
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
3642
|
-
return t.forEach((
|
|
3643
|
-
var
|
|
3644
|
-
const
|
|
3645
|
-
(
|
|
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);
|
|
3646
4052
|
}), i;
|
|
3647
4053
|
}
|
|
3648
4054
|
/**
|
|
@@ -3650,14 +4056,14 @@ class Ht {
|
|
|
3650
4056
|
* @returns Array of matching edges with source/target node info
|
|
3651
4057
|
*/
|
|
3652
4058
|
searchEdges(e) {
|
|
3653
|
-
var
|
|
4059
|
+
var n;
|
|
3654
4060
|
if (!e || e.trim() === "")
|
|
3655
4061
|
return [];
|
|
3656
4062
|
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
3657
|
-
for (const
|
|
3658
|
-
if ((
|
|
3659
|
-
const
|
|
3660
|
-
|
|
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 });
|
|
3661
4067
|
}
|
|
3662
4068
|
return i;
|
|
3663
4069
|
}
|
|
@@ -3690,7 +4096,7 @@ class Ht {
|
|
|
3690
4096
|
* Switches between 2D and 3D view modes
|
|
3691
4097
|
*/
|
|
3692
4098
|
switchView(e) {
|
|
3693
|
-
this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new
|
|
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, {
|
|
3694
4100
|
backgroundColor: "#0a0a0a",
|
|
3695
4101
|
nodeRadius: 24,
|
|
3696
4102
|
onNodeClick: (s) => {
|
|
@@ -3701,16 +4107,16 @@ class Ht {
|
|
|
3701
4107
|
},
|
|
3702
4108
|
onEdgeHover: (s, t) => {
|
|
3703
4109
|
if (s && t) {
|
|
3704
|
-
const i = this.nodeManager.getNode(s.source),
|
|
3705
|
-
if (i &&
|
|
3706
|
-
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);
|
|
3707
4113
|
this.edgeTooltipManager.show(
|
|
3708
4114
|
s,
|
|
3709
4115
|
i,
|
|
3710
|
-
|
|
4116
|
+
n,
|
|
3711
4117
|
t.clientX,
|
|
3712
4118
|
t.clientY,
|
|
3713
|
-
|
|
4119
|
+
o.length > 0 ? o : [s]
|
|
3714
4120
|
);
|
|
3715
4121
|
}
|
|
3716
4122
|
} else
|
|
@@ -3719,12 +4125,12 @@ class Ht {
|
|
|
3719
4125
|
onEdgeClick: (s) => {
|
|
3720
4126
|
const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3721
4127
|
if (t && i) {
|
|
3722
|
-
const
|
|
4128
|
+
const n = this.edgeManager.getAllEdges().filter((o) => o.source === s.source && o.target === s.target);
|
|
3723
4129
|
this.edgePanelManager.show(
|
|
3724
4130
|
s,
|
|
3725
4131
|
t,
|
|
3726
4132
|
i,
|
|
3727
|
-
|
|
4133
|
+
n.length > 0 ? n : [s]
|
|
3728
4134
|
);
|
|
3729
4135
|
}
|
|
3730
4136
|
}
|
|
@@ -3747,7 +4153,7 @@ class Ht {
|
|
|
3747
4153
|
* Sets physics parameters for both 3D and 2D views
|
|
3748
4154
|
*/
|
|
3749
4155
|
setPhysicsParams(e) {
|
|
3750
|
-
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);
|
|
3751
4157
|
}
|
|
3752
4158
|
/**
|
|
3753
4159
|
* Creates dev mode controls (only in development)
|
|
@@ -3827,18 +4233,18 @@ class Ht {
|
|
|
3827
4233
|
const i = parseFloat(t.value) / 100;
|
|
3828
4234
|
this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
|
|
3829
4235
|
}), setInterval(() => {
|
|
3830
|
-
const i = this.devControls.querySelector("#dev-node-count"),
|
|
3831
|
-
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());
|
|
3832
4238
|
}, 500);
|
|
3833
4239
|
}
|
|
3834
4240
|
/**
|
|
3835
4241
|
* Destroys the graph and releases all resources
|
|
3836
4242
|
*/
|
|
3837
4243
|
destroy() {
|
|
3838
|
-
this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
|
|
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;
|
|
3839
4245
|
}
|
|
3840
4246
|
}
|
|
3841
|
-
const
|
|
4247
|
+
const De = [
|
|
3842
4248
|
"Alpha",
|
|
3843
4249
|
"Beta",
|
|
3844
4250
|
"Gamma",
|
|
@@ -3880,7 +4286,7 @@ const Ie = [
|
|
|
3880
4286
|
"partners with",
|
|
3881
4287
|
"collaborates with",
|
|
3882
4288
|
"supports"
|
|
3883
|
-
],
|
|
4289
|
+
], Fe = [
|
|
3884
4290
|
16777215,
|
|
3885
4291
|
// White
|
|
3886
4292
|
16750950,
|
|
@@ -3892,14 +4298,14 @@ const Ie = [
|
|
|
3892
4298
|
16746564
|
|
3893
4299
|
// Darker tangerine
|
|
3894
4300
|
];
|
|
3895
|
-
function
|
|
4301
|
+
function jt(p = 30) {
|
|
3896
4302
|
const e = [], s = [];
|
|
3897
|
-
for (let i = 0; i <
|
|
3898
|
-
const
|
|
4303
|
+
for (let i = 0; i < p; i++) {
|
|
4304
|
+
const n = i < De.length ? De[i] : `Node ${i + 1}`;
|
|
3899
4305
|
e.push({
|
|
3900
4306
|
id: `node-${i}`,
|
|
3901
|
-
label:
|
|
3902
|
-
color:
|
|
4307
|
+
label: n,
|
|
4308
|
+
color: Fe[i % Fe.length],
|
|
3903
4309
|
position: {
|
|
3904
4310
|
x: (Math.random() - 0.5) * 60,
|
|
3905
4311
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -3907,76 +4313,76 @@ function At(h = 30) {
|
|
|
3907
4313
|
}
|
|
3908
4314
|
});
|
|
3909
4315
|
}
|
|
3910
|
-
for (let i = 1; i <
|
|
3911
|
-
const
|
|
4316
|
+
for (let i = 1; i < p; i++) {
|
|
4317
|
+
const n = Math.floor(Math.random() * i);
|
|
3912
4318
|
s.push({
|
|
3913
4319
|
source: `node-${i}`,
|
|
3914
|
-
target: `node-${
|
|
4320
|
+
target: `node-${n}`,
|
|
3915
4321
|
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3916
4322
|
});
|
|
3917
4323
|
}
|
|
3918
|
-
const t = Math.floor(
|
|
4324
|
+
const t = Math.floor(p * 0.5);
|
|
3919
4325
|
for (let i = 0; i < t; i++) {
|
|
3920
|
-
const
|
|
3921
|
-
let
|
|
3922
|
-
|
|
3923
|
-
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}`;
|
|
3924
4330
|
s.some(
|
|
3925
|
-
(
|
|
4331
|
+
(d) => d.source === a && d.target === c || d.source === c && d.target === a
|
|
3926
4332
|
) || s.push({
|
|
3927
|
-
source:
|
|
3928
|
-
target:
|
|
4333
|
+
source: a,
|
|
4334
|
+
target: c,
|
|
3929
4335
|
relationship: J[Math.floor(Math.random() * J.length)]
|
|
3930
4336
|
});
|
|
3931
4337
|
}
|
|
3932
4338
|
return { nodes: e, edges: s };
|
|
3933
4339
|
}
|
|
3934
|
-
function
|
|
3935
|
-
const e = [], s = [], t = Math.ceil(
|
|
3936
|
-
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++)
|
|
3937
4343
|
i.push({
|
|
3938
4344
|
x: (Math.random() - 0.5) * 200,
|
|
3939
4345
|
y: (Math.random() - 0.5) * 200,
|
|
3940
4346
|
z: (Math.random() - 0.5) * 200
|
|
3941
4347
|
});
|
|
3942
|
-
for (let
|
|
3943
|
-
const
|
|
4348
|
+
for (let n = 0; n < p; n++) {
|
|
4349
|
+
const o = i[n % t];
|
|
3944
4350
|
e.push({
|
|
3945
|
-
id: `node-${
|
|
3946
|
-
label: `N${
|
|
4351
|
+
id: `node-${n}`,
|
|
4352
|
+
label: `N${n}`,
|
|
3947
4353
|
position: {
|
|
3948
|
-
x:
|
|
3949
|
-
y:
|
|
3950
|
-
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
|
|
3951
4357
|
}
|
|
3952
4358
|
});
|
|
3953
4359
|
}
|
|
3954
|
-
for (let
|
|
3955
|
-
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));
|
|
3956
4362
|
s.push({
|
|
3957
|
-
source: `node-${
|
|
3958
|
-
target: `node-${Math.min(
|
|
4363
|
+
source: `node-${n}`,
|
|
4364
|
+
target: `node-${Math.min(a, n - 1)}`,
|
|
3959
4365
|
relationship: "links to"
|
|
3960
4366
|
});
|
|
3961
4367
|
}
|
|
3962
|
-
for (let
|
|
3963
|
-
const
|
|
4368
|
+
for (let n = 1; n < t; n++) {
|
|
4369
|
+
const o = n * 50, a = (n - 1) * 50 + Math.floor(Math.random() * 50);
|
|
3964
4370
|
s.push({
|
|
3965
|
-
source: `node-${
|
|
3966
|
-
target: `node-${
|
|
4371
|
+
source: `node-${o}`,
|
|
4372
|
+
target: `node-${a}`,
|
|
3967
4373
|
relationship: "bridges to"
|
|
3968
4374
|
});
|
|
3969
4375
|
}
|
|
3970
4376
|
return { nodes: e, edges: s };
|
|
3971
4377
|
}
|
|
3972
4378
|
export {
|
|
3973
|
-
|
|
4379
|
+
C as DEFAULT_OPTIONS,
|
|
3974
4380
|
Ht as ForceGraph3D,
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
4381
|
+
K as LODLevel,
|
|
4382
|
+
At as createEdgeKey,
|
|
4383
|
+
Bt as generateLargeSampleData,
|
|
4384
|
+
jt as generateSampleData,
|
|
4385
|
+
he as validateEdgeData,
|
|
4386
|
+
ce as validateNodeData
|
|
3981
4387
|
};
|
|
3982
4388
|
//# sourceMappingURL=force-3d-graph.js.map
|