landscape-widget 0.2.0 → 0.3.0-alpha
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/README.md +218 -0
- package/css/widget.css +8 -0
- package/dist/index.js +1 -1
- package/lib/Analysis.types.js +26 -0
- package/lib/Analysis.utils.js +5 -0
- package/lib/ForceAtlasAnimator.js +147 -0
- package/lib/GraphContainer.js +105 -0
- package/lib/GraphLayout.js +440 -0
- package/lib/GraphLayout.types.js +6 -0
- package/lib/InitialLayoutWorker.js +31 -0
- package/lib/IterateNoIntercomponentRepel.js +282 -0
- package/lib/LoadGraph.js +132 -0
- package/lib/SigmaLasso.js +118 -0
- package/lib/enum/layout.js +24 -0
- package/lib/explicitComponentLayout.js +155 -0
- package/lib/extension.js +18 -0
- package/lib/icons/AnalysisIcon.js +4 -0
- package/lib/icons/ChevronSolid.js +4 -0
- package/lib/icons/ClearSelection.js +5 -0
- package/lib/icons/FullScreenEnter.js +8 -0
- package/lib/icons/FullScreenExit.js +8 -0
- package/lib/icons/Gradient.js +6 -0
- package/lib/icons/Grid.js +4 -0
- package/lib/icons/Invert.js +5 -0
- package/lib/icons/Lasso.js +5 -0
- package/lib/icons/List.js +9 -0
- package/lib/icons/Minus.js +4 -0
- package/lib/icons/Plus.js +5 -0
- package/lib/icons/Reset.js +5 -0
- package/lib/icons/index.js +13 -0
- package/lib/index.js +3 -0
- package/lib/landscape.js +226 -0
- package/lib/mui/Tooltip.js +30 -0
- package/lib/plugin.js +27 -0
- package/lib/public-path.js +4 -0
- package/lib/version.js +16 -0
- package/package.json +107 -1
- package/NOTICE.txt +0 -274
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/* eslint no-constant-condition: 0 */
|
|
2
|
+
/* eslint no-restricted-properties: "warn" */
|
|
3
|
+
/* eslint prefer-destructuring: "warn" */
|
|
4
|
+
/* eslint no-param-reassign: "warn" */
|
|
5
|
+
/**
|
|
6
|
+
* Graphology ForceAtlas2 Iteration
|
|
7
|
+
* =================================
|
|
8
|
+
*
|
|
9
|
+
* Function used to perform a single iteration of the algorithm.
|
|
10
|
+
*/
|
|
11
|
+
import { NODE_X, NODE_Y, NODE_DX, NODE_DY, NODE_FIXED, NODE_SIZE, NODE_MASS, NODE_CONVERGENCE, NODE_OLD_DX, NODE_OLD_DY, PPN, PPE, EDGE_SOURCE, EDGE_TARGET, EDGE_WEIGHT, } from './enum/layout';
|
|
12
|
+
const MAX_FORCE = 10;
|
|
13
|
+
/**
|
|
14
|
+
* Function used to calculate dx, dy for each node due to repulsion forces.
|
|
15
|
+
*
|
|
16
|
+
* @param {object} options - Layout options.
|
|
17
|
+
* @param {Float32Array} NodeMatrix - Node data. (operates in place)
|
|
18
|
+
* @param {string[][]} graphComponents - Graph data.
|
|
19
|
+
* @return {object} - Some metadata.
|
|
20
|
+
*/
|
|
21
|
+
function getMovementDifferentialsDueToRepulsion(options, NodeMatrix, graphComponents) {
|
|
22
|
+
var _a;
|
|
23
|
+
const coefficient = (_a = options.scalingRatio) !== null && _a !== void 0 ? _a : 1;
|
|
24
|
+
const adjustSizes = options.adjustSizes;
|
|
25
|
+
let xDist;
|
|
26
|
+
let yDist;
|
|
27
|
+
let distance;
|
|
28
|
+
let factor;
|
|
29
|
+
let n1;
|
|
30
|
+
let n2;
|
|
31
|
+
if (adjustSizes === true) {
|
|
32
|
+
graphComponents.forEach((component) => {
|
|
33
|
+
for (let i = 0; i < component.length; i += 1) {
|
|
34
|
+
n1 = Number(component[i]) * PPN;
|
|
35
|
+
for (let j = 0; j < i; j += 1) {
|
|
36
|
+
n2 = Number(component[j]) * PPN;
|
|
37
|
+
xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X];
|
|
38
|
+
yDist = NodeMatrix[n1 + NODE_Y] - NodeMatrix[n2 + NODE_Y];
|
|
39
|
+
// -- Anticollision Linear Repulsion
|
|
40
|
+
distance =
|
|
41
|
+
Math.sqrt(xDist * xDist + yDist * yDist) -
|
|
42
|
+
NodeMatrix[n1 + NODE_SIZE] -
|
|
43
|
+
NodeMatrix[n2 + NODE_SIZE];
|
|
44
|
+
if (distance > 0) {
|
|
45
|
+
factor =
|
|
46
|
+
(coefficient * NodeMatrix[n1 + NODE_MASS] * NodeMatrix[n2 + NODE_MASS]) /
|
|
47
|
+
distance /
|
|
48
|
+
distance;
|
|
49
|
+
// Updating nodes' dx and dy
|
|
50
|
+
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
|
51
|
+
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
|
52
|
+
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
|
53
|
+
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
|
54
|
+
}
|
|
55
|
+
else if (distance < 0) {
|
|
56
|
+
factor = 100 * coefficient * NodeMatrix[n1 + NODE_MASS] * NodeMatrix[n2 + NODE_MASS];
|
|
57
|
+
// Updating nodes' dx and dy
|
|
58
|
+
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
|
59
|
+
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
|
60
|
+
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
|
61
|
+
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
graphComponents.forEach((component, c) => {
|
|
69
|
+
for (let i = 0; i < component.length; i += 1) {
|
|
70
|
+
n1 = Number(component[i]) * PPN;
|
|
71
|
+
for (let j = 0; j < i; j += 1) {
|
|
72
|
+
n2 = Number(component[j]) * PPN;
|
|
73
|
+
xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X];
|
|
74
|
+
yDist = NodeMatrix[n1 + NODE_Y] - NodeMatrix[n2 + NODE_Y];
|
|
75
|
+
// -- Linear Repulsion
|
|
76
|
+
const distance_squared = xDist * xDist + yDist * yDist;
|
|
77
|
+
if (distance_squared > 0) {
|
|
78
|
+
distance = Math.sqrt(distance_squared);
|
|
79
|
+
factor =
|
|
80
|
+
(coefficient * NodeMatrix[n1 + NODE_MASS] * NodeMatrix[n2 + NODE_MASS]) /
|
|
81
|
+
distance_squared;
|
|
82
|
+
const norm_x = xDist / distance;
|
|
83
|
+
const norm_y = yDist / distance;
|
|
84
|
+
// Updating nodes' dx and dy
|
|
85
|
+
NodeMatrix[n1 + NODE_DX] += norm_x * factor;
|
|
86
|
+
NodeMatrix[n1 + NODE_DY] += norm_y * factor;
|
|
87
|
+
NodeMatrix[n2 + NODE_DX] -= norm_x * factor;
|
|
88
|
+
NodeMatrix[n2 + NODE_DY] -= norm_y * factor;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Function used to perform a single interation of the algorithm.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} options - Layout options.
|
|
99
|
+
* @param {Float32Array} NodeMatrix - Node data.
|
|
100
|
+
* @param {Float32Array} EdgeMatrix - Edge data.
|
|
101
|
+
* @param {string[][]} graphComponents - Graph Components
|
|
102
|
+
* @return {object} - Some metadata.
|
|
103
|
+
*/
|
|
104
|
+
export function iterateNoIntercomponentRepel(options, NodeMatrix, EdgeMatrix, graphComponents) {
|
|
105
|
+
var _a, _b, _c, _d;
|
|
106
|
+
// Initializing variables
|
|
107
|
+
let n;
|
|
108
|
+
let n1;
|
|
109
|
+
let n2;
|
|
110
|
+
let e;
|
|
111
|
+
const adjustSizes = options.adjustSizes;
|
|
112
|
+
let outboundAttCompensation;
|
|
113
|
+
let xDist;
|
|
114
|
+
let yDist;
|
|
115
|
+
let ewc;
|
|
116
|
+
let distance;
|
|
117
|
+
let factor = 0;
|
|
118
|
+
let force;
|
|
119
|
+
let swinging;
|
|
120
|
+
let traction;
|
|
121
|
+
let nodespeed;
|
|
122
|
+
// 1) Initializing layout data
|
|
123
|
+
//-----------------------------
|
|
124
|
+
// Resetting movements.
|
|
125
|
+
for (n = 0; n < NodeMatrix.length; n += PPN) {
|
|
126
|
+
NodeMatrix[n + NODE_OLD_DX] = NodeMatrix[n + NODE_DX];
|
|
127
|
+
NodeMatrix[n + NODE_OLD_DY] = NodeMatrix[n + NODE_DY];
|
|
128
|
+
NodeMatrix[n + NODE_DX] = 0;
|
|
129
|
+
NodeMatrix[n + NODE_DY] = 0;
|
|
130
|
+
}
|
|
131
|
+
const numberVertices = NodeMatrix.length / PPN;
|
|
132
|
+
// If outbound attraction distribution, compensate
|
|
133
|
+
if (options.outboundAttractionDistribution) {
|
|
134
|
+
outboundAttCompensation = 0;
|
|
135
|
+
for (n = 0; n < NodeMatrix.length; n += PPN) {
|
|
136
|
+
outboundAttCompensation += NodeMatrix[n + NODE_MASS];
|
|
137
|
+
}
|
|
138
|
+
outboundAttCompensation /= numberVertices;
|
|
139
|
+
}
|
|
140
|
+
// Repulsion
|
|
141
|
+
//--------------
|
|
142
|
+
// NOTES: adjustSizes = antiCollision & scalingRatio = coefficient
|
|
143
|
+
// Writes to NodeMatrix.
|
|
144
|
+
getMovementDifferentialsDueToRepulsion(options, NodeMatrix, graphComponents);
|
|
145
|
+
// 4) Attraction
|
|
146
|
+
//---------------
|
|
147
|
+
const coefficient = options.outboundAttractionDistribution ? outboundAttCompensation : 1;
|
|
148
|
+
if (!coefficient)
|
|
149
|
+
return;
|
|
150
|
+
const size = EdgeMatrix.length;
|
|
151
|
+
for (e = 0; e < size; e += PPE) {
|
|
152
|
+
n1 = EdgeMatrix[e + EDGE_SOURCE];
|
|
153
|
+
n2 = EdgeMatrix[e + EDGE_TARGET];
|
|
154
|
+
const w = EdgeMatrix[e + EDGE_WEIGHT];
|
|
155
|
+
// Edge weight influence
|
|
156
|
+
ewc = Math.pow(w, ((_a = options.edgeWeightInfluence) !== null && _a !== void 0 ? _a : 1));
|
|
157
|
+
// Common measures
|
|
158
|
+
xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X];
|
|
159
|
+
yDist = NodeMatrix[n1 + NODE_Y] - NodeMatrix[n2 + NODE_Y];
|
|
160
|
+
// Applying attraction to nodes
|
|
161
|
+
if (adjustSizes === true) {
|
|
162
|
+
distance =
|
|
163
|
+
Math.sqrt(xDist * xDist + yDist * yDist) -
|
|
164
|
+
NodeMatrix[n1 + NODE_SIZE] -
|
|
165
|
+
NodeMatrix[n2 + NODE_SIZE];
|
|
166
|
+
if (options.linLogMode) {
|
|
167
|
+
if (options.outboundAttractionDistribution) {
|
|
168
|
+
// -- LinLog Degree Distributed Anti-collision Attraction
|
|
169
|
+
if (distance > 0) {
|
|
170
|
+
factor =
|
|
171
|
+
(-coefficient * ewc * Math.log(1 + distance)) / distance / NodeMatrix[n1 + NODE_MASS];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (distance > 0) {
|
|
175
|
+
// -- LinLog Anti-collision Attraction
|
|
176
|
+
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (options.outboundAttractionDistribution) {
|
|
180
|
+
// -- Linear Degree Distributed Anti-collision Attraction
|
|
181
|
+
if (distance > 0) {
|
|
182
|
+
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (distance > 0) {
|
|
186
|
+
// -- Linear Anti-collision Attraction
|
|
187
|
+
factor = -coefficient * ewc;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
distance = Math.sqrt(xDist * xDist + yDist * yDist);
|
|
192
|
+
if (options.linLogMode) {
|
|
193
|
+
if (options.outboundAttractionDistribution) {
|
|
194
|
+
// -- LinLog Degree Distributed Attraction
|
|
195
|
+
if (distance > 0) {
|
|
196
|
+
factor =
|
|
197
|
+
(-coefficient * ewc * Math.log(1 + distance)) / distance / NodeMatrix[n1 + NODE_MASS];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (distance > 0) {
|
|
201
|
+
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (options.outboundAttractionDistribution) {
|
|
205
|
+
// -- Linear Attraction Mass Distributed
|
|
206
|
+
// NOTE: Distance is set to 1 to override next condition
|
|
207
|
+
distance = 1;
|
|
208
|
+
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS];
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// -- Linear Attraction
|
|
212
|
+
// NOTE: Distance is set to 1 to override next condition
|
|
213
|
+
distance = 1;
|
|
214
|
+
factor = -coefficient * ewc;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Updating nodes' dx and dy
|
|
218
|
+
// TODO: if condition or factor = 1?
|
|
219
|
+
if (distance > 0) {
|
|
220
|
+
// Updating nodes' dx and dy
|
|
221
|
+
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
|
222
|
+
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
|
223
|
+
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
|
224
|
+
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Apply Forces
|
|
228
|
+
// -----------------
|
|
229
|
+
// MATH: sqrt and square distances
|
|
230
|
+
if (adjustSizes === true) {
|
|
231
|
+
for (n = 0; n < NodeMatrix.length; n += PPN) {
|
|
232
|
+
if (NodeMatrix[n + NODE_FIXED] !== 1) {
|
|
233
|
+
force = Math.sqrt(Math.pow(NodeMatrix[n + NODE_DX], 2) + Math.pow(NodeMatrix[n + NODE_DY], 2));
|
|
234
|
+
if (force > MAX_FORCE) {
|
|
235
|
+
NodeMatrix[n + NODE_DX] = (NodeMatrix[n + NODE_DX] * MAX_FORCE) / force;
|
|
236
|
+
NodeMatrix[n + NODE_DY] = (NodeMatrix[n + NODE_DY] * MAX_FORCE) / force;
|
|
237
|
+
}
|
|
238
|
+
swinging =
|
|
239
|
+
NodeMatrix[n + NODE_MASS] *
|
|
240
|
+
Math.sqrt((NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) *
|
|
241
|
+
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) +
|
|
242
|
+
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) *
|
|
243
|
+
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]));
|
|
244
|
+
traction =
|
|
245
|
+
Math.sqrt((NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) *
|
|
246
|
+
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) +
|
|
247
|
+
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) *
|
|
248
|
+
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY])) / 2;
|
|
249
|
+
nodespeed = (0.1 * Math.log(1 + traction)) / (1 + Math.sqrt(swinging));
|
|
250
|
+
// Updating node's positon
|
|
251
|
+
const deltaX = NodeMatrix[n + NODE_DX] * (nodespeed / ((_b = options.slowDown) !== null && _b !== void 0 ? _b : 1));
|
|
252
|
+
NodeMatrix[n + NODE_X] += deltaX;
|
|
253
|
+
const deltaY = NodeMatrix[n + NODE_DY] * (nodespeed / ((_c = options.slowDown) !== null && _c !== void 0 ? _c : 1));
|
|
254
|
+
NodeMatrix[n + NODE_Y] += deltaY;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
for (n = 0; n < NodeMatrix.length; n += PPN) {
|
|
260
|
+
if (NodeMatrix[n + NODE_FIXED] !== 1) {
|
|
261
|
+
const ddx = NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX];
|
|
262
|
+
const ddy = NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY];
|
|
263
|
+
swinging = NodeMatrix[n + NODE_MASS] * Math.sqrt(Math.pow(ddx, 2) + Math.pow(ddy, 2));
|
|
264
|
+
const dxSum = NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX];
|
|
265
|
+
const dySum = NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY];
|
|
266
|
+
const swingingNormTerm = 1 + Math.sqrt(swinging);
|
|
267
|
+
traction = Math.sqrt(Math.pow(dxSum, 2) + Math.pow(dySum, 2)) / 2;
|
|
268
|
+
nodespeed = (NodeMatrix[n + NODE_CONVERGENCE] * Math.log(1 + traction)) / swingingNormTerm;
|
|
269
|
+
// Updating node convergence
|
|
270
|
+
NodeMatrix[n + NODE_CONVERGENCE] = Math.min(1, Math.sqrt((nodespeed * (Math.pow(NodeMatrix[n + NODE_DX], 2) + Math.pow(NodeMatrix[n + NODE_DY], 2))) /
|
|
271
|
+
swingingNormTerm));
|
|
272
|
+
// Updating node's positon
|
|
273
|
+
const stepSize = nodespeed / ((_d = options.slowDown) !== null && _d !== void 0 ? _d : 1);
|
|
274
|
+
NodeMatrix[n + NODE_X] += NodeMatrix[n + NODE_DX] * stepSize;
|
|
275
|
+
NodeMatrix[n + NODE_Y] += NodeMatrix[n + NODE_DY] * stepSize;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// We return the information about the layout (no need to return the matrices)
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=IterateNoIntercomponentRepel.js.map
|
package/lib/LoadGraph.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
import { useRegisterEvents, useSigma } from '@react-sigma/core';
|
|
3
|
+
export const LoadGraph = ({ graph, animator, animate, selectedNodes, setSelectedNodes, addSelectedNodes, }) => {
|
|
4
|
+
const sigma = useSigma();
|
|
5
|
+
const registerEvents = useRegisterEvents();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
sigma.setGraph(graph);
|
|
8
|
+
sigma.refresh();
|
|
9
|
+
}, [graph]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
graph.updateEachNodeAttributes((key, attr) => {
|
|
12
|
+
const n = Number(key);
|
|
13
|
+
if (selectedNodes.some((node) => node === n)) {
|
|
14
|
+
return Object.assign(Object.assign({}, attr), { highlighted: true });
|
|
15
|
+
}
|
|
16
|
+
return Object.assign(Object.assign({}, attr), { highlighted: false });
|
|
17
|
+
});
|
|
18
|
+
}, [selectedNodes, graph]);
|
|
19
|
+
const handleSelectionClear = useCallback(() => {
|
|
20
|
+
setSelectedNodes([]);
|
|
21
|
+
selectedNodes.forEach((node) => {
|
|
22
|
+
graph.setNodeAttribute(node, 'highlighted', false);
|
|
23
|
+
});
|
|
24
|
+
}, [setSelectedNodes, selectedNodes, graph]);
|
|
25
|
+
const onNodeDoubleClick = (e) => {
|
|
26
|
+
e.preventSigmaDefault();
|
|
27
|
+
const nodeValue = Number(e.node);
|
|
28
|
+
const isNodeSelectedAlready = selectedNodes.some((n) => n === nodeValue);
|
|
29
|
+
if (isNodeSelectedAlready) {
|
|
30
|
+
setSelectedNodes(selectedNodes.filter((n) => n !== nodeValue));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
addSelectedNodes([nodeValue]);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
let draggedNode;
|
|
38
|
+
let isDragging = false;
|
|
39
|
+
let isDraggedNodeSelected = false;
|
|
40
|
+
let previousPosition = null;
|
|
41
|
+
registerEvents({
|
|
42
|
+
downNode: (e) => {
|
|
43
|
+
isDragging = true;
|
|
44
|
+
draggedNode = e.node;
|
|
45
|
+
const nodeValue = Number(e.node);
|
|
46
|
+
graph.setNodeAttribute(draggedNode, 'highlighted', true);
|
|
47
|
+
isDraggedNodeSelected = selectedNodes.some((n) => n === nodeValue);
|
|
48
|
+
if (isDraggedNodeSelected) {
|
|
49
|
+
selectedNodes.map((node) => graph.updateNodeAttribute(node, 'fixed', () => true));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
graph.setNodeAttribute(draggedNode, 'fixed', true);
|
|
53
|
+
}
|
|
54
|
+
previousPosition = sigma.viewportToGraph(e.event);
|
|
55
|
+
if (!animator.isRunning() && animate) {
|
|
56
|
+
animator.start();
|
|
57
|
+
}
|
|
58
|
+
animator.resetNIters();
|
|
59
|
+
},
|
|
60
|
+
mousemovebody: (e) => {
|
|
61
|
+
if (!isDragging || !draggedNode || !previousPosition)
|
|
62
|
+
return;
|
|
63
|
+
animator.resetNIters();
|
|
64
|
+
const pos = sigma.viewportToGraph(e);
|
|
65
|
+
const dx = pos.x - previousPosition.x;
|
|
66
|
+
const dy = pos.y - previousPosition.y;
|
|
67
|
+
previousPosition = pos;
|
|
68
|
+
if (isDraggedNodeSelected) {
|
|
69
|
+
selectedNodes.map((node) => graph.updateNodeAttributes(node, (attr) => (Object.assign(Object.assign({}, attr), { x: attr.x + dx, y: attr.y + dy }))));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
graph.updateNodeAttributes(draggedNode, (attr) => (Object.assign(Object.assign({}, attr), { x: attr.x + dx, y: attr.y + dy })));
|
|
73
|
+
}
|
|
74
|
+
// Prevent sigma to move camera:
|
|
75
|
+
e.preventSigmaDefault();
|
|
76
|
+
e.original.preventDefault();
|
|
77
|
+
e.original.stopPropagation();
|
|
78
|
+
},
|
|
79
|
+
mouseup: () => {
|
|
80
|
+
if (draggedNode) {
|
|
81
|
+
if (isDraggedNodeSelected) {
|
|
82
|
+
selectedNodes.map((node) => graph.removeNodeAttribute(node, 'fixed'));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
graph.removeNodeAttribute(draggedNode, 'fixed');
|
|
86
|
+
graph.removeNodeAttribute(draggedNode, 'highlighted');
|
|
87
|
+
}
|
|
88
|
+
previousPosition = null;
|
|
89
|
+
}
|
|
90
|
+
isDragging = false;
|
|
91
|
+
draggedNode = null;
|
|
92
|
+
isDraggedNodeSelected = false;
|
|
93
|
+
},
|
|
94
|
+
mousedown: () => {
|
|
95
|
+
if (!sigma.getCustomBBox())
|
|
96
|
+
sigma.setCustomBBox(sigma.getBBox());
|
|
97
|
+
},
|
|
98
|
+
doubleClickNode: (e) => onNodeDoubleClick(e),
|
|
99
|
+
doubleClickStage: (e) => {
|
|
100
|
+
handleSelectionClear();
|
|
101
|
+
e.preventSigmaDefault();
|
|
102
|
+
},
|
|
103
|
+
wheel: (e) => {
|
|
104
|
+
const camera = sigma.getCamera();
|
|
105
|
+
const cameraState = camera.getState();
|
|
106
|
+
// too large a step makes the motion very unsmooth
|
|
107
|
+
const delta = Math.max(-1.5, Math.min(1.5, e.delta));
|
|
108
|
+
const newRatio = camera.getBoundedRatio(cameraState.ratio * (1 - delta / 8));
|
|
109
|
+
const event = e.original;
|
|
110
|
+
if (event.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) {
|
|
111
|
+
console.warn('WheelEvent.deltaMode is not DOM_DELTA_PIXEL. Zoom behavior may not be as expected.');
|
|
112
|
+
}
|
|
113
|
+
// shift the center so that the mouse cursor is the fixed point of the zoom
|
|
114
|
+
const newState = sigma.getViewportZoomedState(e, newRatio);
|
|
115
|
+
camera.setState(newState);
|
|
116
|
+
e.preventSigmaDefault(); // this call is supposed to work but doesn't
|
|
117
|
+
// eslint-disable-next-line no-param-reassign
|
|
118
|
+
e.sigmaDefaultPrevented = true; // so we have to set this flag manually
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}, [
|
|
122
|
+
registerEvents,
|
|
123
|
+
setSelectedNodes,
|
|
124
|
+
addSelectedNodes,
|
|
125
|
+
selectedNodes,
|
|
126
|
+
handleSelectionClear,
|
|
127
|
+
graph,
|
|
128
|
+
animate,
|
|
129
|
+
]);
|
|
130
|
+
return null;
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=LoadGraph.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React, { useCallback, useRef } from 'react';
|
|
2
|
+
import { useSigma } from '@react-sigma/core';
|
|
3
|
+
export const SigmaLasso = ({ active, setSelectedNodes, addSelectedNodes }) => {
|
|
4
|
+
const sigma = useSigma();
|
|
5
|
+
const lassoCanvasRef = useRef(null);
|
|
6
|
+
const getLassoCanvas = useCallback(() => {
|
|
7
|
+
if (active)
|
|
8
|
+
return lassoCanvasRef.current;
|
|
9
|
+
return null;
|
|
10
|
+
}, [active]);
|
|
11
|
+
const isDrawing = useRef(false);
|
|
12
|
+
const drawnPoints = useRef([]);
|
|
13
|
+
const onDrawingStart = (event) => {
|
|
14
|
+
const lassoCanvas = getLassoCanvas();
|
|
15
|
+
if (!lassoCanvas)
|
|
16
|
+
return;
|
|
17
|
+
isDrawing.current = true;
|
|
18
|
+
drawnPoints.current = [];
|
|
19
|
+
const drawingRectangle = lassoCanvas.getBoundingClientRect();
|
|
20
|
+
drawnPoints.current.push({
|
|
21
|
+
x: event.clientX - drawingRectangle.left,
|
|
22
|
+
y: event.clientY - drawingRectangle.top,
|
|
23
|
+
});
|
|
24
|
+
event.stopPropagation();
|
|
25
|
+
};
|
|
26
|
+
const onDrawing = (event) => {
|
|
27
|
+
const lassoCanvas = getLassoCanvas();
|
|
28
|
+
if (!lassoCanvas)
|
|
29
|
+
return;
|
|
30
|
+
if (isDrawing.current) {
|
|
31
|
+
let x;
|
|
32
|
+
let y;
|
|
33
|
+
const drawingRectangle = lassoCanvas.getBoundingClientRect();
|
|
34
|
+
const drawingContext = lassoCanvas.getContext('2d');
|
|
35
|
+
switch (event.type) {
|
|
36
|
+
case 'touchmove':
|
|
37
|
+
x = event.touches[0].clientX;
|
|
38
|
+
y = event.touches[0].clientY;
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
x = event.clientX;
|
|
42
|
+
y = event.clientY;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
drawnPoints.current.push({
|
|
46
|
+
x: x - drawingRectangle.left,
|
|
47
|
+
y: y - drawingRectangle.top,
|
|
48
|
+
});
|
|
49
|
+
// Drawing styles
|
|
50
|
+
drawingContext.strokeStyle = 'black';
|
|
51
|
+
drawingContext.lineWidth = 2;
|
|
52
|
+
drawingContext.fillStyle = 'rgba(200, 200, 200, 0.25)';
|
|
53
|
+
drawingContext.lineJoin = 'round';
|
|
54
|
+
drawingContext.lineCap = 'round';
|
|
55
|
+
// Clear the canvas
|
|
56
|
+
drawingContext.clearRect(0, 0, drawingContext.canvas.width, drawingContext.canvas.height);
|
|
57
|
+
// Redraw the complete path for a smoother effect
|
|
58
|
+
// Even smoother with quadratic curves
|
|
59
|
+
let sourcePoint = drawnPoints.current[0];
|
|
60
|
+
let destinationPoint = drawnPoints.current[1];
|
|
61
|
+
const pointsLength = drawnPoints.current.length;
|
|
62
|
+
const getMiddlePointCoordinates = (firstPoint, secondPoint) => ({
|
|
63
|
+
x: firstPoint.x + (secondPoint.x - firstPoint.x) / 2,
|
|
64
|
+
y: firstPoint.y + (secondPoint.y - firstPoint.y) / 2,
|
|
65
|
+
});
|
|
66
|
+
drawingContext.beginPath();
|
|
67
|
+
drawingContext.moveTo(sourcePoint.x, sourcePoint.y);
|
|
68
|
+
for (let i = 1; i < pointsLength; i += 1) {
|
|
69
|
+
const middlePoint = getMiddlePointCoordinates(sourcePoint, destinationPoint);
|
|
70
|
+
drawingContext.quadraticCurveTo(sourcePoint.x, sourcePoint.y, middlePoint.x, middlePoint.y);
|
|
71
|
+
sourcePoint = drawnPoints.current[i];
|
|
72
|
+
destinationPoint = drawnPoints.current[i + 1];
|
|
73
|
+
}
|
|
74
|
+
drawingContext.lineTo(sourcePoint.x, sourcePoint.y);
|
|
75
|
+
drawingContext.stroke();
|
|
76
|
+
drawingContext.fill();
|
|
77
|
+
event.stopPropagation();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const onDrawingEnd = useCallback((event) => {
|
|
81
|
+
const lassoCanvas = getLassoCanvas();
|
|
82
|
+
const viewRectangle = sigma.viewRectangle();
|
|
83
|
+
const graph = sigma.getGraph();
|
|
84
|
+
if (!lassoCanvas)
|
|
85
|
+
return;
|
|
86
|
+
isDrawing.current = false;
|
|
87
|
+
// @ts-ignore - There are no other ways to get visible nodes
|
|
88
|
+
const quadtreeRectangle = sigma.quadtree.rectangle(viewRectangle.x1, 1 - viewRectangle.y1, viewRectangle.x2, 1 - viewRectangle.y2, viewRectangle.height);
|
|
89
|
+
const visibleNodes = Array.from(new Set(quadtreeRectangle));
|
|
90
|
+
const drawingContext = lassoCanvas.getContext('2d');
|
|
91
|
+
const newSelectedNodes = [];
|
|
92
|
+
visibleNodes.forEach((key) => {
|
|
93
|
+
const nodeValue = Number(key);
|
|
94
|
+
const { x, y } = graph.getNodeAttributes(nodeValue);
|
|
95
|
+
const coordinates = sigma.graphToViewport({ x, y });
|
|
96
|
+
if (drawingContext.isPointInPath(coordinates.x, coordinates.y)) {
|
|
97
|
+
newSelectedNodes.push(nodeValue);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (event.shiftKey) {
|
|
101
|
+
addSelectedNodes(newSelectedNodes);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
setSelectedNodes(newSelectedNodes);
|
|
105
|
+
}
|
|
106
|
+
drawingContext.clearRect(0, 0, lassoCanvas.width, lassoCanvas.height);
|
|
107
|
+
drawnPoints.current = [];
|
|
108
|
+
event.stopPropagation();
|
|
109
|
+
}, [setSelectedNodes, addSelectedNodes, getLassoCanvas, sigma]);
|
|
110
|
+
const onLassoScroll = (e) => {
|
|
111
|
+
const mouseCaptor = sigma.getMouseCaptor();
|
|
112
|
+
mouseCaptor.handleWheel(e.nativeEvent);
|
|
113
|
+
};
|
|
114
|
+
if (!active)
|
|
115
|
+
return null;
|
|
116
|
+
return (React.createElement("canvas", { ref: lassoCanvasRef, width: sigma.getContainer().offsetWidth, height: sigma.getContainer().offsetHeight, style: { position: 'absolute', top: '0px', cursor: 'crosshair' }, onMouseDown: onDrawingStart, onMouseMove: onDrawing, onMouseUp: onDrawingEnd, onWheel: onLassoScroll }));
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=SigmaLasso.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export var LayoutEnum;
|
|
2
|
+
(function (LayoutEnum) {
|
|
3
|
+
LayoutEnum["GRID"] = "grid";
|
|
4
|
+
LayoutEnum["TABLE"] = "table";
|
|
5
|
+
})(LayoutEnum || (LayoutEnum = {}));
|
|
6
|
+
/**
|
|
7
|
+
* Matrices properties accessors.
|
|
8
|
+
*/
|
|
9
|
+
export const NODE_X = 0;
|
|
10
|
+
export const NODE_Y = 1;
|
|
11
|
+
export const NODE_DX = 2;
|
|
12
|
+
export const NODE_DY = 3;
|
|
13
|
+
export const NODE_OLD_DX = 4;
|
|
14
|
+
export const NODE_OLD_DY = 5;
|
|
15
|
+
export const NODE_MASS = 6;
|
|
16
|
+
export const NODE_CONVERGENCE = 7;
|
|
17
|
+
export const NODE_SIZE = 8;
|
|
18
|
+
export const NODE_FIXED = 9;
|
|
19
|
+
export const PPN = 10;
|
|
20
|
+
export const PPE = 3;
|
|
21
|
+
export const EDGE_SOURCE = 0;
|
|
22
|
+
export const EDGE_TARGET = 1;
|
|
23
|
+
export const EDGE_WEIGHT = 2;
|
|
24
|
+
//# sourceMappingURL=layout.js.map
|