force-3d-graph 1.2.5 → 1.2.6
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 +334 -308
- package/dist/force-3d-graph.js.map +1 -1
- package/dist/force-3d-graph.umd.cjs +18 -18
- package/dist/force-3d-graph.umd.cjs.map +1 -1
- package/package.json +1 -1
package/dist/force-3d-graph.js
CHANGED
|
@@ -92,12 +92,12 @@ class ft extends at {
|
|
|
92
92
|
}, this.saveState = function() {
|
|
93
93
|
t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
|
|
94
94
|
}, this.reset = function() {
|
|
95
|
-
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(),
|
|
95
|
+
t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(), o = i.NONE;
|
|
96
96
|
}, this.update = function() {
|
|
97
97
|
const n = new S(), g = new Te().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), w = new S(), C = new Te(), F = new S(), z = 2 * Math.PI;
|
|
98
98
|
return function(it = null) {
|
|
99
99
|
const Se = t.object.position;
|
|
100
|
-
n.copy(Se).sub(t.target), n.applyQuaternion(g), r.setFromVector3(n), t.autoRotate &&
|
|
100
|
+
n.copy(Se).sub(t.target), n.applyQuaternion(g), r.setFromVector3(n), t.autoRotate && o === i.NONE && B(Ae(it)), t.enableDamping ? (r.theta += c.theta * t.dampingFactor, r.phi += c.phi * t.dampingFactor) : (r.theta += c.theta, r.phi += c.phi);
|
|
101
101
|
let I = t.minAzimuthAngle, L = t.maxAzimuthAngle;
|
|
102
102
|
isFinite(I) && isFinite(L) && (I < -Math.PI ? I += z : I > Math.PI && (I -= z), L < -Math.PI ? L += z : L > Math.PI && (L -= z), I <= L ? r.theta = Math.max(I, Math.min(L, r.theta)) : r.theta = r.theta > (I + L) / 2 ? Math.max(I, r.theta) : Math.min(L, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && H || t.object.isOrthographicCamera ? r.radius = ne(r.radius) : r.radius = ne(r.radius * h), n.setFromSpherical(r), n.applyQuaternion(v), Se.copy(t.target).add(n), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), p.set(0, 0, 0));
|
|
103
103
|
let le = !1;
|
|
@@ -122,7 +122,7 @@ class ft extends at {
|
|
|
122
122
|
}(), this.dispose = function() {
|
|
123
123
|
t.domElement.removeEventListener("contextmenu", Ce), t.domElement.removeEventListener("pointerdown", Me), t.domElement.removeEventListener("pointercancel", K), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", re), t._domElementKeyEvents = null);
|
|
124
124
|
};
|
|
125
|
-
const t = this,
|
|
125
|
+
const t = this, i = {
|
|
126
126
|
NONE: -1,
|
|
127
127
|
ROTATE: 0,
|
|
128
128
|
DOLLY: 1,
|
|
@@ -132,7 +132,7 @@ class ft extends at {
|
|
|
132
132
|
TOUCH_DOLLY_PAN: 5,
|
|
133
133
|
TOUCH_DOLLY_ROTATE: 6
|
|
134
134
|
};
|
|
135
|
-
let
|
|
135
|
+
let o = i.NONE;
|
|
136
136
|
const a = 1e-6, r = new ze(), c = new ze();
|
|
137
137
|
let h = 1;
|
|
138
138
|
const p = new S(), x = new T(), m = new T(), u = new T(), f = new T(), b = new T(), M = new T(), N = new T(), O = new T(), R = new T(), Y = new S(), k = new T();
|
|
@@ -296,7 +296,7 @@ class ft extends at {
|
|
|
296
296
|
t.enabled !== !1 && (n.pointerType === "touch" ? et(n) : We(n));
|
|
297
297
|
}
|
|
298
298
|
function K(n) {
|
|
299
|
-
st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Re),
|
|
299
|
+
st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Re), o = i.NONE;
|
|
300
300
|
}
|
|
301
301
|
function qe(n) {
|
|
302
302
|
let g;
|
|
@@ -316,49 +316,49 @@ class ft extends at {
|
|
|
316
316
|
switch (g) {
|
|
317
317
|
case $.DOLLY:
|
|
318
318
|
if (t.enableZoom === !1) return;
|
|
319
|
-
je(n),
|
|
319
|
+
je(n), o = i.DOLLY;
|
|
320
320
|
break;
|
|
321
321
|
case $.ROTATE:
|
|
322
322
|
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
323
323
|
if (t.enablePan === !1) return;
|
|
324
|
-
ue(n),
|
|
324
|
+
ue(n), o = i.PAN;
|
|
325
325
|
} else {
|
|
326
326
|
if (t.enableRotate === !1) return;
|
|
327
|
-
ge(n),
|
|
327
|
+
ge(n), o = i.ROTATE;
|
|
328
328
|
}
|
|
329
329
|
break;
|
|
330
330
|
case $.PAN:
|
|
331
331
|
if (n.ctrlKey || n.metaKey || n.shiftKey) {
|
|
332
332
|
if (t.enableRotate === !1) return;
|
|
333
|
-
ge(n),
|
|
333
|
+
ge(n), o = i.ROTATE;
|
|
334
334
|
} else {
|
|
335
335
|
if (t.enablePan === !1) return;
|
|
336
|
-
ue(n),
|
|
336
|
+
ue(n), o = i.PAN;
|
|
337
337
|
}
|
|
338
338
|
break;
|
|
339
339
|
default:
|
|
340
|
-
|
|
340
|
+
o = i.NONE;
|
|
341
341
|
}
|
|
342
|
-
|
|
342
|
+
o !== i.NONE && t.dispatchEvent(ce);
|
|
343
343
|
}
|
|
344
344
|
function We(n) {
|
|
345
|
-
switch (
|
|
346
|
-
case
|
|
345
|
+
switch (o) {
|
|
346
|
+
case i.ROTATE:
|
|
347
347
|
if (t.enableRotate === !1) return;
|
|
348
348
|
$e(n);
|
|
349
349
|
break;
|
|
350
|
-
case
|
|
350
|
+
case i.DOLLY:
|
|
351
351
|
if (t.enableZoom === !1) return;
|
|
352
352
|
Ge(n);
|
|
353
353
|
break;
|
|
354
|
-
case
|
|
354
|
+
case i.PAN:
|
|
355
355
|
if (t.enablePan === !1) return;
|
|
356
356
|
Ye(n);
|
|
357
357
|
break;
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
function we(n) {
|
|
361
|
-
t.enabled === !1 || t.enableZoom === !1 ||
|
|
361
|
+
t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (n.preventDefault(), t.dispatchEvent(ce), Be(Ze(n)), t.dispatchEvent(Re));
|
|
362
362
|
}
|
|
363
363
|
function Ze(n) {
|
|
364
364
|
const g = n.deltaMode, v = {
|
|
@@ -391,55 +391,55 @@ class ft extends at {
|
|
|
391
391
|
switch (t.touches.ONE) {
|
|
392
392
|
case G.ROTATE:
|
|
393
393
|
if (t.enableRotate === !1) return;
|
|
394
|
-
fe(n),
|
|
394
|
+
fe(n), o = i.TOUCH_ROTATE;
|
|
395
395
|
break;
|
|
396
396
|
case G.PAN:
|
|
397
397
|
if (t.enablePan === !1) return;
|
|
398
|
-
me(n),
|
|
398
|
+
me(n), o = i.TOUCH_PAN;
|
|
399
399
|
break;
|
|
400
400
|
default:
|
|
401
|
-
|
|
401
|
+
o = i.NONE;
|
|
402
402
|
}
|
|
403
403
|
break;
|
|
404
404
|
case 2:
|
|
405
405
|
switch (t.touches.TWO) {
|
|
406
406
|
case G.DOLLY_PAN:
|
|
407
407
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
408
|
-
Ue(n),
|
|
408
|
+
Ue(n), o = i.TOUCH_DOLLY_PAN;
|
|
409
409
|
break;
|
|
410
410
|
case G.DOLLY_ROTATE:
|
|
411
411
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
412
|
-
_e(n),
|
|
412
|
+
_e(n), o = i.TOUCH_DOLLY_ROTATE;
|
|
413
413
|
break;
|
|
414
414
|
default:
|
|
415
|
-
|
|
415
|
+
o = i.NONE;
|
|
416
416
|
}
|
|
417
417
|
break;
|
|
418
418
|
default:
|
|
419
|
-
|
|
419
|
+
o = i.NONE;
|
|
420
420
|
}
|
|
421
|
-
|
|
421
|
+
o !== i.NONE && t.dispatchEvent(ce);
|
|
422
422
|
}
|
|
423
423
|
function et(n) {
|
|
424
|
-
switch (Ne(n),
|
|
425
|
-
case
|
|
424
|
+
switch (Ne(n), o) {
|
|
425
|
+
case i.TOUCH_ROTATE:
|
|
426
426
|
if (t.enableRotate === !1) return;
|
|
427
427
|
xe(n), t.update();
|
|
428
428
|
break;
|
|
429
|
-
case
|
|
429
|
+
case i.TOUCH_PAN:
|
|
430
430
|
if (t.enablePan === !1) return;
|
|
431
431
|
be(n), t.update();
|
|
432
432
|
break;
|
|
433
|
-
case
|
|
433
|
+
case i.TOUCH_DOLLY_PAN:
|
|
434
434
|
if (t.enableZoom === !1 && t.enablePan === !1) return;
|
|
435
435
|
Xe(n), t.update();
|
|
436
436
|
break;
|
|
437
|
-
case
|
|
437
|
+
case i.TOUCH_DOLLY_ROTATE:
|
|
438
438
|
if (t.enableZoom === !1 && t.enableRotate === !1) return;
|
|
439
439
|
Ve(n), t.update();
|
|
440
440
|
break;
|
|
441
441
|
default:
|
|
442
|
-
|
|
442
|
+
o = i.NONE;
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
function Ce(n) {
|
|
@@ -478,14 +478,14 @@ class mt {
|
|
|
478
478
|
this.container = e, this.scene = new y.Scene(), this.scene.background = new y.Color(
|
|
479
479
|
s.backgroundColor ?? 657930
|
|
480
480
|
);
|
|
481
|
-
const { width: t, height:
|
|
482
|
-
this.camera = new y.PerspectiveCamera(
|
|
481
|
+
const { width: t, height: i } = ke(e), o = s.cameraFov ?? 75;
|
|
482
|
+
this.camera = new y.PerspectiveCamera(o, t / i, 0.1, 2e3);
|
|
483
483
|
const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
|
|
484
484
|
this.camera.position.set(a.x, a.y, a.z), this.renderer = new y.WebGLRenderer({
|
|
485
485
|
antialias: !0,
|
|
486
486
|
alpha: !0,
|
|
487
487
|
powerPreference: "high-performance"
|
|
488
|
-
}), this.renderer.setSize(t,
|
|
488
|
+
}), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = y.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = y.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ft(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
|
|
489
489
|
}
|
|
490
490
|
/**
|
|
491
491
|
* Sets up scene lighting for gradient glass on dark background
|
|
@@ -497,10 +497,10 @@ class mt {
|
|
|
497
497
|
s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
|
|
498
498
|
const t = new y.DirectionalLight(16773344, 0.4);
|
|
499
499
|
t.position.set(-50, 30, -40), this.scene.add(t);
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
500
|
+
const i = new y.DirectionalLight(16777215, 0.3);
|
|
501
|
+
i.position.set(0, -30, -50), this.scene.add(i);
|
|
502
|
+
const o = new y.PointLight(16750950, 0.5, 150);
|
|
503
|
+
o.position.set(40, 20, 40), this.scene.add(o);
|
|
504
504
|
const a = new y.PointLight(16764057, 0.4, 150);
|
|
505
505
|
a.position.set(-40, -20, 40), this.scene.add(a);
|
|
506
506
|
const r = new y.PointLight(6724095, 0.2, 100);
|
|
@@ -588,20 +588,20 @@ const V = class V {
|
|
|
588
588
|
return !1;
|
|
589
589
|
if (this.nodes.has(e.id))
|
|
590
590
|
return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
|
|
591
|
-
const t = Math.max(50, Math.cbrt(V.expectedNodeCount) * 10),
|
|
591
|
+
const t = Math.max(50, Math.cbrt(V.expectedNodeCount) * 10), i = e.position ?? {
|
|
592
592
|
x: (Math.random() - 0.5) * t,
|
|
593
593
|
y: (Math.random() - 0.5) * t,
|
|
594
594
|
z: (Math.random() - 0.5) * t
|
|
595
|
-
},
|
|
595
|
+
}, o = {
|
|
596
596
|
...e,
|
|
597
|
-
position:
|
|
597
|
+
position: i,
|
|
598
598
|
velocity: { x: 0, y: 0, z: 0 },
|
|
599
599
|
mass: 1
|
|
600
600
|
}, a = this.nodeFactory.createNode(
|
|
601
|
-
{ ...e, position:
|
|
601
|
+
{ ...e, position: i },
|
|
602
602
|
s
|
|
603
603
|
);
|
|
604
|
-
return this.sceneManager.add(a.group), this.nodes.set(e.id,
|
|
604
|
+
return this.sceneManager.add(a.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, a), !0;
|
|
605
605
|
}
|
|
606
606
|
/**
|
|
607
607
|
* Removes a node from the graph
|
|
@@ -615,17 +615,17 @@ const V = class V {
|
|
|
615
615
|
* Updates a node's properties
|
|
616
616
|
*/
|
|
617
617
|
updateNode(e, s) {
|
|
618
|
-
const t = this.nodes.get(e),
|
|
619
|
-
return !t || !
|
|
620
|
-
|
|
618
|
+
const t = this.nodes.get(e), i = this.nodeObjects.get(e);
|
|
619
|
+
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((o) => {
|
|
620
|
+
o !== "id" && o !== "label" && o !== "color" && o !== "position" && (t[o] = s[o]);
|
|
621
621
|
}), !0);
|
|
622
622
|
}
|
|
623
623
|
/**
|
|
624
624
|
* Updates a node's position (called by physics engine)
|
|
625
625
|
*/
|
|
626
626
|
updateNodePosition(e, s) {
|
|
627
|
-
const t = this.nodes.get(e),
|
|
628
|
-
t &&
|
|
627
|
+
const t = this.nodes.get(e), i = this.nodeObjects.get(e);
|
|
628
|
+
t && i && (t.position = s, i.group.position.set(s.x, s.y, s.z));
|
|
629
629
|
}
|
|
630
630
|
/**
|
|
631
631
|
* Updates a node's LOD level
|
|
@@ -726,14 +726,14 @@ class yt {
|
|
|
726
726
|
const s = D(e.source, e.target);
|
|
727
727
|
if (this.edgeKeySet.has(s))
|
|
728
728
|
return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
|
|
729
|
-
const t = this.nodeManager.getNode(e.source),
|
|
729
|
+
const t = this.nodeManager.getNode(e.source), i = this.nodeManager.getNode(e.target), o = this.edgeFactory.createEdge(
|
|
730
730
|
e,
|
|
731
731
|
t,
|
|
732
|
-
|
|
732
|
+
i,
|
|
733
733
|
t.position,
|
|
734
|
-
|
|
734
|
+
i.position
|
|
735
735
|
);
|
|
736
|
-
return this.sceneManager.add(
|
|
736
|
+
return this.sceneManager.add(o.line), this.edges.push(e), this.edgeObjects.push(o), this.edgeKeySet.add(s), !0;
|
|
737
737
|
}
|
|
738
738
|
/**
|
|
739
739
|
* Removes an edge from the graph
|
|
@@ -743,13 +743,13 @@ class yt {
|
|
|
743
743
|
const t = D(e, s);
|
|
744
744
|
if (!this.edgeKeySet.has(t))
|
|
745
745
|
return !1;
|
|
746
|
-
const
|
|
746
|
+
const i = this.edges.findIndex(
|
|
747
747
|
(a) => D(a.source, a.target) === t
|
|
748
748
|
);
|
|
749
|
-
if (
|
|
749
|
+
if (i === -1)
|
|
750
750
|
return !1;
|
|
751
|
-
const
|
|
752
|
-
return this.sceneManager.remove(
|
|
751
|
+
const o = this.edgeObjects[i];
|
|
752
|
+
return this.sceneManager.remove(o.line), this.edgeFactory.disposeEdge(o), this.edges.splice(i, 1), this.edgeObjects.splice(i, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
|
|
753
753
|
}
|
|
754
754
|
/**
|
|
755
755
|
* Highlights an edge
|
|
@@ -757,10 +757,10 @@ class yt {
|
|
|
757
757
|
highlightEdge(e, s) {
|
|
758
758
|
const t = D(e, s);
|
|
759
759
|
this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
|
|
760
|
-
const
|
|
761
|
-
(
|
|
760
|
+
const i = this.edges.findIndex(
|
|
761
|
+
(o) => D(o.source, o.target) === t
|
|
762
762
|
);
|
|
763
|
-
|
|
763
|
+
i !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[i]), this.highlightedEdgeKey = t);
|
|
764
764
|
}
|
|
765
765
|
/**
|
|
766
766
|
* Unhighlights the currently highlighted edge
|
|
@@ -803,11 +803,11 @@ class yt {
|
|
|
803
803
|
*/
|
|
804
804
|
updateEdgePositions() {
|
|
805
805
|
this.edgeObjects.forEach((e, s) => {
|
|
806
|
-
const t = this.edges[s],
|
|
807
|
-
|
|
806
|
+
const t = this.edges[s], i = this.nodeManager.getNode(t.source), o = this.nodeManager.getNode(t.target);
|
|
807
|
+
i && o && this.edgeFactory.updateEdgePositions(
|
|
808
808
|
e,
|
|
809
|
-
|
|
810
|
-
|
|
809
|
+
i.position,
|
|
810
|
+
o.position
|
|
811
811
|
);
|
|
812
812
|
});
|
|
813
813
|
}
|
|
@@ -861,14 +861,36 @@ class Le {
|
|
|
861
861
|
// Skip repulsion beyond this distance
|
|
862
862
|
l(this, "REPULSION_CUTOFF_SQ");
|
|
863
863
|
// Precomputed cutoff²
|
|
864
|
-
l(this, "GRAVITY_STRENGTH", 0.
|
|
864
|
+
l(this, "GRAVITY_STRENGTH", 0.05);
|
|
865
865
|
// Centering force strength
|
|
866
|
+
// Hub node pinning
|
|
867
|
+
l(this, "pinnedNodeId", null);
|
|
866
868
|
// Simulation state
|
|
867
869
|
l(this, "alpha", 1);
|
|
868
870
|
l(this, "alphaDecay", 0.0228);
|
|
869
871
|
l(this, "alphaMin", 1e-3);
|
|
870
872
|
l(this, "alphaTarget", 0);
|
|
871
|
-
this.nodes = e, this.edges = s, this.repulsionStrength = t.repulsionStrength ?? 100, this.attractionStrength = t.attractionStrength ?? 0.01, this.damping = t.damping ?? 0.9, this.useBarnesHut = t.useBarnesHut ?? !1, this.barnesHutTheta = t.barnesHutTheta ?? 0.5, this.REPULSION_CUTOFF_SQ = this.REPULSION_CUTOFF * this.REPULSION_CUTOFF;
|
|
873
|
+
this.nodes = e, this.edges = s, this.repulsionStrength = t.repulsionStrength ?? 100, this.attractionStrength = t.attractionStrength ?? 0.01, this.damping = t.damping ?? 0.9, this.useBarnesHut = t.useBarnesHut ?? !1, this.barnesHutTheta = t.barnesHutTheta ?? 0.5, this.REPULSION_CUTOFF_SQ = this.REPULSION_CUTOFF * this.REPULSION_CUTOFF, this.initializeNodeMassAndPin();
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Assigns mass proportional to node degree (connection count) and
|
|
877
|
+
* pins the most-connected hub node at the origin.
|
|
878
|
+
*/
|
|
879
|
+
initializeNodeMassAndPin() {
|
|
880
|
+
const e = /* @__PURE__ */ new Map();
|
|
881
|
+
for (const i of this.edges)
|
|
882
|
+
e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
|
|
883
|
+
let s = 0, t = null;
|
|
884
|
+
for (const [i, o] of e) {
|
|
885
|
+
o > s && (s = o, t = i);
|
|
886
|
+
const a = this.nodes.get(i);
|
|
887
|
+
a && (a.mass = 1 + Math.log2(1 + o));
|
|
888
|
+
}
|
|
889
|
+
if (t && s > 10) {
|
|
890
|
+
this.pinnedNodeId = t;
|
|
891
|
+
const i = this.nodes.get(t);
|
|
892
|
+
i && (i.position.x = 0, i.position.y = 0, i.position.z = 0, i.velocity.x = 0, i.velocity.y = 0, i.velocity.z = 0);
|
|
893
|
+
}
|
|
872
894
|
}
|
|
873
895
|
/**
|
|
874
896
|
* Computes effective repulsion strength scaled by node count.
|
|
@@ -891,15 +913,15 @@ class Le {
|
|
|
891
913
|
*/
|
|
892
914
|
calculateRepulsionBruteForce() {
|
|
893
915
|
const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
|
|
894
|
-
for (let
|
|
895
|
-
const
|
|
896
|
-
for (let a =
|
|
897
|
-
const r = e[a], c = r.position.x -
|
|
916
|
+
for (let i = 0; i < s; i++) {
|
|
917
|
+
const o = e[i];
|
|
918
|
+
for (let a = i + 1; a < s; a++) {
|
|
919
|
+
const r = e[a], c = r.position.x - o.position.x, h = r.position.y - o.position.y, p = r.position.z - o.position.z;
|
|
898
920
|
let x = c * c + h * h + p * p;
|
|
899
921
|
if (x > this.REPULSION_CUTOFF_SQ) continue;
|
|
900
922
|
x < 0.01 && (x = 0.01);
|
|
901
923
|
const m = Math.sqrt(x), u = t * this.alpha / x, f = c / m * u, b = h / m * u, M = p / m * u;
|
|
902
|
-
|
|
924
|
+
o.velocity.x -= f / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= M / o.mass, r.velocity.x += f / r.mass, r.velocity.y += b / r.mass, r.velocity.z += M / r.mass;
|
|
903
925
|
}
|
|
904
926
|
}
|
|
905
927
|
}
|
|
@@ -920,12 +942,12 @@ class Le {
|
|
|
920
942
|
return;
|
|
921
943
|
}
|
|
922
944
|
if (s.mass === 0) return;
|
|
923
|
-
const t = s.centerOfMass.x - e.position.x,
|
|
945
|
+
const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z, a = t * t + i * i + o * o;
|
|
924
946
|
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
925
947
|
const r = Math.sqrt(a), c = this.getEffectiveRepulsion();
|
|
926
948
|
if (r > 0 && s.size / r < this.barnesHutTheta) {
|
|
927
949
|
const h = Math.max(a, 0.01), p = c * this.alpha * s.mass / h;
|
|
928
|
-
e.velocity.x -= t / r * p / e.mass, e.velocity.y -=
|
|
950
|
+
e.velocity.x -= t / r * p / e.mass, e.velocity.y -= i / r * p / e.mass, e.velocity.z -= o / r * p / e.mass;
|
|
929
951
|
} else
|
|
930
952
|
for (const h of s.children)
|
|
931
953
|
h && this.calculateForceFromOctree(e, h);
|
|
@@ -934,12 +956,12 @@ class Le {
|
|
|
934
956
|
* Apply repulsion between two nodes (with cutoff)
|
|
935
957
|
*/
|
|
936
958
|
applyRepulsionBetween(e, s) {
|
|
937
|
-
const t = s.position.x - e.position.x,
|
|
938
|
-
let a = t * t +
|
|
959
|
+
const t = s.position.x - e.position.x, i = s.position.y - e.position.y, o = s.position.z - e.position.z;
|
|
960
|
+
let a = t * t + i * i + o * o;
|
|
939
961
|
if (a > this.REPULSION_CUTOFF_SQ) return;
|
|
940
962
|
a < 0.01 && (a = 0.01);
|
|
941
963
|
const r = Math.sqrt(a), h = this.getEffectiveRepulsion() * this.alpha / a;
|
|
942
|
-
e.velocity.x -= t / r * h / e.mass, e.velocity.y -=
|
|
964
|
+
e.velocity.x -= t / r * h / e.mass, e.velocity.y -= i / r * h / e.mass, e.velocity.z -= o / r * h / e.mass;
|
|
943
965
|
}
|
|
944
966
|
/**
|
|
945
967
|
* Calculate attraction forces along edges
|
|
@@ -947,12 +969,12 @@ class Le {
|
|
|
947
969
|
calculateAttraction() {
|
|
948
970
|
const e = this.nodes.size, s = e > 500 ? 1 + Math.log10(e / 500) : 1;
|
|
949
971
|
for (const t of this.edges) {
|
|
950
|
-
const
|
|
951
|
-
if (!
|
|
952
|
-
const a =
|
|
972
|
+
const i = this.nodes.get(t.source), o = this.nodes.get(t.target);
|
|
973
|
+
if (!i || !o) continue;
|
|
974
|
+
const a = o.position.x - i.position.x, r = o.position.y - i.position.y, c = o.position.z - i.position.z, h = Math.sqrt(a * a + r * r + c * c);
|
|
953
975
|
if (h < 0.01) continue;
|
|
954
976
|
const x = (h - 15) * this.attractionStrength * s * this.alpha, m = a / h * x, u = r / h * x, f = c / h * x;
|
|
955
|
-
|
|
977
|
+
i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += f / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= f / o.mass;
|
|
956
978
|
}
|
|
957
979
|
}
|
|
958
980
|
/**
|
|
@@ -969,6 +991,10 @@ class Le {
|
|
|
969
991
|
*/
|
|
970
992
|
applyForces() {
|
|
971
993
|
for (const e of this.nodes.values()) {
|
|
994
|
+
if (e.id === this.pinnedNodeId) {
|
|
995
|
+
e.position.x = 0, e.position.y = 0, e.position.z = 0, e.velocity.x = 0, e.velocity.y = 0, e.velocity.z = 0;
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
972
998
|
e.velocity.x *= this.damping, e.velocity.y *= this.damping, e.velocity.z *= this.damping;
|
|
973
999
|
const s = Math.sqrt(
|
|
974
1000
|
e.velocity.x * e.velocity.x + e.velocity.y * e.velocity.y + e.velocity.z * e.velocity.z
|
|
@@ -1018,13 +1044,13 @@ class xt {
|
|
|
1018
1044
|
max: { x: 100, y: 100, z: 100 }
|
|
1019
1045
|
};
|
|
1020
1046
|
const s = { x: 1 / 0, y: 1 / 0, z: 1 / 0 }, t = { x: -1 / 0, y: -1 / 0, z: -1 / 0 };
|
|
1021
|
-
for (const
|
|
1022
|
-
s.x = Math.min(s.x,
|
|
1023
|
-
const
|
|
1024
|
-
return s.x -=
|
|
1047
|
+
for (const o of e)
|
|
1048
|
+
s.x = Math.min(s.x, o.position.x), s.y = Math.min(s.y, o.position.y), s.z = Math.min(s.z, o.position.z), t.x = Math.max(t.x, o.position.x), t.y = Math.max(t.y, o.position.y), t.z = Math.max(t.z, o.position.z);
|
|
1049
|
+
const i = 10;
|
|
1050
|
+
return s.x -= i, s.y -= i, s.z -= i, t.x += i, t.y += i, t.z += i, { min: s, max: t };
|
|
1025
1051
|
}
|
|
1026
1052
|
buildTree(e, s, t = 0) {
|
|
1027
|
-
const
|
|
1053
|
+
const i = Math.max(
|
|
1028
1054
|
s.max.x - s.min.x,
|
|
1029
1055
|
s.max.y - s.min.y,
|
|
1030
1056
|
s.max.z - s.min.z
|
|
@@ -1032,7 +1058,7 @@ class xt {
|
|
|
1032
1058
|
if (e.length === 0)
|
|
1033
1059
|
return {
|
|
1034
1060
|
bounds: s,
|
|
1035
|
-
size:
|
|
1061
|
+
size: i,
|
|
1036
1062
|
centerOfMass: { x: 0, y: 0, z: 0 },
|
|
1037
1063
|
mass: 0,
|
|
1038
1064
|
isLeaf: !0,
|
|
@@ -1046,7 +1072,7 @@ class xt {
|
|
|
1046
1072
|
u += b.mass, f.x += b.position.x * b.mass, f.y += b.position.y * b.mass, f.z += b.position.z * b.mass;
|
|
1047
1073
|
return u > 0 && (f.x /= u, f.y /= u, f.z /= u), {
|
|
1048
1074
|
bounds: s,
|
|
1049
|
-
size:
|
|
1075
|
+
size: i,
|
|
1050
1076
|
centerOfMass: f,
|
|
1051
1077
|
mass: u,
|
|
1052
1078
|
isLeaf: !0,
|
|
@@ -1054,20 +1080,20 @@ class xt {
|
|
|
1054
1080
|
children: []
|
|
1055
1081
|
};
|
|
1056
1082
|
}
|
|
1057
|
-
const
|
|
1083
|
+
const o = (s.min.x + s.max.x) / 2, a = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
|
|
1058
1084
|
for (const u of e) {
|
|
1059
|
-
const f = (u.position.x >=
|
|
1085
|
+
const f = (u.position.x >= o ? 1 : 0) + (u.position.y >= a ? 2 : 0) + (u.position.z >= r ? 4 : 0);
|
|
1060
1086
|
c[f].push(u);
|
|
1061
1087
|
}
|
|
1062
1088
|
const h = [
|
|
1063
|
-
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x:
|
|
1064
|
-
{ min: { x:
|
|
1065
|
-
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x:
|
|
1066
|
-
{ min: { x:
|
|
1067
|
-
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x:
|
|
1068
|
-
{ min: { x:
|
|
1069
|
-
{ min: { x: s.min.x, y: a, z: r }, max: { x:
|
|
1070
|
-
{ min: { x:
|
|
1089
|
+
{ min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: a, z: r } },
|
|
1090
|
+
{ min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: r } },
|
|
1091
|
+
{ min: { x: s.min.x, y: a, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
|
|
1092
|
+
{ min: { x: o, y: a, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
|
|
1093
|
+
{ min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y: a, z: s.max.z } },
|
|
1094
|
+
{ min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y: a, z: s.max.z } },
|
|
1095
|
+
{ min: { x: s.min.x, y: a, z: r }, max: { x: o, y: s.max.y, z: s.max.z } },
|
|
1096
|
+
{ min: { x: o, y: a, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
|
|
1071
1097
|
], p = [];
|
|
1072
1098
|
let x = 0;
|
|
1073
1099
|
const m = { x: 0, y: 0, z: 0 };
|
|
@@ -1079,7 +1105,7 @@ class xt {
|
|
|
1079
1105
|
p.push(null);
|
|
1080
1106
|
return x > 0 && (m.x /= x, m.y /= x, m.z /= x), {
|
|
1081
1107
|
bounds: s,
|
|
1082
|
-
size:
|
|
1108
|
+
size: i,
|
|
1083
1109
|
centerOfMass: m,
|
|
1084
1110
|
mass: x,
|
|
1085
1111
|
isLeaf: !1,
|
|
@@ -1089,7 +1115,7 @@ class xt {
|
|
|
1089
1115
|
}
|
|
1090
1116
|
}
|
|
1091
1117
|
class bt {
|
|
1092
|
-
constructor(e, s, t,
|
|
1118
|
+
constructor(e, s, t, i = 60) {
|
|
1093
1119
|
l(this, "sceneManager");
|
|
1094
1120
|
l(this, "animationId", null);
|
|
1095
1121
|
l(this, "isRunning", !1);
|
|
@@ -1114,7 +1140,7 @@ class bt {
|
|
|
1114
1140
|
const t = e - this.fpsStartTime;
|
|
1115
1141
|
t >= 1e3 && (this.currentFPS = this.frameCount / (t / 1e3), this.frameCount = 0, this.fpsStartTime = e), this.onSimulate(), this.onRender(), this.sceneManager.render();
|
|
1116
1142
|
});
|
|
1117
|
-
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 /
|
|
1143
|
+
this.sceneManager = e, this.onSimulate = s, this.onRender = t, this.frameInterval = 1e3 / i;
|
|
1118
1144
|
}
|
|
1119
1145
|
/**
|
|
1120
1146
|
* Starts the animation loop
|
|
@@ -1191,10 +1217,10 @@ class vt {
|
|
|
1191
1217
|
{ colors: ["#2d1a1a", "#1a0a0a", "#0f0505"] }
|
|
1192
1218
|
// -z
|
|
1193
1219
|
];
|
|
1194
|
-
for (const
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
const a =
|
|
1220
|
+
for (const i of t) {
|
|
1221
|
+
const o = document.createElement("canvas");
|
|
1222
|
+
o.width = 256, o.height = 256;
|
|
1223
|
+
const a = o.getContext("2d"), r = a.createRadialGradient(
|
|
1198
1224
|
256 / 2,
|
|
1199
1225
|
256 / 2,
|
|
1200
1226
|
0,
|
|
@@ -1202,17 +1228,17 @@ class vt {
|
|
|
1202
1228
|
256 / 2,
|
|
1203
1229
|
256 * 0.8
|
|
1204
1230
|
);
|
|
1205
|
-
r.addColorStop(0,
|
|
1231
|
+
r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), a.fillStyle = r, a.fillRect(0, 0, 256, 256);
|
|
1206
1232
|
const c = a.getImageData(0, 0, 256, 256);
|
|
1207
1233
|
for (let h = 0; h < c.data.length; h += 4) {
|
|
1208
1234
|
const p = (Math.random() - 0.5) * 5;
|
|
1209
1235
|
c.data[h] = Math.min(255, Math.max(0, c.data[h] + p)), c.data[h + 1] = Math.min(255, Math.max(0, c.data[h + 1] + p)), c.data[h + 2] = Math.min(255, Math.max(0, c.data[h + 2] + p));
|
|
1210
1236
|
}
|
|
1211
|
-
a.putImageData(c, 0, 0), s.push(
|
|
1237
|
+
a.putImageData(c, 0, 0), s.push(o);
|
|
1212
1238
|
}
|
|
1213
|
-
this.envMap = new y.CubeTexture(s.map((
|
|
1214
|
-
const
|
|
1215
|
-
return
|
|
1239
|
+
this.envMap = new y.CubeTexture(s.map((i) => {
|
|
1240
|
+
const o = new Image();
|
|
1241
|
+
return o.src = i.toDataURL(), o;
|
|
1216
1242
|
})), this.envMap.needsUpdate = !0;
|
|
1217
1243
|
}
|
|
1218
1244
|
/**
|
|
@@ -1229,9 +1255,9 @@ class vt {
|
|
|
1229
1255
|
const t = "glass-single";
|
|
1230
1256
|
if (this.materialCache.has(t))
|
|
1231
1257
|
return this.materialCache.get(t).clone();
|
|
1232
|
-
const
|
|
1258
|
+
const i = new y.Color(16750950), o = new y.ShaderMaterial({
|
|
1233
1259
|
uniforms: {
|
|
1234
|
-
uColor: { value:
|
|
1260
|
+
uColor: { value: i },
|
|
1235
1261
|
uEnvMap: { value: this.envMap },
|
|
1236
1262
|
uGlowColor: { value: new y.Color(16777215) },
|
|
1237
1263
|
uGlowIntensity: { value: 0.8 },
|
|
@@ -1303,7 +1329,7 @@ class vt {
|
|
|
1303
1329
|
depthWrite: !0,
|
|
1304
1330
|
blending: y.NormalBlending
|
|
1305
1331
|
});
|
|
1306
|
-
return this.materialCache.set(t,
|
|
1332
|
+
return this.materialCache.set(t, o), o.clone();
|
|
1307
1333
|
}
|
|
1308
1334
|
/**
|
|
1309
1335
|
* Creates material for edges (light color for dark background)
|
|
@@ -1332,10 +1358,10 @@ class vt {
|
|
|
1332
1358
|
* Creates a sprite material for labels (light text for dark background)
|
|
1333
1359
|
*/
|
|
1334
1360
|
createLabelMaterial(e, s = 24) {
|
|
1335
|
-
const t = document.createElement("canvas"),
|
|
1336
|
-
|
|
1337
|
-
const a =
|
|
1338
|
-
t.width = Math.max(128, a + 24), t.height = s + 20,
|
|
1361
|
+
const t = document.createElement("canvas"), i = t.getContext("2d");
|
|
1362
|
+
i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
|
|
1363
|
+
const a = i.measureText(e).width;
|
|
1364
|
+
t.width = Math.max(128, a + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
|
|
1339
1365
|
const r = new y.CanvasTexture(t);
|
|
1340
1366
|
return r.needsUpdate = !0, new y.SpriteMaterial({
|
|
1341
1367
|
map: r,
|
|
@@ -1358,13 +1384,13 @@ class vt {
|
|
|
1358
1384
|
}
|
|
1359
1385
|
}
|
|
1360
1386
|
class Mt {
|
|
1361
|
-
constructor(e, s = 2, t = [32, 16, 8],
|
|
1387
|
+
constructor(e, s = 2, t = [32, 16, 8], i = 16750950) {
|
|
1362
1388
|
l(this, "materialFactory");
|
|
1363
1389
|
l(this, "geometryCache", /* @__PURE__ */ new Map());
|
|
1364
1390
|
l(this, "nodeRadius");
|
|
1365
1391
|
l(this, "lodSegments");
|
|
1366
1392
|
l(this, "defaultNodeColor");
|
|
1367
|
-
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.defaultNodeColor =
|
|
1393
|
+
this.materialFactory = e, this.nodeRadius = s, this.lodSegments = t, this.defaultNodeColor = i, this.initGeometryCache();
|
|
1368
1394
|
}
|
|
1369
1395
|
/**
|
|
1370
1396
|
* Pre-create geometries for each LOD level
|
|
@@ -1391,9 +1417,9 @@ class Mt {
|
|
|
1391
1417
|
createNode(e, s = 0) {
|
|
1392
1418
|
const t = new y.Group();
|
|
1393
1419
|
t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
|
|
1394
|
-
const
|
|
1420
|
+
const i = this.getGeometry(s), o = this.materialFactory.createGlassMaterial(
|
|
1395
1421
|
e.color ?? this.defaultNodeColor
|
|
1396
|
-
), a = new y.Mesh(
|
|
1422
|
+
), a = new y.Mesh(i, o);
|
|
1397
1423
|
a.castShadow = !0, a.receiveShadow = !0, t.add(a);
|
|
1398
1424
|
const r = this.materialFactory.createLabelMaterial(e.label), c = new y.Sprite(r);
|
|
1399
1425
|
return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
|
|
@@ -1467,14 +1493,14 @@ class wt {
|
|
|
1467
1493
|
/**
|
|
1468
1494
|
* Creates an edge line between two positions
|
|
1469
1495
|
*/
|
|
1470
|
-
createEdge(e, s, t,
|
|
1496
|
+
createEdge(e, s, t, i, o) {
|
|
1471
1497
|
const a = new y.BufferGeometry(), r = new Float32Array([
|
|
1472
|
-
o.x,
|
|
1473
|
-
o.y,
|
|
1474
|
-
o.z,
|
|
1475
1498
|
i.x,
|
|
1476
1499
|
i.y,
|
|
1477
|
-
i.z
|
|
1500
|
+
i.z,
|
|
1501
|
+
o.x,
|
|
1502
|
+
o.y,
|
|
1503
|
+
o.z
|
|
1478
1504
|
]);
|
|
1479
1505
|
a.setAttribute("position", new y.BufferAttribute(r, 3));
|
|
1480
1506
|
const c = this.getDefaultMaterial().clone(), h = new y.Line(a, c);
|
|
@@ -1506,8 +1532,8 @@ class wt {
|
|
|
1506
1532
|
* Updates an edge's positions
|
|
1507
1533
|
*/
|
|
1508
1534
|
updateEdgePositions(e, s, t) {
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1535
|
+
const i = e.line.geometry.attributes.position, o = i.array;
|
|
1536
|
+
o[0] = s.x, o[1] = s.y, o[2] = s.z, o[3] = t.x, o[4] = t.y, o[5] = t.z, i.needsUpdate = !0, e.line.geometry.computeBoundingSphere();
|
|
1511
1537
|
}
|
|
1512
1538
|
/**
|
|
1513
1539
|
* Disposes an edge's resources
|
|
@@ -1535,15 +1561,15 @@ class Et {
|
|
|
1535
1561
|
getLODLevel(e) {
|
|
1536
1562
|
if (!this.enabled)
|
|
1537
1563
|
return X.HIGH;
|
|
1538
|
-
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y,
|
|
1539
|
-
return
|
|
1564
|
+
const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, o = Math.sqrt(s * s + t * t + i * i);
|
|
1565
|
+
return o < this.lodDistances[0] ? X.HIGH : o < this.lodDistances[1] ? X.MEDIUM : X.LOW;
|
|
1540
1566
|
}
|
|
1541
1567
|
/**
|
|
1542
1568
|
* Checks if a node should be visible based on distance
|
|
1543
1569
|
*/
|
|
1544
1570
|
shouldRenderNode(e, s = 500) {
|
|
1545
|
-
const t = e.x - this.camera.position.x,
|
|
1546
|
-
return Math.sqrt(t * t +
|
|
1571
|
+
const t = e.x - this.camera.position.x, i = e.y - this.camera.position.y, o = e.z - this.camera.position.z;
|
|
1572
|
+
return Math.sqrt(t * t + i * i + o * o) < s;
|
|
1547
1573
|
}
|
|
1548
1574
|
/**
|
|
1549
1575
|
* Sets the LOD distances
|
|
@@ -1599,14 +1625,14 @@ class Ct {
|
|
|
1599
1625
|
*/
|
|
1600
1626
|
isLineVisible(e, s) {
|
|
1601
1627
|
if (!this.enabled) return !0;
|
|
1602
|
-
const t = new y.Vector3(e.x, e.y, e.z),
|
|
1603
|
-
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(
|
|
1628
|
+
const t = new y.Vector3(e.x, e.y, e.z), i = new y.Vector3(s.x, s.y, s.z);
|
|
1629
|
+
if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
|
|
1604
1630
|
return !0;
|
|
1605
|
-
const
|
|
1631
|
+
const o = new y.Vector3(
|
|
1606
1632
|
(e.x + s.x) / 2,
|
|
1607
1633
|
(e.y + s.y) / 2,
|
|
1608
1634
|
(e.z + s.z) / 2
|
|
1609
|
-
), a =
|
|
1635
|
+
), a = o.distanceTo(t), r = new y.Sphere(o, a);
|
|
1610
1636
|
return this.frustum.intersectsSphere(r);
|
|
1611
1637
|
}
|
|
1612
1638
|
/**
|
|
@@ -1689,23 +1715,23 @@ class Nt {
|
|
|
1689
1715
|
this.hoveredEdgeKey !== null && this.onEdgeHover && (this.hoveredEdgeKey = null, this.onEdgeHover(null)), this.container.style.cursor = "pointer";
|
|
1690
1716
|
return;
|
|
1691
1717
|
}
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1718
|
+
const i = this.getIntersectedEdge(e), o = i ? `${i.edge.source}-${i.edge.target}` : null;
|
|
1719
|
+
o !== this.hoveredEdgeKey && (this.hoveredEdgeKey = o, this.onEdgeHover && this.onEdgeHover(i)), this.container.style.cursor = i ? "pointer" : "default";
|
|
1694
1720
|
}
|
|
1695
1721
|
/**
|
|
1696
1722
|
* Gets the intersected node from a mouse event
|
|
1697
1723
|
*/
|
|
1698
1724
|
getIntersectedNode(e) {
|
|
1699
|
-
var
|
|
1725
|
+
var i;
|
|
1700
1726
|
const s = this.container.getBoundingClientRect();
|
|
1701
1727
|
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);
|
|
1702
1728
|
const t = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1703
1729
|
if (t.length > 0) {
|
|
1704
|
-
let
|
|
1705
|
-
for (;
|
|
1706
|
-
if ((
|
|
1707
|
-
return
|
|
1708
|
-
|
|
1730
|
+
let o = t[0].object;
|
|
1731
|
+
for (; o; ) {
|
|
1732
|
+
if ((i = o.userData) != null && i.nodeData)
|
|
1733
|
+
return o.userData.nodeData;
|
|
1734
|
+
o = o.parent;
|
|
1709
1735
|
}
|
|
1710
1736
|
}
|
|
1711
1737
|
return null;
|
|
@@ -1718,13 +1744,13 @@ class Nt {
|
|
|
1718
1744
|
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);
|
|
1719
1745
|
const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
|
|
1720
1746
|
if (t.length > 0) {
|
|
1721
|
-
const
|
|
1722
|
-
if (
|
|
1747
|
+
const i = t[0].object, o = i.userData;
|
|
1748
|
+
if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode))
|
|
1723
1749
|
return {
|
|
1724
|
-
edge:
|
|
1725
|
-
sourceNode:
|
|
1726
|
-
targetNode:
|
|
1727
|
-
edgeLine:
|
|
1750
|
+
edge: o.edge,
|
|
1751
|
+
sourceNode: o.sourceNode,
|
|
1752
|
+
targetNode: o.targetNode,
|
|
1753
|
+
edgeLine: i
|
|
1728
1754
|
};
|
|
1729
1755
|
}
|
|
1730
1756
|
return null;
|
|
@@ -1733,14 +1759,14 @@ class Nt {
|
|
|
1733
1759
|
* Performs a raycast and returns the intersected node ID
|
|
1734
1760
|
*/
|
|
1735
1761
|
getIntersectedNodeId(e, s) {
|
|
1736
|
-
var
|
|
1762
|
+
var o;
|
|
1737
1763
|
const t = this.container.getBoundingClientRect();
|
|
1738
1764
|
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);
|
|
1739
|
-
const
|
|
1740
|
-
if (
|
|
1741
|
-
let a =
|
|
1765
|
+
const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
|
|
1766
|
+
if (i.length > 0) {
|
|
1767
|
+
let a = i[0].object;
|
|
1742
1768
|
for (; a; ) {
|
|
1743
|
-
if ((
|
|
1769
|
+
if ((o = a.userData) != null && o.nodeId)
|
|
1744
1770
|
return a.userData.nodeId;
|
|
1745
1771
|
a = a.parent;
|
|
1746
1772
|
}
|
|
@@ -1824,10 +1850,10 @@ class St {
|
|
|
1824
1850
|
this.currentNodeId = e.id;
|
|
1825
1851
|
let t;
|
|
1826
1852
|
this.panelTemplate ? t = this.panelTemplate(e, s) : t = this.generateDefaultContent(e, s), this.panel.innerHTML = t;
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1853
|
+
const i = this.panel.querySelector('[data-action="expand"]'), o = this.panel.querySelector("[data-depth-select]");
|
|
1854
|
+
i && this.onExpand && i.addEventListener("click", () => {
|
|
1829
1855
|
if (this.currentNodeId) {
|
|
1830
|
-
const r =
|
|
1856
|
+
const r = o ? parseInt(o.value, 10) : 1;
|
|
1831
1857
|
this.onExpand(this.currentNodeId, r);
|
|
1832
1858
|
}
|
|
1833
1859
|
});
|
|
@@ -1986,7 +2012,7 @@ class St {
|
|
|
1986
2012
|
<div class="neighbors-section">
|
|
1987
2013
|
<div class="neighbors-title">Connected To</div>
|
|
1988
2014
|
${s.slice(0, 5).map(
|
|
1989
|
-
(
|
|
2015
|
+
(i) => `<span class="neighbor-chip">${this.escapeHtml(i.label)}</span>`
|
|
1990
2016
|
).join("")}
|
|
1991
2017
|
${s.length > 5 ? `<span class="neighbor-chip">+${s.length - 5} more</span>` : ""}
|
|
1992
2018
|
</div>
|
|
@@ -2104,10 +2130,10 @@ class zt {
|
|
|
2104
2130
|
show(e, s, t) {
|
|
2105
2131
|
if (!this.panel) return;
|
|
2106
2132
|
this.currentEdgeKey = `${e.source}-${e.target}`;
|
|
2107
|
-
let
|
|
2108
|
-
this.panelTemplate ?
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2133
|
+
let i;
|
|
2134
|
+
this.panelTemplate ? i = this.panelTemplate(e, s, t) : i = this.generateDefaultContent(e, s, t), this.panel.innerHTML = i;
|
|
2135
|
+
const o = this.panel.querySelector('[data-action="close"]');
|
|
2136
|
+
o && o.addEventListener("click", () => {
|
|
2111
2137
|
this.hide(), this.onClose && this.onClose();
|
|
2112
2138
|
});
|
|
2113
2139
|
const a = this.panel.querySelector('[data-action="goto-source"]');
|
|
@@ -2123,7 +2149,7 @@ class zt {
|
|
|
2123
2149
|
* Generates default panel content
|
|
2124
2150
|
*/
|
|
2125
2151
|
generateDefaultContent(e, s, t) {
|
|
2126
|
-
const
|
|
2152
|
+
const i = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", o = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = e.relationship || "connected to";
|
|
2127
2153
|
return `
|
|
2128
2154
|
<style>
|
|
2129
2155
|
.force-graph-edge-panel .panel-header {
|
|
@@ -2233,7 +2259,7 @@ class zt {
|
|
|
2233
2259
|
<div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
|
|
2234
2260
|
<div class="node-type">Source</div>
|
|
2235
2261
|
<div class="node-card-header">
|
|
2236
|
-
<span class="color-dot" style="background: ${
|
|
2262
|
+
<span class="color-dot" style="background: ${i}; box-shadow: 0 0 8px ${i}80;"></span>
|
|
2237
2263
|
<span class="node-label">${this.escapeHtml(s.label)}</span>
|
|
2238
2264
|
</div>
|
|
2239
2265
|
</div>
|
|
@@ -2243,7 +2269,7 @@ class zt {
|
|
|
2243
2269
|
<div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
|
|
2244
2270
|
<div class="node-type">Target</div>
|
|
2245
2271
|
<div class="node-card-header">
|
|
2246
|
-
<span class="color-dot" style="background: ${
|
|
2272
|
+
<span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
|
|
2247
2273
|
<span class="node-label">${this.escapeHtml(t.label)}</span>
|
|
2248
2274
|
</div>
|
|
2249
2275
|
</div>
|
|
@@ -2331,14 +2357,14 @@ class Tt {
|
|
|
2331
2357
|
*/
|
|
2332
2358
|
positionTooltip(e, s) {
|
|
2333
2359
|
if (!this.tooltip) return;
|
|
2334
|
-
const t = this.tooltip.getBoundingClientRect(),
|
|
2360
|
+
const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, o = window.innerHeight;
|
|
2335
2361
|
let a = e + 15, r = s + 15;
|
|
2336
|
-
a + t.width >
|
|
2362
|
+
a + t.width > i - 10 && (a = e - t.width - 15), r + t.height > o - 10 && (r = s - t.height - 15), a < 10 && (a = 10), r < 10 && (r = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${r}px`;
|
|
2337
2363
|
}
|
|
2338
2364
|
/**
|
|
2339
2365
|
* Shows the tooltip with edge info
|
|
2340
2366
|
*/
|
|
2341
|
-
show(e, s, t,
|
|
2367
|
+
show(e, s, t, i, o) {
|
|
2342
2368
|
if (!this.tooltip) return;
|
|
2343
2369
|
const a = e.relationship || "connected to";
|
|
2344
2370
|
this.tooltip.innerHTML = `
|
|
@@ -2353,7 +2379,7 @@ class Tt {
|
|
|
2353
2379
|
<span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
|
|
2354
2380
|
</div>
|
|
2355
2381
|
</div>
|
|
2356
|
-
`, this.positionTooltip(
|
|
2382
|
+
`, this.positionTooltip(i, o), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
|
|
2357
2383
|
}
|
|
2358
2384
|
/**
|
|
2359
2385
|
* Updates tooltip position (called externally on mouse move)
|
|
@@ -2530,25 +2556,25 @@ class kt {
|
|
|
2530
2556
|
this.searchResults.innerHTML = '<div class="f3d-no-results">No results found</div>', this.searchResults.style.display = "block";
|
|
2531
2557
|
return;
|
|
2532
2558
|
}
|
|
2533
|
-
let
|
|
2534
|
-
s.length > 0 && (
|
|
2535
|
-
const a =
|
|
2536
|
-
|
|
2537
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2538
|
-
<div class="f3d-result-label">${this.escapeHtml(
|
|
2559
|
+
let i = "";
|
|
2560
|
+
s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((o) => {
|
|
2561
|
+
const a = o.type || "Node";
|
|
2562
|
+
i += `
|
|
2563
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.id)}">
|
|
2564
|
+
<div class="f3d-result-label">${this.escapeHtml(o.label)}</div>
|
|
2539
2565
|
<div class="f3d-result-type">${this.escapeHtml(a)}</div>
|
|
2540
2566
|
</div>
|
|
2541
2567
|
`;
|
|
2542
|
-
}), s.length > 10 && (
|
|
2543
|
-
|
|
2544
|
-
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(
|
|
2568
|
+
}), 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: o, sourceNode: a, targetNode: r }) => {
|
|
2569
|
+
i += `
|
|
2570
|
+
<div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.source)}">
|
|
2545
2571
|
<div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(r.label)}</div>
|
|
2546
|
-
<div class="f3d-result-relationship">${this.escapeHtml(
|
|
2572
|
+
<div class="f3d-result-relationship">${this.escapeHtml(o.relationship || "connected")}</div>
|
|
2547
2573
|
</div>
|
|
2548
2574
|
`;
|
|
2549
|
-
}), t.length > 5 && (
|
|
2550
|
-
|
|
2551
|
-
const a =
|
|
2575
|
+
}), 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((o) => {
|
|
2576
|
+
o.addEventListener("click", () => {
|
|
2577
|
+
const a = o.dataset.nodeId;
|
|
2552
2578
|
a && (this.onResultClick(a), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
|
|
2553
2579
|
});
|
|
2554
2580
|
});
|
|
@@ -2706,31 +2732,31 @@ class It {
|
|
|
2706
2732
|
setupInteractions() {
|
|
2707
2733
|
this.canvas.addEventListener("wheel", (e) => {
|
|
2708
2734
|
e.preventDefault();
|
|
2709
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2710
|
-
this.transform.x = t - (t - this.transform.x) * r, this.transform.y =
|
|
2735
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1, a = Math.max(0.1, Math.min(5, this.transform.scale * o)), r = a / this.transform.scale;
|
|
2736
|
+
this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale = a;
|
|
2711
2737
|
}), this.canvas.addEventListener("mousedown", (e) => {
|
|
2712
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2738
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i), a = this.findNodeAt(o.x, o.y);
|
|
2713
2739
|
this.dragStartPos = { x: e.clientX, y: e.clientY }, a ? (this.isDragging = !0, this.draggedNode = a, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2714
2740
|
}), this.canvas.addEventListener("mousemove", (e) => {
|
|
2715
|
-
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left,
|
|
2741
|
+
const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i);
|
|
2716
2742
|
if (this.isDragging && this.draggedNode)
|
|
2717
|
-
this.draggedNode.x =
|
|
2743
|
+
this.draggedNode.x = o.x, this.draggedNode.y = o.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
|
|
2718
2744
|
else if (this.isPanning) {
|
|
2719
2745
|
const a = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
|
|
2720
2746
|
this.transform.x += a, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
|
|
2721
2747
|
} else {
|
|
2722
|
-
const a = this.findNodeAt(
|
|
2748
|
+
const a = this.findNodeAt(o.x, o.y);
|
|
2723
2749
|
if (a !== this.hoveredNode && (this.hoveredNode = a, a ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(a.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !a) {
|
|
2724
|
-
const r = this.findEdgeAt(
|
|
2750
|
+
const r = this.findEdgeAt(o.x, o.y);
|
|
2725
2751
|
r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
|
|
2726
2752
|
}
|
|
2727
2753
|
}
|
|
2728
2754
|
}), this.canvas.addEventListener("mouseup", (e) => {
|
|
2729
|
-
const s = Math.abs(e.clientX - this.dragStartPos.x), t = Math.abs(e.clientY - this.dragStartPos.y),
|
|
2755
|
+
const s = Math.abs(e.clientX - this.dragStartPos.x), t = Math.abs(e.clientY - this.dragStartPos.y), i = s < 5 && t < 5;
|
|
2730
2756
|
if (this.isDragging && this.draggedNode)
|
|
2731
|
-
|
|
2732
|
-
else if (
|
|
2733
|
-
const
|
|
2757
|
+
i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
|
|
2758
|
+
else if (i) {
|
|
2759
|
+
const o = this.canvas.getBoundingClientRect(), a = this.screenToWorld(e.clientX - o.left, e.clientY - o.top), r = this.findEdgeAt(a.x, a.y);
|
|
2734
2760
|
r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
|
|
2735
2761
|
}
|
|
2736
2762
|
this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
|
|
@@ -2746,21 +2772,21 @@ class It {
|
|
|
2746
2772
|
}
|
|
2747
2773
|
findNodeAt(e, s) {
|
|
2748
2774
|
for (const t of this.nodes.values()) {
|
|
2749
|
-
const
|
|
2750
|
-
if (Math.sqrt(
|
|
2775
|
+
const i = t.x - e, o = t.y - s;
|
|
2776
|
+
if (Math.sqrt(i * i + o * o) < t.radius)
|
|
2751
2777
|
return t;
|
|
2752
2778
|
}
|
|
2753
2779
|
return null;
|
|
2754
2780
|
}
|
|
2755
2781
|
findEdgeAt(e, s) {
|
|
2756
|
-
for (const
|
|
2757
|
-
const
|
|
2758
|
-
if (!
|
|
2759
|
-
const r = a.x -
|
|
2782
|
+
for (const i of this.edges) {
|
|
2783
|
+
const o = this.nodes.get(i.source), a = this.nodes.get(i.target);
|
|
2784
|
+
if (!o || !a) continue;
|
|
2785
|
+
const r = a.x - o.x, c = a.y - o.y, h = r * r + c * c;
|
|
2760
2786
|
if (h === 0) continue;
|
|
2761
|
-
const p = Math.max(0, Math.min(1, ((e -
|
|
2787
|
+
const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * c) / h)), x = o.x + p * r, m = o.y + p * c, u = e - x, f = s - m;
|
|
2762
2788
|
if (Math.sqrt(u * u + f * f) < 12)
|
|
2763
|
-
return
|
|
2789
|
+
return i;
|
|
2764
2790
|
}
|
|
2765
2791
|
return null;
|
|
2766
2792
|
}
|
|
@@ -2773,8 +2799,8 @@ class It {
|
|
|
2773
2799
|
simulate() {
|
|
2774
2800
|
const e = Array.from(this.nodes.values()), s = e.length;
|
|
2775
2801
|
if (s === 0) return;
|
|
2776
|
-
const t = 60,
|
|
2777
|
-
let
|
|
2802
|
+
const t = 60, i = 5;
|
|
2803
|
+
let o = 0;
|
|
2778
2804
|
for (let r = 0; r < s; r++)
|
|
2779
2805
|
for (let c = r + 1; c < s; c++) {
|
|
2780
2806
|
const h = e[r], p = e[c];
|
|
@@ -2798,35 +2824,35 @@ class It {
|
|
|
2798
2824
|
if (this.draggedNode === r) continue;
|
|
2799
2825
|
r.vx *= this.options.damping, r.vy *= this.options.damping;
|
|
2800
2826
|
const c = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
|
|
2801
|
-
c >
|
|
2827
|
+
c > i && (r.vx = r.vx / c * i, r.vy = r.vy / c * i), r.x += r.vx, r.y += r.vy, o += r.vx * r.vx + r.vy * r.vy;
|
|
2802
2828
|
}
|
|
2803
|
-
|
|
2829
|
+
o < 0.01 && !this.draggedNode && (this.isSimulating = !1);
|
|
2804
2830
|
}
|
|
2805
2831
|
render() {
|
|
2806
2832
|
const e = this.ctx, s = this.canvas.width / (window.devicePixelRatio || 1), t = this.canvas.height / (window.devicePixelRatio || 1);
|
|
2807
2833
|
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();
|
|
2808
2834
|
}
|
|
2809
2835
|
renderGrid(e, s) {
|
|
2810
|
-
const t = this.ctx,
|
|
2836
|
+
const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, a = this.transform.x % i, r = this.transform.y % i;
|
|
2811
2837
|
t.fillStyle = this.options.gridColor;
|
|
2812
|
-
for (let c = a; c < e; c +=
|
|
2813
|
-
for (let h = r; h < s; h +=
|
|
2814
|
-
t.beginPath(), t.arc(c, h,
|
|
2838
|
+
for (let c = a; c < e; c += i)
|
|
2839
|
+
for (let h = r; h < s; h += i)
|
|
2840
|
+
t.beginPath(), t.arc(c, h, o, 0, Math.PI * 2), t.fill();
|
|
2815
2841
|
}
|
|
2816
2842
|
renderEdges() {
|
|
2817
2843
|
const e = this.ctx;
|
|
2818
2844
|
for (const s of this.edges) {
|
|
2819
|
-
const t = this.nodes.get(s.source),
|
|
2820
|
-
if (!t || !
|
|
2821
|
-
const
|
|
2822
|
-
|
|
2845
|
+
const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
|
|
2846
|
+
if (!t || !i) continue;
|
|
2847
|
+
const o = s === this.hoveredEdge, a = e.createLinearGradient(t.x, t.y, i.x, i.y);
|
|
2848
|
+
o ? (a.addColorStop(0, "rgba(255, 153, 102, 0.8)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), a.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (a.addColorStop(0, "rgba(255, 153, 102, 0.3)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), a.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = a, e.stroke();
|
|
2823
2849
|
}
|
|
2824
2850
|
}
|
|
2825
2851
|
renderNodes() {
|
|
2826
2852
|
const e = this.ctx;
|
|
2827
2853
|
for (const s of this.nodes.values()) {
|
|
2828
|
-
const t = s === this.hoveredNode,
|
|
2829
|
-
if (t ||
|
|
2854
|
+
const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), a = s.radius * (t ? 1.1 : 1);
|
|
2855
|
+
if (t || i || o) {
|
|
2830
2856
|
const f = e.createRadialGradient(
|
|
2831
2857
|
s.x,
|
|
2832
2858
|
s.y,
|
|
@@ -2834,7 +2860,7 @@ class It {
|
|
|
2834
2860
|
s.x,
|
|
2835
2861
|
s.y,
|
|
2836
2862
|
a * 2
|
|
2837
|
-
), b = t ||
|
|
2863
|
+
), b = t || i ? 0.4 : 0.25;
|
|
2838
2864
|
f.addColorStop(0, `rgba(255, 153, 102, ${b})`), f.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = f, e.beginPath(), e.arc(s.x, s.y, a * 2, 0, Math.PI * 2), e.fill();
|
|
2839
2865
|
}
|
|
2840
2866
|
const r = e.createRadialGradient(
|
|
@@ -2861,14 +2887,14 @@ class It {
|
|
|
2861
2887
|
*/
|
|
2862
2888
|
setData(e) {
|
|
2863
2889
|
this.nodes.clear(), this.edges = [], this.nodeIdToIndex.clear(), e.nodes.forEach((s, t) => {
|
|
2864
|
-
const
|
|
2890
|
+
const i = s.position || {
|
|
2865
2891
|
x: (Math.random() - 0.5) * 300,
|
|
2866
2892
|
y: (Math.random() - 0.5) * 300
|
|
2867
|
-
},
|
|
2893
|
+
}, o = {
|
|
2868
2894
|
id: s.id,
|
|
2869
2895
|
label: s.label,
|
|
2870
|
-
x:
|
|
2871
|
-
y:
|
|
2896
|
+
x: i.x,
|
|
2897
|
+
y: i.y,
|
|
2872
2898
|
vx: 0,
|
|
2873
2899
|
vy: 0,
|
|
2874
2900
|
color: s.color || 16750950,
|
|
@@ -2876,7 +2902,7 @@ class It {
|
|
|
2876
2902
|
radius: this.options.nodeRadius,
|
|
2877
2903
|
data: s
|
|
2878
2904
|
};
|
|
2879
|
-
this.nodes.set(s.id,
|
|
2905
|
+
this.nodes.set(s.id, o), this.nodeIdToIndex.set(s.id, t);
|
|
2880
2906
|
}), this.edges = e.edges.map((s) => ({
|
|
2881
2907
|
source: s.source,
|
|
2882
2908
|
target: s.target,
|
|
@@ -2952,9 +2978,9 @@ class It {
|
|
|
2952
2978
|
focusOnNode(e) {
|
|
2953
2979
|
const s = this.nodes.get(e);
|
|
2954
2980
|
if (!s) return;
|
|
2955
|
-
const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale,
|
|
2981
|
+
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, o = this.transform.x, a = this.transform.y, r = 500, c = performance.now(), h = () => {
|
|
2956
2982
|
const p = performance.now() - c, x = Math.min(p / r, 1), m = 1 - Math.pow(1 - x, 3);
|
|
2957
|
-
this.transform.x =
|
|
2983
|
+
this.transform.x = o + (t - o) * m, this.transform.y = a + (i - a) * m, x < 1 ? requestAnimationFrame(h) : this.selectedNode = s;
|
|
2958
2984
|
};
|
|
2959
2985
|
h();
|
|
2960
2986
|
}
|
|
@@ -2963,8 +2989,8 @@ class It {
|
|
|
2963
2989
|
*/
|
|
2964
2990
|
syncFrom3D(e) {
|
|
2965
2991
|
e.forEach((s, t) => {
|
|
2966
|
-
const
|
|
2967
|
-
|
|
2992
|
+
const i = this.nodes.get(t);
|
|
2993
|
+
i && (i.x = s.position.x * 3, i.y = s.position.y * 3, i.vx = 0, i.vy = 0);
|
|
2968
2994
|
}), this.isSimulating = !1;
|
|
2969
2995
|
}
|
|
2970
2996
|
/**
|
|
@@ -3106,8 +3132,8 @@ class Ht {
|
|
|
3106
3132
|
onEdgeHover(e) {
|
|
3107
3133
|
if (e) {
|
|
3108
3134
|
this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
|
|
3109
|
-
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2,
|
|
3110
|
-
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t,
|
|
3135
|
+
const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, i = s.top + s.height / 2;
|
|
3136
|
+
this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, i), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
|
|
3111
3137
|
} else
|
|
3112
3138
|
this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
|
|
3113
3139
|
}
|
|
@@ -3116,7 +3142,7 @@ class Ht {
|
|
|
3116
3142
|
*/
|
|
3117
3143
|
onNodeClick(e) {
|
|
3118
3144
|
this.edgePanelManager.hide();
|
|
3119
|
-
const t = this.edgeManager.getNeighborIds(e.id).map((
|
|
3145
|
+
const t = this.edgeManager.getNeighborIds(e.id).map((i) => this.nodeManager.getNode(i)).filter((i) => i !== void 0);
|
|
3120
3146
|
this.options.showPanel !== !1 && this.panelManager.show(e, t), this.options.onNodeClick && this.options.onNodeClick(e), this.emit("nodeClick", e);
|
|
3121
3147
|
}
|
|
3122
3148
|
/**
|
|
@@ -3151,20 +3177,20 @@ class Ht {
|
|
|
3151
3177
|
*/
|
|
3152
3178
|
setData(e) {
|
|
3153
3179
|
var a;
|
|
3154
|
-
const s = (a = e.data) != null && a.nodes ? e.data : e,
|
|
3180
|
+
const s = (a = e.data) != null && a.nodes ? e.data : e, i = (s.edges || s.links || []).map((r) => ({
|
|
3155
3181
|
...r,
|
|
3156
3182
|
relationship: r.relationship || r.label || "related_to"
|
|
3157
|
-
})),
|
|
3183
|
+
})), o = {
|
|
3158
3184
|
nodes: s.nodes || [],
|
|
3159
|
-
edges:
|
|
3185
|
+
edges: i
|
|
3160
3186
|
};
|
|
3161
|
-
if (this.graphData =
|
|
3162
|
-
te.setExpectedNodeCount(
|
|
3163
|
-
for (const r of
|
|
3187
|
+
if (this.graphData = o, this.edgeManager.clear(), this.nodeManager.clear(), o.nodes && Array.isArray(o.nodes)) {
|
|
3188
|
+
te.setExpectedNodeCount(o.nodes.length);
|
|
3189
|
+
for (const r of o.nodes)
|
|
3164
3190
|
this.addNode(r);
|
|
3165
3191
|
}
|
|
3166
|
-
if (
|
|
3167
|
-
for (const r of
|
|
3192
|
+
if (o.edges && Array.isArray(o.edges))
|
|
3193
|
+
for (const r of o.edges)
|
|
3168
3194
|
this.addEdge(r);
|
|
3169
3195
|
this.graphEngine = new Le(
|
|
3170
3196
|
this.nodeManager.getAllNodes(),
|
|
@@ -3176,7 +3202,7 @@ class Ht {
|
|
|
3176
3202
|
useBarnesHut: this.options.useBarnesHut,
|
|
3177
3203
|
barnesHutTheta: this.options.barnesHutTheta
|
|
3178
3204
|
}
|
|
3179
|
-
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(
|
|
3205
|
+
), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o);
|
|
3180
3206
|
}
|
|
3181
3207
|
/**
|
|
3182
3208
|
* Adds a node to the graph
|
|
@@ -3230,20 +3256,20 @@ class Ht {
|
|
|
3230
3256
|
* @param fetchFn - Optional fetch function to override the default
|
|
3231
3257
|
*/
|
|
3232
3258
|
async expandNode(e, s = 1, t) {
|
|
3233
|
-
const
|
|
3234
|
-
if (!
|
|
3259
|
+
const i = t ?? this.options.onExpand;
|
|
3260
|
+
if (!i)
|
|
3235
3261
|
return console.warn("[ForceGraph3D] No expand callback provided"), !1;
|
|
3236
3262
|
try {
|
|
3237
|
-
const
|
|
3238
|
-
if (
|
|
3239
|
-
for (const a of
|
|
3263
|
+
const o = await i(e, s);
|
|
3264
|
+
if (o.nodes && Array.isArray(o.nodes))
|
|
3265
|
+
for (const a of o.nodes)
|
|
3240
3266
|
this.addNode(a);
|
|
3241
|
-
if (
|
|
3242
|
-
for (const a of
|
|
3267
|
+
if (o.edges && Array.isArray(o.edges))
|
|
3268
|
+
for (const a of o.edges)
|
|
3243
3269
|
this.addEdge(a);
|
|
3244
|
-
return this.panelManager.hide(), this.emit("expand", e,
|
|
3245
|
-
} catch (
|
|
3246
|
-
return console.error("[ForceGraph3D] Error expanding node:",
|
|
3270
|
+
return this.panelManager.hide(), this.emit("expand", e, o), !0;
|
|
3271
|
+
} catch (o) {
|
|
3272
|
+
return console.error("[ForceGraph3D] Error expanding node:", o), !1;
|
|
3247
3273
|
}
|
|
3248
3274
|
}
|
|
3249
3275
|
/**
|
|
@@ -3290,13 +3316,13 @@ class Ht {
|
|
|
3290
3316
|
console.warn(`[ForceGraph3D] Node "${e}" not found`);
|
|
3291
3317
|
return;
|
|
3292
3318
|
}
|
|
3293
|
-
const
|
|
3294
|
-
x:
|
|
3295
|
-
y:
|
|
3296
|
-
z:
|
|
3297
|
-
}, h = { x:
|
|
3319
|
+
const i = t.position, o = this.sceneManager.camera, a = this.sceneManager.controls, r = o.position.clone().sub(a.target).normalize(), c = {
|
|
3320
|
+
x: i.x + r.x * s,
|
|
3321
|
+
y: i.y + r.y * s,
|
|
3322
|
+
z: i.z + r.z * s
|
|
3323
|
+
}, h = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: a.target.x, y: a.target.y, z: a.target.z }, x = 800, m = performance.now(), u = () => {
|
|
3298
3324
|
const f = performance.now() - m, b = Math.min(f / x, 1), M = 1 - Math.pow(1 - b, 3);
|
|
3299
|
-
|
|
3325
|
+
o.position.x = h.x + (c.x - h.x) * M, o.position.y = h.y + (c.y - h.y) * M, o.position.z = h.z + (c.z - h.z) * M, a.target.x = p.x + (i.x - p.x) * M, a.target.y = p.y + (i.y - p.y) * M, a.target.z = p.z + (i.z - p.z) * M, a.update(), b < 1 && requestAnimationFrame(u);
|
|
3300
3326
|
};
|
|
3301
3327
|
u();
|
|
3302
3328
|
}
|
|
@@ -3305,16 +3331,16 @@ class Ht {
|
|
|
3305
3331
|
* Camera targets the midpoint and zooms out enough to see both nodes
|
|
3306
3332
|
*/
|
|
3307
3333
|
focusOnEdge(e, s, t = 1.5) {
|
|
3308
|
-
const
|
|
3309
|
-
if (!
|
|
3334
|
+
const i = this.nodeManager.getNode(e), o = this.nodeManager.getNode(s);
|
|
3335
|
+
if (!i || !o) {
|
|
3310
3336
|
console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
|
|
3311
3337
|
return;
|
|
3312
3338
|
}
|
|
3313
3339
|
const a = this.sceneManager.camera, r = this.sceneManager.controls, c = {
|
|
3314
|
-
x: (
|
|
3315
|
-
y: (
|
|
3316
|
-
z: (
|
|
3317
|
-
}, h =
|
|
3340
|
+
x: (i.position.x + o.position.x) / 2,
|
|
3341
|
+
y: (i.position.y + o.position.y) / 2,
|
|
3342
|
+
z: (i.position.z + o.position.z) / 2
|
|
3343
|
+
}, h = o.position.x - i.position.x, p = o.position.y - i.position.y, x = o.position.z - i.position.z, m = Math.sqrt(h * h + p * p + x * x), u = Math.max(m * t, 40), f = a.position.clone().sub(r.target).normalize(), b = {
|
|
3318
3344
|
x: c.x + f.x * u,
|
|
3319
3345
|
y: c.y + f.y * u,
|
|
3320
3346
|
z: c.z + f.z * u
|
|
@@ -3343,28 +3369,28 @@ class Ht {
|
|
|
3343
3369
|
searchNodes(e) {
|
|
3344
3370
|
if (!e || e.trim() === "")
|
|
3345
3371
|
return [];
|
|
3346
|
-
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(),
|
|
3347
|
-
return t.forEach((
|
|
3372
|
+
const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
|
|
3373
|
+
return t.forEach((o) => {
|
|
3348
3374
|
var h, p, x;
|
|
3349
|
-
const a = (h =
|
|
3350
|
-
(a || r || c) &&
|
|
3351
|
-
}),
|
|
3375
|
+
const a = (h = o.label) == null ? void 0 : h.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), c = (x = o.type) == null ? void 0 : x.toLowerCase().includes(s);
|
|
3376
|
+
(a || r || c) && i.push(o);
|
|
3377
|
+
}), i;
|
|
3352
3378
|
}
|
|
3353
3379
|
/**
|
|
3354
3380
|
* Searches edges by relationship (case-insensitive)
|
|
3355
3381
|
* @returns Array of matching edges with source/target node info
|
|
3356
3382
|
*/
|
|
3357
3383
|
searchEdges(e) {
|
|
3358
|
-
var
|
|
3384
|
+
var o;
|
|
3359
3385
|
if (!e || e.trim() === "")
|
|
3360
3386
|
return [];
|
|
3361
|
-
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(),
|
|
3387
|
+
const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
|
|
3362
3388
|
for (const a of t)
|
|
3363
|
-
if ((
|
|
3389
|
+
if ((o = a.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
|
|
3364
3390
|
const c = this.nodeManager.getNode(a.source), h = this.nodeManager.getNode(a.target);
|
|
3365
|
-
c && h &&
|
|
3391
|
+
c && h && i.push({ edge: a, sourceNode: c, targetNode: h });
|
|
3366
3392
|
}
|
|
3367
|
-
return
|
|
3393
|
+
return i;
|
|
3368
3394
|
}
|
|
3369
3395
|
/**
|
|
3370
3396
|
* Gets all nodes as an array
|
|
@@ -3406,11 +3432,11 @@ class Ht {
|
|
|
3406
3432
|
},
|
|
3407
3433
|
onEdgeHover: (s, t) => {
|
|
3408
3434
|
if (s && t) {
|
|
3409
|
-
const
|
|
3410
|
-
|
|
3435
|
+
const i = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
|
|
3436
|
+
i && o && this.edgeTooltipManager.show(
|
|
3411
3437
|
s,
|
|
3412
|
-
o,
|
|
3413
3438
|
i,
|
|
3439
|
+
o,
|
|
3414
3440
|
t.clientX,
|
|
3415
3441
|
t.clientY
|
|
3416
3442
|
);
|
|
@@ -3418,8 +3444,8 @@ class Ht {
|
|
|
3418
3444
|
this.edgeTooltipManager.hide();
|
|
3419
3445
|
},
|
|
3420
3446
|
onEdgeClick: (s) => {
|
|
3421
|
-
const t = this.nodeManager.getNode(s.source),
|
|
3422
|
-
t &&
|
|
3447
|
+
const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
|
|
3448
|
+
t && i && this.edgePanelManager.show(s, t, i);
|
|
3423
3449
|
}
|
|
3424
3450
|
}), this.graphData && (this.forceGraph2D.setData(this.graphData), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())))) : (this.forceGraph2D && this.forceGraph2D.hide(), this.sceneManager.renderer.domElement.style.display = "block", this.rendererManager.start()), this.emit("viewChange", e));
|
|
3425
3451
|
}
|
|
@@ -3434,7 +3460,7 @@ class Ht {
|
|
|
3434
3460
|
*/
|
|
3435
3461
|
emit(e, ...s) {
|
|
3436
3462
|
const t = this.eventCallbacks.get(e);
|
|
3437
|
-
t && t.forEach((
|
|
3463
|
+
t && t.forEach((i) => i(...s));
|
|
3438
3464
|
}
|
|
3439
3465
|
/**
|
|
3440
3466
|
* Sets physics parameters for both 3D and 2D views
|
|
@@ -3511,17 +3537,17 @@ class Ht {
|
|
|
3511
3537
|
`, this.container.appendChild(this.devControls);
|
|
3512
3538
|
const e = this.devControls.querySelector("#dev-repulsion"), s = this.devControls.querySelector("#dev-attraction"), t = this.devControls.querySelector("#dev-damping");
|
|
3513
3539
|
e == null || e.addEventListener("input", () => {
|
|
3514
|
-
const
|
|
3515
|
-
this.setPhysicsParams({ repulsionStrength:
|
|
3540
|
+
const i = parseFloat(e.value);
|
|
3541
|
+
this.setPhysicsParams({ repulsionStrength: i }), this.devControls.querySelector("#dev-repulsion-val").textContent = i.toString();
|
|
3516
3542
|
}), s == null || s.addEventListener("input", () => {
|
|
3517
|
-
const
|
|
3518
|
-
this.setPhysicsParams({ attractionStrength:
|
|
3543
|
+
const i = parseFloat(s.value) / 1e3;
|
|
3544
|
+
this.setPhysicsParams({ attractionStrength: i }), this.devControls.querySelector("#dev-attraction-val").textContent = i.toFixed(3);
|
|
3519
3545
|
}), t == null || t.addEventListener("input", () => {
|
|
3520
|
-
const
|
|
3521
|
-
this.setPhysicsParams({ damping:
|
|
3546
|
+
const i = parseFloat(t.value) / 100;
|
|
3547
|
+
this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
|
|
3522
3548
|
}), setInterval(() => {
|
|
3523
|
-
const
|
|
3524
|
-
|
|
3549
|
+
const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
|
|
3550
|
+
i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
|
|
3525
3551
|
}, 500);
|
|
3526
3552
|
}
|
|
3527
3553
|
/**
|
|
@@ -3587,12 +3613,12 @@ const Oe = [
|
|
|
3587
3613
|
];
|
|
3588
3614
|
function Dt(d = 30) {
|
|
3589
3615
|
const e = [], s = [];
|
|
3590
|
-
for (let
|
|
3591
|
-
const
|
|
3616
|
+
for (let i = 0; i < d; i++) {
|
|
3617
|
+
const o = i < Oe.length ? Oe[i] : `Node ${i + 1}`;
|
|
3592
3618
|
e.push({
|
|
3593
|
-
id: `node-${
|
|
3594
|
-
label:
|
|
3595
|
-
color: Fe[
|
|
3619
|
+
id: `node-${i}`,
|
|
3620
|
+
label: o,
|
|
3621
|
+
color: Fe[i % Fe.length],
|
|
3596
3622
|
position: {
|
|
3597
3623
|
x: (Math.random() - 0.5) * 60,
|
|
3598
3624
|
y: (Math.random() - 0.5) * 60,
|
|
@@ -3600,20 +3626,20 @@ function Dt(d = 30) {
|
|
|
3600
3626
|
}
|
|
3601
3627
|
});
|
|
3602
3628
|
}
|
|
3603
|
-
for (let
|
|
3604
|
-
const
|
|
3629
|
+
for (let i = 1; i < d; i++) {
|
|
3630
|
+
const o = Math.floor(Math.random() * i);
|
|
3605
3631
|
s.push({
|
|
3606
|
-
source: `node-${
|
|
3607
|
-
target: `node-${
|
|
3632
|
+
source: `node-${i}`,
|
|
3633
|
+
target: `node-${o}`,
|
|
3608
3634
|
relationship: ee[Math.floor(Math.random() * ee.length)]
|
|
3609
3635
|
});
|
|
3610
3636
|
}
|
|
3611
3637
|
const t = Math.floor(d * 0.5);
|
|
3612
|
-
for (let
|
|
3613
|
-
const
|
|
3638
|
+
for (let i = 0; i < t; i++) {
|
|
3639
|
+
const o = Math.floor(Math.random() * d);
|
|
3614
3640
|
let a = Math.floor(Math.random() * d);
|
|
3615
|
-
|
|
3616
|
-
const r = `node-${
|
|
3641
|
+
o === a && (a = (a + 1) % d);
|
|
3642
|
+
const r = `node-${o}`, c = `node-${a}`;
|
|
3617
3643
|
s.some(
|
|
3618
3644
|
(p) => p.source === r && p.target === c || p.source === c && p.target === r
|
|
3619
3645
|
) || s.push({
|
|
@@ -3625,18 +3651,18 @@ function Dt(d = 30) {
|
|
|
3625
3651
|
return { nodes: e, edges: s };
|
|
3626
3652
|
}
|
|
3627
3653
|
function At(d = 1e3) {
|
|
3628
|
-
const e = [], s = [], t = Math.ceil(d / 50),
|
|
3629
|
-
for (let
|
|
3630
|
-
|
|
3654
|
+
const e = [], s = [], t = Math.ceil(d / 50), i = [];
|
|
3655
|
+
for (let o = 0; o < t; o++)
|
|
3656
|
+
i.push({
|
|
3631
3657
|
x: (Math.random() - 0.5) * 200,
|
|
3632
3658
|
y: (Math.random() - 0.5) * 200,
|
|
3633
3659
|
z: (Math.random() - 0.5) * 200
|
|
3634
3660
|
});
|
|
3635
|
-
for (let
|
|
3636
|
-
const a = o
|
|
3661
|
+
for (let o = 0; o < d; o++) {
|
|
3662
|
+
const a = i[o % t];
|
|
3637
3663
|
e.push({
|
|
3638
|
-
id: `node-${
|
|
3639
|
-
label: `N${
|
|
3664
|
+
id: `node-${o}`,
|
|
3665
|
+
label: `N${o}`,
|
|
3640
3666
|
position: {
|
|
3641
3667
|
x: a.x + (Math.random() - 0.5) * 40,
|
|
3642
3668
|
y: a.y + (Math.random() - 0.5) * 40,
|
|
@@ -3644,16 +3670,16 @@ function At(d = 1e3) {
|
|
|
3644
3670
|
}
|
|
3645
3671
|
});
|
|
3646
3672
|
}
|
|
3647
|
-
for (let
|
|
3648
|
-
const a = Math.floor(
|
|
3673
|
+
for (let o = 1; o < d; o++) {
|
|
3674
|
+
const a = Math.floor(o / 50) * 50, r = a === 0 ? Math.floor(Math.random() * o) : a + Math.floor(Math.random() * Math.min(o - a, 50));
|
|
3649
3675
|
s.push({
|
|
3650
|
-
source: `node-${
|
|
3651
|
-
target: `node-${Math.min(r,
|
|
3676
|
+
source: `node-${o}`,
|
|
3677
|
+
target: `node-${Math.min(r, o - 1)}`,
|
|
3652
3678
|
relationship: "links to"
|
|
3653
3679
|
});
|
|
3654
3680
|
}
|
|
3655
|
-
for (let
|
|
3656
|
-
const a =
|
|
3681
|
+
for (let o = 1; o < t; o++) {
|
|
3682
|
+
const a = o * 50, r = (o - 1) * 50 + Math.floor(Math.random() * 50);
|
|
3657
3683
|
s.push({
|
|
3658
3684
|
source: `node-${a}`,
|
|
3659
3685
|
target: `node-${r}`,
|