@krainovsd/graph 0.13.0-beta3 → 0.14.0-beta.2
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 +772 -466
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/constants/force-controls.js +4 -2
- package/lib/esm/constants/force-controls.js.map +1 -1
- package/lib/esm/constants/highlight-controls.js +9 -16
- package/lib/esm/constants/highlight-controls.js.map +1 -1
- package/lib/esm/constants/link-controls.js +17 -30
- package/lib/esm/constants/link-controls.js.map +1 -1
- package/lib/esm/constants/node-controls.js +4 -2
- package/lib/esm/constants/node-controls.js.map +1 -1
- package/lib/esm/index.js +8 -7
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/module/GraphCanvas/GraphCanvas.js +265 -74
- package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/force-settings.js +11 -1
- package/lib/esm/module/GraphCanvas/constants/force-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/graph-settings.js +17 -5
- package/lib/esm/module/GraphCanvas/constants/graph-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/highlight-settings.js +5 -3
- package/lib/esm/module/GraphCanvas/constants/highlight-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/index.js +5 -2
- package/lib/esm/module/GraphCanvas/constants/index.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/link-settings.js +19 -10
- package/lib/esm/module/GraphCanvas/constants/link-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/constants/node-settings.js +13 -3
- package/lib/esm/module/GraphCanvas/constants/node-settings.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js +4 -2
- package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js +4 -0
- package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/settings/highlight-settings-getter.js +4 -2
- package/lib/esm/module/GraphCanvas/lib/settings/highlight-settings-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js +8 -5
- package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js +14 -2
- package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js +5 -2
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js +5 -2
- package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/compute-graph-bounds.js +26 -0
- package/lib/esm/module/GraphCanvas/lib/utils/compute-graph-bounds.js.map +1 -0
- package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js +40 -30
- package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/is-empty-object.js +11 -0
- package/lib/esm/module/GraphCanvas/lib/utils/is-empty-object.js.map +1 -0
- package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js +5 -2
- package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/draw-links.js +47 -62
- package/lib/esm/module/GraphCanvas/slices/draw-links.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/draw-nodes.js +9 -4
- package/lib/esm/module/GraphCanvas/slices/draw-nodes.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-dnd.js +21 -4
- package/lib/esm/module/GraphCanvas/slices/init-dnd.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-draw.js +1 -52
- package/lib/esm/module/GraphCanvas/slices/init-draw.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js +6 -34
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-simulation.js +12 -2
- package/lib/esm/module/GraphCanvas/slices/init-simulation.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-zoom.js +85 -16
- package/lib/esm/module/GraphCanvas/slices/init-zoom.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/update-link-cache.js +33 -3
- package/lib/esm/module/GraphCanvas/slices/update-link-cache.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/update-node-cache.js +61 -16
- package/lib/esm/module/GraphCanvas/slices/update-node-cache.js.map +1 -1
- package/lib/index.d.ts +85 -46
- package/package.json +3 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -4,11 +4,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
4
4
|
|
|
5
5
|
const jsHelpers = require('@krainovsd/js-helpers');
|
|
6
6
|
const d3Force = require('d3-force');
|
|
7
|
+
const d3Selection = require('d3-selection');
|
|
7
8
|
const d3Zoom = require('d3-zoom');
|
|
8
9
|
const d3Array = require('d3-array');
|
|
9
|
-
const d3Selection = require('d3-selection');
|
|
10
10
|
const d3Drag = require('d3-drag');
|
|
11
|
-
const lodash = require('lodash');
|
|
12
11
|
|
|
13
12
|
const COMMON_SETTINGS = {
|
|
14
13
|
nodeRadius: 5,
|
|
@@ -23,6 +22,10 @@ const FORCE_SETTINGS = {
|
|
|
23
22
|
xForce: true,
|
|
24
23
|
yForce: true,
|
|
25
24
|
collideForce: true,
|
|
25
|
+
precompute: true,
|
|
26
|
+
precomputeMaxTicks: 300,
|
|
27
|
+
precomputeMaxTimeMs: 200,
|
|
28
|
+
precomputeDisableForcesAfter: false,
|
|
26
29
|
centerPosition: {},
|
|
27
30
|
centerStrength: 1,
|
|
28
31
|
collideStrength: 0.1,
|
|
@@ -40,6 +43,12 @@ const FORCE_SETTINGS = {
|
|
|
40
43
|
linkStrength: 1,
|
|
41
44
|
collideRadius: null,
|
|
42
45
|
};
|
|
46
|
+
const PERFORMANCE_FORCE_SETTINGS = {
|
|
47
|
+
...FORCE_SETTINGS,
|
|
48
|
+
collideIterations: 1,
|
|
49
|
+
precomputeMaxTimeMs: 350,
|
|
50
|
+
precomputeDisableForcesAfter: true,
|
|
51
|
+
};
|
|
43
52
|
|
|
44
53
|
function forceSettingsGetter(settings, prevSettings) {
|
|
45
54
|
return {
|
|
@@ -59,17 +68,18 @@ function linkSettingsGetter(settings, prevSettings) {
|
|
|
59
68
|
return { ...(prevSettings ?? LINK_SETTINGS), ...settings };
|
|
60
69
|
}
|
|
61
70
|
function linkOptionsGetter() {
|
|
71
|
+
const aboveScale = this.areaTransform && this.areaTransform.k > this.linkSettings.linkScaleSwitch;
|
|
62
72
|
return {
|
|
63
73
|
...LINK_OPTIONS,
|
|
64
74
|
drawExtraLink: null,
|
|
65
75
|
drawLink: null,
|
|
66
|
-
color:
|
|
76
|
+
color: aboveScale
|
|
67
77
|
? this.linkSettings.linkColorAfterScaleSwitch
|
|
68
78
|
: this.linkSettings.linkColorBeforeScaleSwitch,
|
|
69
|
-
arrowColor:
|
|
79
|
+
arrowColor: aboveScale
|
|
70
80
|
? this.linkSettings.linkColorAfterScaleSwitch
|
|
71
81
|
: this.linkSettings.linkColorBeforeScaleSwitch,
|
|
72
|
-
width:
|
|
82
|
+
width: aboveScale
|
|
73
83
|
? this.linkSettings.linkWidthAfterScaleSwitch
|
|
74
84
|
: this.linkSettings.linkWidthBeforeScaleSwitch,
|
|
75
85
|
};
|
|
@@ -185,11 +195,20 @@ function resetDrawTime() {
|
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
function nodeSettingsGetter(settings, prevNodeSettings) {
|
|
188
|
-
|
|
198
|
+
const result = {
|
|
189
199
|
...(prevNodeSettings ?? NODE_SETTINGS),
|
|
190
200
|
idGetter: nodeIdGetter,
|
|
191
201
|
...settings,
|
|
192
202
|
};
|
|
203
|
+
if (result.smartCache && (!result.textScaleSteps || result.textScaleSteps.length === 0)) {
|
|
204
|
+
const step = (result.textScaleMax - result.textScaleMin) / 9;
|
|
205
|
+
const steps = [];
|
|
206
|
+
for (let i = 0; i < 10; i++) {
|
|
207
|
+
steps.push(result.textScaleMin + i * step);
|
|
208
|
+
}
|
|
209
|
+
result.textScaleSteps = steps;
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
193
212
|
}
|
|
194
213
|
const color = colorGetter();
|
|
195
214
|
function nodeOptionsGetter(node) {
|
|
@@ -271,6 +290,30 @@ function highlightSettingsGetter(settings, prevSettings) {
|
|
|
271
290
|
};
|
|
272
291
|
}
|
|
273
292
|
|
|
293
|
+
function computeGraphBounds(nodes) {
|
|
294
|
+
if (!nodes.length)
|
|
295
|
+
return null;
|
|
296
|
+
let minX = Infinity;
|
|
297
|
+
let minY = Infinity;
|
|
298
|
+
let maxX = -Infinity;
|
|
299
|
+
let maxY = -Infinity;
|
|
300
|
+
for (const node of nodes) {
|
|
301
|
+
if (node.x === null || node.x === undefined || node.y === null || node.y === undefined)
|
|
302
|
+
continue;
|
|
303
|
+
if (node.visible === false)
|
|
304
|
+
continue;
|
|
305
|
+
let radius = node._radius;
|
|
306
|
+
radius ??= Math.max(node._width ?? 0, node._height ?? 0) / 2;
|
|
307
|
+
minX = Math.min(minX, node.x - radius);
|
|
308
|
+
minY = Math.min(minY, node.y - radius);
|
|
309
|
+
maxX = Math.max(maxX, node.x + radius);
|
|
310
|
+
maxY = Math.max(maxY, node.y + radius);
|
|
311
|
+
}
|
|
312
|
+
if (!isFinite(minX))
|
|
313
|
+
return null;
|
|
314
|
+
return { minX, minY, maxX, maxY };
|
|
315
|
+
}
|
|
316
|
+
|
|
274
317
|
function pointerGetter(mouseEvent, areaRect, areaTransform) {
|
|
275
318
|
const clientX = "clientX" in mouseEvent
|
|
276
319
|
? mouseEvent.clientX
|
|
@@ -735,42 +778,52 @@ function isNodeVisible(opts) {
|
|
|
735
778
|
}
|
|
736
779
|
}
|
|
737
780
|
|
|
781
|
+
const FRAME_INTERVAL = 1000 / 60;
|
|
738
782
|
function getParticlePosition(opts) {
|
|
739
|
-
const
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
opts.particle.prev.step !== 0 &&
|
|
746
|
-
opts.particle.prev.step < opts.particle.step &&
|
|
747
|
-
prevStepDifference < opts.distance;
|
|
748
|
-
if (opts.particle.step === 0 && needWait) {
|
|
749
|
-
opts.particle.x = undefined;
|
|
750
|
-
opts.particle.y = undefined;
|
|
751
|
-
return;
|
|
783
|
+
const now = performance.now();
|
|
784
|
+
const particle = opts.particle;
|
|
785
|
+
if (particle._lastTime == undefined) {
|
|
786
|
+
particle._distanceTraveled = (opts.distance / opts.totalCount) * particle.index;
|
|
787
|
+
particle._lastTime = now;
|
|
788
|
+
particle._lastDistance = opts.distance;
|
|
752
789
|
}
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
opts.particle.step = 0;
|
|
759
|
-
return;
|
|
790
|
+
const lastDistance = particle._lastDistance ?? 0;
|
|
791
|
+
if (lastDistance !== opts.distance && lastDistance > 0) {
|
|
792
|
+
const scale = opts.distance / lastDistance;
|
|
793
|
+
particle._distanceTraveled = ((particle._distanceTraveled ?? 0) * scale) % opts.distance;
|
|
794
|
+
particle._lastDistance = opts.distance;
|
|
760
795
|
}
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
opts.
|
|
767
|
-
|
|
768
|
-
|
|
796
|
+
const delta = Math.min(now - particle._lastTime, FRAME_INTERVAL);
|
|
797
|
+
particle._lastTime = now;
|
|
798
|
+
particle._distanceTraveled =
|
|
799
|
+
((particle._distanceTraveled ?? 0) + opts.speed * delta) % opts.distance;
|
|
800
|
+
const progress = particle._distanceTraveled / opts.distance;
|
|
801
|
+
if (opts.xControl !== 0 && opts.yControl !== 0) {
|
|
802
|
+
const t = 1 - progress;
|
|
803
|
+
particle.x =
|
|
804
|
+
t * t * opts.xStart + 2 * t * progress * opts.xControl + progress * progress * opts.xEnd;
|
|
805
|
+
particle.y =
|
|
806
|
+
t * t * opts.yStart + 2 * t * progress * opts.yControl + progress * progress * opts.yEnd;
|
|
769
807
|
}
|
|
770
808
|
else {
|
|
771
|
-
opts.
|
|
809
|
+
const dx = opts.xEnd - opts.xStart;
|
|
810
|
+
const dy = opts.yEnd - opts.yStart;
|
|
811
|
+
particle.x = opts.xStart + dx * progress;
|
|
812
|
+
particle.y = opts.yStart + dy * progress;
|
|
772
813
|
}
|
|
773
814
|
}
|
|
815
|
+
function approximateQuadraticBezierLength(x0, y0, x1, y1, x2, y2) {
|
|
816
|
+
const dx01 = x1 - x0;
|
|
817
|
+
const dy01 = y1 - y0;
|
|
818
|
+
const dx12 = x2 - x1;
|
|
819
|
+
const dy12 = y2 - y1;
|
|
820
|
+
const dx02 = x2 - x0;
|
|
821
|
+
const dy02 = y2 - y0;
|
|
822
|
+
const a = Math.sqrt(dx01 * dx01 + dy01 * dy01);
|
|
823
|
+
const b = Math.sqrt(dx12 * dx12 + dy12 * dy12);
|
|
824
|
+
const c = Math.sqrt(dx02 * dx02 + dy02 * dy02);
|
|
825
|
+
return (a + b + c) / 2;
|
|
826
|
+
}
|
|
774
827
|
|
|
775
828
|
function linkByPointerGetter({ areaRect, areaTransform, mouseEvent, links, linkHoverExtraZone, curve, }) {
|
|
776
829
|
if (!areaRect)
|
|
@@ -1046,22 +1099,38 @@ function extractLinkPointIds(link) {
|
|
|
1046
1099
|
return { sourceId, targetId };
|
|
1047
1100
|
}
|
|
1048
1101
|
|
|
1102
|
+
function isEmptyObject(obj) {
|
|
1103
|
+
for (const key in obj) {
|
|
1104
|
+
if (Object.hasOwn(obj, key)) {
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1049
1111
|
const GRAPH_SETTINGS = {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1112
|
+
zoomAnimation: true,
|
|
1113
|
+
zoomAnimationDuration: 300,
|
|
1114
|
+
zoomToNodeScale: 5,
|
|
1115
|
+
zoomToFitMargin: 0.25,
|
|
1116
|
+
zoomExtent: null,
|
|
1117
|
+
zoomExtentMargin: 0.3,
|
|
1118
|
+
translateExtent: null,
|
|
1119
|
+
translateExtentCoefficient: null,
|
|
1120
|
+
translateExtentOverlap: 0.3,
|
|
1054
1121
|
dragPlaceCoefficient: dragPlaceCoefficientGetter,
|
|
1055
1122
|
zoomInitial: null,
|
|
1056
1123
|
showDrawTime: true,
|
|
1057
1124
|
showDrawTimeEveryTick: false,
|
|
1058
1125
|
};
|
|
1126
|
+
const PERFORMANCE_GRAPH_SETTINGS = {
|
|
1127
|
+
...GRAPH_SETTINGS,
|
|
1128
|
+
};
|
|
1059
1129
|
|
|
1060
1130
|
const HIGHLIGHT_SETTINGS = {
|
|
1061
1131
|
highlightByHoverNode: true,
|
|
1062
1132
|
highlightByHoverLink: true,
|
|
1063
|
-
|
|
1064
|
-
highlightDownFrames: 5,
|
|
1133
|
+
highlightDuration: 150,
|
|
1065
1134
|
linkHoverExtraZone: 2,
|
|
1066
1135
|
/** Node */
|
|
1067
1136
|
highlightByNodeOnlyRoot: true,
|
|
@@ -1116,37 +1185,51 @@ const HIGHLIGHT_SETTINGS = {
|
|
|
1116
1185
|
highlightByLinkForArrowBorderColor: null,
|
|
1117
1186
|
highlightByLinkForArrowBorderSizingAdditional: 0,
|
|
1118
1187
|
};
|
|
1188
|
+
const PERFORMANCE_HIGHLIGHT_SETTINGS = {
|
|
1189
|
+
...HIGHLIGHT_SETTINGS,
|
|
1190
|
+
};
|
|
1119
1191
|
|
|
1120
1192
|
const LINK_SETTINGS = {
|
|
1193
|
+
smartCache: true,
|
|
1121
1194
|
prettyDraw: true,
|
|
1122
|
-
curve:
|
|
1195
|
+
curve: true,
|
|
1123
1196
|
linkScaleSwitch: 1,
|
|
1124
|
-
linkColorAfterScaleSwitch: "#
|
|
1197
|
+
linkColorAfterScaleSwitch: "#C5C5C5FF",
|
|
1125
1198
|
linkColorBeforeScaleSwitch: "#999",
|
|
1126
1199
|
linkWidthAfterScaleSwitch: 0.1,
|
|
1127
1200
|
linkWidthBeforeScaleSwitch: 1,
|
|
1128
1201
|
arrow: true,
|
|
1129
1202
|
arrowByHighlight: true,
|
|
1130
1203
|
particles: true,
|
|
1131
|
-
|
|
1132
|
-
|
|
1204
|
+
// spawn one more particle for N px
|
|
1205
|
+
particleCountByDistance: 25,
|
|
1206
|
+
// speed for 1px
|
|
1207
|
+
particleSpeedByDistance: 50,
|
|
1133
1208
|
};
|
|
1134
1209
|
const LINK_OPTIONS = {
|
|
1135
1210
|
alpha: 1,
|
|
1136
1211
|
arrowAlpha: 1,
|
|
1137
1212
|
arrowSize: 2,
|
|
1138
|
-
arrowBorderColor: "#
|
|
1213
|
+
arrowBorderColor: "#C5C5C5FF",
|
|
1139
1214
|
arrowBorderWidth: 0.1,
|
|
1140
1215
|
particleAlpha: 1,
|
|
1141
|
-
particleColor: "#
|
|
1142
|
-
particleBorderColor: "#
|
|
1216
|
+
particleColor: "#C5C5C5FF",
|
|
1217
|
+
particleBorderColor: "#C5C5C5FF",
|
|
1143
1218
|
particleBorderWidth: 0.1,
|
|
1144
|
-
particleCount: 2,
|
|
1145
1219
|
particleRadius: 0.5,
|
|
1146
|
-
|
|
1220
|
+
};
|
|
1221
|
+
const PERFORMANCE_LINK_SETTINGS = {
|
|
1222
|
+
...LINK_SETTINGS,
|
|
1223
|
+
particles: false,
|
|
1224
|
+
prettyDraw: false,
|
|
1225
|
+
};
|
|
1226
|
+
const PERFORMANCE_LINK_OPTIONS = {
|
|
1227
|
+
...LINK_OPTIONS,
|
|
1147
1228
|
};
|
|
1148
1229
|
|
|
1149
1230
|
const NODE_SETTINGS = {
|
|
1231
|
+
smartCache: true,
|
|
1232
|
+
textScaleSteps: [],
|
|
1150
1233
|
nodeRadiusFlexible: true,
|
|
1151
1234
|
nodeRadiusLinkCountForStep: 5,
|
|
1152
1235
|
nodeRadiusIncrementByStep: 1,
|
|
@@ -1183,14 +1266,14 @@ const NODE_OPTIONS = {
|
|
|
1183
1266
|
textShiftX: 0,
|
|
1184
1267
|
textFont: "Arial",
|
|
1185
1268
|
textAlign: "center",
|
|
1186
|
-
textColor: "#
|
|
1269
|
+
textColor: "#d2d2d2",
|
|
1187
1270
|
textStyle: "normal",
|
|
1188
1271
|
textWeight: 500,
|
|
1189
1272
|
textGap: 0,
|
|
1190
1273
|
label: null,
|
|
1191
1274
|
labelAlpha: 1,
|
|
1192
1275
|
labelAlign: "center",
|
|
1193
|
-
labelColor: "#
|
|
1276
|
+
labelColor: "#ffffff",
|
|
1194
1277
|
labelFont: "Arial",
|
|
1195
1278
|
labelGap: 0,
|
|
1196
1279
|
labelSize: 3.5,
|
|
@@ -1198,6 +1281,14 @@ const NODE_OPTIONS = {
|
|
|
1198
1281
|
labelWeight: 500,
|
|
1199
1282
|
labelWidth: 20,
|
|
1200
1283
|
};
|
|
1284
|
+
const PERFORMANCE_NODE_SETTINGS = {
|
|
1285
|
+
...NODE_SETTINGS,
|
|
1286
|
+
nodeRadiusFlexible: false,
|
|
1287
|
+
nodeSizeFlexible: false,
|
|
1288
|
+
};
|
|
1289
|
+
const PERFOMANCE_NODE_OPTIONS = {
|
|
1290
|
+
...NODE_OPTIONS,
|
|
1291
|
+
};
|
|
1201
1292
|
|
|
1202
1293
|
const GRAPH_CACHE_TYPE = {
|
|
1203
1294
|
NodeOptions: "nodeOptions",
|
|
@@ -1264,8 +1355,14 @@ function initDnd() {
|
|
|
1264
1355
|
return;
|
|
1265
1356
|
const mouseEvent = event.sourceEvent;
|
|
1266
1357
|
const [pointerX, pointerY] = pointerGetter(mouseEvent, this.areaRect, this.areaTransform);
|
|
1267
|
-
|
|
1268
|
-
|
|
1358
|
+
if (this._translateExtent) {
|
|
1359
|
+
event.subject.fx = Math.max(this._translateExtent[0][0], Math.min(this._translateExtent[1][0], pointerX));
|
|
1360
|
+
event.subject.fy = Math.max(this._translateExtent[0][1], Math.min(this._translateExtent[1][1], pointerY));
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
event.subject.fx = pointerX;
|
|
1364
|
+
event.subject.fy = pointerY;
|
|
1365
|
+
}
|
|
1269
1366
|
this.listeners.onMoveDragFinished?.call?.(this, event);
|
|
1270
1367
|
})
|
|
1271
1368
|
.on("end", (event) => {
|
|
@@ -1275,8 +1372,14 @@ function initDnd() {
|
|
|
1275
1372
|
const sourceEvent = event.sourceEvent;
|
|
1276
1373
|
if (sourceEvent.altKey && this.areaRect) {
|
|
1277
1374
|
const [pointerX, pointerY] = pointerGetter(sourceEvent, this.areaRect, this.areaTransform);
|
|
1278
|
-
|
|
1279
|
-
|
|
1375
|
+
if (this._translateExtent) {
|
|
1376
|
+
event.subject.fx = Math.max(this._translateExtent[0][0], Math.min(this._translateExtent[1][0], pointerX));
|
|
1377
|
+
event.subject.fy = Math.max(this._translateExtent[0][1], Math.min(this._translateExtent[1][1], pointerY));
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
event.subject.fx = pointerX;
|
|
1381
|
+
event.subject.fy = pointerY;
|
|
1382
|
+
}
|
|
1280
1383
|
}
|
|
1281
1384
|
else {
|
|
1282
1385
|
event.subject.fx = null;
|
|
@@ -1324,7 +1427,7 @@ function adjustPoint(x, y, xControl, yControl, radius) {
|
|
|
1324
1427
|
}
|
|
1325
1428
|
|
|
1326
1429
|
function getDrawLink() {
|
|
1327
|
-
return function drawLink(link) {
|
|
1430
|
+
return function drawLink(link, index) {
|
|
1328
1431
|
if (!this.context ||
|
|
1329
1432
|
typeof link.source !== "object" ||
|
|
1330
1433
|
typeof link.target !== "object" ||
|
|
@@ -1339,8 +1442,7 @@ function getDrawLink() {
|
|
|
1339
1442
|
return;
|
|
1340
1443
|
if (!link.source._visible && !link.target._visible)
|
|
1341
1444
|
return;
|
|
1342
|
-
const
|
|
1343
|
-
const linkOptions = this.linkOptionsCache[id];
|
|
1445
|
+
const linkOptions = this.linkOptionsCache[index];
|
|
1344
1446
|
if (!linkOptions)
|
|
1345
1447
|
return;
|
|
1346
1448
|
if (linkOptions.drawLink) {
|
|
@@ -1355,10 +1457,12 @@ function getDrawLink() {
|
|
|
1355
1457
|
let arrowSize = linkOptions.arrowSize;
|
|
1356
1458
|
let arrowBorderWidth = linkOptions.arrowBorderWidth;
|
|
1357
1459
|
let arrowBorderColor = linkOptions.arrowBorderColor;
|
|
1460
|
+
const currentNodeHighlighted = this.highlightedNode &&
|
|
1461
|
+
(this.highlightedNode.id == link.source.id || this.highlightedNode.id == link.target.id);
|
|
1358
1462
|
/** Highlight */
|
|
1359
1463
|
if (this.highlightedNeighbors && this.highlightedNode) {
|
|
1360
1464
|
/** By Node Not Highlight */
|
|
1361
|
-
if (
|
|
1465
|
+
if (!currentNodeHighlighted) {
|
|
1362
1466
|
const fadeOptions = linkFade({
|
|
1363
1467
|
arrow: this.linkSettings.arrow,
|
|
1364
1468
|
arrowByHighlight: this.linkSettings.arrowByHighlight,
|
|
@@ -1392,9 +1496,10 @@ function getDrawLink() {
|
|
|
1392
1496
|
arrowBorderColor = highlightOptions.arrowBorderColor;
|
|
1393
1497
|
}
|
|
1394
1498
|
}
|
|
1499
|
+
const currentLinkHighlighted = this.highlightedLink === link;
|
|
1395
1500
|
if (this.highlightedNeighbors && this.highlightedLink) {
|
|
1396
1501
|
/** By Link Not Highlight */
|
|
1397
|
-
if (
|
|
1502
|
+
if (!currentLinkHighlighted) {
|
|
1398
1503
|
const fadeOptions = linkFade({
|
|
1399
1504
|
arrow: this.linkSettings.arrow,
|
|
1400
1505
|
arrowByHighlight: this.linkSettings.arrowByHighlight,
|
|
@@ -1446,8 +1551,8 @@ function getDrawLink() {
|
|
|
1446
1551
|
const isHasArrow = this.linkSettings.arrow && arrowAlpha > 0;
|
|
1447
1552
|
if (!this.linkSettings.curve) {
|
|
1448
1553
|
if (this.linkSettings.prettyDraw ||
|
|
1449
|
-
this.linkSettings.
|
|
1450
|
-
(this.
|
|
1554
|
+
(this.linkSettings.arrow && arrowAlpha > 0) ||
|
|
1555
|
+
(this.particles && (currentNodeHighlighted || currentLinkHighlighted))) {
|
|
1451
1556
|
const position = calculateLinkPositionByNode(xStart, yStart, xEnd, yEnd, link.source, link.target, isHasArrow ? arrowSize : 0);
|
|
1452
1557
|
xStart = position.xStart;
|
|
1453
1558
|
xEnd = position.xEnd;
|
|
@@ -1485,6 +1590,7 @@ function getDrawLink() {
|
|
|
1485
1590
|
link._cy = position.yControl;
|
|
1486
1591
|
link._ax = position.xEndArrow;
|
|
1487
1592
|
link._ay = position.yEndArrow;
|
|
1593
|
+
linkDistance = approximateQuadraticBezierLength(position.xStart, position.yStart, position.xControl, position.yControl, position.xEnd, position.yEnd);
|
|
1488
1594
|
this.context.beginPath();
|
|
1489
1595
|
this.context.moveTo(position.xStart, position.yStart);
|
|
1490
1596
|
this.context.quadraticCurveTo(position.xControl, position.yControl, position.xEnd, position.yEnd);
|
|
@@ -1495,69 +1601,46 @@ function getDrawLink() {
|
|
|
1495
1601
|
this.context.stroke();
|
|
1496
1602
|
}
|
|
1497
1603
|
/** Particle */
|
|
1498
|
-
if (this.linkSettings.particles &&
|
|
1499
|
-
(
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
(this.highlightedLink && this.highlightedLink === link))) {
|
|
1503
|
-
const particleSteps = this.linkSettings.particleFlexSpeed
|
|
1504
|
-
? linkDistance <= 0
|
|
1505
|
-
? 0
|
|
1506
|
-
: linkDistance * this.linkSettings.particleFlexSpeedCoefficient
|
|
1507
|
-
: linkOptions.particleSteps;
|
|
1508
|
-
const particleCount = linkOptions.particleCount;
|
|
1509
|
-
if (!this.particles[id]) {
|
|
1510
|
-
const sourceId = link.source.id;
|
|
1511
|
-
const targetId = link.target.id;
|
|
1604
|
+
if (this.linkSettings.particles && (currentLinkHighlighted || currentNodeHighlighted)) {
|
|
1605
|
+
const speed = (1 / this.linkSettings.particleSpeedByDistance) * (1 + 0.5);
|
|
1606
|
+
if (!this.particles[index]) {
|
|
1607
|
+
const particleCount = Math.max(1, Math.floor(linkDistance / this.linkSettings.particleCountByDistance));
|
|
1512
1608
|
const particles = [];
|
|
1513
|
-
let prevParticle;
|
|
1514
1609
|
for (let i = 0; i < particleCount; i++) {
|
|
1515
1610
|
const particle = {
|
|
1516
|
-
step: 0,
|
|
1517
|
-
sourceId,
|
|
1518
|
-
targetId,
|
|
1519
|
-
prev: prevParticle,
|
|
1520
|
-
next: undefined,
|
|
1521
1611
|
index: i,
|
|
1522
1612
|
};
|
|
1523
|
-
if (prevParticle)
|
|
1524
|
-
prevParticle.next = particle;
|
|
1525
1613
|
particles.push(particle);
|
|
1526
|
-
prevParticle = particle;
|
|
1527
1614
|
}
|
|
1528
|
-
|
|
1529
|
-
particles[0].prev = particles[particles.length - 1];
|
|
1530
|
-
particles[particles.length - 1].next = particles[0];
|
|
1531
|
-
}
|
|
1532
|
-
this.particles[id] = particles;
|
|
1615
|
+
this.particles[index] = particles;
|
|
1533
1616
|
}
|
|
1534
|
-
|
|
1535
|
-
this.
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
if (particle.x != undefined && particle.y != undefined) {
|
|
1549
|
-
this.context.beginPath();
|
|
1550
|
-
this.context.strokeStyle = linkOptions.particleBorderColor;
|
|
1551
|
-
this.context.lineWidth = linkOptions.particleBorderWidth;
|
|
1552
|
-
this.context.arc(particle.x, particle.y, linkOptions.particleRadius, 0, Math.PI * 2);
|
|
1553
|
-
this.context.fillStyle = linkOptions.particleColor;
|
|
1554
|
-
this.context.fill();
|
|
1555
|
-
if (linkOptions.particleBorderWidth > 0) {
|
|
1556
|
-
this.context.stroke();
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1617
|
+
this.particles[index].forEach((particle, _, particles) => {
|
|
1618
|
+
if (!this.context)
|
|
1619
|
+
return;
|
|
1620
|
+
getParticlePosition({
|
|
1621
|
+
distance: linkDistance,
|
|
1622
|
+
particle,
|
|
1623
|
+
totalCount: particles.length,
|
|
1624
|
+
xEnd,
|
|
1625
|
+
xStart,
|
|
1626
|
+
yEnd,
|
|
1627
|
+
yStart,
|
|
1628
|
+
xControl,
|
|
1629
|
+
yControl,
|
|
1630
|
+
speed,
|
|
1559
1631
|
});
|
|
1560
|
-
|
|
1632
|
+
if (particle.x != undefined && particle.y != undefined) {
|
|
1633
|
+
this.context.beginPath();
|
|
1634
|
+
this.context.strokeStyle = linkOptions.particleBorderColor;
|
|
1635
|
+
this.context.lineWidth = linkOptions.particleBorderWidth;
|
|
1636
|
+
this.context.arc(particle.x, particle.y, linkOptions.particleRadius, 0, Math.PI * 2);
|
|
1637
|
+
this.context.fillStyle = linkOptions.particleColor;
|
|
1638
|
+
this.context.fill();
|
|
1639
|
+
if (linkOptions.particleBorderWidth > 0) {
|
|
1640
|
+
this.context.stroke();
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1561
1644
|
}
|
|
1562
1645
|
/** Arrow */
|
|
1563
1646
|
if (this.linkSettings.arrow && arrowAlpha > 0) {
|
|
@@ -1641,7 +1724,7 @@ function getTextLines({ context, textAlign, textColor, textFont, textStyle, text
|
|
|
1641
1724
|
}
|
|
1642
1725
|
|
|
1643
1726
|
function getDrawNode(nodeRenders, textRenders) {
|
|
1644
|
-
return function drawNode(node) {
|
|
1727
|
+
return function drawNode(node, index) {
|
|
1645
1728
|
if (!this.context || !node.x || !node.y)
|
|
1646
1729
|
return;
|
|
1647
1730
|
if (node.visible != undefined && !node.visible) {
|
|
@@ -1650,7 +1733,7 @@ function getDrawNode(nodeRenders, textRenders) {
|
|
|
1650
1733
|
node._height = 0;
|
|
1651
1734
|
return;
|
|
1652
1735
|
}
|
|
1653
|
-
const nodeOptions = this.nodeOptionsCache[
|
|
1736
|
+
const nodeOptions = this.nodeOptionsCache[index];
|
|
1654
1737
|
if (!nodeOptions)
|
|
1655
1738
|
return;
|
|
1656
1739
|
if (nodeOptions.nodeDraw && nodeOptions.textDraw) {
|
|
@@ -1809,7 +1892,7 @@ function getDrawNode(nodeRenders, textRenders) {
|
|
|
1809
1892
|
this.context.lineWidth = borderWidth;
|
|
1810
1893
|
this.context.strokeStyle = borderColor;
|
|
1811
1894
|
this.context.fillStyle = color;
|
|
1812
|
-
const labelLines = this.cachedNodeLabel[
|
|
1895
|
+
const labelLines = this.cachedNodeLabel[index];
|
|
1813
1896
|
switch (nodeOptions.shape) {
|
|
1814
1897
|
case "circle": {
|
|
1815
1898
|
if (!node.image) {
|
|
@@ -1935,7 +2018,7 @@ function getDrawNode(nodeRenders, textRenders) {
|
|
|
1935
2018
|
});
|
|
1936
2019
|
}
|
|
1937
2020
|
/** Text draw */
|
|
1938
|
-
const textLines = this.cachedNodeText[
|
|
2021
|
+
const textLines = this.cachedNodeText[index];
|
|
1939
2022
|
if (nodeOptions.textVisible && nodeOptions.text && textLines) {
|
|
1940
2023
|
textRenders.push(() => {
|
|
1941
2024
|
if (nodeOptions.textDraw) {
|
|
@@ -1995,57 +2078,7 @@ function getDrawNode(nodeRenders, textRenders) {
|
|
|
1995
2078
|
}
|
|
1996
2079
|
|
|
1997
2080
|
function initDraw() {
|
|
1998
|
-
|
|
1999
|
-
function calculateHighlight(recursive) {
|
|
2000
|
-
if (hasHighlight && !recursive)
|
|
2001
|
-
return;
|
|
2002
|
-
/** animation up */
|
|
2003
|
-
if (!this.highlightWorking && this.highlightProgress > 0) {
|
|
2004
|
-
const highlightDownStep = 1 / this.highlightSettings.highlightDownFrames;
|
|
2005
|
-
this.highlightProgress -= highlightDownStep;
|
|
2006
|
-
if (!this.simulationWorking) {
|
|
2007
|
-
hasHighlight = true;
|
|
2008
|
-
return void requestAnimationFrame(() => this.draw(true));
|
|
2009
|
-
}
|
|
2010
|
-
if (!this.linkSettings.particles) {
|
|
2011
|
-
hasHighlight = false;
|
|
2012
|
-
return;
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
/** animation down */
|
|
2016
|
-
if (this.highlightWorking && this.highlightProgress < 1) {
|
|
2017
|
-
const highlightUpStep = 1 / this.highlightSettings.highlightUpFrames;
|
|
2018
|
-
this.highlightProgress += highlightUpStep;
|
|
2019
|
-
if (!this.simulationWorking) {
|
|
2020
|
-
hasHighlight = true;
|
|
2021
|
-
return void requestAnimationFrame(() => this.draw(true));
|
|
2022
|
-
}
|
|
2023
|
-
if (!this.linkSettings.particles) {
|
|
2024
|
-
hasHighlight = false;
|
|
2025
|
-
return;
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
/** animation active */
|
|
2029
|
-
if (this.linkSettings.particles && this.highlightWorking && !this.simulationWorking) {
|
|
2030
|
-
hasHighlight = true;
|
|
2031
|
-
return void requestAnimationFrame(() => this.draw(true));
|
|
2032
|
-
}
|
|
2033
|
-
/** animation stop */
|
|
2034
|
-
if (!this.highlightWorking && this.highlightProgress <= 0) {
|
|
2035
|
-
if (this.highlightedNeighbors || this.highlightedNode || this.highlightedLink) {
|
|
2036
|
-
this.highlightedNeighbors = null;
|
|
2037
|
-
this.highlightedNode = null;
|
|
2038
|
-
this.highlightedLink = null;
|
|
2039
|
-
this.particles = {};
|
|
2040
|
-
if (!this.simulationWorking) {
|
|
2041
|
-
hasHighlight = true;
|
|
2042
|
-
return void requestAnimationFrame(() => this.draw(true));
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
hasHighlight = false;
|
|
2047
|
-
}
|
|
2048
|
-
function draw(recursive = false) {
|
|
2081
|
+
function draw() {
|
|
2049
2082
|
if (!this.context)
|
|
2050
2083
|
return;
|
|
2051
2084
|
if (this.listeners.onDraw) {
|
|
@@ -2066,7 +2099,6 @@ function initDraw() {
|
|
|
2066
2099
|
textRenders.forEach((render) => render());
|
|
2067
2100
|
this.context.restore();
|
|
2068
2101
|
this.listeners.onDrawFinished?.call?.(this);
|
|
2069
|
-
calculateHighlight.bind(this)(recursive);
|
|
2070
2102
|
}
|
|
2071
2103
|
if (this.graphSettings.showDrawTime) {
|
|
2072
2104
|
return setDrawTime(draw.bind(this), this.graphSettings.showDrawTimeEveryTick);
|
|
@@ -2120,38 +2152,7 @@ function initPointer() {
|
|
|
2120
2152
|
else {
|
|
2121
2153
|
this.area.style.cursor = "default";
|
|
2122
2154
|
}
|
|
2123
|
-
|
|
2124
|
-
this.highlightedNode = currentNode;
|
|
2125
|
-
this.highlightedLink = null;
|
|
2126
|
-
this.highlightedNeighbors = new Set(this.highlightedNode?.neighbors ?? []);
|
|
2127
|
-
this.highlightWorking = true;
|
|
2128
|
-
if (!this.simulationWorking)
|
|
2129
|
-
requestAnimationFrame(() => {
|
|
2130
|
-
this.draw();
|
|
2131
|
-
});
|
|
2132
|
-
}
|
|
2133
|
-
else if (currentLink &&
|
|
2134
|
-
highlightLink &&
|
|
2135
|
-
checkType(currentLink.source, lodash.isObject(currentLink.source)) &&
|
|
2136
|
-
checkType(currentLink.target, lodash.isObject(currentLink.target)) &&
|
|
2137
|
-
this.highlightedLink !== currentLink) {
|
|
2138
|
-
this.highlightProgress = 0;
|
|
2139
|
-
this.highlightedLink = currentLink;
|
|
2140
|
-
this.highlightedNode = null;
|
|
2141
|
-
this.highlightedNeighbors = new Set([currentLink.source.id, currentLink.target.id]);
|
|
2142
|
-
this.highlightWorking = true;
|
|
2143
|
-
if (!this.simulationWorking)
|
|
2144
|
-
requestAnimationFrame(() => {
|
|
2145
|
-
this.draw();
|
|
2146
|
-
});
|
|
2147
|
-
}
|
|
2148
|
-
else if (!currentNode && !currentLink && (this.highlightedNode || this.highlightedLink)) {
|
|
2149
|
-
this.highlightWorking = false;
|
|
2150
|
-
if (!this.simulationWorking)
|
|
2151
|
-
requestAnimationFrame(() => {
|
|
2152
|
-
this.draw();
|
|
2153
|
-
});
|
|
2154
|
-
}
|
|
2155
|
+
this.animateHighlight(highlightNode ? currentNode : undefined, highlightLink ? currentLink : undefined);
|
|
2155
2156
|
if (!this.listeners.onMove)
|
|
2156
2157
|
return;
|
|
2157
2158
|
if (!currentNode && !checkHighlightNode)
|
|
@@ -2321,132 +2322,42 @@ function initResize() {
|
|
|
2321
2322
|
observer.observe(this.area);
|
|
2322
2323
|
}
|
|
2323
2324
|
|
|
2324
|
-
function
|
|
2325
|
-
if (!this.
|
|
2326
|
-
this.simulation = d3Force.forceSimulation()
|
|
2327
|
-
.nodes(this.nodes)
|
|
2328
|
-
.force("link", d3Force.forceLink(this.links).id(this.nodeSettings.idGetter.bind(this)))
|
|
2329
|
-
.on("tick", () => {
|
|
2330
|
-
this.draw();
|
|
2331
|
-
})
|
|
2332
|
-
.on("end", () => {
|
|
2333
|
-
this.listeners.onSimulationEnd?.call?.(this);
|
|
2334
|
-
if (this.graphSettings.showDrawTime) {
|
|
2335
|
-
getDrawTime();
|
|
2336
|
-
// eslint-disable-next-line no-console
|
|
2337
|
-
console.log(`nodes: ${this.nodes.length} | links: ${this.links.length}`);
|
|
2338
|
-
resetDrawTime();
|
|
2339
|
-
}
|
|
2340
|
-
});
|
|
2341
|
-
initSimulationForces.call(this);
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
function initSimulationForces() {
|
|
2345
|
-
if (!this.simulation)
|
|
2346
|
-
return;
|
|
2347
|
-
const linkForce = this.simulation.force("link");
|
|
2348
|
-
if (!linkForce)
|
|
2349
|
-
return;
|
|
2350
|
-
if ((!this.forceSettings.forces || !this.forceSettings.xForce) && this.simulation.force("x")) {
|
|
2351
|
-
this.simulation.force("x", null);
|
|
2352
|
-
}
|
|
2353
|
-
if ((!this.forceSettings.forces || !this.forceSettings.yForce) && this.simulation.force("y")) {
|
|
2354
|
-
this.simulation.force("y", null);
|
|
2355
|
-
}
|
|
2356
|
-
if ((!this.forceSettings.forces || !this.forceSettings.chargeForce) &&
|
|
2357
|
-
this.simulation.force("charge")) {
|
|
2358
|
-
this.simulation.force("charge", null);
|
|
2359
|
-
}
|
|
2360
|
-
if ((!this.forceSettings.forces || !this.forceSettings.centerForce) &&
|
|
2361
|
-
this.simulation.force("center")) {
|
|
2362
|
-
this.simulation.force("center", null);
|
|
2363
|
-
}
|
|
2364
|
-
if (!this.forceSettings.forces || !this.forceSettings.linkForce) {
|
|
2365
|
-
linkForce.distance(0).strength(0).iterations(0);
|
|
2366
|
-
}
|
|
2367
|
-
if (this.forceSettings.forces && this.forceSettings.linkForce) {
|
|
2368
|
-
linkForce
|
|
2369
|
-
.distance(this.forceSettings.linkDistance)
|
|
2370
|
-
.strength(this.forceSettings.linkStrength)
|
|
2371
|
-
.iterations(this.forceSettings.linkIterations);
|
|
2372
|
-
}
|
|
2373
|
-
if (this.forceSettings.forces && this.forceSettings.xForce) {
|
|
2374
|
-
this.simulation.force("x", d3Force.forceX(this.forceSettings.xPosition).strength(this.forceSettings.xStrength));
|
|
2375
|
-
}
|
|
2376
|
-
if (this.forceSettings.forces && this.forceSettings.yForce) {
|
|
2377
|
-
this.simulation.force("y", d3Force.forceY(this.forceSettings.yPosition).strength(this.forceSettings.yStrength));
|
|
2378
|
-
}
|
|
2379
|
-
if (this.forceSettings.forces && this.forceSettings.chargeForce) {
|
|
2380
|
-
this.simulation.force("charge", d3Force.forceManyBody()
|
|
2381
|
-
.strength(this.forceSettings.chargeStrength)
|
|
2382
|
-
.distanceMax(Infinity)
|
|
2383
|
-
.distanceMin(this.forceSettings.chargeDistanceMin));
|
|
2384
|
-
}
|
|
2385
|
-
if (this.forceSettings.forces && this.forceSettings.centerForce) {
|
|
2386
|
-
this.simulation.force("center", d3Force.forceCenter(this.forceSettings.centerPosition.x ?? 0, this.forceSettings.centerPosition.y ?? 0).strength(this.forceSettings.centerStrength));
|
|
2387
|
-
}
|
|
2388
|
-
initCollideForce.call(this, true);
|
|
2389
|
-
}
|
|
2390
|
-
function initCollideForce(forceUpdate) {
|
|
2391
|
-
if (!this.simulation)
|
|
2325
|
+
function updateLinkCache() {
|
|
2326
|
+
if (!this.context)
|
|
2392
2327
|
return;
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
this.
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
this.simulation.force("collide", null);
|
|
2328
|
+
const current = this.areaTransform.k;
|
|
2329
|
+
if (this.linkSettings.smartCache &&
|
|
2330
|
+
this._lastLinkZoomK !== undefined &&
|
|
2331
|
+
this.linkOptionsCache.length > 0) {
|
|
2332
|
+
const corner = this.linkSettings.linkScaleSwitch;
|
|
2333
|
+
const prev = this._lastLinkZoomK;
|
|
2334
|
+
this._lastLinkZoomK = current;
|
|
2335
|
+
if (!((prev <= corner && current >= corner) || (prev >= corner && current <= corner))) {
|
|
2336
|
+
return;
|
|
2403
2337
|
}
|
|
2404
|
-
|
|
2405
|
-
this.
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
return nodeIterationExtractor(node, index, this.nodes, this, this.forceSettings.collideRadius, undefined);
|
|
2414
|
-
}
|
|
2415
|
-
return nodeOptions.radius + this.forceSettings.collideAdditionalRadius;
|
|
2416
|
-
}
|
|
2417
|
-
case "square": {
|
|
2418
|
-
return (Math.sqrt(nodeOptions.width ** 2 + nodeOptions.height ** 2) / 2 +
|
|
2419
|
-
this.forceSettings.collideAdditionalRadius);
|
|
2420
|
-
}
|
|
2421
|
-
case "text": {
|
|
2422
|
-
return (Math.sqrt(nodeOptions.width ** 2 + nodeOptions.height ** 2) / 2 +
|
|
2423
|
-
this.forceSettings.collideAdditionalRadius);
|
|
2424
|
-
}
|
|
2425
|
-
default: {
|
|
2426
|
-
if (this.forceSettings.collideRadius) {
|
|
2427
|
-
return nodeIterationExtractor(node, index, this.nodes, this, this.forceSettings.collideRadius, undefined);
|
|
2428
|
-
}
|
|
2429
|
-
return nodeOptions.radius + this.forceSettings.collideAdditionalRadius;
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
})
|
|
2433
|
-
.strength(this.forceSettings.collideStrength)
|
|
2434
|
-
.iterations(this.forceSettings.collideIterations));
|
|
2338
|
+
for (let i = 0; i < this.links.length; i++) {
|
|
2339
|
+
const link = this.links[i];
|
|
2340
|
+
const linkOptions = linkIterationExtractor(link, i, this.links, this, this.linkSettings.options ?? {}, linkOptionsGetter);
|
|
2341
|
+
const cache = this.linkOptionsCache[i];
|
|
2342
|
+
if (cache) {
|
|
2343
|
+
cache.color = linkOptions.color;
|
|
2344
|
+
cache.arrowColor = linkOptions.arrowColor;
|
|
2345
|
+
cache.width = linkOptions.width;
|
|
2346
|
+
}
|
|
2435
2347
|
}
|
|
2348
|
+
return;
|
|
2436
2349
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
function updateLinkCache() {
|
|
2440
|
-
this.linkOptionsCache = {};
|
|
2350
|
+
this._lastLinkZoomK = current;
|
|
2351
|
+
this.linkOptionsCache.length = 0;
|
|
2441
2352
|
const groupMap = {};
|
|
2442
2353
|
const groupSelfMap = {};
|
|
2443
2354
|
for (let i = 0; i < this.links.length; i++) {
|
|
2444
2355
|
const link = this.links[i];
|
|
2445
2356
|
const { sourceId, targetId } = extractLinkPointIds(link);
|
|
2446
2357
|
const linkOptions = linkIterationExtractor(link, i, this.links, this, this.linkSettings.options ?? {}, linkOptionsGetter);
|
|
2447
|
-
|
|
2448
|
-
this.linkOptionsCache[id] = linkOptions;
|
|
2358
|
+
this.linkOptionsCache[i] = linkOptions;
|
|
2449
2359
|
if (this.linkSettings.curve) {
|
|
2360
|
+
const id = `${targetId}${sourceId}`;
|
|
2450
2361
|
if (sourceId === targetId) {
|
|
2451
2362
|
groupSelfMap[id] ??= 0;
|
|
2452
2363
|
link._groupIndex = ++groupSelfMap[id];
|
|
@@ -2477,13 +2388,34 @@ function indexToPosition(n) {
|
|
|
2477
2388
|
// return n & 1 ? -(n >> 1) : n >> 1;
|
|
2478
2389
|
}
|
|
2479
2390
|
|
|
2480
|
-
function updateNodeCache(
|
|
2481
|
-
this.nodeOptionsCache = {};
|
|
2391
|
+
function updateNodeCache() {
|
|
2482
2392
|
if (!this.context)
|
|
2483
2393
|
return;
|
|
2394
|
+
const currentZoom = this.areaTransform.k;
|
|
2395
|
+
const options = this.nodeOptionsCache.length === 0;
|
|
2396
|
+
const label = this.cachedNodeLabel.length === 0;
|
|
2397
|
+
let text = this.cachedNodeText.length === 0;
|
|
2398
|
+
if (this.nodeSettings.smartCache &&
|
|
2399
|
+
this._lastNodeZoomK !== undefined &&
|
|
2400
|
+
!options &&
|
|
2401
|
+
!text &&
|
|
2402
|
+
!label) {
|
|
2403
|
+
const thresholds = this.nodeSettings.textScaleSteps;
|
|
2404
|
+
if (thresholds && thresholds.length > 0) {
|
|
2405
|
+
const min = Math.min(this._lastNodeZoomK, currentZoom);
|
|
2406
|
+
const max = Math.max(this._lastNodeZoomK, currentZoom);
|
|
2407
|
+
const crossed = thresholds.some((t) => t >= min && t <= max);
|
|
2408
|
+
this._lastNodeZoomK = currentZoom;
|
|
2409
|
+
if (!crossed)
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
this.cachedNodeText.length = 0;
|
|
2413
|
+
text = true;
|
|
2414
|
+
}
|
|
2415
|
+
this._lastNodeZoomK = currentZoom;
|
|
2484
2416
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
2485
2417
|
const node = this.nodes[i];
|
|
2486
|
-
if (
|
|
2418
|
+
if (options) {
|
|
2487
2419
|
const nodeOptions = nodeIterationExtractor(node, i, this.nodes, this, this.nodeSettings.options ?? {}, nodeOptionsGetter);
|
|
2488
2420
|
const radius = nodeOptions.shape === "circle"
|
|
2489
2421
|
? nodeRadiusGetter({
|
|
@@ -2534,11 +2466,19 @@ function updateNodeCache(types) {
|
|
|
2534
2466
|
nodeOptions.height = node.visible === false ? 0 : height;
|
|
2535
2467
|
nodeOptions.radius = node.visible === false ? 0 : radius;
|
|
2536
2468
|
nodeOptions.labelSize = labelSize;
|
|
2537
|
-
this.nodeOptionsCache[
|
|
2469
|
+
this.nodeOptionsCache[i] = nodeOptions;
|
|
2538
2470
|
}
|
|
2539
|
-
|
|
2471
|
+
let nodeOptions = this.nodeOptionsCache[i];
|
|
2472
|
+
if (!nodeOptions)
|
|
2473
|
+
continue;
|
|
2540
2474
|
/** label */
|
|
2541
|
-
if (
|
|
2475
|
+
if (label) {
|
|
2476
|
+
if (!options) {
|
|
2477
|
+
nodeOptions = nodeIterationExtractor(node, i, this.nodes, this, this.nodeSettings.options ?? {}, nodeOptionsGetter);
|
|
2478
|
+
const cache = this.nodeOptionsCache[i];
|
|
2479
|
+
if (cache)
|
|
2480
|
+
cache.label = nodeOptions.label;
|
|
2481
|
+
}
|
|
2542
2482
|
/** label in not text shape */
|
|
2543
2483
|
if (nodeOptions.shape !== "text" && nodeOptions.label) {
|
|
2544
2484
|
this.context.font = `${nodeOptions.labelStyle} normal ${nodeOptions.labelWeight} ${nodeOptions.labelSize}px ${nodeOptions.labelFont}`;
|
|
@@ -2546,7 +2486,7 @@ function updateNodeCache(types) {
|
|
|
2546
2486
|
this.context.textAlign = nodeOptions.labelAlign;
|
|
2547
2487
|
if (nodeOptions.labelWidth == undefined ||
|
|
2548
2488
|
this.context.measureText(nodeOptions.label).width <= nodeOptions.labelWidth) {
|
|
2549
|
-
this.cachedNodeLabel[
|
|
2489
|
+
this.cachedNodeLabel[i] = [nodeOptions.label];
|
|
2550
2490
|
}
|
|
2551
2491
|
const { lines } = getTextLines({
|
|
2552
2492
|
context: this.context,
|
|
@@ -2559,7 +2499,7 @@ function updateNodeCache(types) {
|
|
|
2559
2499
|
textStyle: nodeOptions.labelStyle,
|
|
2560
2500
|
textWeight: nodeOptions.labelWeight,
|
|
2561
2501
|
});
|
|
2562
|
-
this.cachedNodeLabel[
|
|
2502
|
+
this.cachedNodeLabel[i] = lines;
|
|
2563
2503
|
/** label in text shape */
|
|
2564
2504
|
}
|
|
2565
2505
|
else if (nodeOptions.shape === "text" && nodeOptions.label) {
|
|
@@ -2583,18 +2523,29 @@ function updateNodeCache(types) {
|
|
|
2583
2523
|
(lines.length - 1) * nodeOptions.labelGap +
|
|
2584
2524
|
nodeOptions.labelYPadding;
|
|
2585
2525
|
nodeOptions.width = maxSize + nodeOptions.labelXPadding;
|
|
2586
|
-
this.cachedNodeLabel[
|
|
2526
|
+
this.cachedNodeLabel[i] = lines;
|
|
2527
|
+
}
|
|
2528
|
+
else {
|
|
2529
|
+
this.cachedNodeLabel[i] = [];
|
|
2587
2530
|
}
|
|
2588
2531
|
}
|
|
2589
2532
|
/** text */
|
|
2590
|
-
if (
|
|
2533
|
+
if (text) {
|
|
2534
|
+
if (!options) {
|
|
2535
|
+
nodeOptions = nodeIterationExtractor(node, i, this.nodes, this, this.nodeSettings.options ?? {}, nodeOptionsGetter);
|
|
2536
|
+
const cache = this.nodeOptionsCache[i];
|
|
2537
|
+
if (cache) {
|
|
2538
|
+
cache.text = nodeOptions.text;
|
|
2539
|
+
cache.textVisible = nodeOptions.textVisible;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
if (!nodeOptions.text) {
|
|
2543
|
+
this.cachedNodeText[i] = [];
|
|
2544
|
+
return;
|
|
2545
|
+
}
|
|
2591
2546
|
this.context.font = `${nodeOptions.textStyle} normal ${nodeOptions.textWeight} ${nodeOptions.textSize}px ${nodeOptions.textFont}`;
|
|
2592
2547
|
this.context.fillStyle = nodeOptions.textColor;
|
|
2593
2548
|
this.context.textAlign = nodeOptions.textAlign;
|
|
2594
|
-
if (nodeOptions.textWidth == undefined ||
|
|
2595
|
-
this.context.measureText(nodeOptions.text).width <= nodeOptions.textWidth) {
|
|
2596
|
-
this.cachedNodeText[node.id] = [nodeOptions.text];
|
|
2597
|
-
}
|
|
2598
2549
|
const { lines } = getTextLines({
|
|
2599
2550
|
context: this.context,
|
|
2600
2551
|
maxWidth: nodeOptions.textWidth,
|
|
@@ -2606,49 +2557,230 @@ function updateNodeCache(types) {
|
|
|
2606
2557
|
textStyle: nodeOptions.textStyle,
|
|
2607
2558
|
textWeight: nodeOptions.textWeight,
|
|
2608
2559
|
});
|
|
2609
|
-
this.cachedNodeText[
|
|
2560
|
+
this.cachedNodeText[i] = lines;
|
|
2610
2561
|
}
|
|
2611
2562
|
}
|
|
2612
2563
|
}
|
|
2613
2564
|
|
|
2565
|
+
const DEFAULT_ZOOM_EXTENT = [0.3, 10];
|
|
2566
|
+
const DEFAULT_TRANSLATE_EXTENT_COEFFICIENT = 8;
|
|
2567
|
+
const DEFAULT_MARGIN = 0.25;
|
|
2614
2568
|
function initZoom(currentZoom) {
|
|
2615
2569
|
if (!this.area)
|
|
2616
2570
|
throw new Error("bad init data");
|
|
2571
|
+
const bounds = computeGraphBounds(this.nodes);
|
|
2572
|
+
const zoomExtent = resolveZoomExtent(this.graphSettings.zoomExtent, this.width, this.height, this.graphSettings.zoomExtentMargin ?? DEFAULT_MARGIN, bounds);
|
|
2573
|
+
const translateExtent = resolveTranslateExtent(this.graphSettings.translateExtent, this.graphSettings.translateExtentCoefficient, this.graphSettings.translateExtentOverlap, this.width, this.height, zoomExtent, bounds);
|
|
2574
|
+
this._translateExtent = translateExtent;
|
|
2617
2575
|
const zoomInstance = d3Zoom.zoom()
|
|
2618
|
-
.scaleExtent(
|
|
2576
|
+
.scaleExtent(zoomExtent)
|
|
2619
2577
|
.on("zoom", (event) => {
|
|
2578
|
+
if (this._zoomAnimating)
|
|
2579
|
+
return;
|
|
2620
2580
|
this.listeners.onZoom?.call?.(this, event);
|
|
2621
2581
|
const oldTransform = this.areaTransform;
|
|
2622
2582
|
this.areaTransform = event.transform;
|
|
2623
2583
|
if (this.areaTransform.k !== oldTransform.k) {
|
|
2624
2584
|
updateLinkCache.call(this);
|
|
2625
|
-
updateNodeCache.call(this
|
|
2585
|
+
updateNodeCache.call(this);
|
|
2626
2586
|
}
|
|
2627
|
-
|
|
2628
|
-
requestAnimationFrame(() => this.draw());
|
|
2587
|
+
this.tick();
|
|
2629
2588
|
});
|
|
2630
|
-
|
|
2631
|
-
const coefficient = this.graphSettings.translateExtentCoefficient;
|
|
2632
|
-
const [coefficientX, coefficientY] = jsHelpers.isArray(coefficient)
|
|
2633
|
-
? coefficient
|
|
2634
|
-
: [coefficient, coefficient];
|
|
2635
|
-
const [[minX = -this.width * coefficientX, minY = -this.height * coefficientY], [maxX = this.width * coefficientX, maxY = this.height * coefficientY],] = this.graphSettings.translateExtent;
|
|
2636
|
-
zoomInstance.translateExtent([
|
|
2637
|
-
[minX, minY],
|
|
2638
|
-
[maxX, maxY],
|
|
2639
|
-
]);
|
|
2640
|
-
}
|
|
2589
|
+
zoomInstance.translateExtent(translateExtent);
|
|
2641
2590
|
d3Selection.select(this.area).call(zoomInstance).on("dblclick.zoom", null);
|
|
2642
2591
|
const zoomInitial = currentZoom ?? this.graphSettings.zoomInitial;
|
|
2643
|
-
this.areaTransform = new d3Zoom.ZoomTransform(zoomInitial?.k ?? 1, zoomInitial?.x ?? this.width / 2, zoomInitial?.y ?? this.height / 2);
|
|
2592
|
+
this.areaTransform = new d3Zoom.ZoomTransform(Math.max(zoomInitial?.k ?? 1, zoomExtent[0]), zoomInitial?.x ?? this.width / 2, zoomInitial?.y ?? this.height / 2);
|
|
2644
2593
|
d3Zoom.zoom().transform(d3Selection.select(this.area), this.areaTransform);
|
|
2645
2594
|
}
|
|
2595
|
+
function resolveZoomExtent(userExtent, viewWidth, viewHeight, margin, bounds) {
|
|
2596
|
+
if (userExtent &&
|
|
2597
|
+
userExtent.length === 2 &&
|
|
2598
|
+
userExtent[0] != undefined &&
|
|
2599
|
+
userExtent[1] != undefined) {
|
|
2600
|
+
return userExtent;
|
|
2601
|
+
}
|
|
2602
|
+
if (!bounds)
|
|
2603
|
+
return DEFAULT_ZOOM_EXTENT;
|
|
2604
|
+
const graphWidth = bounds.maxX - bounds.minX;
|
|
2605
|
+
const graphHeight = bounds.maxY - bounds.minY;
|
|
2606
|
+
if (graphWidth === 0 || graphHeight === 0)
|
|
2607
|
+
return DEFAULT_ZOOM_EXTENT;
|
|
2608
|
+
const scaleToFit = (1 - margin) / Math.max(graphWidth / viewWidth, graphHeight / viewHeight);
|
|
2609
|
+
return [scaleToFit, DEFAULT_ZOOM_EXTENT[1]];
|
|
2610
|
+
}
|
|
2611
|
+
function resolveTranslateExtent(userExtent, userCoefficient, overlap, viewWidth, viewHeight, zoomExtent, bounds) {
|
|
2612
|
+
if (userExtent && userExtent.length >= 2 && jsHelpers.isArray(userExtent[0]) && jsHelpers.isArray(userExtent[1])) {
|
|
2613
|
+
return [
|
|
2614
|
+
[
|
|
2615
|
+
userExtent[0][0] ?? -viewWidth * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2616
|
+
userExtent[0][1] ?? -viewHeight * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2617
|
+
],
|
|
2618
|
+
[
|
|
2619
|
+
userExtent[1][0] ?? viewWidth * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2620
|
+
userExtent[1][1] ?? viewHeight * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2621
|
+
],
|
|
2622
|
+
];
|
|
2623
|
+
}
|
|
2624
|
+
if (userCoefficient) {
|
|
2625
|
+
const [cx, cy] = jsHelpers.isArray(userCoefficient)
|
|
2626
|
+
? userCoefficient
|
|
2627
|
+
: [userCoefficient, userCoefficient];
|
|
2628
|
+
return [
|
|
2629
|
+
[-viewWidth * cx, -viewHeight * cy],
|
|
2630
|
+
[viewWidth * cx, viewHeight * cy],
|
|
2631
|
+
];
|
|
2632
|
+
}
|
|
2633
|
+
if (!bounds) {
|
|
2634
|
+
return [
|
|
2635
|
+
[
|
|
2636
|
+
-viewWidth * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2637
|
+
-viewHeight * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2638
|
+
],
|
|
2639
|
+
[
|
|
2640
|
+
viewWidth * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2641
|
+
viewHeight * DEFAULT_TRANSLATE_EXTENT_COEFFICIENT,
|
|
2642
|
+
],
|
|
2643
|
+
];
|
|
2644
|
+
}
|
|
2645
|
+
const minZoom = zoomExtent[0];
|
|
2646
|
+
const zoomViewWidth = viewWidth / minZoom;
|
|
2647
|
+
const zoomViewHeight = viewHeight / minZoom;
|
|
2648
|
+
const graphWidth = bounds.maxX - bounds.minX;
|
|
2649
|
+
const graphHeight = bounds.maxY - bounds.minY;
|
|
2650
|
+
const minX = bounds.minX + overlap * graphWidth - zoomViewWidth;
|
|
2651
|
+
const maxX = bounds.maxX - overlap * graphWidth + zoomViewWidth;
|
|
2652
|
+
const minY = bounds.minY + overlap * graphHeight - zoomViewHeight;
|
|
2653
|
+
const maxY = bounds.maxY - overlap * graphHeight + zoomViewHeight;
|
|
2654
|
+
return [
|
|
2655
|
+
[minX, minY],
|
|
2656
|
+
[maxX, maxY],
|
|
2657
|
+
];
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
function initSimulation() {
|
|
2661
|
+
if (!this.simulation) {
|
|
2662
|
+
this.simulation = d3Force.forceSimulation()
|
|
2663
|
+
.nodes(this.nodes)
|
|
2664
|
+
.force("link", d3Force.forceLink(this.links).id(this.nodeSettings.idGetter.bind(this)))
|
|
2665
|
+
.on("tick", () => {
|
|
2666
|
+
this.draw();
|
|
2667
|
+
})
|
|
2668
|
+
.on("end", () => {
|
|
2669
|
+
this.listeners.onSimulationEnd?.call?.(this);
|
|
2670
|
+
if (this.area) {
|
|
2671
|
+
initZoom.call(this, this.areaTransform);
|
|
2672
|
+
}
|
|
2673
|
+
if (this.graphSettings.showDrawTime) {
|
|
2674
|
+
getDrawTime();
|
|
2675
|
+
// eslint-disable-next-line no-console
|
|
2676
|
+
console.log(`nodes: ${this.nodes.length} | links: ${this.links.length}`);
|
|
2677
|
+
resetDrawTime();
|
|
2678
|
+
}
|
|
2679
|
+
})
|
|
2680
|
+
.stop();
|
|
2681
|
+
initSimulationForces.call(this);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
function initSimulationForces() {
|
|
2685
|
+
if (!this.simulation)
|
|
2686
|
+
return;
|
|
2687
|
+
const linkForce = this.simulation.force("link");
|
|
2688
|
+
if (!linkForce)
|
|
2689
|
+
return;
|
|
2690
|
+
if ((!this.forceSettings.forces || !this.forceSettings.xForce) && this.simulation.force("x")) {
|
|
2691
|
+
this.simulation.force("x", null);
|
|
2692
|
+
}
|
|
2693
|
+
if ((!this.forceSettings.forces || !this.forceSettings.yForce) && this.simulation.force("y")) {
|
|
2694
|
+
this.simulation.force("y", null);
|
|
2695
|
+
}
|
|
2696
|
+
if ((!this.forceSettings.forces || !this.forceSettings.chargeForce) &&
|
|
2697
|
+
this.simulation.force("charge")) {
|
|
2698
|
+
this.simulation.force("charge", null);
|
|
2699
|
+
}
|
|
2700
|
+
if ((!this.forceSettings.forces || !this.forceSettings.centerForce) &&
|
|
2701
|
+
this.simulation.force("center")) {
|
|
2702
|
+
this.simulation.force("center", null);
|
|
2703
|
+
}
|
|
2704
|
+
if (!this.forceSettings.forces || !this.forceSettings.linkForce) {
|
|
2705
|
+
linkForce.distance(0).strength(0).iterations(0);
|
|
2706
|
+
}
|
|
2707
|
+
if (this.forceSettings.forces && this.forceSettings.linkForce) {
|
|
2708
|
+
linkForce
|
|
2709
|
+
.distance(this.forceSettings.linkDistance)
|
|
2710
|
+
.strength(this.forceSettings.linkStrength)
|
|
2711
|
+
.iterations(this.forceSettings.linkIterations);
|
|
2712
|
+
}
|
|
2713
|
+
if (this.forceSettings.forces && this.forceSettings.xForce) {
|
|
2714
|
+
this.simulation.force("x", d3Force.forceX(this.forceSettings.xPosition).strength(this.forceSettings.xStrength));
|
|
2715
|
+
}
|
|
2716
|
+
if (this.forceSettings.forces && this.forceSettings.yForce) {
|
|
2717
|
+
this.simulation.force("y", d3Force.forceY(this.forceSettings.yPosition).strength(this.forceSettings.yStrength));
|
|
2718
|
+
}
|
|
2719
|
+
if (this.forceSettings.forces && this.forceSettings.chargeForce) {
|
|
2720
|
+
this.simulation.force("charge", d3Force.forceManyBody()
|
|
2721
|
+
.strength(this.forceSettings.chargeStrength)
|
|
2722
|
+
.distanceMax(Infinity)
|
|
2723
|
+
.distanceMin(this.forceSettings.chargeDistanceMin));
|
|
2724
|
+
}
|
|
2725
|
+
if (this.forceSettings.forces && this.forceSettings.centerForce) {
|
|
2726
|
+
this.simulation.force("center", d3Force.forceCenter(this.forceSettings.centerPosition.x ?? 0, this.forceSettings.centerPosition.y ?? 0).strength(this.forceSettings.centerStrength));
|
|
2727
|
+
}
|
|
2728
|
+
initCollideForce.call(this, true);
|
|
2729
|
+
}
|
|
2730
|
+
function initCollideForce(forceUpdate) {
|
|
2731
|
+
if (!this.simulation)
|
|
2732
|
+
return;
|
|
2733
|
+
if ((!this.forceSettings.collideForce || !this.forceSettings.forces) &&
|
|
2734
|
+
this.simulation.force("collide")) {
|
|
2735
|
+
this.simulation.force("collide", null);
|
|
2736
|
+
}
|
|
2737
|
+
if (this.forceSettings.forces && this.forceSettings.collideForce) {
|
|
2738
|
+
const isHasMax = this.forceSettings.collideOffMax.links != 0 && this.forceSettings.collideOffMax.nodes != 0;
|
|
2739
|
+
const isMaxCollideNodes = isHasMax && this.forceSettings.collideOffMax.nodes < this.nodes.length;
|
|
2740
|
+
const isMaxCollideLinks = isHasMax && this.forceSettings.collideOffMax.links < this.links.length;
|
|
2741
|
+
if (isMaxCollideNodes && isMaxCollideLinks) {
|
|
2742
|
+
this.simulation.force("collide", null);
|
|
2743
|
+
}
|
|
2744
|
+
else if (!this.simulation.force("collide") || forceUpdate) {
|
|
2745
|
+
this.simulation.force("collide", d3Force.forceCollide()
|
|
2746
|
+
.radius((node, index) => {
|
|
2747
|
+
const nodeOptions = this.nodeOptionsCache[index];
|
|
2748
|
+
if (!nodeOptions)
|
|
2749
|
+
return 0;
|
|
2750
|
+
switch (nodeOptions.shape) {
|
|
2751
|
+
case "circle": {
|
|
2752
|
+
if (this.forceSettings.collideRadius) {
|
|
2753
|
+
return nodeIterationExtractor(node, index, this.nodes, this, this.forceSettings.collideRadius, undefined);
|
|
2754
|
+
}
|
|
2755
|
+
return nodeOptions.radius + this.forceSettings.collideAdditionalRadius;
|
|
2756
|
+
}
|
|
2757
|
+
case "square": {
|
|
2758
|
+
return (Math.sqrt(nodeOptions.width ** 2 + nodeOptions.height ** 2) / 2 +
|
|
2759
|
+
this.forceSettings.collideAdditionalRadius);
|
|
2760
|
+
}
|
|
2761
|
+
case "text": {
|
|
2762
|
+
return (Math.sqrt(nodeOptions.width ** 2 + nodeOptions.height ** 2) / 2 +
|
|
2763
|
+
this.forceSettings.collideAdditionalRadius);
|
|
2764
|
+
}
|
|
2765
|
+
default: {
|
|
2766
|
+
if (this.forceSettings.collideRadius) {
|
|
2767
|
+
return nodeIterationExtractor(node, index, this.nodes, this, this.forceSettings.collideRadius, undefined);
|
|
2768
|
+
}
|
|
2769
|
+
return nodeOptions.radius + this.forceSettings.collideAdditionalRadius;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
})
|
|
2773
|
+
.strength(this.forceSettings.collideStrength)
|
|
2774
|
+
.iterations(this.forceSettings.collideIterations));
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2646
2778
|
|
|
2647
2779
|
class GraphCanvas {
|
|
2648
2780
|
/** initial data */
|
|
2649
2781
|
nodes;
|
|
2650
2782
|
links;
|
|
2651
|
-
particles =
|
|
2783
|
+
particles = [];
|
|
2652
2784
|
width;
|
|
2653
2785
|
height;
|
|
2654
2786
|
root;
|
|
@@ -2665,19 +2797,25 @@ class GraphCanvas {
|
|
|
2665
2797
|
context;
|
|
2666
2798
|
simulation;
|
|
2667
2799
|
areaTransform = d3Zoom.zoomIdentity;
|
|
2800
|
+
_translateExtent;
|
|
2668
2801
|
areaRect;
|
|
2669
2802
|
draw;
|
|
2670
2803
|
eventAbortController;
|
|
2671
|
-
cachedNodeText =
|
|
2672
|
-
cachedNodeLabel =
|
|
2673
|
-
linkOptionsCache =
|
|
2674
|
-
nodeOptionsCache =
|
|
2804
|
+
cachedNodeText = [];
|
|
2805
|
+
cachedNodeLabel = [];
|
|
2806
|
+
linkOptionsCache = [];
|
|
2807
|
+
nodeOptionsCache = [];
|
|
2675
2808
|
isDragging = false;
|
|
2676
2809
|
highlightedNode = null;
|
|
2677
2810
|
highlightedLink = null;
|
|
2678
2811
|
highlightedNeighbors = null;
|
|
2679
2812
|
highlightProgress = 1;
|
|
2680
|
-
|
|
2813
|
+
highlightStart = null;
|
|
2814
|
+
highlightPositive = false;
|
|
2815
|
+
highlightController;
|
|
2816
|
+
_lastNodeZoomK;
|
|
2817
|
+
_lastLinkZoomK;
|
|
2818
|
+
_zoomAnimating = false;
|
|
2681
2819
|
get simulationWorking() {
|
|
2682
2820
|
const simulationAlpha = this.simulation?.alpha?.() ?? 0;
|
|
2683
2821
|
const simulationAlphaMin = this.simulation?.alphaMin?.() ?? 0;
|
|
@@ -2706,21 +2844,72 @@ class GraphCanvas {
|
|
|
2706
2844
|
get dpi() {
|
|
2707
2845
|
return devicePixelRatio;
|
|
2708
2846
|
}
|
|
2709
|
-
getData() {
|
|
2847
|
+
getData = () => {
|
|
2710
2848
|
return {
|
|
2711
2849
|
links: this.links,
|
|
2712
2850
|
nodes: this.nodes,
|
|
2713
2851
|
};
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2852
|
+
};
|
|
2853
|
+
fitToView = (margin = this.graphSettings.zoomToFitMargin, duration = this.graphSettings.zoomAnimationDuration) => {
|
|
2854
|
+
const area = this.area;
|
|
2855
|
+
if (!area)
|
|
2856
|
+
return;
|
|
2857
|
+
const bounds = computeGraphBounds(this.nodes);
|
|
2858
|
+
if (!bounds)
|
|
2859
|
+
return;
|
|
2860
|
+
const graphWidth = bounds.maxX - bounds.minX;
|
|
2861
|
+
const graphHeight = bounds.maxY - bounds.minY;
|
|
2862
|
+
if (graphWidth === 0 || graphHeight === 0)
|
|
2863
|
+
return;
|
|
2864
|
+
const graphCenterX = bounds.minX + graphWidth / 2;
|
|
2865
|
+
const graphCenterY = bounds.minY + graphHeight / 2;
|
|
2866
|
+
const scale = (1 - margin) / Math.max(graphWidth / this.width, graphHeight / this.height);
|
|
2867
|
+
const clampedScale = Math.min(scale, this.graphSettings.zoomExtent?.[1] ?? scale);
|
|
2868
|
+
const target = d3Zoom.zoomIdentity
|
|
2869
|
+
.translate(this.width / 2, this.height / 2)
|
|
2870
|
+
.scale(clampedScale)
|
|
2871
|
+
.translate(-graphCenterX, -graphCenterY);
|
|
2872
|
+
if (!this.graphSettings.zoomAnimation) {
|
|
2873
|
+
this.areaTransform = target;
|
|
2874
|
+
d3Zoom.zoom().transform(d3Selection.select(area), target);
|
|
2875
|
+
this.clearCache(true);
|
|
2876
|
+
this.tick();
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
this.animateZoom(area, target, this.areaTransform, duration);
|
|
2880
|
+
};
|
|
2881
|
+
focusOnNode = (nodeId, scale = this.graphSettings.zoomToNodeScale, duration = this.graphSettings.zoomAnimationDuration) => {
|
|
2882
|
+
const area = this.area;
|
|
2883
|
+
if (!area)
|
|
2884
|
+
return;
|
|
2885
|
+
const node = this.nodes.find((n) => n.id === nodeId);
|
|
2886
|
+
if (!node)
|
|
2887
|
+
return;
|
|
2888
|
+
if (node.x == undefined || node.y == undefined)
|
|
2889
|
+
return;
|
|
2890
|
+
const target = d3Zoom.zoomIdentity
|
|
2891
|
+
.translate(this.width / 2, this.height / 2)
|
|
2892
|
+
.scale(scale)
|
|
2893
|
+
.translate(-node.x, -node.y);
|
|
2894
|
+
if (!this.graphSettings.zoomAnimation) {
|
|
2895
|
+
this.areaTransform = target;
|
|
2896
|
+
d3Zoom.zoom().transform(d3Selection.select(area), target);
|
|
2897
|
+
this.clearCache(true);
|
|
2898
|
+
this.tick();
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
this.animateZoom(area, target, this.areaTransform, duration);
|
|
2902
|
+
};
|
|
2903
|
+
changeData = (options, alpha = 0.5, clearCache = true) => {
|
|
2716
2904
|
if (options.links != undefined)
|
|
2717
2905
|
this.links = options.links;
|
|
2718
2906
|
if (options.nodes != undefined)
|
|
2719
2907
|
this.nodes = options.nodes;
|
|
2720
|
-
if (options.nodes != undefined || options.links != undefined)
|
|
2908
|
+
if (options.nodes != undefined || options.links != undefined) {
|
|
2721
2909
|
this.updateData(alpha, clearCache);
|
|
2722
|
-
|
|
2723
|
-
|
|
2910
|
+
}
|
|
2911
|
+
};
|
|
2912
|
+
changeSettings = (options, clearCache = true) => {
|
|
2724
2913
|
if (options.graphSettings) {
|
|
2725
2914
|
this.graphSettings = graphSettingsGetter(options.graphSettings, this.graphSettings);
|
|
2726
2915
|
this.draw = initDraw.call(this);
|
|
@@ -2770,13 +2959,13 @@ class GraphCanvas {
|
|
|
2770
2959
|
return void this.updateSimulation();
|
|
2771
2960
|
}
|
|
2772
2961
|
this.tick();
|
|
2773
|
-
}
|
|
2774
|
-
updateRect() {
|
|
2962
|
+
};
|
|
2963
|
+
updateRect = () => {
|
|
2775
2964
|
if (!this.area)
|
|
2776
2965
|
return;
|
|
2777
2966
|
this.areaRect = this.area.getBoundingClientRect();
|
|
2778
|
-
}
|
|
2779
|
-
updateSize() {
|
|
2967
|
+
};
|
|
2968
|
+
updateSize = () => {
|
|
2780
2969
|
if (!this.area)
|
|
2781
2970
|
return;
|
|
2782
2971
|
const { width, height } = this.root.getBoundingClientRect();
|
|
@@ -2790,51 +2979,96 @@ class GraphCanvas {
|
|
|
2790
2979
|
throw new Error("couldn't create canvas context");
|
|
2791
2980
|
this.context.scale(this.dpi, this.dpi);
|
|
2792
2981
|
this.draw();
|
|
2793
|
-
}
|
|
2794
|
-
clearCache(keys) {
|
|
2982
|
+
};
|
|
2983
|
+
clearCache = (keys) => {
|
|
2795
2984
|
if (keys === true) {
|
|
2796
|
-
|
|
2797
|
-
|
|
2985
|
+
this.nodeOptionsCache.length = 0;
|
|
2986
|
+
this.linkOptionsCache.length = 0;
|
|
2987
|
+
this.cachedNodeLabel.length = 0;
|
|
2988
|
+
this.cachedNodeText.length = 0;
|
|
2798
2989
|
}
|
|
2799
2990
|
else if (jsHelpers.isArray(keys)) {
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2991
|
+
for (const key of keys) {
|
|
2992
|
+
switch (key) {
|
|
2993
|
+
case GRAPH_CACHE_TYPE.NodeText: {
|
|
2994
|
+
this.cachedNodeText.length = 0;
|
|
2995
|
+
break;
|
|
2996
|
+
}
|
|
2997
|
+
case GRAPH_CACHE_TYPE.NodeLabel: {
|
|
2998
|
+
this.cachedNodeLabel.length = 0;
|
|
2999
|
+
break;
|
|
3000
|
+
}
|
|
3001
|
+
case GRAPH_CACHE_TYPE.NodeOptions: {
|
|
3002
|
+
this.nodeOptionsCache.length = 0;
|
|
3003
|
+
break;
|
|
3004
|
+
}
|
|
3005
|
+
case GRAPH_CACHE_TYPE.LinkOptions: {
|
|
3006
|
+
this.linkOptionsCache.length = 0;
|
|
3007
|
+
break;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
2811
3010
|
}
|
|
2812
3011
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
3012
|
+
updateNodeCache.call(this);
|
|
3013
|
+
updateLinkCache.call(this);
|
|
3014
|
+
};
|
|
3015
|
+
tick = () => {
|
|
3016
|
+
if (!this.simulationWorking)
|
|
2816
3017
|
this.draw();
|
|
2817
|
-
}
|
|
2818
|
-
restart(alpha) {
|
|
2819
|
-
if (this.simulation)
|
|
3018
|
+
};
|
|
3019
|
+
restart = (alpha, options) => {
|
|
3020
|
+
if (!this.simulation)
|
|
3021
|
+
return;
|
|
3022
|
+
const settings = {
|
|
3023
|
+
precompute: options?.precompute ?? this.forceSettings.precompute,
|
|
3024
|
+
precomputeMaxTimeMs: options?.precomputeMaxTimeMs ?? this.forceSettings?.precomputeMaxTimeMs,
|
|
3025
|
+
precomputeMaxTicks: options?.precomputeMaxTicks ?? this.forceSettings?.precomputeMaxTicks ?? 300,
|
|
3026
|
+
precomputeDisableForcesAfter: options?.precomputeDisableForcesAfter ??
|
|
3027
|
+
this.forceSettings?.precomputeDisableForcesAfter ??
|
|
3028
|
+
false,
|
|
3029
|
+
};
|
|
3030
|
+
if (!settings.precompute) {
|
|
2820
3031
|
this.simulation.alpha(alpha ?? 1).restart();
|
|
2821
|
-
|
|
2822
|
-
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
if (settings.precomputeDisableForcesAfter) {
|
|
3035
|
+
this.forceSettings = forceSettingsGetter({ forces: true }, this.forceSettings);
|
|
3036
|
+
}
|
|
3037
|
+
initSimulationForces.call(this);
|
|
3038
|
+
this.simulation.stop();
|
|
3039
|
+
this.simulation.alpha(alpha ?? 1);
|
|
3040
|
+
const startTime = performance.now();
|
|
3041
|
+
let ticks = 0;
|
|
3042
|
+
while (performance.now() - startTime < settings.precomputeMaxTimeMs &&
|
|
3043
|
+
ticks < settings.precomputeMaxTicks) {
|
|
3044
|
+
this.simulation.tick(1);
|
|
3045
|
+
ticks++;
|
|
3046
|
+
if (this.simulation.alpha() <= this.simulation.alphaMin())
|
|
3047
|
+
break;
|
|
3048
|
+
}
|
|
3049
|
+
if (settings.precomputeDisableForcesAfter) {
|
|
3050
|
+
this.forceSettings = forceSettingsGetter({ forces: false }, this.forceSettings);
|
|
3051
|
+
}
|
|
3052
|
+
initSimulationForces.call(this);
|
|
3053
|
+
this.simulation.restart();
|
|
3054
|
+
this.tick();
|
|
3055
|
+
};
|
|
3056
|
+
start = () => {
|
|
2823
3057
|
if (this.simulation)
|
|
2824
3058
|
this.simulation.alpha(1).restart();
|
|
2825
3059
|
if (this.container)
|
|
2826
3060
|
this.container.style.display = "block";
|
|
2827
|
-
}
|
|
2828
|
-
stop() {
|
|
3061
|
+
};
|
|
3062
|
+
stop = () => {
|
|
2829
3063
|
if (this.simulation)
|
|
2830
3064
|
this.simulation.stop();
|
|
2831
3065
|
if (this.container)
|
|
2832
3066
|
this.container.style.display = "none";
|
|
2833
|
-
}
|
|
2834
|
-
create() {
|
|
3067
|
+
};
|
|
3068
|
+
create = () => {
|
|
2835
3069
|
this.init();
|
|
2836
|
-
}
|
|
2837
|
-
destroy() {
|
|
3070
|
+
};
|
|
3071
|
+
destroy = () => {
|
|
2838
3072
|
if (this.simulation) {
|
|
2839
3073
|
this.simulation.stop();
|
|
2840
3074
|
this.simulation = undefined;
|
|
@@ -2842,39 +3076,51 @@ class GraphCanvas {
|
|
|
2842
3076
|
this.clearHTMLElements();
|
|
2843
3077
|
this.clearState();
|
|
2844
3078
|
this.clearCache(true);
|
|
2845
|
-
}
|
|
2846
|
-
clearHTMLElements() {
|
|
3079
|
+
};
|
|
3080
|
+
clearHTMLElements = () => {
|
|
2847
3081
|
this.root.replaceChildren();
|
|
2848
3082
|
this.area = undefined;
|
|
2849
3083
|
this.context = undefined;
|
|
2850
3084
|
this.container = undefined;
|
|
2851
3085
|
this.eventAbortController.abort();
|
|
2852
3086
|
this.eventAbortController = new AbortController();
|
|
2853
|
-
}
|
|
2854
|
-
updateSimulation() {
|
|
3087
|
+
};
|
|
3088
|
+
updateSimulation = () => {
|
|
2855
3089
|
if (this.simulation) {
|
|
2856
3090
|
initSimulationForces.call(this);
|
|
2857
|
-
this.
|
|
2858
|
-
this.simulation.restart();
|
|
3091
|
+
this.restart(1);
|
|
2859
3092
|
}
|
|
2860
|
-
}
|
|
2861
|
-
clearState() {
|
|
3093
|
+
};
|
|
3094
|
+
clearState = () => {
|
|
2862
3095
|
this.isDragging = false;
|
|
2863
3096
|
this.highlightedNode = null;
|
|
2864
3097
|
this.highlightedLink = null;
|
|
2865
3098
|
this.highlightedNeighbors = null;
|
|
2866
3099
|
this.highlightProgress = 0;
|
|
2867
|
-
this.
|
|
2868
|
-
|
|
2869
|
-
|
|
3100
|
+
this.highlightStart = null;
|
|
3101
|
+
this.highlightPositive = false;
|
|
3102
|
+
};
|
|
3103
|
+
init = () => {
|
|
3104
|
+
initArea.call(this);
|
|
3105
|
+
updateNodeCache.call(this);
|
|
3106
|
+
updateLinkCache.call(this);
|
|
3107
|
+
initSimulation.call(this);
|
|
3108
|
+
this.restart(1);
|
|
3109
|
+
initDnd.call(this);
|
|
3110
|
+
initZoom.call(this);
|
|
3111
|
+
initResize.call(this);
|
|
3112
|
+
initPointer.call(this);
|
|
3113
|
+
updateNodeCache.call(this);
|
|
3114
|
+
updateLinkCache.call(this);
|
|
3115
|
+
this.tick();
|
|
3116
|
+
};
|
|
3117
|
+
updateData = (alpha = 0.5, clearCache = true) => {
|
|
2870
3118
|
if (clearCache) {
|
|
2871
3119
|
this.clearCache(clearCache);
|
|
2872
3120
|
}
|
|
2873
3121
|
if (this.simulation) {
|
|
2874
3122
|
initCollideForce.call(this, false);
|
|
2875
|
-
this.simulation
|
|
2876
|
-
.nodes(this.nodes)
|
|
2877
|
-
.force("link", d3Force.forceLink(this.links)
|
|
3123
|
+
this.simulation.nodes(this.nodes).force("link", d3Force.forceLink(this.links)
|
|
2878
3124
|
.id(this.nodeSettings.idGetter.bind(this))
|
|
2879
3125
|
.distance(this.forceSettings.forces && this.forceSettings.linkForce
|
|
2880
3126
|
? this.forceSettings.linkDistance
|
|
@@ -2884,21 +3130,95 @@ class GraphCanvas {
|
|
|
2884
3130
|
: 0)
|
|
2885
3131
|
.iterations(this.forceSettings.forces && this.forceSettings.linkForce
|
|
2886
3132
|
? this.forceSettings.linkIterations
|
|
2887
|
-
: 0))
|
|
2888
|
-
|
|
2889
|
-
|
|
3133
|
+
: 0));
|
|
3134
|
+
this.restart(alpha);
|
|
3135
|
+
initZoom.call(this, this.areaTransform);
|
|
2890
3136
|
}
|
|
2891
|
-
}
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
3137
|
+
};
|
|
3138
|
+
animateHighlight = (node, link, baseDuration = this.highlightSettings.highlightDuration) => {
|
|
3139
|
+
let positive = true;
|
|
3140
|
+
if (node && (this.highlightedNode !== node || !this.highlightPositive)) {
|
|
3141
|
+
this.highlightedNode = node;
|
|
3142
|
+
this.highlightedNeighbors = new Set(node.neighbors);
|
|
3143
|
+
this.particles = [];
|
|
3144
|
+
this.highlightedLink = null;
|
|
3145
|
+
this.highlightStart = performance.now();
|
|
3146
|
+
}
|
|
3147
|
+
else if (link && (this.highlightedLink !== link || !this.highlightPositive)) {
|
|
3148
|
+
const { sourceId, targetId } = extractLinkPointIds(link);
|
|
3149
|
+
this.highlightProgress = 0;
|
|
3150
|
+
this.highlightedLink = link;
|
|
3151
|
+
this.highlightedNeighbors = new Set([sourceId, targetId]);
|
|
3152
|
+
this.particles = [];
|
|
3153
|
+
this.highlightedNode = null;
|
|
3154
|
+
this.highlightStart = performance.now();
|
|
3155
|
+
}
|
|
3156
|
+
else if (!node && !link && this.highlightPositive) {
|
|
3157
|
+
positive = false;
|
|
3158
|
+
}
|
|
3159
|
+
else {
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
if (this.highlightController) {
|
|
3163
|
+
this.highlightController.abort();
|
|
3164
|
+
}
|
|
3165
|
+
const controller = new AbortController();
|
|
3166
|
+
this.highlightPositive = positive;
|
|
3167
|
+
this.highlightController = controller;
|
|
3168
|
+
const startTime = performance.now();
|
|
3169
|
+
const startProgress = this.highlightProgress;
|
|
3170
|
+
const targetProgress = positive ? 1 : 0;
|
|
3171
|
+
const delta = targetProgress - startProgress;
|
|
3172
|
+
const duration = baseDuration * Math.abs(delta);
|
|
3173
|
+
const animate = () => {
|
|
3174
|
+
if (controller.signal.aborted)
|
|
3175
|
+
return;
|
|
3176
|
+
const elapsed = performance.now() - startTime;
|
|
3177
|
+
const t = Math.min(elapsed / duration, 1);
|
|
3178
|
+
const current = startProgress + delta * t;
|
|
3179
|
+
const eased = current < 0.5 ? 4 * current * current * current : 1 - (-2 * current + 2) ** 3 / 2;
|
|
3180
|
+
this.highlightProgress = eased;
|
|
3181
|
+
if (t < 1 || positive) {
|
|
3182
|
+
requestAnimationFrame(animate);
|
|
3183
|
+
this.draw();
|
|
3184
|
+
}
|
|
3185
|
+
else {
|
|
3186
|
+
this.highlightedNode = null;
|
|
3187
|
+
this.highlightedLink = null;
|
|
3188
|
+
this.highlightedNeighbors = null;
|
|
3189
|
+
this.highlightStart = null;
|
|
3190
|
+
this.particles = [];
|
|
3191
|
+
this.tick();
|
|
3192
|
+
}
|
|
3193
|
+
};
|
|
3194
|
+
requestAnimationFrame(animate);
|
|
3195
|
+
};
|
|
3196
|
+
animateZoom = (area, target, start, duration) => {
|
|
3197
|
+
this._zoomAnimating = true;
|
|
3198
|
+
const startTime = performance.now();
|
|
3199
|
+
const animate = () => {
|
|
3200
|
+
const elapsed = performance.now() - startTime;
|
|
3201
|
+
const t = Math.min(elapsed / duration, 1);
|
|
3202
|
+
const eased = t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
|
|
3203
|
+
const x = start.x + (target.x - start.x) * eased;
|
|
3204
|
+
const y = start.y + (target.y - start.y) * eased;
|
|
3205
|
+
const k = start.k + (target.k - start.k) * eased;
|
|
3206
|
+
this.areaTransform = new d3Zoom.ZoomTransform(k, x, y);
|
|
3207
|
+
d3Zoom.zoom().transform(d3Selection.select(area), this.areaTransform);
|
|
3208
|
+
updateLinkCache.call(this);
|
|
3209
|
+
updateNodeCache.call(this);
|
|
3210
|
+
this.tick();
|
|
3211
|
+
if (t < 1) {
|
|
3212
|
+
requestAnimationFrame(animate);
|
|
3213
|
+
}
|
|
3214
|
+
else {
|
|
3215
|
+
this._zoomAnimating = false;
|
|
3216
|
+
this.clearCache(true);
|
|
3217
|
+
this.tick();
|
|
3218
|
+
}
|
|
3219
|
+
};
|
|
3220
|
+
requestAnimationFrame(animate);
|
|
3221
|
+
};
|
|
2902
3222
|
}
|
|
2903
3223
|
|
|
2904
3224
|
const FORCE_CONTROLS = [
|
|
@@ -3059,22 +3379,13 @@ const HIGHLIGHT_COMMON_CONTROLS = [
|
|
|
3059
3379
|
label: "Расширение границы связи для курсора",
|
|
3060
3380
|
},
|
|
3061
3381
|
{
|
|
3062
|
-
id: "
|
|
3063
|
-
initialValue: HIGHLIGHT_SETTINGS.
|
|
3064
|
-
max:
|
|
3065
|
-
min:
|
|
3066
|
-
step: 1,
|
|
3067
|
-
type: "range",
|
|
3068
|
-
label: "Скорость отмены анимации в кадрах",
|
|
3069
|
-
},
|
|
3070
|
-
{
|
|
3071
|
-
id: "highlightUpFrames",
|
|
3072
|
-
initialValue: HIGHLIGHT_SETTINGS.highlightUpFrames,
|
|
3073
|
-
max: 60,
|
|
3074
|
-
min: 1,
|
|
3382
|
+
id: "highlightDuration",
|
|
3383
|
+
initialValue: HIGHLIGHT_SETTINGS.highlightDuration,
|
|
3384
|
+
max: 1000,
|
|
3385
|
+
min: 10,
|
|
3075
3386
|
step: 1,
|
|
3076
3387
|
type: "range",
|
|
3077
|
-
label: "Скорость
|
|
3388
|
+
label: "Скорость анимации",
|
|
3078
3389
|
},
|
|
3079
3390
|
];
|
|
3080
3391
|
const HIGHLIGHT_BY_NODE_FOR_NODE_CONTROLS = [
|
|
@@ -3547,19 +3858,22 @@ const LINK_SETTINGS_CONTROLS = [
|
|
|
3547
3858
|
initialValue: LINK_SETTINGS.particles,
|
|
3548
3859
|
},
|
|
3549
3860
|
{
|
|
3550
|
-
id: "
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3861
|
+
id: "particleSpeedByDistance",
|
|
3862
|
+
initialValue: LINK_SETTINGS.particleSpeedByDistance,
|
|
3863
|
+
max: 500,
|
|
3864
|
+
min: 1,
|
|
3865
|
+
step: 1,
|
|
3866
|
+
type: "range",
|
|
3867
|
+
label: "Скорость частицы на один пиксель",
|
|
3554
3868
|
},
|
|
3555
3869
|
{
|
|
3556
|
-
id: "
|
|
3557
|
-
initialValue: LINK_SETTINGS.
|
|
3558
|
-
max:
|
|
3559
|
-
min:
|
|
3560
|
-
step:
|
|
3870
|
+
id: "particleCountByDistance",
|
|
3871
|
+
initialValue: LINK_SETTINGS.particleCountByDistance,
|
|
3872
|
+
max: 250,
|
|
3873
|
+
min: 1,
|
|
3874
|
+
step: 1,
|
|
3561
3875
|
type: "range",
|
|
3562
|
-
label: "
|
|
3876
|
+
label: "Количество пикселей для появления частицы",
|
|
3563
3877
|
},
|
|
3564
3878
|
];
|
|
3565
3879
|
const LINK_OPTIONS_LINK_CONTROLS = [
|
|
@@ -3654,24 +3968,6 @@ const LINK_OPTIONS_PARTICLE_CONTROLS = [
|
|
|
3654
3968
|
label: "Радиус",
|
|
3655
3969
|
initialValue: LINK_OPTIONS.particleRadius,
|
|
3656
3970
|
},
|
|
3657
|
-
{
|
|
3658
|
-
id: "particleCount",
|
|
3659
|
-
type: "range",
|
|
3660
|
-
max: 20,
|
|
3661
|
-
min: 1,
|
|
3662
|
-
step: 1,
|
|
3663
|
-
label: "Количество",
|
|
3664
|
-
initialValue: LINK_OPTIONS.particleCount,
|
|
3665
|
-
},
|
|
3666
|
-
{
|
|
3667
|
-
id: "particleSteps",
|
|
3668
|
-
type: "range",
|
|
3669
|
-
max: 200,
|
|
3670
|
-
min: 1,
|
|
3671
|
-
step: 1,
|
|
3672
|
-
label: "Количество кадров",
|
|
3673
|
-
initialValue: LINK_OPTIONS.particleSteps,
|
|
3674
|
-
},
|
|
3675
3971
|
{
|
|
3676
3972
|
id: "particleBorderColor",
|
|
3677
3973
|
type: "color",
|
|
@@ -4105,10 +4401,19 @@ exports.NODE_OPTIONS_NODE_CONTROLS = NODE_OPTIONS_NODE_CONTROLS;
|
|
|
4105
4401
|
exports.NODE_OPTIONS_TEXT_CONTROLS = NODE_OPTIONS_TEXT_CONTROLS;
|
|
4106
4402
|
exports.NODE_SETTINGS = NODE_SETTINGS;
|
|
4107
4403
|
exports.NODE_SETTINGS_CONTROLS = NODE_SETTINGS_CONTROLS;
|
|
4404
|
+
exports.PERFOMANCE_NODE_OPTIONS = PERFOMANCE_NODE_OPTIONS;
|
|
4405
|
+
exports.PERFORMANCE_FORCE_SETTINGS = PERFORMANCE_FORCE_SETTINGS;
|
|
4406
|
+
exports.PERFORMANCE_GRAPH_SETTINGS = PERFORMANCE_GRAPH_SETTINGS;
|
|
4407
|
+
exports.PERFORMANCE_HIGHLIGHT_SETTINGS = PERFORMANCE_HIGHLIGHT_SETTINGS;
|
|
4408
|
+
exports.PERFORMANCE_LINK_OPTIONS = PERFORMANCE_LINK_OPTIONS;
|
|
4409
|
+
exports.PERFORMANCE_LINK_SETTINGS = PERFORMANCE_LINK_SETTINGS;
|
|
4410
|
+
exports.PERFORMANCE_NODE_SETTINGS = PERFORMANCE_NODE_SETTINGS;
|
|
4108
4411
|
exports.animationByProgress = animationByProgress;
|
|
4412
|
+
exports.approximateQuadraticBezierLength = approximateQuadraticBezierLength;
|
|
4109
4413
|
exports.calculateLinkPositionByNode = calculateLinkPositionByNode;
|
|
4110
4414
|
exports.colorGetter = colorGetter;
|
|
4111
4415
|
exports.colorToRgb = colorToRgb;
|
|
4416
|
+
exports.computeGraphBounds = computeGraphBounds;
|
|
4112
4417
|
exports.dragPlaceCoefficientGetter = dragPlaceCoefficientGetter;
|
|
4113
4418
|
exports.drawText = drawText;
|
|
4114
4419
|
exports.extractLinkPointIds = extractLinkPointIds;
|
|
@@ -4120,6 +4425,7 @@ exports.getDrawNode = getDrawNode;
|
|
|
4120
4425
|
exports.getParticlePosition = getParticlePosition;
|
|
4121
4426
|
exports.graphSettingsGetter = graphSettingsGetter;
|
|
4122
4427
|
exports.highlightSettingsGetter = highlightSettingsGetter;
|
|
4428
|
+
exports.isEmptyObject = isEmptyObject;
|
|
4123
4429
|
exports.isNodeVisible = isNodeVisible;
|
|
4124
4430
|
exports.isOverlapsNode = isOverlapsNode;
|
|
4125
4431
|
exports.linkByPointerGetter = linkByPointerGetter;
|