@krainovsd/graph 0.1.1 → 0.2.0
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/esm/index.js +318 -190
- package/lib/esm/index.js.map +1 -1
- package/lib/index.d.ts +180 -0
- package/package.json +8 -7
- package/tmp/app/index.d.ts +0 -1
- package/tmp/app/lib/get-link-count.d.ts +0 -3
- package/tmp/app/lib/get-node-neighbors.d.ts +0 -3
- package/tmp/app/lib/index.d.ts +0 -3
- package/tmp/app/lib/perf-test-wrapper.d.ts +0 -1
- package/tmp/app/mock/custom-mock.d.ts +0 -3
- package/tmp/app/mock/dynamic-mock.d.ts +0 -3
- package/tmp/app/mock/index.d.ts +0 -7
- package/tmp/app/types.d.ts +0 -4
- package/tmp/index.d.ts +0 -2
- package/tmp/lib/check-type.d.ts +0 -1
- package/tmp/lib/color-getter.d.ts +0 -1
- package/tmp/lib/index.d.ts +0 -3
- package/tmp/lib/underscore.d.ts +0 -2
- package/tmp/module/Graph/Graph.d.ts +0 -0
- package/tmp/module/Graph/Graph.types.d.ts +0 -0
- package/tmp/module/Graph/index.d.ts +0 -0
- package/tmp/module/GraphCanvas/GraphCanvas.d.ts +0 -49
- package/tmp/module/GraphCanvas/constants/index.d.ts +0 -1
- package/tmp/module/GraphCanvas/constants/settings.d.ts +0 -50
- package/tmp/module/GraphCanvas/index.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/force-settings-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/graph-settings-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/index.d.ts +0 -6
- package/tmp/module/GraphCanvas/lib/links/index.d.ts +0 -3
- package/tmp/module/GraphCanvas/lib/links/link-iteration-extractor.d.ts +0 -5
- package/tmp/module/GraphCanvas/lib/links/link-options-getter.d.ts +0 -4
- package/tmp/module/GraphCanvas/lib/links/link-settings-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/listeners-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/nodes/drag-place-coefficient-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/nodes/index.d.ts +0 -8
- package/tmp/module/GraphCanvas/lib/nodes/is-overlaps-node.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/nodes/node-by-pointer-getter.d.ts +0 -11
- package/tmp/module/GraphCanvas/lib/nodes/node-id-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/nodes/node-iteration-extractor.d.ts +0 -5
- package/tmp/module/GraphCanvas/lib/nodes/node-options-getter.d.ts +0 -4
- package/tmp/module/GraphCanvas/lib/nodes/node-radius-getter.d.ts +0 -8
- package/tmp/module/GraphCanvas/lib/nodes/node-settings-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/lib/pointer-getter.d.ts +0 -2
- package/tmp/module/GraphCanvas/types/graph.d.ts +0 -15
- package/tmp/module/GraphCanvas/types/index.d.ts +0 -3
- package/tmp/module/GraphCanvas/types/listeners.d.ts +0 -21
- package/tmp/module/GraphCanvas/types/settings.d.ts +0 -62
- package/tmp/module/GraphCanvasEngine/GraphCanvasEngine.d.ts +0 -0
- package/tmp/module/GraphCanvasEngine/GraphCanvasEngine.types.d.ts +0 -0
- package/tmp/module/GraphCanvasEngine/index.d.ts +0 -0
- package/tmp/types/index.d.ts +0 -2
- package/tmp/types/links.d.ts +0 -5
- package/tmp/types/nodes.d.ts +0 -8
package/lib/esm/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { least } from 'd3-array';
|
|
2
2
|
import { drag } from 'd3-drag';
|
|
3
|
-
import { forceLink,
|
|
3
|
+
import { forceLink, forceSimulation, forceX, forceY, forceManyBody, forceCenter, forceCollide } from 'd3-force';
|
|
4
4
|
import { create, select } from 'd3-selection';
|
|
5
|
-
import { zoomIdentity, zoom } from 'd3-zoom';
|
|
5
|
+
import { zoomIdentity, zoom, ZoomTransform } from 'd3-zoom';
|
|
6
6
|
import debounce from 'lodash/debounce';
|
|
7
7
|
|
|
8
8
|
function checkType(value, condition) {
|
|
@@ -38,9 +38,9 @@ function colorGetter() {
|
|
|
38
38
|
const FORCE_SETTINGS = {
|
|
39
39
|
centerPosition: {},
|
|
40
40
|
centerStrength: 1,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
collideIterations:
|
|
41
|
+
collideStrength: 0.1,
|
|
42
|
+
collideAdditionalRadius: 4,
|
|
43
|
+
collideIterations: 2,
|
|
44
44
|
collideOffMax: { links: 0, nodes: 0 },
|
|
45
45
|
collideOn: true,
|
|
46
46
|
chargeStrength: -40,
|
|
@@ -50,100 +50,84 @@ const FORCE_SETTINGS = {
|
|
|
50
50
|
xStrength: 0.1,
|
|
51
51
|
yForce: 0,
|
|
52
52
|
yStrength: 0.1,
|
|
53
|
-
linkDistance:
|
|
53
|
+
linkDistance: 30,
|
|
54
54
|
linkIterations: 1,
|
|
55
55
|
linkStrength: 1,
|
|
56
|
+
collideRadius: null,
|
|
56
57
|
};
|
|
57
58
|
const GRAPH_SETTINGS = {
|
|
58
|
-
zoomExtent: [0.1,
|
|
59
|
+
zoomExtent: [0.1, 20],
|
|
59
60
|
stickAfterDrag: false,
|
|
60
61
|
highlightByHover: false,
|
|
61
|
-
|
|
62
|
+
highlightFadingMin: 0.21,
|
|
63
|
+
highlightDownStep: 0.09,
|
|
64
|
+
highlightUpStep: 0.09,
|
|
65
|
+
dragPlaceCoefficient: dragPlaceCoefficientGetter,
|
|
66
|
+
nodeRadiusInitial: 4,
|
|
67
|
+
nodeRadiusCoefficient: 5,
|
|
68
|
+
nodeRadiusFactor: 1,
|
|
69
|
+
nodeRadiusFlexible: true,
|
|
62
70
|
};
|
|
63
71
|
const NODE_SETTINGS = {
|
|
64
72
|
alpha: 1,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
borderColor: "#000000FF",
|
|
74
|
+
borderWidth: 0.1,
|
|
75
|
+
textWidth: 20,
|
|
76
|
+
textShiftX: 0,
|
|
77
|
+
textFont: "Arial",
|
|
78
|
+
textAlign: "center",
|
|
79
|
+
textColor: "#333",
|
|
69
80
|
width: 1,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
flexibleRadius: true,
|
|
81
|
+
radius: 4,
|
|
82
|
+
textStyle: "normal",
|
|
83
|
+
textWeight: "500",
|
|
84
|
+
textGap: 1,
|
|
75
85
|
};
|
|
76
86
|
const LINK_SETTINGS = {
|
|
77
87
|
alpha: 1,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
};
|
|
89
|
+
const COMMON_SETTINGS = {
|
|
90
|
+
linkColorZoomFar: "#999",
|
|
91
|
+
linkColorZoomNear: "#000000FF",
|
|
92
|
+
linkWidthZoomFar: 1,
|
|
93
|
+
linkWidthZoomNear: 0.1,
|
|
94
|
+
linkWidthZoomBorder: 1,
|
|
95
|
+
linkColorZoomBorder: 1,
|
|
96
|
+
nodeTextScaleMin: 1.5,
|
|
97
|
+
nodeTextScaleMax: 20,
|
|
98
|
+
nodeTextSizeMin: 1.5,
|
|
99
|
+
nodeTextSizeMax: 3.5,
|
|
100
|
+
nodeTextShiftYMin: 2.5,
|
|
101
|
+
nodeTextShiftYMax: 4,
|
|
102
|
+
nodeTextChangeStepCount: 200,
|
|
84
103
|
};
|
|
85
104
|
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else
|
|
92
|
-
customOptions = option;
|
|
93
|
-
if (optionConstantGetter) {
|
|
94
|
-
if (typeof optionConstantGetter === "function")
|
|
95
|
-
constantOptions = optionConstantGetter(link, i, links, transform);
|
|
96
|
-
else
|
|
97
|
-
constantOptions = optionConstantGetter;
|
|
98
|
-
if (constantOptions &&
|
|
99
|
-
typeof constantOptions === "object" &&
|
|
100
|
-
!Array.isArray(constantOptions) &&
|
|
101
|
-
checkType(customOptions, customOptions === undefined ||
|
|
102
|
-
(typeof customOptions === "object" && !Array.isArray(constantOptions)))) {
|
|
103
|
-
return {
|
|
104
|
-
...constantOptions,
|
|
105
|
-
...(customOptions || {}),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return customOptions;
|
|
105
|
+
function forceSettingsGetter(settings, prevSettings) {
|
|
106
|
+
return {
|
|
107
|
+
...(prevSettings ?? FORCE_SETTINGS),
|
|
108
|
+
...settings,
|
|
109
|
+
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
function
|
|
112
|
+
function graphSettingsGetter(settings, prevSettings) {
|
|
113
113
|
return {
|
|
114
|
-
...
|
|
115
|
-
|
|
116
|
-
? LINK_SETTINGS.colorNear
|
|
117
|
-
: LINK_SETTINGS.colorFar,
|
|
118
|
-
width: transform && transform.k > LINK_SETTINGS.zoomWidthBorder
|
|
119
|
-
? LINK_SETTINGS.widthNear
|
|
120
|
-
: LINK_SETTINGS.widthFar,
|
|
114
|
+
...(prevSettings ?? GRAPH_SETTINGS),
|
|
115
|
+
...settings,
|
|
121
116
|
};
|
|
122
117
|
}
|
|
123
118
|
|
|
124
119
|
function linkSettingsGetter(settings) {
|
|
125
120
|
return { options: settings?.options };
|
|
126
121
|
}
|
|
127
|
-
|
|
128
|
-
function forceSettingsGetter(settings) {
|
|
122
|
+
function linkOptionsGetter(_, __, ___, transform) {
|
|
129
123
|
return {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
chargeStrength: settings?.chargeStrength ?? FORCE_SETTINGS.chargeStrength,
|
|
138
|
-
chargeDistanceMax: settings?.chargeDistanceMax ?? FORCE_SETTINGS.chargeDistanceMax,
|
|
139
|
-
chargeDistanceMin: settings?.chargeDistanceMin ?? FORCE_SETTINGS.chargeDistanceMin,
|
|
140
|
-
xForce: settings?.xForce ?? FORCE_SETTINGS.xForce,
|
|
141
|
-
xStrength: settings?.xStrength ?? FORCE_SETTINGS.xStrength,
|
|
142
|
-
yForce: settings?.yForce ?? FORCE_SETTINGS.yForce,
|
|
143
|
-
yStrength: settings?.yStrength ?? FORCE_SETTINGS.yStrength,
|
|
144
|
-
linkDistance: settings?.linkDistance ?? FORCE_SETTINGS.linkDistance,
|
|
145
|
-
linkIterations: settings?.linkIterations ?? FORCE_SETTINGS.linkIterations,
|
|
146
|
-
linkStrength: settings?.linkStrength ?? FORCE_SETTINGS.linkStrength,
|
|
124
|
+
...LINK_SETTINGS,
|
|
125
|
+
color: transform && transform.k > COMMON_SETTINGS.linkColorZoomBorder
|
|
126
|
+
? COMMON_SETTINGS.linkColorZoomNear
|
|
127
|
+
: COMMON_SETTINGS.linkColorZoomFar,
|
|
128
|
+
width: transform && transform.k > COMMON_SETTINGS.linkWidthZoomBorder
|
|
129
|
+
? COMMON_SETTINGS.linkWidthZoomNear
|
|
130
|
+
: COMMON_SETTINGS.linkWidthZoomFar,
|
|
147
131
|
};
|
|
148
132
|
}
|
|
149
133
|
|
|
@@ -151,6 +135,59 @@ function listenersGetter(settings) {
|
|
|
151
135
|
return settings || {};
|
|
152
136
|
}
|
|
153
137
|
|
|
138
|
+
function nodeSettingsGetter(settings) {
|
|
139
|
+
return {
|
|
140
|
+
idGetter: settings?.idGetter ?? nodeIdGetter,
|
|
141
|
+
options: settings?.options,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const color = colorGetter();
|
|
145
|
+
function nodeOptionsGetter(node, _, __, transform) {
|
|
146
|
+
const { textShiftY, textSize } = nodeTextSizeGetter(transform);
|
|
147
|
+
return {
|
|
148
|
+
...NODE_SETTINGS,
|
|
149
|
+
color: color(String(node.group || "_DEFAULT")),
|
|
150
|
+
textVisible: Boolean(transform && transform.k > COMMON_SETTINGS.nodeTextScaleMin),
|
|
151
|
+
text: node.id != undefined ? String(node.id) : null,
|
|
152
|
+
textShiftY,
|
|
153
|
+
textSize,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function nodeTextSizeGetter(transform) {
|
|
157
|
+
let textSize = COMMON_SETTINGS.nodeTextSizeMax;
|
|
158
|
+
let textShiftY = COMMON_SETTINGS.nodeTextShiftYMax;
|
|
159
|
+
if (transform) {
|
|
160
|
+
const scaleStepCoefficient = (COMMON_SETTINGS.nodeTextScaleMax - COMMON_SETTINGS.nodeTextScaleMin) /
|
|
161
|
+
COMMON_SETTINGS.nodeTextChangeStepCount;
|
|
162
|
+
const textStepCoefficient = (COMMON_SETTINGS.nodeTextSizeMax - COMMON_SETTINGS.nodeTextSizeMin) /
|
|
163
|
+
COMMON_SETTINGS.nodeTextChangeStepCount;
|
|
164
|
+
const shiftStepCoefficient = (COMMON_SETTINGS.nodeTextShiftYMax - COMMON_SETTINGS.nodeTextShiftYMin) /
|
|
165
|
+
COMMON_SETTINGS.nodeTextChangeStepCount;
|
|
166
|
+
if (transform.k >= COMMON_SETTINGS.nodeTextScaleMax) {
|
|
167
|
+
textSize = COMMON_SETTINGS.nodeTextSizeMin;
|
|
168
|
+
textShiftY = COMMON_SETTINGS.nodeTextShiftYMin;
|
|
169
|
+
}
|
|
170
|
+
else if (transform.k > COMMON_SETTINGS.nodeTextScaleMin) {
|
|
171
|
+
const transformSteps = (transform.k - COMMON_SETTINGS.nodeTextScaleMin) / scaleStepCoefficient;
|
|
172
|
+
textSize -= transformSteps * textStepCoefficient;
|
|
173
|
+
textShiftY -= transformSteps * shiftStepCoefficient;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { textSize, textShiftY };
|
|
177
|
+
}
|
|
178
|
+
function nodeRadiusGetter({ radiusFlexible, radiusInitial, linkCount, radiusCoefficient, radiusFactor, }) {
|
|
179
|
+
return ((radiusFlexible && linkCount ? linkCount / radiusCoefficient : 0) * radiusFactor + radiusInitial);
|
|
180
|
+
}
|
|
181
|
+
function nodeIdGetter(d) {
|
|
182
|
+
return d.id;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function pointerGetter(mouseEvent, areaRect, areaTransform) {
|
|
186
|
+
const px = (mouseEvent.clientX - areaRect.left - areaTransform.x) / areaTransform.k;
|
|
187
|
+
const py = (mouseEvent.clientY - areaRect.top - areaTransform.y) / areaTransform.k;
|
|
188
|
+
return [px, py];
|
|
189
|
+
}
|
|
190
|
+
|
|
154
191
|
function isOverlapsNode(node, radius, pointerX, pointerY) {
|
|
155
192
|
if (node.x == undefined || node.y == undefined)
|
|
156
193
|
return false;
|
|
@@ -167,32 +204,16 @@ function dragPlaceCoefficientGetter(node, pointerX, pointerY, radius) {
|
|
|
167
204
|
return undefined;
|
|
168
205
|
}
|
|
169
206
|
|
|
170
|
-
function
|
|
171
|
-
return {
|
|
172
|
-
zoomExtent: settings?.zoomExtent || GRAPH_SETTINGS.zoomExtent,
|
|
173
|
-
dragPlaceCoefficient: settings?.dragPlaceCoefficient || dragPlaceCoefficientGetter,
|
|
174
|
-
stickAfterDrag: settings?.stickAfterDrag || GRAPH_SETTINGS.stickAfterDrag,
|
|
175
|
-
highlightByHover: settings?.highlightByHover || GRAPH_SETTINGS.highlightByHover,
|
|
176
|
-
minFading: settings?.minFading || GRAPH_SETTINGS.minFading,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function pointerGetter(mouseEvent, areaRect, areaTransform) {
|
|
181
|
-
const px = (mouseEvent.clientX - areaRect.left - areaTransform.x) / areaTransform.k;
|
|
182
|
-
const py = (mouseEvent.clientY - areaRect.top - areaTransform.y) / areaTransform.k;
|
|
183
|
-
return [px, py];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function nodeIterationExtractor(node, i, nodes, transform, option, optionConstantGetter) {
|
|
207
|
+
function linkIterationExtractor(link, i, links, transform, option, optionConstantGetter) {
|
|
187
208
|
let customOptions;
|
|
188
209
|
let constantOptions;
|
|
189
210
|
if (typeof option === "function")
|
|
190
|
-
customOptions = option(
|
|
211
|
+
customOptions = option(link, i, links, transform);
|
|
191
212
|
else
|
|
192
213
|
customOptions = option;
|
|
193
214
|
if (optionConstantGetter) {
|
|
194
215
|
if (typeof optionConstantGetter === "function")
|
|
195
|
-
constantOptions = optionConstantGetter(
|
|
216
|
+
constantOptions = optionConstantGetter(link, i, links, transform);
|
|
196
217
|
else
|
|
197
218
|
constantOptions = optionConstantGetter;
|
|
198
219
|
if (constantOptions &&
|
|
@@ -202,7 +223,6 @@ function nodeIterationExtractor(node, i, nodes, transform, option, optionConstan
|
|
|
202
223
|
(typeof customOptions === "object" && !Array.isArray(constantOptions)))) {
|
|
203
224
|
return {
|
|
204
225
|
...constantOptions,
|
|
205
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
206
226
|
...(customOptions || {}),
|
|
207
227
|
};
|
|
208
228
|
}
|
|
@@ -210,47 +230,92 @@ function nodeIterationExtractor(node, i, nodes, transform, option, optionConstan
|
|
|
210
230
|
return customOptions;
|
|
211
231
|
}
|
|
212
232
|
|
|
213
|
-
|
|
214
|
-
function nodeOptionsGetter(node, _, __, transform) {
|
|
215
|
-
return {
|
|
216
|
-
...NODE_SETTINGS,
|
|
217
|
-
colorInner: color(String(node.group || "_DEFAULT")),
|
|
218
|
-
text: transform && node.id != undefined && transform.k > NODE_SETTINGS.zoomTextBorder
|
|
219
|
-
? String(node.id)
|
|
220
|
-
: null,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function nodeRadiusGetter({ flexibleRadius, initialRadius, linkCount, radiusCoefficient, radiusFactor, }) {
|
|
225
|
-
return ((flexibleRadius && linkCount ? linkCount / radiusCoefficient : 0) * radiusFactor + initialRadius);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function nodeByPointerGetter({ areaRect, areaTransform, mouseEvent, nodes, nodeCustomOptions, }) {
|
|
233
|
+
function nodeByPointerGetter({ areaRect, areaTransform, mouseEvent, nodes, graphSettings, }) {
|
|
229
234
|
if (!areaRect)
|
|
230
235
|
return undefined;
|
|
231
236
|
const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
|
|
232
|
-
return nodes.find((node
|
|
233
|
-
const nodeOptions = nodeIterationExtractor(node, index, nodes, areaTransform, nodeCustomOptions || {}, nodeOptionsGetter);
|
|
237
|
+
return nodes.find((node) => {
|
|
234
238
|
const radius = nodeRadiusGetter({
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
radiusCoefficient:
|
|
238
|
-
radiusFactor:
|
|
239
|
+
radiusFlexible: graphSettings.nodeRadiusFlexible,
|
|
240
|
+
radiusInitial: graphSettings.nodeRadiusInitial,
|
|
241
|
+
radiusCoefficient: graphSettings.nodeRadiusCoefficient,
|
|
242
|
+
radiusFactor: graphSettings.nodeRadiusFactor,
|
|
239
243
|
linkCount: node.linkCount,
|
|
240
244
|
});
|
|
241
245
|
return isOverlapsNode(node, radius, pointerX, pointerY);
|
|
242
246
|
});
|
|
243
247
|
}
|
|
244
248
|
|
|
245
|
-
function
|
|
246
|
-
|
|
249
|
+
function nodeIterationExtractor(node, i, nodes, transform, option, optionConstantGetter) {
|
|
250
|
+
let customOptions;
|
|
251
|
+
let constantOptions;
|
|
252
|
+
if (typeof option === "function")
|
|
253
|
+
customOptions = option(node, i, nodes, transform);
|
|
254
|
+
else
|
|
255
|
+
customOptions = option;
|
|
256
|
+
if (optionConstantGetter) {
|
|
257
|
+
if (typeof optionConstantGetter === "function")
|
|
258
|
+
constantOptions = optionConstantGetter(node, i, nodes, transform);
|
|
259
|
+
else
|
|
260
|
+
constantOptions = optionConstantGetter;
|
|
261
|
+
if (constantOptions &&
|
|
262
|
+
typeof constantOptions === "object" &&
|
|
263
|
+
!Array.isArray(constantOptions) &&
|
|
264
|
+
checkType(customOptions, customOptions === undefined ||
|
|
265
|
+
(typeof customOptions === "object" && !Array.isArray(constantOptions)))) {
|
|
266
|
+
return {
|
|
267
|
+
...constantOptions,
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
269
|
+
...(customOptions || {}),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return customOptions;
|
|
247
274
|
}
|
|
248
275
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
276
|
+
const SPACE = " ";
|
|
277
|
+
function drawText({ context, id, textAlign, textColor, textFont, textStyle, textGap, textWeight, textSize, text, x, y, cachedNodeText, maxWidth, }) {
|
|
278
|
+
context.font = `${textStyle} normal ${textWeight} ${textSize}px ${textFont}`;
|
|
279
|
+
context.fillStyle = textColor;
|
|
280
|
+
context.textAlign = textAlign;
|
|
281
|
+
if (cachedNodeText[id] != undefined) {
|
|
282
|
+
cachedNodeText[id].forEach((line, index) => {
|
|
283
|
+
context.fillText(line, x, y + index * textSize + index * textGap);
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (maxWidth == undefined || context.measureText(text).width <= maxWidth) {
|
|
288
|
+
cachedNodeText[id] = [text];
|
|
289
|
+
context.fillText(text, x, y);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const spaceWidth = context.measureText(" ").width;
|
|
293
|
+
const lines = [];
|
|
294
|
+
let lineWidth = 0;
|
|
295
|
+
let line = "";
|
|
296
|
+
for (const word of text.split(" ")) {
|
|
297
|
+
const size = context.measureText(word).width;
|
|
298
|
+
lineWidth += size;
|
|
299
|
+
if (line === "") {
|
|
300
|
+
line = word;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (lineWidth > maxWidth) {
|
|
304
|
+
lineWidth = 0;
|
|
305
|
+
lines.push(line);
|
|
306
|
+
line = word;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
lineWidth += spaceWidth;
|
|
310
|
+
line += `${SPACE}${word}`;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (line !== "")
|
|
314
|
+
lines.push(line);
|
|
315
|
+
cachedNodeText[id] = lines;
|
|
316
|
+
lines.forEach((line, index) => {
|
|
317
|
+
context.fillText(line, x, y + index * textSize + index * textGap);
|
|
318
|
+
});
|
|
254
319
|
}
|
|
255
320
|
|
|
256
321
|
class GraphCanvas {
|
|
@@ -275,12 +340,15 @@ class GraphCanvas {
|
|
|
275
340
|
areaTransform = zoomIdentity;
|
|
276
341
|
areaRect;
|
|
277
342
|
draw;
|
|
343
|
+
eventAbortController;
|
|
344
|
+
cachedNodeText = {};
|
|
278
345
|
simulationWorking = false;
|
|
279
346
|
isDragging = false;
|
|
280
|
-
eventAbortController;
|
|
281
347
|
highlightedNode = null;
|
|
282
348
|
highlightedNeighbors = null;
|
|
283
|
-
|
|
349
|
+
highlighFading = 1;
|
|
350
|
+
highlightFadingWorking = false;
|
|
351
|
+
highlightDrawing = false;
|
|
284
352
|
constructor({ links, nodes, root, forceSettings, linkSettings, listeners, nodeSettings, graphSettings, }) {
|
|
285
353
|
root.style.position = "relative";
|
|
286
354
|
root.style.overflow = "hidden";
|
|
@@ -309,20 +377,33 @@ class GraphCanvas {
|
|
|
309
377
|
this.links = options.links;
|
|
310
378
|
if (options.nodes != undefined)
|
|
311
379
|
this.nodes = options.nodes;
|
|
312
|
-
|
|
380
|
+
if (options.nodes != undefined || options.links != undefined)
|
|
381
|
+
this.updateData();
|
|
313
382
|
}
|
|
314
383
|
changeSettings(options) {
|
|
384
|
+
if (options.graphSettings)
|
|
385
|
+
this.graphSettings = graphSettingsGetter(options.graphSettings, this.graphSettings);
|
|
315
386
|
if (options.forceSettings)
|
|
316
|
-
this.forceSettings = forceSettingsGetter(options.forceSettings);
|
|
387
|
+
this.forceSettings = forceSettingsGetter(options.forceSettings, this.forceSettings);
|
|
317
388
|
if (options.linkSettings)
|
|
318
389
|
this.linkSettings = linkSettingsGetter(options.linkSettings);
|
|
319
390
|
if (options.nodeSettings)
|
|
320
391
|
this.nodeSettings = nodeSettingsGetter(options.nodeSettings);
|
|
321
|
-
if (options.graphSettings)
|
|
322
|
-
this.graphSettings = graphSettingsGetter(options.graphSettings);
|
|
323
392
|
this.updateSettings();
|
|
324
393
|
}
|
|
325
394
|
start() {
|
|
395
|
+
if (this.simulation)
|
|
396
|
+
this.simulation.alpha(1).restart();
|
|
397
|
+
if (this.container)
|
|
398
|
+
this.container.style.display = "block";
|
|
399
|
+
}
|
|
400
|
+
stop() {
|
|
401
|
+
if (this.simulation)
|
|
402
|
+
this.simulation.stop();
|
|
403
|
+
if (this.container)
|
|
404
|
+
this.container.style.display = "none";
|
|
405
|
+
}
|
|
406
|
+
create() {
|
|
326
407
|
this.init();
|
|
327
408
|
}
|
|
328
409
|
destroy() {
|
|
@@ -336,26 +417,40 @@ class GraphCanvas {
|
|
|
336
417
|
this.container = undefined;
|
|
337
418
|
this.eventAbortController.abort();
|
|
338
419
|
this.eventAbortController = new AbortController();
|
|
420
|
+
this.clearState();
|
|
421
|
+
this.clearDataDependencies();
|
|
339
422
|
}
|
|
340
423
|
updateSettings() {
|
|
341
424
|
if (this.simulation) {
|
|
342
|
-
this.simulation.stop().alpha(1);
|
|
343
425
|
this.initSimulationForces();
|
|
426
|
+
this.simulation.alpha(1);
|
|
344
427
|
this.simulation.restart();
|
|
345
428
|
}
|
|
346
429
|
}
|
|
430
|
+
clearState() {
|
|
431
|
+
this.isDragging = false;
|
|
432
|
+
this.simulationWorking = false;
|
|
433
|
+
this.highlightedNode = null;
|
|
434
|
+
this.highlightedNeighbors = null;
|
|
435
|
+
this.highlighFading = 1;
|
|
436
|
+
this.highlightFadingWorking = false;
|
|
437
|
+
this.highlightDrawing = false;
|
|
438
|
+
}
|
|
439
|
+
clearDataDependencies() {
|
|
440
|
+
this.cachedNodeText = {};
|
|
441
|
+
}
|
|
347
442
|
updateData() {
|
|
443
|
+
this.clearDataDependencies();
|
|
348
444
|
if (this.simulation) {
|
|
349
445
|
this.initCollideForce();
|
|
350
446
|
this.simulation
|
|
351
|
-
.stop()
|
|
352
|
-
.alpha(1)
|
|
353
447
|
.nodes(this.nodes)
|
|
354
448
|
.force("link", forceLink(this.links)
|
|
355
449
|
.id(this.nodeSettings.idGetter)
|
|
356
450
|
.distance(this.forceSettings.linkDistance)
|
|
357
451
|
.strength(this.forceSettings.linkStrength)
|
|
358
452
|
.iterations(this.forceSettings.linkIterations))
|
|
453
|
+
.alpha(0.5)
|
|
359
454
|
.restart();
|
|
360
455
|
}
|
|
361
456
|
}
|
|
@@ -368,11 +463,7 @@ class GraphCanvas {
|
|
|
368
463
|
this.area.width = this.dpi * width;
|
|
369
464
|
this.area.height = this.dpi * height;
|
|
370
465
|
this.areaRect = this.area.getBoundingClientRect();
|
|
371
|
-
this.simulation
|
|
372
|
-
.stop()
|
|
373
|
-
.alpha(1)
|
|
374
|
-
.force("center", forceCenter(this.forceSettings.centerPosition.x || this.width / 2, this.forceSettings.centerPosition.y || this.height / 2).strength(this.forceSettings.centerStrength))
|
|
375
|
-
.restart();
|
|
466
|
+
this.simulation.alpha(0.1).restart();
|
|
376
467
|
if (!this.simulationWorking)
|
|
377
468
|
this.draw();
|
|
378
469
|
}
|
|
@@ -415,7 +506,7 @@ class GraphCanvas {
|
|
|
415
506
|
.strength(this.forceSettings.chargeStrength)
|
|
416
507
|
.distanceMax(this.forceSettings.chargeDistanceMax)
|
|
417
508
|
.distanceMin(this.forceSettings.chargeDistanceMin))
|
|
418
|
-
.force("center", forceCenter(this.forceSettings.centerPosition.x ||
|
|
509
|
+
.force("center", forceCenter(this.forceSettings.centerPosition.x || 0, this.forceSettings.centerPosition.y || 0).strength(this.forceSettings.centerStrength));
|
|
419
510
|
this.initCollideForce();
|
|
420
511
|
}
|
|
421
512
|
initCollideForce() {
|
|
@@ -439,13 +530,13 @@ class GraphCanvas {
|
|
|
439
530
|
}
|
|
440
531
|
const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
|
|
441
532
|
const radius = nodeRadiusGetter({
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
radiusCoefficient:
|
|
445
|
-
radiusFactor:
|
|
533
|
+
radiusFlexible: this.graphSettings.nodeRadiusFlexible,
|
|
534
|
+
radiusInitial: this.graphSettings.nodeRadiusInitial,
|
|
535
|
+
radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
|
|
536
|
+
radiusFactor: this.graphSettings.nodeRadiusFactor,
|
|
446
537
|
linkCount: node.linkCount,
|
|
447
|
-
});
|
|
448
|
-
return radius +
|
|
538
|
+
}) ?? nodeOptions.radius;
|
|
539
|
+
return radius + this.forceSettings.collideAdditionalRadius;
|
|
449
540
|
})
|
|
450
541
|
.strength(this.forceSettings.collideStrength)
|
|
451
542
|
.iterations(this.forceSettings.collideIterations));
|
|
@@ -477,16 +568,43 @@ class GraphCanvas {
|
|
|
477
568
|
}
|
|
478
569
|
}
|
|
479
570
|
initDraw() {
|
|
571
|
+
function calculateHighlightFading() {
|
|
572
|
+
this.highlightDrawing = true;
|
|
573
|
+
if (!this.highlightFadingWorking) {
|
|
574
|
+
switch (true) {
|
|
575
|
+
case this.highlighFading < 1: {
|
|
576
|
+
this.highlighFading += this.graphSettings.highlightUpStep;
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
default: {
|
|
580
|
+
if (this.highlightedNeighbors)
|
|
581
|
+
this.highlightedNeighbors = null;
|
|
582
|
+
if (this.highlightedNode)
|
|
583
|
+
this.highlightedNode = null;
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (this.highlighFading < 1 && !this.simulationWorking)
|
|
588
|
+
return void requestAnimationFrame(() => this.draw());
|
|
589
|
+
}
|
|
590
|
+
if (this.highlightFadingWorking) {
|
|
591
|
+
switch (true) {
|
|
592
|
+
case this.highlighFading > this.graphSettings.highlightFadingMin: {
|
|
593
|
+
this.highlighFading -= this.graphSettings.highlightDownStep;
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (this.highlighFading > this.graphSettings.highlightFadingMin && !this.simulationWorking)
|
|
598
|
+
return void requestAnimationFrame(() => this.draw());
|
|
599
|
+
}
|
|
600
|
+
this.highlightDrawing = false;
|
|
601
|
+
}
|
|
480
602
|
function draw() {
|
|
481
603
|
if (!this.context)
|
|
482
604
|
return;
|
|
483
605
|
if (this.listeners.onDraw) {
|
|
484
606
|
return void this.listeners.onDraw(this.context, this.areaTransform);
|
|
485
607
|
}
|
|
486
|
-
if (this.highlightedNeighbors && this.highlightedNode) {
|
|
487
|
-
if (this.fading === 0)
|
|
488
|
-
this.fading = 1;
|
|
489
|
-
}
|
|
490
608
|
this.context.save();
|
|
491
609
|
this.context.clearRect(0, 0, this.width, this.height);
|
|
492
610
|
this.context.translate(this.areaTransform.x, this.areaTransform.y);
|
|
@@ -499,12 +617,7 @@ class GraphCanvas {
|
|
|
499
617
|
this.nodes.forEach(drawNode.bind(this));
|
|
500
618
|
this.context.restore();
|
|
501
619
|
this.listeners.onDrawFinished?.(this.context, this.areaTransform);
|
|
502
|
-
|
|
503
|
-
this.fading -= 0.1;
|
|
504
|
-
requestAnimationFrame(() => {
|
|
505
|
-
this.draw();
|
|
506
|
-
});
|
|
507
|
-
}
|
|
620
|
+
calculateHighlightFading.bind(this)();
|
|
508
621
|
}
|
|
509
622
|
function drawLink(link, index) {
|
|
510
623
|
if (!this.context ||
|
|
@@ -520,7 +633,7 @@ class GraphCanvas {
|
|
|
520
633
|
if (this.highlightedNeighbors && this.highlightedNode) {
|
|
521
634
|
if (this.highlightedNode.id != link.source.id &&
|
|
522
635
|
this.highlightedNode.id != link.target.id) {
|
|
523
|
-
alpha = this.
|
|
636
|
+
alpha = this.highlighFading;
|
|
524
637
|
}
|
|
525
638
|
this.context.beginPath();
|
|
526
639
|
}
|
|
@@ -537,33 +650,44 @@ class GraphCanvas {
|
|
|
537
650
|
return;
|
|
538
651
|
const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
|
|
539
652
|
const radius = nodeRadiusGetter({
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
radiusCoefficient:
|
|
543
|
-
radiusFactor:
|
|
653
|
+
radiusFlexible: this.graphSettings.nodeRadiusFlexible,
|
|
654
|
+
radiusInitial: this.graphSettings.nodeRadiusInitial,
|
|
655
|
+
radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
|
|
656
|
+
radiusFactor: this.graphSettings.nodeRadiusFactor,
|
|
544
657
|
linkCount: node.linkCount,
|
|
545
|
-
});
|
|
658
|
+
}) ?? nodeOptions.radius;
|
|
546
659
|
let alpha = nodeOptions.alpha;
|
|
547
660
|
if (this.highlightedNeighbors && this.highlightedNode) {
|
|
548
661
|
if (!this.highlightedNeighbors.has(node.id) && this.highlightedNode.id != node.id) {
|
|
549
|
-
alpha = this.
|
|
662
|
+
alpha = this.highlighFading;
|
|
550
663
|
}
|
|
551
664
|
}
|
|
552
665
|
this.context.beginPath();
|
|
553
666
|
this.context.globalAlpha = alpha;
|
|
554
667
|
/** text */
|
|
555
|
-
if (nodeOptions.text) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
668
|
+
if (nodeOptions.textVisible && nodeOptions.text) {
|
|
669
|
+
drawText({
|
|
670
|
+
id: node.id,
|
|
671
|
+
cachedNodeText: this.cachedNodeText,
|
|
672
|
+
context: this.context,
|
|
673
|
+
text: nodeOptions.text,
|
|
674
|
+
textAlign: nodeOptions.textAlign,
|
|
675
|
+
textColor: nodeOptions.textColor,
|
|
676
|
+
textFont: nodeOptions.textFont,
|
|
677
|
+
textSize: nodeOptions.textSize,
|
|
678
|
+
x: node.x + nodeOptions.textShiftX,
|
|
679
|
+
y: node.y + radius + nodeOptions.textShiftY,
|
|
680
|
+
maxWidth: nodeOptions.textWidth,
|
|
681
|
+
textStyle: nodeOptions.textStyle,
|
|
682
|
+
textWeight: nodeOptions.textWeight,
|
|
683
|
+
textGap: nodeOptions.textGap,
|
|
684
|
+
});
|
|
560
685
|
}
|
|
561
686
|
/** circle */
|
|
562
|
-
this.context.
|
|
563
|
-
this.context.
|
|
564
|
-
this.context.
|
|
687
|
+
this.context.lineWidth = nodeOptions.borderWidth;
|
|
688
|
+
this.context.strokeStyle = nodeOptions.borderColor;
|
|
689
|
+
this.context.fillStyle = nodeOptions.color;
|
|
565
690
|
this.context.arc(node.x, node.y, radius, 0, 2 * Math.PI);
|
|
566
|
-
this.context.fillStyle = nodeOptions.colorInner;
|
|
567
691
|
this.context.fill();
|
|
568
692
|
this.context.stroke();
|
|
569
693
|
}
|
|
@@ -586,9 +710,9 @@ class GraphCanvas {
|
|
|
586
710
|
throw new Error("bad init data");
|
|
587
711
|
this.area.addEventListener("pointermove", (event) => {
|
|
588
712
|
let currentNode;
|
|
589
|
-
if (this.graphSettings.highlightByHover) {
|
|
713
|
+
if (this.graphSettings.highlightByHover && !this.isDragging) {
|
|
590
714
|
currentNode = nodeByPointerGetter({
|
|
591
|
-
|
|
715
|
+
graphSettings: this.graphSettings,
|
|
592
716
|
areaRect: this.areaRect,
|
|
593
717
|
areaTransform: this.areaTransform,
|
|
594
718
|
mouseEvent: event,
|
|
@@ -597,23 +721,25 @@ class GraphCanvas {
|
|
|
597
721
|
if (currentNode && currentNode.neighbors && this.highlightedNode !== currentNode) {
|
|
598
722
|
this.highlightedNode = currentNode;
|
|
599
723
|
this.highlightedNeighbors = new Set(this.highlightedNode.neighbors);
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
724
|
+
this.highlightFadingWorking = true;
|
|
725
|
+
if (!this.simulationWorking && !this.highlightDrawing)
|
|
726
|
+
requestAnimationFrame(() => {
|
|
727
|
+
this.draw();
|
|
728
|
+
});
|
|
603
729
|
}
|
|
604
730
|
else if (!currentNode && this.highlightedNode) {
|
|
605
|
-
this.
|
|
606
|
-
this.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
731
|
+
this.highlightFadingWorking = false;
|
|
732
|
+
if (!this.simulationWorking && !this.highlightDrawing)
|
|
733
|
+
requestAnimationFrame(() => {
|
|
734
|
+
this.draw();
|
|
735
|
+
});
|
|
610
736
|
}
|
|
611
737
|
}
|
|
612
738
|
if (!this.listeners.onMove)
|
|
613
739
|
return;
|
|
614
740
|
if (!currentNode)
|
|
615
741
|
currentNode = nodeByPointerGetter({
|
|
616
|
-
|
|
742
|
+
graphSettings: this.graphSettings,
|
|
617
743
|
areaRect: this.areaRect,
|
|
618
744
|
areaTransform: this.areaTransform,
|
|
619
745
|
mouseEvent: event,
|
|
@@ -627,7 +753,7 @@ class GraphCanvas {
|
|
|
627
753
|
if (!this.listeners.onDoubleClick)
|
|
628
754
|
return;
|
|
629
755
|
const currentNode = nodeByPointerGetter({
|
|
630
|
-
|
|
756
|
+
graphSettings: this.graphSettings,
|
|
631
757
|
areaRect: this.areaRect,
|
|
632
758
|
areaTransform: this.areaTransform,
|
|
633
759
|
mouseEvent: event,
|
|
@@ -642,7 +768,7 @@ class GraphCanvas {
|
|
|
642
768
|
if (!this.listeners.onClick)
|
|
643
769
|
return;
|
|
644
770
|
const currentNode = nodeByPointerGetter({
|
|
645
|
-
|
|
771
|
+
graphSettings: this.graphSettings,
|
|
646
772
|
areaRect: this.areaRect,
|
|
647
773
|
areaTransform: this.areaTransform,
|
|
648
774
|
mouseEvent: event,
|
|
@@ -654,7 +780,7 @@ class GraphCanvas {
|
|
|
654
780
|
if (!this.listeners.onWheelClick)
|
|
655
781
|
return;
|
|
656
782
|
const currentNode = nodeByPointerGetter({
|
|
657
|
-
|
|
783
|
+
graphSettings: this.graphSettings,
|
|
658
784
|
areaRect: this.areaRect,
|
|
659
785
|
areaTransform: this.areaTransform,
|
|
660
786
|
mouseEvent: event,
|
|
@@ -669,7 +795,7 @@ class GraphCanvas {
|
|
|
669
795
|
if (!this.listeners.onContextMenu)
|
|
670
796
|
return;
|
|
671
797
|
const currentNode = nodeByPointerGetter({
|
|
672
|
-
|
|
798
|
+
graphSettings: this.graphSettings,
|
|
673
799
|
areaRect: this.areaRect,
|
|
674
800
|
areaTransform: this.areaTransform,
|
|
675
801
|
mouseEvent: event,
|
|
@@ -698,12 +824,12 @@ class GraphCanvas {
|
|
|
698
824
|
return undefined;
|
|
699
825
|
const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
|
|
700
826
|
const radius = nodeRadiusGetter({
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
radiusCoefficient:
|
|
704
|
-
radiusFactor:
|
|
827
|
+
radiusFlexible: this.graphSettings.nodeRadiusFlexible,
|
|
828
|
+
radiusInitial: this.graphSettings.nodeRadiusInitial,
|
|
829
|
+
radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
|
|
830
|
+
radiusFactor: this.graphSettings.nodeRadiusFactor,
|
|
705
831
|
linkCount: node.linkCount,
|
|
706
|
-
});
|
|
832
|
+
}) ?? nodeOptions.radius;
|
|
707
833
|
index++;
|
|
708
834
|
return this.graphSettings.dragPlaceCoefficient(node, pointerX, pointerY, radius);
|
|
709
835
|
});
|
|
@@ -757,6 +883,8 @@ class GraphCanvas {
|
|
|
757
883
|
requestAnimationFrame(() => this.draw());
|
|
758
884
|
}))
|
|
759
885
|
.on("dblclick.zoom", null);
|
|
886
|
+
this.areaTransform = new ZoomTransform(1, this.width / 2, this.height / 2);
|
|
887
|
+
zoom().transform(select(this.area), this.areaTransform);
|
|
760
888
|
}
|
|
761
889
|
}
|
|
762
890
|
|