@krainovsd/graph 0.13.0-beta1 → 0.13.0-beta3
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/lib/cjs/index.cjs +258 -74
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/link-settings.js +1 -0
- package/lib/esm/module/GraphCanvas/constants/link-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js +42 -0
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js.map +1 -0
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js +37 -35
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js +72 -22
- package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/draw-links.js +70 -17
- package/lib/esm/module/GraphCanvas/slices/draw-links.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js +6 -0
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-resize.js +7 -0
- package/lib/esm/module/GraphCanvas/slices/init-resize.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/update-link-cache.js +30 -0
- package/lib/esm/module/GraphCanvas/slices/update-link-cache.js.map +1 -1
- package/lib/index.d.ts +22 -7
- package/package.json +1 -1
package/lib/cjs/index.cjs
CHANGED
|
@@ -421,50 +421,52 @@ function nodeIterationExtractor(node, i, nodes, state, option, optionConstantGet
|
|
|
421
421
|
return customOptions;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
function calculateLinkPositionByNode(
|
|
425
|
-
const source = link.source;
|
|
426
|
-
const target = link.target;
|
|
427
|
-
if (typeof source != "object" || typeof target != "object")
|
|
428
|
-
return null;
|
|
424
|
+
function calculateLinkPositionByNode(xStart, yStart, xEnd, yEnd, sourceNode, targetNode, arrowSize = 0) {
|
|
429
425
|
const sourcePoint = getLinkPoint({
|
|
426
|
+
xStart,
|
|
427
|
+
yStart,
|
|
428
|
+
xEnd,
|
|
429
|
+
yEnd,
|
|
430
430
|
arrowSize: 0,
|
|
431
|
-
node:
|
|
432
|
-
oppositeNode: target,
|
|
433
|
-
});
|
|
431
|
+
node: sourceNode});
|
|
434
432
|
const targetPoint = getLinkPoint({
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
433
|
+
xStart: xEnd,
|
|
434
|
+
yStart: yEnd,
|
|
435
|
+
xEnd: xStart,
|
|
436
|
+
yEnd: yStart,
|
|
437
|
+
arrowSize: arrowSize > 0 ? arrowSize * 0.9 : 0,
|
|
438
|
+
node: targetNode});
|
|
439
|
+
const targetPointArrow = getLinkPoint({
|
|
440
|
+
xStart: xEnd,
|
|
441
|
+
yStart: yEnd,
|
|
442
|
+
xEnd: xStart,
|
|
443
|
+
yEnd: yStart,
|
|
444
|
+
arrowSize: 0,
|
|
445
|
+
node: targetNode});
|
|
441
446
|
const distance = targetPoint && sourcePoint
|
|
442
447
|
? Math.sqrt((targetPoint.x - sourcePoint.x) ** 2 + (targetPoint.y - sourcePoint.y) ** 2)
|
|
443
448
|
: 0;
|
|
444
449
|
return {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
450
|
+
xStart: sourcePoint.x,
|
|
451
|
+
yStart: sourcePoint.y,
|
|
452
|
+
xEnd: targetPoint.x,
|
|
453
|
+
yEnd: targetPoint.y,
|
|
454
|
+
xEndArrow: targetPointArrow.x,
|
|
455
|
+
yEndArrow: targetPointArrow.y,
|
|
449
456
|
distance,
|
|
450
457
|
};
|
|
451
458
|
}
|
|
452
459
|
function getLinkPoint(opts) {
|
|
453
|
-
if (opts.node.x == undefined ||
|
|
454
|
-
opts.node.y == undefined ||
|
|
455
|
-
opts.oppositeNode.x == undefined ||
|
|
456
|
-
opts.oppositeNode.y == undefined)
|
|
457
|
-
return null;
|
|
458
460
|
let nodePoint;
|
|
459
461
|
switch (opts.node._shape) {
|
|
460
462
|
case "circle": {
|
|
461
463
|
nodePoint = getCircleIntersection({
|
|
462
|
-
x: opts.
|
|
463
|
-
y: opts.
|
|
464
|
+
x: opts.xStart,
|
|
465
|
+
y: opts.yStart,
|
|
464
466
|
radius: (opts.node._radius ?? COMMON_SETTINGS.nodeRadius) +
|
|
465
467
|
(opts.node._borderWidth ? opts.node._borderWidth / 2 : 0),
|
|
466
|
-
oppositeX: opts.
|
|
467
|
-
oppositeY: opts.
|
|
468
|
+
oppositeX: opts.xEnd,
|
|
469
|
+
oppositeY: opts.yEnd,
|
|
468
470
|
arrowSize: opts.arrowSize,
|
|
469
471
|
});
|
|
470
472
|
break;
|
|
@@ -473,23 +475,23 @@ function getLinkPoint(opts) {
|
|
|
473
475
|
case "text": {
|
|
474
476
|
nodePoint = getRectangleIntersection({
|
|
475
477
|
arrowSize: opts.arrowSize,
|
|
476
|
-
x: opts.
|
|
477
|
-
y: opts.
|
|
478
|
+
x: opts.xStart,
|
|
479
|
+
y: opts.yStart,
|
|
478
480
|
height: (opts.node._height ?? COMMON_SETTINGS.nodeSize) + (opts.node._borderWidth ?? 0),
|
|
479
481
|
width: (opts.node._width ?? COMMON_SETTINGS.nodeSize) + (opts.node._borderWidth ?? 0),
|
|
480
|
-
oppositeX: opts.
|
|
481
|
-
oppositeY: opts.
|
|
482
|
+
oppositeX: opts.xEnd,
|
|
483
|
+
oppositeY: opts.yEnd,
|
|
482
484
|
borderRadius: opts.node._shape === "text" ? 0 : (opts.node._borderRadius ?? 0),
|
|
483
485
|
});
|
|
484
486
|
break;
|
|
485
487
|
}
|
|
486
488
|
default: {
|
|
487
489
|
nodePoint = getCircleIntersection({
|
|
488
|
-
x: opts.
|
|
489
|
-
y: opts.
|
|
490
|
+
x: opts.xStart,
|
|
491
|
+
y: opts.yStart,
|
|
490
492
|
radius: opts.node._radius ?? COMMON_SETTINGS.nodeRadius,
|
|
491
|
-
oppositeX: opts.
|
|
492
|
-
oppositeY: opts.
|
|
493
|
+
oppositeX: opts.xEnd,
|
|
494
|
+
oppositeY: opts.yEnd,
|
|
493
495
|
arrowSize: opts.arrowSize,
|
|
494
496
|
});
|
|
495
497
|
}
|
|
@@ -770,31 +772,34 @@ function getParticlePosition(opts) {
|
|
|
770
772
|
}
|
|
771
773
|
}
|
|
772
774
|
|
|
773
|
-
function linkByPointerGetter({ areaRect, areaTransform, mouseEvent, links, linkHoverExtraZone, }) {
|
|
775
|
+
function linkByPointerGetter({ areaRect, areaTransform, mouseEvent, links, linkHoverExtraZone, curve, }) {
|
|
774
776
|
if (!areaRect)
|
|
775
777
|
return undefined;
|
|
776
778
|
const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
|
|
777
779
|
return d3Array.greatest(links, (link) => {
|
|
778
|
-
if (
|
|
779
|
-
|
|
780
|
+
if (!jsHelpers.isObject(link.source) ||
|
|
781
|
+
!jsHelpers.isObject(link.target) ||
|
|
782
|
+
link.source.visible === false ||
|
|
783
|
+
link.target.visible === false)
|
|
784
|
+
return;
|
|
785
|
+
const x1 = (link._x1 ?? link.source.x);
|
|
786
|
+
const y1 = (link._y1 ?? link.source.y);
|
|
787
|
+
const x2 = (link._x2 ?? link.target.x);
|
|
788
|
+
const y2 = (link._y2 ?? link.target.y);
|
|
789
|
+
const cx = link._cx ?? 0;
|
|
790
|
+
const cy = link._cy ?? 0;
|
|
791
|
+
return curve
|
|
792
|
+
? hitTestQuadratic(pointerX, pointerY, x1, y1, cx, cy, x2, y2, linkHoverExtraZone)
|
|
793
|
+
? link.index
|
|
794
|
+
: undefined
|
|
795
|
+
: isNearLink(pointerX, pointerY, x1, y1, x2, y2, linkHoverExtraZone)
|
|
796
|
+
? link.index
|
|
797
|
+
: undefined;
|
|
780
798
|
});
|
|
781
799
|
}
|
|
782
|
-
function isNearLink(
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
link.source.visible === false ||
|
|
786
|
-
link.target.visible === false)
|
|
787
|
-
return false;
|
|
788
|
-
const x1 = link.source.x;
|
|
789
|
-
const y1 = link.source.y;
|
|
790
|
-
const x2 = link.target.x;
|
|
791
|
-
const y2 = link.target.y;
|
|
792
|
-
const distance = distanceToLine(mouseX, mouseY, x1, y1, x2, y2);
|
|
793
|
-
return distance <= threshold;
|
|
794
|
-
}
|
|
795
|
-
function distanceToLine(x, y, x1, y1, x2, y2) {
|
|
796
|
-
const A = x - x1;
|
|
797
|
-
const B = y - y1;
|
|
800
|
+
function isNearLink(px, py, x1, y1, x2, y2, threshold = 2) {
|
|
801
|
+
const A = px - x1;
|
|
802
|
+
const B = py - y1;
|
|
798
803
|
const C = x2 - x1;
|
|
799
804
|
const D = y2 - y1;
|
|
800
805
|
const dot = A * C + B * D;
|
|
@@ -816,9 +821,56 @@ function distanceToLine(x, y, x1, y1, x2, y2) {
|
|
|
816
821
|
xx = x1 + param * C;
|
|
817
822
|
yy = y1 + param * D;
|
|
818
823
|
}
|
|
819
|
-
const dx =
|
|
820
|
-
const dy =
|
|
821
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
824
|
+
const dx = px - xx;
|
|
825
|
+
const dy = py - yy;
|
|
826
|
+
return Math.sqrt(dx * dx + dy * dy) <= threshold;
|
|
827
|
+
}
|
|
828
|
+
function hitTestQuadratic(px, py, x0, y0, xc, yc, x2, y2, tol) {
|
|
829
|
+
const bb = bezierBBox(x0, y0, xc, yc, x2, y2);
|
|
830
|
+
if (px < bb.minX - tol || px > bb.maxX + tol || py < bb.minY - tol || py > bb.maxY + tol) {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
const chord = Math.hypot(x2 - x0, y2 - y0);
|
|
834
|
+
const contLen = Math.hypot(xc - x0, yc - y0) + Math.hypot(x2 - xc, y2 - yc);
|
|
835
|
+
const approxLen = (chord + contLen) / 2;
|
|
836
|
+
const steps = Math.max(16, Math.ceil(approxLen / 1));
|
|
837
|
+
const tol2 = tol * tol;
|
|
838
|
+
for (let i = 0; i <= steps; i++) {
|
|
839
|
+
const t = i / steps;
|
|
840
|
+
const mt = 1 - t;
|
|
841
|
+
const x = mt * mt * x0 + 2 * mt * t * xc + t * t * x2;
|
|
842
|
+
const y = mt * mt * y0 + 2 * mt * t * yc + t * t * y2;
|
|
843
|
+
const dx = px - x;
|
|
844
|
+
const dy = py - y;
|
|
845
|
+
if (dx * dx + dy * dy <= tol2)
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
function bezierBBox(x0, y0, x1, y1, x2, y2) {
|
|
851
|
+
function extrema(p0, p1, p2) {
|
|
852
|
+
const denom = p0 - 2 * p1 + p2;
|
|
853
|
+
if (denom === 0)
|
|
854
|
+
return [];
|
|
855
|
+
const t = (p0 - p1) / denom;
|
|
856
|
+
return t > 0 && t < 1 ? [t] : [];
|
|
857
|
+
}
|
|
858
|
+
const ts = [...extrema(x0, x1, x2), ...extrema(y0, y1, y2), 0, 1];
|
|
859
|
+
let maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity;
|
|
860
|
+
for (const t of ts) {
|
|
861
|
+
const mt = 1 - t;
|
|
862
|
+
const x = mt * mt * x0 + 2 * mt * t * x1 + t * t * x2;
|
|
863
|
+
const y = mt * mt * y0 + 2 * mt * t * y1 + t * t * y2;
|
|
864
|
+
if (x < minX)
|
|
865
|
+
minX = x;
|
|
866
|
+
if (x > maxX)
|
|
867
|
+
maxX = x;
|
|
868
|
+
if (y < minY)
|
|
869
|
+
minY = y;
|
|
870
|
+
if (y > maxY)
|
|
871
|
+
maxY = y;
|
|
872
|
+
}
|
|
873
|
+
return { minX, minY, maxX, maxY };
|
|
822
874
|
}
|
|
823
875
|
|
|
824
876
|
function nodeHighlight(opts) {
|
|
@@ -1067,6 +1119,7 @@ const HIGHLIGHT_SETTINGS = {
|
|
|
1067
1119
|
|
|
1068
1120
|
const LINK_SETTINGS = {
|
|
1069
1121
|
prettyDraw: true,
|
|
1122
|
+
curve: false,
|
|
1070
1123
|
linkScaleSwitch: 1,
|
|
1071
1124
|
linkColorAfterScaleSwitch: "#000000FF",
|
|
1072
1125
|
linkColorBeforeScaleSwitch: "#999",
|
|
@@ -1234,6 +1287,42 @@ function initDnd() {
|
|
|
1234
1287
|
d3Selection.select(this.area).call(dragHandler);
|
|
1235
1288
|
}
|
|
1236
1289
|
|
|
1290
|
+
function calculateCurveLinkPositionByNode(xStart, yStart, xEnd, yEnd, sourceNode, targetNode, groupIndex, arrowSize = 0) {
|
|
1291
|
+
const sourceRadius = (sourceNode._radius ?? COMMON_SETTINGS.nodeRadius) + (sourceNode._borderWidth ?? 0) * 0.5;
|
|
1292
|
+
const targetRadius = (targetNode._radius ?? COMMON_SETTINGS.nodeRadius) + (targetNode._borderWidth ?? 0) * 0.5;
|
|
1293
|
+
const effectiveTargetRadius = targetRadius + arrowSize * 0.9;
|
|
1294
|
+
const mx = (xStart + xEnd) * 0.5;
|
|
1295
|
+
const my = (yStart + yEnd) * 0.5;
|
|
1296
|
+
const dx = xEnd - xStart;
|
|
1297
|
+
const dy = yEnd - yStart;
|
|
1298
|
+
const lenSq = dx * dx + dy * dy;
|
|
1299
|
+
const len = lenSq > 0 ? Math.sqrt(lenSq) : 1;
|
|
1300
|
+
const nx = -dy / len;
|
|
1301
|
+
const ny = dx / len;
|
|
1302
|
+
const offset = 0.12 * len * groupIndex;
|
|
1303
|
+
const xControl = mx + nx * offset;
|
|
1304
|
+
const yControl = my + ny * offset;
|
|
1305
|
+
const [xStartP, yStartP] = adjustPoint(xStart, yStart, xControl, yControl, sourceRadius);
|
|
1306
|
+
const [xEndP, yEndP] = adjustPoint(xEnd, yEnd, xControl, yControl, effectiveTargetRadius);
|
|
1307
|
+
const [xEndArrow, yEndArrow] = adjustPoint(xEnd, yEnd, xControl, yControl, targetRadius);
|
|
1308
|
+
return {
|
|
1309
|
+
xStart: xStartP,
|
|
1310
|
+
yStart: yStartP,
|
|
1311
|
+
xEnd: xEndP,
|
|
1312
|
+
yEnd: yEndP,
|
|
1313
|
+
xControl,
|
|
1314
|
+
yControl,
|
|
1315
|
+
xEndArrow,
|
|
1316
|
+
yEndArrow,
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function adjustPoint(x, y, xControl, yControl, radius) {
|
|
1320
|
+
const dxToControl = xControl - x;
|
|
1321
|
+
const dyToControl = yControl - y;
|
|
1322
|
+
const distToControl = Math.sqrt(dxToControl * dxToControl + dyToControl * dyToControl) || 1;
|
|
1323
|
+
return [x + (dxToControl / distToControl) * radius, y + (dyToControl / distToControl) * radius];
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1237
1326
|
function getDrawLink() {
|
|
1238
1327
|
return function drawLink(link) {
|
|
1239
1328
|
if (!this.context ||
|
|
@@ -1349,23 +1438,62 @@ function getDrawLink() {
|
|
|
1349
1438
|
let yStart = link.source.y;
|
|
1350
1439
|
let xEnd = link.target.x;
|
|
1351
1440
|
let yEnd = link.target.y;
|
|
1441
|
+
let xControl = 0;
|
|
1442
|
+
let yControl = 0;
|
|
1443
|
+
let xEndArrow = xEnd;
|
|
1444
|
+
let yEndArrow = yEnd;
|
|
1352
1445
|
let linkDistance = 0;
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
(this.linkSettings.
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
xStart = position.
|
|
1360
|
-
xEnd = position.
|
|
1361
|
-
yStart = position.
|
|
1362
|
-
yEnd = position.
|
|
1446
|
+
const isHasArrow = this.linkSettings.arrow && arrowAlpha > 0;
|
|
1447
|
+
if (!this.linkSettings.curve) {
|
|
1448
|
+
if (this.linkSettings.prettyDraw ||
|
|
1449
|
+
this.linkSettings.particleFlexSpeed ||
|
|
1450
|
+
(this.linkSettings.arrow && arrowAlpha > 0)) {
|
|
1451
|
+
const position = calculateLinkPositionByNode(xStart, yStart, xEnd, yEnd, link.source, link.target, isHasArrow ? arrowSize : 0);
|
|
1452
|
+
xStart = position.xStart;
|
|
1453
|
+
xEnd = position.xEnd;
|
|
1454
|
+
yStart = position.yStart;
|
|
1455
|
+
yEnd = position.yEnd;
|
|
1456
|
+
xEndArrow = position.xEndArrow;
|
|
1457
|
+
yEndArrow = position.yEndArrow;
|
|
1363
1458
|
linkDistance = position.distance;
|
|
1364
1459
|
}
|
|
1460
|
+
link._x1 = xStart;
|
|
1461
|
+
link._y1 = yStart;
|
|
1462
|
+
link._x2 = xEnd;
|
|
1463
|
+
link._y2 = yEnd;
|
|
1464
|
+
link._ax = xEndArrow;
|
|
1465
|
+
link._ay = yEndArrow;
|
|
1466
|
+
this.context.moveTo(xStart, yStart);
|
|
1467
|
+
this.context.lineTo(xEnd, yEnd);
|
|
1468
|
+
this.context.stroke();
|
|
1469
|
+
}
|
|
1470
|
+
else {
|
|
1471
|
+
const position = calculateCurveLinkPositionByNode(xStart, yStart, xEnd, yEnd, link.source, link.target, link._groupIndex ?? 0, isHasArrow ? arrowSize : 0);
|
|
1472
|
+
xStart = position.xStart;
|
|
1473
|
+
yStart = position.yStart;
|
|
1474
|
+
xEnd = position.xEnd;
|
|
1475
|
+
yEnd = position.yEnd;
|
|
1476
|
+
xControl = position.xControl;
|
|
1477
|
+
yControl = position.yControl;
|
|
1478
|
+
xEndArrow = position.xEndArrow;
|
|
1479
|
+
yEndArrow = position.yEndArrow;
|
|
1480
|
+
link._x1 = position.xStart;
|
|
1481
|
+
link._y1 = position.yStart;
|
|
1482
|
+
link._x2 = position.xEnd;
|
|
1483
|
+
link._y2 = position.yEnd;
|
|
1484
|
+
link._cx = position.xControl;
|
|
1485
|
+
link._cy = position.yControl;
|
|
1486
|
+
link._ax = position.xEndArrow;
|
|
1487
|
+
link._ay = position.yEndArrow;
|
|
1488
|
+
this.context.beginPath();
|
|
1489
|
+
this.context.moveTo(position.xStart, position.yStart);
|
|
1490
|
+
this.context.quadraticCurveTo(position.xControl, position.yControl, position.xEnd, position.yEnd);
|
|
1491
|
+
this.context.setLineDash([]);
|
|
1492
|
+
// if (isDashed) {
|
|
1493
|
+
// this.context.setLineDash([10, 5]);
|
|
1494
|
+
// }
|
|
1495
|
+
this.context.stroke();
|
|
1365
1496
|
}
|
|
1366
|
-
this.context.moveTo(xStart, yStart);
|
|
1367
|
-
this.context.lineTo(xEnd, yEnd);
|
|
1368
|
-
this.context.stroke();
|
|
1369
1497
|
/** Particle */
|
|
1370
1498
|
if (this.linkSettings.particles &&
|
|
1371
1499
|
((this.highlightedNode &&
|
|
@@ -1433,15 +1561,28 @@ function getDrawLink() {
|
|
|
1433
1561
|
}
|
|
1434
1562
|
/** Arrow */
|
|
1435
1563
|
if (this.linkSettings.arrow && arrowAlpha > 0) {
|
|
1564
|
+
let angle = 0;
|
|
1565
|
+
if (!this.linkSettings.curve) {
|
|
1566
|
+
angle = Math.atan2(yEndArrow - yStart, xEndArrow - xStart);
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
const tangentX = 2 * (xEndArrow - xControl);
|
|
1570
|
+
const tangentY = 2 * (yEndArrow - yControl);
|
|
1571
|
+
if (Math.abs(tangentX) < 0.001 && Math.abs(tangentY) < 0.001) {
|
|
1572
|
+
angle = Math.atan2(yEndArrow - yControl, xEndArrow - xControl);
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1575
|
+
angle = Math.atan2(tangentY, tangentX);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1436
1578
|
this.context.beginPath();
|
|
1437
1579
|
this.context.globalAlpha = arrowAlpha;
|
|
1438
1580
|
this.context.strokeStyle = arrowBorderColor;
|
|
1439
1581
|
this.context.lineWidth = arrowBorderWidth;
|
|
1440
1582
|
this.context.fillStyle = arrowColor;
|
|
1441
|
-
|
|
1442
|
-
this.context.
|
|
1443
|
-
this.context.lineTo(
|
|
1444
|
-
this.context.lineTo(xEnd - arrowSize * Math.cos(angle + Math.PI / 6), yEnd - arrowSize * Math.sin(angle + Math.PI / 6));
|
|
1583
|
+
this.context.moveTo(xEndArrow, yEndArrow);
|
|
1584
|
+
this.context.lineTo(xEndArrow - arrowSize * Math.cos(angle - Math.PI / 6), yEndArrow - arrowSize * Math.sin(angle - Math.PI / 6));
|
|
1585
|
+
this.context.lineTo(xEndArrow - arrowSize * Math.cos(angle + Math.PI / 6), yEndArrow - arrowSize * Math.sin(angle + Math.PI / 6));
|
|
1445
1586
|
this.context.closePath();
|
|
1446
1587
|
this.context.fill();
|
|
1447
1588
|
if (arrowBorderWidth > 0) {
|
|
@@ -1965,6 +2106,7 @@ function initPointer() {
|
|
|
1965
2106
|
areaTransform: this.areaTransform,
|
|
1966
2107
|
mouseEvent: event,
|
|
1967
2108
|
links: this.links,
|
|
2109
|
+
curve: this.linkSettings.curve,
|
|
1968
2110
|
});
|
|
1969
2111
|
if (currentLink?.highlight != undefined)
|
|
1970
2112
|
highlightLink = currentLink?.highlight;
|
|
@@ -2026,6 +2168,7 @@ function initPointer() {
|
|
|
2026
2168
|
areaTransform: this.areaTransform,
|
|
2027
2169
|
mouseEvent: event,
|
|
2028
2170
|
links: this.links,
|
|
2171
|
+
curve: this.linkSettings.curve,
|
|
2029
2172
|
});
|
|
2030
2173
|
}
|
|
2031
2174
|
if (!currentNode)
|
|
@@ -2050,6 +2193,7 @@ function initPointer() {
|
|
|
2050
2193
|
areaTransform: this.areaTransform,
|
|
2051
2194
|
mouseEvent: event,
|
|
2052
2195
|
links: this.links,
|
|
2196
|
+
curve: this.linkSettings.curve,
|
|
2053
2197
|
});
|
|
2054
2198
|
return void this.listeners.onWheelClick.call(this, event, undefined, currentLink);
|
|
2055
2199
|
}
|
|
@@ -2071,6 +2215,7 @@ function initPointer() {
|
|
|
2071
2215
|
areaTransform: this.areaTransform,
|
|
2072
2216
|
mouseEvent: event,
|
|
2073
2217
|
links: this.links,
|
|
2218
|
+
curve: this.linkSettings.curve,
|
|
2074
2219
|
});
|
|
2075
2220
|
return void this.listeners.onContextMenu.call(this, event, undefined, currentLink);
|
|
2076
2221
|
}
|
|
@@ -2092,6 +2237,7 @@ function initPointer() {
|
|
|
2092
2237
|
areaTransform: this.areaTransform,
|
|
2093
2238
|
mouseEvent: event,
|
|
2094
2239
|
links: this.links,
|
|
2240
|
+
curve: this.linkSettings.curve,
|
|
2095
2241
|
});
|
|
2096
2242
|
return void this.listeners.onDoubleClick.call(this, event, undefined, currentLink);
|
|
2097
2243
|
}
|
|
@@ -2113,6 +2259,7 @@ function initPointer() {
|
|
|
2113
2259
|
areaTransform: this.areaTransform,
|
|
2114
2260
|
mouseEvent: event,
|
|
2115
2261
|
links: this.links,
|
|
2262
|
+
curve: this.linkSettings.curve,
|
|
2116
2263
|
});
|
|
2117
2264
|
return void this.listeners.onClick.call(this, event, undefined, currentLink);
|
|
2118
2265
|
}
|
|
@@ -2158,12 +2305,19 @@ function initResize() {
|
|
|
2158
2305
|
return;
|
|
2159
2306
|
}
|
|
2160
2307
|
this.updateSize();
|
|
2308
|
+
requestAnimationFrame(() => {
|
|
2309
|
+
this.updateSize();
|
|
2310
|
+
});
|
|
2161
2311
|
});
|
|
2162
2312
|
document.addEventListener("scroll", this.updateRect.bind(this), {
|
|
2163
2313
|
capture: true,
|
|
2164
2314
|
passive: true,
|
|
2165
2315
|
signal: abortController.signal,
|
|
2166
2316
|
});
|
|
2317
|
+
document.addEventListener("visibilitychange", () => {
|
|
2318
|
+
if (!document.hidden)
|
|
2319
|
+
this.updateRect();
|
|
2320
|
+
}, { signal: abortController.signal });
|
|
2167
2321
|
observer.observe(this.area);
|
|
2168
2322
|
}
|
|
2169
2323
|
|
|
@@ -2284,14 +2438,44 @@ function initCollideForce(forceUpdate) {
|
|
|
2284
2438
|
|
|
2285
2439
|
function updateLinkCache() {
|
|
2286
2440
|
this.linkOptionsCache = {};
|
|
2441
|
+
const groupMap = {};
|
|
2442
|
+
const groupSelfMap = {};
|
|
2287
2443
|
for (let i = 0; i < this.links.length; i++) {
|
|
2288
2444
|
const link = this.links[i];
|
|
2289
2445
|
const { sourceId, targetId } = extractLinkPointIds(link);
|
|
2290
2446
|
const linkOptions = linkIterationExtractor(link, i, this.links, this, this.linkSettings.options ?? {}, linkOptionsGetter);
|
|
2291
2447
|
const id = `${targetId}${sourceId}`;
|
|
2292
2448
|
this.linkOptionsCache[id] = linkOptions;
|
|
2449
|
+
if (this.linkSettings.curve) {
|
|
2450
|
+
if (sourceId === targetId) {
|
|
2451
|
+
groupSelfMap[id] ??= 0;
|
|
2452
|
+
link._groupIndex = ++groupSelfMap[id];
|
|
2453
|
+
link._self = true;
|
|
2454
|
+
continue;
|
|
2455
|
+
}
|
|
2456
|
+
const reverseId = `${sourceId}${targetId}`;
|
|
2457
|
+
groupMap[reverseId] ??= 0;
|
|
2458
|
+
groupMap[reverseId]++;
|
|
2459
|
+
groupMap[id] ??= 0;
|
|
2460
|
+
link._groupIndex = indexToPosition(++groupMap[id]);
|
|
2461
|
+
link._self = false;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
if (this.linkSettings.curve) {
|
|
2465
|
+
for (const link of this.links) {
|
|
2466
|
+
const { sourceId, targetId } = extractLinkPointIds(link);
|
|
2467
|
+
const id = `${targetId}${sourceId}`;
|
|
2468
|
+
link._groupSize = link._self ? (groupSelfMap[id] ?? 1) : (groupMap[id] ?? 1);
|
|
2469
|
+
}
|
|
2293
2470
|
}
|
|
2294
2471
|
}
|
|
2472
|
+
function indexToPosition(n) {
|
|
2473
|
+
if (n === 1)
|
|
2474
|
+
return 0;
|
|
2475
|
+
const index = n >> 1;
|
|
2476
|
+
return n & 1 ? -(index * 2) : index * 2;
|
|
2477
|
+
// return n & 1 ? -(n >> 1) : n >> 1;
|
|
2478
|
+
}
|
|
2295
2479
|
|
|
2296
2480
|
function updateNodeCache(types) {
|
|
2297
2481
|
this.nodeOptionsCache = {};
|