force-graph 1.42.3

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/force-graph.common.js +1754 -0
  4. package/dist/force-graph.d.ts +195 -0
  5. package/dist/force-graph.js +12168 -0
  6. package/dist/force-graph.js.map +1 -0
  7. package/dist/force-graph.min.js +5 -0
  8. package/dist/force-graph.module.js +1743 -0
  9. package/example/auto-colored/index.html +34 -0
  10. package/example/basic/index.html +29 -0
  11. package/example/build-a-graph/index.html +108 -0
  12. package/example/click-to-focus/index.html +28 -0
  13. package/example/collision-detection/index.html +50 -0
  14. package/example/curved-links/index.html +37 -0
  15. package/example/curved-links-computed-curvature/index.html +76 -0
  16. package/example/custom-node-shape/index.html +44 -0
  17. package/example/dag-yarn/index.html +96 -0
  18. package/example/dagre/index.html +119 -0
  19. package/example/dash-odd-links/index.html +47 -0
  20. package/example/datasets/blocks.json +1 -0
  21. package/example/datasets/d3-dependencies.csv +464 -0
  22. package/example/datasets/miserables.json +337 -0
  23. package/example/datasets/mplate.mtx +74090 -0
  24. package/example/directional-links-arrows/index.html +29 -0
  25. package/example/directional-links-particles/index.html +22 -0
  26. package/example/dynamic/index.html +42 -0
  27. package/example/emit-particles/index.html +50 -0
  28. package/example/expandable-nodes/index.html +66 -0
  29. package/example/expandable-tree/index.html +85 -0
  30. package/example/fit-to-canvas/index.html +34 -0
  31. package/example/fix-dragged-nodes/index.html +24 -0
  32. package/example/highlight/index.html +84 -0
  33. package/example/huge-1M/index.html +37 -0
  34. package/example/img-nodes/imgs/cat.jpg +0 -0
  35. package/example/img-nodes/imgs/dog.jpg +0 -0
  36. package/example/img-nodes/imgs/eagle.jpg +0 -0
  37. package/example/img-nodes/imgs/elephant.jpg +0 -0
  38. package/example/img-nodes/imgs/grasshopper.jpg +0 -0
  39. package/example/img-nodes/imgs/octopus.jpg +0 -0
  40. package/example/img-nodes/imgs/owl.jpg +0 -0
  41. package/example/img-nodes/imgs/panda.jpg +0 -0
  42. package/example/img-nodes/imgs/squirrel.jpg +0 -0
  43. package/example/img-nodes/imgs/tiger.jpg +0 -0
  44. package/example/img-nodes/imgs/whale.jpg +0 -0
  45. package/example/img-nodes/index.html +43 -0
  46. package/example/large-graph/index.html +41 -0
  47. package/example/load-json/index.html +24 -0
  48. package/example/medium-graph/index.html +26 -0
  49. package/example/medium-graph/preview.png +0 -0
  50. package/example/move-viewport/index.html +42 -0
  51. package/example/multi-selection/index.html +57 -0
  52. package/example/responsive/index.html +37 -0
  53. package/example/text-links/index.html +69 -0
  54. package/example/text-nodes/index.html +42 -0
  55. package/example/tree/index.html +71 -0
  56. package/package.json +72 -0
  57. package/src/canvas-force-graph.js +544 -0
  58. package/src/color-utils.js +17 -0
  59. package/src/dagDepths.js +51 -0
  60. package/src/force-graph.css +35 -0
  61. package/src/force-graph.js +644 -0
  62. package/src/index.d.ts +195 -0
  63. package/src/index.js +3 -0
  64. package/src/kapsule-link.js +34 -0
@@ -0,0 +1,1754 @@
1
+ 'use strict';
2
+
3
+ var d3Selection = require('d3-selection');
4
+ var d3Zoom = require('d3-zoom');
5
+ var d3Drag = require('d3-drag');
6
+ var d3Array = require('d3-array');
7
+ var throttle = require('lodash.throttle');
8
+ var TWEEN = require('@tweenjs/tween.js');
9
+ var Kapsule = require('kapsule');
10
+ var accessorFn = require('accessor-fn');
11
+ var ColorTracker = require('canvas-color-tracker');
12
+ var d3Force3d = require('d3-force-3d');
13
+ var bezierJs = require('bezier-js');
14
+ var indexBy = require('index-array-by');
15
+ var d3Scale = require('d3-scale');
16
+ var d3ScaleChromatic = require('d3-scale-chromatic');
17
+
18
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
19
+
20
+ var throttle__default = /*#__PURE__*/_interopDefaultLegacy(throttle);
21
+ var TWEEN__default = /*#__PURE__*/_interopDefaultLegacy(TWEEN);
22
+ var Kapsule__default = /*#__PURE__*/_interopDefaultLegacy(Kapsule);
23
+ var accessorFn__default = /*#__PURE__*/_interopDefaultLegacy(accessorFn);
24
+ var ColorTracker__default = /*#__PURE__*/_interopDefaultLegacy(ColorTracker);
25
+ var indexBy__default = /*#__PURE__*/_interopDefaultLegacy(indexBy);
26
+
27
+ function styleInject(css, ref) {
28
+ if (ref === void 0) ref = {};
29
+ var insertAt = ref.insertAt;
30
+
31
+ if (!css || typeof document === 'undefined') {
32
+ return;
33
+ }
34
+
35
+ var head = document.head || document.getElementsByTagName('head')[0];
36
+ var style = document.createElement('style');
37
+ style.type = 'text/css';
38
+
39
+ if (insertAt === 'top') {
40
+ if (head.firstChild) {
41
+ head.insertBefore(style, head.firstChild);
42
+ } else {
43
+ head.appendChild(style);
44
+ }
45
+ } else {
46
+ head.appendChild(style);
47
+ }
48
+
49
+ if (style.styleSheet) {
50
+ style.styleSheet.cssText = css;
51
+ } else {
52
+ style.appendChild(document.createTextNode(css));
53
+ }
54
+ }
55
+
56
+ var css_248z = ".force-graph-container canvas {\n display: block;\n user-select: none;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.force-graph-container .graph-tooltip {\n position: absolute;\n transform: translate(-50%, 25px);\n font-family: sans-serif;\n font-size: 16px;\n padding: 4px;\n border-radius: 3px;\n color: #eee;\n background: rgba(0,0,0,0.65);\n visibility: hidden; /* by default */\n}\n\n.force-graph-container .clickable {\n cursor: pointer;\n}\n\n.force-graph-container .grabbable {\n cursor: move;\n cursor: grab;\n cursor: -moz-grab;\n cursor: -webkit-grab;\n}\n\n.force-graph-container .grabbable:active {\n cursor: grabbing;\n cursor: -moz-grabbing;\n cursor: -webkit-grabbing;\n}\n";
57
+ styleInject(css_248z);
58
+
59
+ function ownKeys(object, enumerableOnly) {
60
+ var keys = Object.keys(object);
61
+
62
+ if (Object.getOwnPropertySymbols) {
63
+ var symbols = Object.getOwnPropertySymbols(object);
64
+
65
+ if (enumerableOnly) {
66
+ symbols = symbols.filter(function (sym) {
67
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
68
+ });
69
+ }
70
+
71
+ keys.push.apply(keys, symbols);
72
+ }
73
+
74
+ return keys;
75
+ }
76
+
77
+ function _objectSpread2(target) {
78
+ for (var i = 1; i < arguments.length; i++) {
79
+ var source = arguments[i] != null ? arguments[i] : {};
80
+
81
+ if (i % 2) {
82
+ ownKeys(Object(source), true).forEach(function (key) {
83
+ _defineProperty(target, key, source[key]);
84
+ });
85
+ } else if (Object.getOwnPropertyDescriptors) {
86
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
87
+ } else {
88
+ ownKeys(Object(source)).forEach(function (key) {
89
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
90
+ });
91
+ }
92
+ }
93
+
94
+ return target;
95
+ }
96
+
97
+ function _typeof(obj) {
98
+ "@babel/helpers - typeof";
99
+
100
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
101
+ _typeof = function (obj) {
102
+ return typeof obj;
103
+ };
104
+ } else {
105
+ _typeof = function (obj) {
106
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
107
+ };
108
+ }
109
+
110
+ return _typeof(obj);
111
+ }
112
+
113
+ function _defineProperty(obj, key, value) {
114
+ if (key in obj) {
115
+ Object.defineProperty(obj, key, {
116
+ value: value,
117
+ enumerable: true,
118
+ configurable: true,
119
+ writable: true
120
+ });
121
+ } else {
122
+ obj[key] = value;
123
+ }
124
+
125
+ return obj;
126
+ }
127
+
128
+ function _setPrototypeOf(o, p) {
129
+ _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
130
+ o.__proto__ = p;
131
+ return o;
132
+ };
133
+
134
+ return _setPrototypeOf(o, p);
135
+ }
136
+
137
+ function _isNativeReflectConstruct() {
138
+ if (typeof Reflect === "undefined" || !Reflect.construct) return false;
139
+ if (Reflect.construct.sham) return false;
140
+ if (typeof Proxy === "function") return true;
141
+
142
+ try {
143
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
144
+ return true;
145
+ } catch (e) {
146
+ return false;
147
+ }
148
+ }
149
+
150
+ function _construct(Parent, args, Class) {
151
+ if (_isNativeReflectConstruct()) {
152
+ _construct = Reflect.construct;
153
+ } else {
154
+ _construct = function _construct(Parent, args, Class) {
155
+ var a = [null];
156
+ a.push.apply(a, args);
157
+ var Constructor = Function.bind.apply(Parent, a);
158
+ var instance = new Constructor();
159
+ if (Class) _setPrototypeOf(instance, Class.prototype);
160
+ return instance;
161
+ };
162
+ }
163
+
164
+ return _construct.apply(null, arguments);
165
+ }
166
+
167
+ function _slicedToArray(arr, i) {
168
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
169
+ }
170
+
171
+ function _toConsumableArray(arr) {
172
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
173
+ }
174
+
175
+ function _arrayWithoutHoles(arr) {
176
+ if (Array.isArray(arr)) return _arrayLikeToArray(arr);
177
+ }
178
+
179
+ function _arrayWithHoles(arr) {
180
+ if (Array.isArray(arr)) return arr;
181
+ }
182
+
183
+ function _iterableToArray(iter) {
184
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
185
+ }
186
+
187
+ function _iterableToArrayLimit(arr, i) {
188
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
189
+
190
+ if (_i == null) return;
191
+ var _arr = [];
192
+ var _n = true;
193
+ var _d = false;
194
+
195
+ var _s, _e;
196
+
197
+ try {
198
+ for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
199
+ _arr.push(_s.value);
200
+
201
+ if (i && _arr.length === i) break;
202
+ }
203
+ } catch (err) {
204
+ _d = true;
205
+ _e = err;
206
+ } finally {
207
+ try {
208
+ if (!_n && _i["return"] != null) _i["return"]();
209
+ } finally {
210
+ if (_d) throw _e;
211
+ }
212
+ }
213
+
214
+ return _arr;
215
+ }
216
+
217
+ function _unsupportedIterableToArray(o, minLen) {
218
+ if (!o) return;
219
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
220
+ var n = Object.prototype.toString.call(o).slice(8, -1);
221
+ if (n === "Object" && o.constructor) n = o.constructor.name;
222
+ if (n === "Map" || n === "Set") return Array.from(o);
223
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
224
+ }
225
+
226
+ function _arrayLikeToArray(arr, len) {
227
+ if (len == null || len > arr.length) len = arr.length;
228
+
229
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
230
+
231
+ return arr2;
232
+ }
233
+
234
+ function _nonIterableSpread() {
235
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
236
+ }
237
+
238
+ function _nonIterableRest() {
239
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
240
+ }
241
+
242
+ var autoColorScale = d3Scale.scaleOrdinal(d3ScaleChromatic.schemePaired); // Autoset attribute colorField by colorByAccessor property
243
+ // If an object has already a color, don't set it
244
+ // Objects can be nodes or links
245
+
246
+ function autoColorObjects(objects, colorByAccessor, colorField) {
247
+ if (!colorByAccessor || typeof colorField !== 'string') return;
248
+ objects.filter(function (obj) {
249
+ return !obj[colorField];
250
+ }).forEach(function (obj) {
251
+ obj[colorField] = autoColorScale(colorByAccessor(obj));
252
+ });
253
+ }
254
+
255
+ function getDagDepths (_ref, idAccessor) {
256
+ var nodes = _ref.nodes,
257
+ links = _ref.links;
258
+
259
+ var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
260
+ _ref2$nodeFilter = _ref2.nodeFilter,
261
+ nodeFilter = _ref2$nodeFilter === void 0 ? function () {
262
+ return true;
263
+ } : _ref2$nodeFilter,
264
+ _ref2$onLoopError = _ref2.onLoopError,
265
+ onLoopError = _ref2$onLoopError === void 0 ? function (loopIds) {
266
+ throw "Invalid DAG structure! Found cycle in node path: ".concat(loopIds.join(' -> '), ".");
267
+ } : _ref2$onLoopError;
268
+
269
+ // linked graph
270
+ var graph = {};
271
+ nodes.forEach(function (node) {
272
+ return graph[idAccessor(node)] = {
273
+ data: node,
274
+ out: [],
275
+ depth: -1,
276
+ skip: !nodeFilter(node)
277
+ };
278
+ });
279
+ links.forEach(function (_ref3) {
280
+ var source = _ref3.source,
281
+ target = _ref3.target;
282
+ var sourceId = getNodeId(source);
283
+ var targetId = getNodeId(target);
284
+ if (!graph.hasOwnProperty(sourceId)) throw "Missing source node with id: ".concat(sourceId);
285
+ if (!graph.hasOwnProperty(targetId)) throw "Missing target node with id: ".concat(targetId);
286
+ var sourceNode = graph[sourceId];
287
+ var targetNode = graph[targetId];
288
+ sourceNode.out.push(targetNode);
289
+
290
+ function getNodeId(node) {
291
+ return _typeof(node) === 'object' ? idAccessor(node) : node;
292
+ }
293
+ });
294
+ var foundLoops = [];
295
+ traverse(Object.values(graph));
296
+ var nodeDepths = Object.assign.apply(Object, [{}].concat(_toConsumableArray(Object.entries(graph).filter(function (_ref4) {
297
+ var _ref5 = _slicedToArray(_ref4, 2),
298
+ node = _ref5[1];
299
+
300
+ return !node.skip;
301
+ }).map(function (_ref6) {
302
+ var _ref7 = _slicedToArray(_ref6, 2),
303
+ id = _ref7[0],
304
+ node = _ref7[1];
305
+
306
+ return _defineProperty({}, id, node.depth);
307
+ }))));
308
+ return nodeDepths;
309
+
310
+ function traverse(nodes) {
311
+ var nodeStack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
312
+ var currentDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
313
+
314
+ for (var i = 0, l = nodes.length; i < l; i++) {
315
+ var node = nodes[i];
316
+
317
+ if (nodeStack.indexOf(node) !== -1) {
318
+ var _ret = function () {
319
+ var loop = [].concat(_toConsumableArray(nodeStack.slice(nodeStack.indexOf(node))), [node]).map(function (d) {
320
+ return idAccessor(d.data);
321
+ });
322
+
323
+ if (!foundLoops.some(function (foundLoop) {
324
+ return foundLoop.length === loop.length && foundLoop.every(function (id, idx) {
325
+ return id === loop[idx];
326
+ });
327
+ })) {
328
+ foundLoops.push(loop);
329
+ onLoopError(loop);
330
+ }
331
+
332
+ return "continue";
333
+ }();
334
+
335
+ if (_ret === "continue") continue;
336
+ }
337
+
338
+ if (currentDepth > node.depth) {
339
+ // Don't unnecessarily revisit chunks of the graph
340
+ node.depth = currentDepth;
341
+ traverse(node.out, [].concat(_toConsumableArray(nodeStack), [node]), currentDepth + (node.skip ? 0 : 1));
342
+ }
343
+ }
344
+ }
345
+ }
346
+
347
+ var DAG_LEVEL_NODE_RATIO = 2; // whenever styling props are changed that require a canvas redraw
348
+
349
+ var notifyRedraw = function notifyRedraw(_, state) {
350
+ return state.onNeedsRedraw && state.onNeedsRedraw();
351
+ };
352
+
353
+ var CanvasForceGraph = Kapsule__default["default"]({
354
+ props: {
355
+ graphData: {
356
+ "default": {
357
+ nodes: [],
358
+ links: []
359
+ },
360
+ onChange: function onChange(_, state) {
361
+ state.engineRunning = false;
362
+ } // Pause simulation
363
+
364
+ },
365
+ dagMode: {
366
+ onChange: function onChange(dagMode, state) {
367
+ // td, bu, lr, rl, radialin, radialout
368
+ !dagMode && (state.graphData.nodes || []).forEach(function (n) {
369
+ return n.fx = n.fy = undefined;
370
+ }); // unfix nodes when disabling dag mode
371
+ }
372
+ },
373
+ dagLevelDistance: {},
374
+ dagNodeFilter: {
375
+ "default": function _default(node) {
376
+ return true;
377
+ }
378
+ },
379
+ onDagError: {
380
+ triggerUpdate: false
381
+ },
382
+ nodeRelSize: {
383
+ "default": 4,
384
+ triggerUpdate: false,
385
+ onChange: notifyRedraw
386
+ },
387
+ // area per val unit
388
+ nodeId: {
389
+ "default": 'id'
390
+ },
391
+ nodeVal: {
392
+ "default": 'val',
393
+ triggerUpdate: false,
394
+ onChange: notifyRedraw
395
+ },
396
+ nodeColor: {
397
+ "default": 'color',
398
+ triggerUpdate: false,
399
+ onChange: notifyRedraw
400
+ },
401
+ nodeAutoColorBy: {},
402
+ nodeCanvasObject: {
403
+ triggerUpdate: false,
404
+ onChange: notifyRedraw
405
+ },
406
+ nodeCanvasObjectMode: {
407
+ "default": function _default() {
408
+ return 'replace';
409
+ },
410
+ triggerUpdate: false,
411
+ onChange: notifyRedraw
412
+ },
413
+ nodeVisibility: {
414
+ "default": true,
415
+ triggerUpdate: false,
416
+ onChange: notifyRedraw
417
+ },
418
+ linkSource: {
419
+ "default": 'source'
420
+ },
421
+ linkTarget: {
422
+ "default": 'target'
423
+ },
424
+ linkVisibility: {
425
+ "default": true,
426
+ triggerUpdate: false,
427
+ onChange: notifyRedraw
428
+ },
429
+ linkColor: {
430
+ "default": 'color',
431
+ triggerUpdate: false,
432
+ onChange: notifyRedraw
433
+ },
434
+ linkAutoColorBy: {},
435
+ linkLineDash: {
436
+ triggerUpdate: false,
437
+ onChange: notifyRedraw
438
+ },
439
+ linkWidth: {
440
+ "default": 1,
441
+ triggerUpdate: false,
442
+ onChange: notifyRedraw
443
+ },
444
+ linkCurvature: {
445
+ "default": 0,
446
+ triggerUpdate: false,
447
+ onChange: notifyRedraw
448
+ },
449
+ linkCanvasObject: {
450
+ triggerUpdate: false,
451
+ onChange: notifyRedraw
452
+ },
453
+ linkCanvasObjectMode: {
454
+ "default": function _default() {
455
+ return 'replace';
456
+ },
457
+ triggerUpdate: false,
458
+ onChange: notifyRedraw
459
+ },
460
+ linkDirectionalArrowLength: {
461
+ "default": 0,
462
+ triggerUpdate: false,
463
+ onChange: notifyRedraw
464
+ },
465
+ linkDirectionalArrowColor: {
466
+ triggerUpdate: false,
467
+ onChange: notifyRedraw
468
+ },
469
+ linkDirectionalArrowRelPos: {
470
+ "default": 0.5,
471
+ triggerUpdate: false,
472
+ onChange: notifyRedraw
473
+ },
474
+ // value between 0<>1 indicating the relative pos along the (exposed) line
475
+ linkDirectionalParticles: {
476
+ "default": 0
477
+ },
478
+ // animate photons travelling in the link direction
479
+ linkDirectionalParticleSpeed: {
480
+ "default": 0.01,
481
+ triggerUpdate: false
482
+ },
483
+ // in link length ratio per frame
484
+ linkDirectionalParticleWidth: {
485
+ "default": 4,
486
+ triggerUpdate: false
487
+ },
488
+ linkDirectionalParticleColor: {
489
+ triggerUpdate: false
490
+ },
491
+ globalScale: {
492
+ "default": 1,
493
+ triggerUpdate: false
494
+ },
495
+ d3AlphaMin: {
496
+ "default": 0,
497
+ triggerUpdate: false
498
+ },
499
+ d3AlphaDecay: {
500
+ "default": 0.0228,
501
+ triggerUpdate: false,
502
+ onChange: function onChange(alphaDecay, state) {
503
+ state.forceLayout.alphaDecay(alphaDecay);
504
+ }
505
+ },
506
+ d3AlphaTarget: {
507
+ "default": 0,
508
+ triggerUpdate: false,
509
+ onChange: function onChange(alphaTarget, state) {
510
+ state.forceLayout.alphaTarget(alphaTarget);
511
+ }
512
+ },
513
+ d3VelocityDecay: {
514
+ "default": 0.4,
515
+ triggerUpdate: false,
516
+ onChange: function onChange(velocityDecay, state) {
517
+ state.forceLayout.velocityDecay(velocityDecay);
518
+ }
519
+ },
520
+ warmupTicks: {
521
+ "default": 0,
522
+ triggerUpdate: false
523
+ },
524
+ // how many times to tick the force engine at init before starting to render
525
+ cooldownTicks: {
526
+ "default": Infinity,
527
+ triggerUpdate: false
528
+ },
529
+ cooldownTime: {
530
+ "default": 15000,
531
+ triggerUpdate: false
532
+ },
533
+ // ms
534
+ onUpdate: {
535
+ "default": function _default() {},
536
+ triggerUpdate: false
537
+ },
538
+ onFinishUpdate: {
539
+ "default": function _default() {},
540
+ triggerUpdate: false
541
+ },
542
+ onEngineTick: {
543
+ "default": function _default() {},
544
+ triggerUpdate: false
545
+ },
546
+ onEngineStop: {
547
+ "default": function _default() {},
548
+ triggerUpdate: false
549
+ },
550
+ onNeedsRedraw: {
551
+ triggerUpdate: false
552
+ },
553
+ isShadow: {
554
+ "default": false,
555
+ triggerUpdate: false
556
+ }
557
+ },
558
+ methods: {
559
+ // Expose d3 forces for external manipulation
560
+ d3Force: function d3Force(state, forceName, forceFn) {
561
+ if (forceFn === undefined) {
562
+ return state.forceLayout.force(forceName); // Force getter
563
+ }
564
+
565
+ state.forceLayout.force(forceName, forceFn); // Force setter
566
+
567
+ return this;
568
+ },
569
+ d3ReheatSimulation: function d3ReheatSimulation(state) {
570
+ state.forceLayout.alpha(1);
571
+ this.resetCountdown();
572
+ return this;
573
+ },
574
+ // reset cooldown state
575
+ resetCountdown: function resetCountdown(state) {
576
+ state.cntTicks = 0;
577
+ state.startTickTime = new Date();
578
+ state.engineRunning = true;
579
+ return this;
580
+ },
581
+ isEngineRunning: function isEngineRunning(state) {
582
+ return !!state.engineRunning;
583
+ },
584
+ tickFrame: function tickFrame(state) {
585
+ !state.isShadow && layoutTick();
586
+ paintLinks();
587
+ !state.isShadow && paintArrows();
588
+ !state.isShadow && paintPhotons();
589
+ paintNodes();
590
+ return this; //
591
+
592
+ function layoutTick() {
593
+ if (state.engineRunning) {
594
+ if (++state.cntTicks > state.cooldownTicks || new Date() - state.startTickTime > state.cooldownTime || state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin) {
595
+ state.engineRunning = false; // Stop ticking graph
596
+
597
+ state.onEngineStop();
598
+ } else {
599
+ state.forceLayout.tick(); // Tick it
600
+
601
+ state.onEngineTick();
602
+ }
603
+ }
604
+ }
605
+
606
+ function paintNodes() {
607
+ var getVisibility = accessorFn__default["default"](state.nodeVisibility);
608
+ var getVal = accessorFn__default["default"](state.nodeVal);
609
+ var getColor = accessorFn__default["default"](state.nodeColor);
610
+ var getNodeCanvasObjectMode = accessorFn__default["default"](state.nodeCanvasObjectMode);
611
+ var ctx = state.ctx; // Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
612
+
613
+ var padAmount = state.isShadow / state.globalScale;
614
+ var visibleNodes = state.graphData.nodes.filter(getVisibility);
615
+ ctx.save();
616
+ visibleNodes.forEach(function (node) {
617
+ var nodeCanvasObjectMode = getNodeCanvasObjectMode(node);
618
+
619
+ if (state.nodeCanvasObject && (nodeCanvasObjectMode === 'before' || nodeCanvasObjectMode === 'replace')) {
620
+ // Custom node before/replace paint
621
+ state.nodeCanvasObject(node, ctx, state.globalScale);
622
+
623
+ if (nodeCanvasObjectMode === 'replace') {
624
+ ctx.restore();
625
+ return;
626
+ }
627
+ } // Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
628
+
629
+
630
+ var r = Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize + padAmount;
631
+ ctx.beginPath();
632
+ ctx.arc(node.x, node.y, r, 0, 2 * Math.PI, false);
633
+ ctx.fillStyle = getColor(node) || 'rgba(31, 120, 180, 0.92)';
634
+ ctx.fill();
635
+
636
+ if (state.nodeCanvasObject && nodeCanvasObjectMode === 'after') {
637
+ // Custom node after paint
638
+ state.nodeCanvasObject(node, state.ctx, state.globalScale);
639
+ }
640
+ });
641
+ ctx.restore();
642
+ }
643
+
644
+ function paintLinks() {
645
+ var getVisibility = accessorFn__default["default"](state.linkVisibility);
646
+ var getColor = accessorFn__default["default"](state.linkColor);
647
+ var getWidth = accessorFn__default["default"](state.linkWidth);
648
+ var getLineDash = accessorFn__default["default"](state.linkLineDash);
649
+ var getCurvature = accessorFn__default["default"](state.linkCurvature);
650
+ var getLinkCanvasObjectMode = accessorFn__default["default"](state.linkCanvasObjectMode);
651
+ var ctx = state.ctx; // Draw wider lines by 2px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
652
+
653
+ var padAmount = state.isShadow * 2;
654
+ var visibleLinks = state.graphData.links.filter(getVisibility);
655
+ visibleLinks.forEach(calcLinkControlPoints); // calculate curvature control points for all visible links
656
+
657
+ var beforeCustomLinks = [],
658
+ afterCustomLinks = [],
659
+ defaultPaintLinks = visibleLinks;
660
+
661
+ if (state.linkCanvasObject) {
662
+ var replaceCustomLinks = [],
663
+ otherCustomLinks = [];
664
+ visibleLinks.forEach(function (d) {
665
+ return ({
666
+ before: beforeCustomLinks,
667
+ after: afterCustomLinks,
668
+ replace: replaceCustomLinks
669
+ }[getLinkCanvasObjectMode(d)] || otherCustomLinks).push(d);
670
+ });
671
+ defaultPaintLinks = [].concat(_toConsumableArray(beforeCustomLinks), afterCustomLinks, otherCustomLinks);
672
+ beforeCustomLinks = beforeCustomLinks.concat(replaceCustomLinks);
673
+ } // Custom link before paints
674
+
675
+
676
+ ctx.save();
677
+ beforeCustomLinks.forEach(function (link) {
678
+ return state.linkCanvasObject(link, ctx, state.globalScale);
679
+ });
680
+ ctx.restore(); // Bundle strokes per unique color/width/dash for performance optimization
681
+
682
+ var linksPerColor = indexBy__default["default"](defaultPaintLinks, [getColor, getWidth, getLineDash]);
683
+ ctx.save();
684
+ Object.entries(linksPerColor).forEach(function (_ref) {
685
+ var _ref2 = _slicedToArray(_ref, 2),
686
+ color = _ref2[0],
687
+ linksPerWidth = _ref2[1];
688
+
689
+ var lineColor = !color || color === 'undefined' ? 'rgba(0,0,0,0.15)' : color;
690
+ Object.entries(linksPerWidth).forEach(function (_ref3) {
691
+ var _ref4 = _slicedToArray(_ref3, 2),
692
+ width = _ref4[0],
693
+ linesPerLineDash = _ref4[1];
694
+
695
+ var lineWidth = (width || 1) / state.globalScale + padAmount;
696
+ Object.entries(linesPerLineDash).forEach(function (_ref5) {
697
+ var _ref6 = _slicedToArray(_ref5, 2);
698
+ _ref6[0];
699
+ var links = _ref6[1];
700
+
701
+ var lineDashSegments = getLineDash(links[0]);
702
+ ctx.beginPath();
703
+ links.forEach(function (link) {
704
+ var start = link.source;
705
+ var end = link.target;
706
+ if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
707
+
708
+ ctx.moveTo(start.x, start.y);
709
+ var controlPoints = link.__controlPoints;
710
+
711
+ if (!controlPoints) {
712
+ // Straight line
713
+ ctx.lineTo(end.x, end.y);
714
+ } else {
715
+ // Use quadratic curves for regular lines and bezier for loops
716
+ ctx[controlPoints.length === 2 ? 'quadraticCurveTo' : 'bezierCurveTo'].apply(ctx, _toConsumableArray(controlPoints).concat([end.x, end.y]));
717
+ }
718
+ });
719
+ ctx.strokeStyle = lineColor;
720
+ ctx.lineWidth = lineWidth;
721
+ ctx.setLineDash(lineDashSegments || []);
722
+ ctx.stroke();
723
+ });
724
+ });
725
+ });
726
+ ctx.restore(); // Custom link after paints
727
+
728
+ ctx.save();
729
+ afterCustomLinks.forEach(function (link) {
730
+ return state.linkCanvasObject(link, ctx, state.globalScale);
731
+ });
732
+ ctx.restore(); //
733
+
734
+ function calcLinkControlPoints(link) {
735
+ var curvature = getCurvature(link);
736
+
737
+ if (!curvature) {
738
+ // straight line
739
+ link.__controlPoints = null;
740
+ return;
741
+ }
742
+
743
+ var start = link.source;
744
+ var end = link.target;
745
+ if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
746
+
747
+ var l = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)); // line length
748
+
749
+ if (l > 0) {
750
+ var a = Math.atan2(end.y - start.y, end.x - start.x); // line angle
751
+
752
+ var d = l * curvature; // control point distance
753
+
754
+ var cp = {
755
+ // control point
756
+ x: (start.x + end.x) / 2 + d * Math.cos(a - Math.PI / 2),
757
+ y: (start.y + end.y) / 2 + d * Math.sin(a - Math.PI / 2)
758
+ };
759
+ link.__controlPoints = [cp.x, cp.y];
760
+ } else {
761
+ // Same point, draw a loop
762
+ var _d = curvature * 70;
763
+
764
+ link.__controlPoints = [end.x, end.y - _d, end.x + _d, end.y];
765
+ }
766
+ }
767
+ }
768
+
769
+ function paintArrows() {
770
+ var ARROW_WH_RATIO = 1.6;
771
+ var ARROW_VLEN_RATIO = 0.2;
772
+ var getLength = accessorFn__default["default"](state.linkDirectionalArrowLength);
773
+ var getRelPos = accessorFn__default["default"](state.linkDirectionalArrowRelPos);
774
+ var getVisibility = accessorFn__default["default"](state.linkVisibility);
775
+ var getColor = accessorFn__default["default"](state.linkDirectionalArrowColor || state.linkColor);
776
+ var getNodeVal = accessorFn__default["default"](state.nodeVal);
777
+ var ctx = state.ctx;
778
+ ctx.save();
779
+ state.graphData.links.filter(getVisibility).forEach(function (link) {
780
+ var arrowLength = getLength(link);
781
+ if (!arrowLength || arrowLength < 0) return;
782
+ var start = link.source;
783
+ var end = link.target;
784
+ if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
785
+
786
+ var startR = Math.sqrt(Math.max(0, getNodeVal(start) || 1)) * state.nodeRelSize;
787
+ var endR = Math.sqrt(Math.max(0, getNodeVal(end) || 1)) * state.nodeRelSize;
788
+ var arrowRelPos = Math.min(1, Math.max(0, getRelPos(link)));
789
+ var arrowColor = getColor(link) || 'rgba(0,0,0,0.28)';
790
+ var arrowHalfWidth = arrowLength / ARROW_WH_RATIO / 2; // Construct bezier for curved lines
791
+
792
+ var bzLine = link.__controlPoints && _construct(bezierJs.Bezier, [start.x, start.y].concat(_toConsumableArray(link.__controlPoints), [end.x, end.y]));
793
+
794
+ var getCoordsAlongLine = bzLine ? function (t) {
795
+ return bzLine.get(t);
796
+ } // get position along bezier line
797
+ : function (t) {
798
+ return {
799
+ // straight line: interpolate linearly
800
+ x: start.x + (end.x - start.x) * t || 0,
801
+ y: start.y + (end.y - start.y) * t || 0
802
+ };
803
+ };
804
+ var lineLen = bzLine ? bzLine.length() : Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
805
+ var posAlongLine = startR + arrowLength + (lineLen - startR - endR - arrowLength) * arrowRelPos;
806
+ var arrowHead = getCoordsAlongLine(posAlongLine / lineLen);
807
+ var arrowTail = getCoordsAlongLine((posAlongLine - arrowLength) / lineLen);
808
+ var arrowTailVertex = getCoordsAlongLine((posAlongLine - arrowLength * (1 - ARROW_VLEN_RATIO)) / lineLen);
809
+ var arrowTailAngle = Math.atan2(arrowHead.y - arrowTail.y, arrowHead.x - arrowTail.x) - Math.PI / 2;
810
+ ctx.beginPath();
811
+ ctx.moveTo(arrowHead.x, arrowHead.y);
812
+ ctx.lineTo(arrowTail.x + arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y + arrowHalfWidth * Math.sin(arrowTailAngle));
813
+ ctx.lineTo(arrowTailVertex.x, arrowTailVertex.y);
814
+ ctx.lineTo(arrowTail.x - arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y - arrowHalfWidth * Math.sin(arrowTailAngle));
815
+ ctx.fillStyle = arrowColor;
816
+ ctx.fill();
817
+ });
818
+ ctx.restore();
819
+ }
820
+
821
+ function paintPhotons() {
822
+ var getNumPhotons = accessorFn__default["default"](state.linkDirectionalParticles);
823
+ var getSpeed = accessorFn__default["default"](state.linkDirectionalParticleSpeed);
824
+ var getDiameter = accessorFn__default["default"](state.linkDirectionalParticleWidth);
825
+ var getVisibility = accessorFn__default["default"](state.linkVisibility);
826
+ var getColor = accessorFn__default["default"](state.linkDirectionalParticleColor || state.linkColor);
827
+ var ctx = state.ctx;
828
+ ctx.save();
829
+ state.graphData.links.filter(getVisibility).forEach(function (link) {
830
+ var numCyclePhotons = getNumPhotons(link);
831
+ if (!link.hasOwnProperty('__photons') || !link.__photons.length) return;
832
+ var start = link.source;
833
+ var end = link.target;
834
+ if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
835
+
836
+ var particleSpeed = getSpeed(link);
837
+ var photons = link.__photons || [];
838
+ var photonR = Math.max(0, getDiameter(link) / 2) / Math.sqrt(state.globalScale);
839
+ var photonColor = getColor(link) || 'rgba(0,0,0,0.28)';
840
+ ctx.fillStyle = photonColor; // Construct bezier for curved lines
841
+
842
+ var bzLine = link.__controlPoints ? _construct(bezierJs.Bezier, [start.x, start.y].concat(_toConsumableArray(link.__controlPoints), [end.x, end.y])) : null;
843
+ var cyclePhotonIdx = 0;
844
+ var needsCleanup = false; // whether some photons need to be removed from list
845
+
846
+ photons.forEach(function (photon) {
847
+ var singleHop = !!photon.__singleHop;
848
+
849
+ if (!photon.hasOwnProperty('__progressRatio')) {
850
+ photon.__progressRatio = singleHop ? 0 : cyclePhotonIdx / numCyclePhotons;
851
+ }
852
+
853
+ !singleHop && cyclePhotonIdx++; // increase regular photon index
854
+
855
+ photon.__progressRatio += particleSpeed;
856
+
857
+ if (photon.__progressRatio >= 1) {
858
+ if (!singleHop) {
859
+ photon.__progressRatio = photon.__progressRatio % 1;
860
+ } else {
861
+ needsCleanup = true;
862
+ return;
863
+ }
864
+ }
865
+
866
+ var photonPosRatio = photon.__progressRatio;
867
+ var coords = bzLine ? bzLine.get(photonPosRatio) // get position along bezier line
868
+ : {
869
+ // straight line: interpolate linearly
870
+ x: start.x + (end.x - start.x) * photonPosRatio || 0,
871
+ y: start.y + (end.y - start.y) * photonPosRatio || 0
872
+ };
873
+ ctx.beginPath();
874
+ ctx.arc(coords.x, coords.y, photonR, 0, 2 * Math.PI, false);
875
+ ctx.fill();
876
+ });
877
+
878
+ if (needsCleanup) {
879
+ // remove expired single hop photons
880
+ link.__photons = link.__photons.filter(function (photon) {
881
+ return !photon.__singleHop || photon.__progressRatio <= 1;
882
+ });
883
+ }
884
+ });
885
+ ctx.restore();
886
+ }
887
+ },
888
+ emitParticle: function emitParticle(state, link) {
889
+ if (link) {
890
+ !link.__photons && (link.__photons = []);
891
+
892
+ link.__photons.push({
893
+ __singleHop: true
894
+ }); // add a single hop particle
895
+
896
+ }
897
+
898
+ return this;
899
+ }
900
+ },
901
+ stateInit: function stateInit() {
902
+ return {
903
+ forceLayout: d3Force3d.forceSimulation().force('link', d3Force3d.forceLink()).force('charge', d3Force3d.forceManyBody()).force('center', d3Force3d.forceCenter()).force('dagRadial', null).stop(),
904
+ engineRunning: false
905
+ };
906
+ },
907
+ init: function init(canvasCtx, state) {
908
+ // Main canvas object to manipulate
909
+ state.ctx = canvasCtx;
910
+ },
911
+ update: function update(state) {
912
+ state.engineRunning = false; // Pause simulation
913
+
914
+ state.onUpdate();
915
+
916
+ if (state.nodeAutoColorBy !== null) {
917
+ // Auto add color to uncolored nodes
918
+ autoColorObjects(state.graphData.nodes, accessorFn__default["default"](state.nodeAutoColorBy), state.nodeColor);
919
+ }
920
+
921
+ if (state.linkAutoColorBy !== null) {
922
+ // Auto add color to uncolored links
923
+ autoColorObjects(state.graphData.links, accessorFn__default["default"](state.linkAutoColorBy), state.linkColor);
924
+ } // parse links
925
+
926
+
927
+ state.graphData.links.forEach(function (link) {
928
+ link.source = link[state.linkSource];
929
+ link.target = link[state.linkTarget];
930
+ });
931
+
932
+ if (!state.isShadow) {
933
+ // Add photon particles
934
+ var linkParticlesAccessor = accessorFn__default["default"](state.linkDirectionalParticles);
935
+ state.graphData.links.forEach(function (link) {
936
+ var numPhotons = Math.round(Math.abs(linkParticlesAccessor(link)));
937
+
938
+ if (numPhotons) {
939
+ link.__photons = _toConsumableArray(Array(numPhotons)).map(function () {
940
+ return {};
941
+ });
942
+ } else {
943
+ delete link.__photons;
944
+ }
945
+ });
946
+ } // Feed data to force-directed layout
947
+
948
+
949
+ state.forceLayout.stop().alpha(1) // re-heat the simulation
950
+ .nodes(state.graphData.nodes); // add links (if link force is still active)
951
+
952
+ var linkForce = state.forceLayout.force('link');
953
+
954
+ if (linkForce) {
955
+ linkForce.id(function (d) {
956
+ return d[state.nodeId];
957
+ }).links(state.graphData.links);
958
+ } // setup dag force constraints
959
+
960
+
961
+ var nodeDepths = state.dagMode && getDagDepths(state.graphData, function (node) {
962
+ return node[state.nodeId];
963
+ }, {
964
+ nodeFilter: state.dagNodeFilter,
965
+ onLoopError: state.onDagError || undefined
966
+ });
967
+ var maxDepth = Math.max.apply(Math, _toConsumableArray(Object.values(nodeDepths || [])));
968
+ var dagLevelDistance = state.dagLevelDistance || state.graphData.nodes.length / (maxDepth || 1) * DAG_LEVEL_NODE_RATIO * (['radialin', 'radialout'].indexOf(state.dagMode) !== -1 ? 0.7 : 1); // Fix nodes to x,y for dag mode
969
+
970
+ if (state.dagMode) {
971
+ var getFFn = function getFFn(fix, invert) {
972
+ return function (node) {
973
+ return !fix ? undefined : (nodeDepths[node[state.nodeId]] - maxDepth / 2) * dagLevelDistance * (invert ? -1 : 1);
974
+ };
975
+ };
976
+
977
+ var fxFn = getFFn(['lr', 'rl'].indexOf(state.dagMode) !== -1, state.dagMode === 'rl');
978
+ var fyFn = getFFn(['td', 'bu'].indexOf(state.dagMode) !== -1, state.dagMode === 'bu');
979
+ state.graphData.nodes.filter(state.dagNodeFilter).forEach(function (node) {
980
+ node.fx = fxFn(node);
981
+ node.fy = fyFn(node);
982
+ });
983
+ } // Use radial force for radial dags
984
+
985
+
986
+ state.forceLayout.force('dagRadial', ['radialin', 'radialout'].indexOf(state.dagMode) !== -1 ? d3Force3d.forceRadial(function (node) {
987
+ var nodeDepth = nodeDepths[node[state.nodeId]] || -1;
988
+ return (state.dagMode === 'radialin' ? maxDepth - nodeDepth : nodeDepth) * dagLevelDistance;
989
+ }).strength(function (node) {
990
+ return state.dagNodeFilter(node) ? 1 : 0;
991
+ }) : null);
992
+
993
+ for (var i = 0; i < state.warmupTicks && !(state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin); i++) {
994
+ state.forceLayout.tick();
995
+ } // Initial ticks before starting to render
996
+
997
+
998
+ this.resetCountdown();
999
+ state.onFinishUpdate();
1000
+ }
1001
+ });
1002
+
1003
+ function linkKapsule (kapsulePropNames, kapsuleType) {
1004
+ var propNames = kapsulePropNames instanceof Array ? kapsulePropNames : [kapsulePropNames];
1005
+ var dummyK = new kapsuleType(); // To extract defaults
1006
+
1007
+ return {
1008
+ linkProp: function linkProp(prop) {
1009
+ // link property config
1010
+ return {
1011
+ "default": dummyK[prop](),
1012
+ onChange: function onChange(v, state) {
1013
+ propNames.forEach(function (propName) {
1014
+ return state[propName][prop](v);
1015
+ });
1016
+ },
1017
+ triggerUpdate: false
1018
+ };
1019
+ },
1020
+ linkMethod: function linkMethod(method) {
1021
+ // link method pass-through
1022
+ return function (state) {
1023
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
1024
+ args[_key - 1] = arguments[_key];
1025
+ }
1026
+
1027
+ var returnVals = [];
1028
+ propNames.forEach(function (propName) {
1029
+ var kapsuleInstance = state[propName];
1030
+ var returnVal = kapsuleInstance[method].apply(kapsuleInstance, args);
1031
+
1032
+ if (returnVal !== kapsuleInstance) {
1033
+ returnVals.push(returnVal);
1034
+ }
1035
+ });
1036
+ return returnVals.length ? returnVals[0] : this; // chain based on the parent object, not the inner kapsule
1037
+ };
1038
+ }
1039
+ };
1040
+ }
1041
+
1042
+ var HOVER_CANVAS_THROTTLE_DELAY = 800; // ms to throttle shadow canvas updates for perf improvement
1043
+
1044
+ var ZOOM2NODES_FACTOR = 4; // Expose config from forceGraph
1045
+
1046
+ var bindFG = linkKapsule('forceGraph', CanvasForceGraph);
1047
+ var bindBoth = linkKapsule(['forceGraph', 'shadowGraph'], CanvasForceGraph);
1048
+ var linkedProps = Object.assign.apply(Object, _toConsumableArray(['nodeColor', 'nodeAutoColorBy', 'nodeCanvasObject', 'nodeCanvasObjectMode', 'linkColor', 'linkAutoColorBy', 'linkLineDash', 'linkWidth', 'linkCanvasObject', 'linkCanvasObjectMode', 'linkDirectionalArrowLength', 'linkDirectionalArrowColor', 'linkDirectionalArrowRelPos', 'linkDirectionalParticles', 'linkDirectionalParticleSpeed', 'linkDirectionalParticleWidth', 'linkDirectionalParticleColor', 'dagMode', 'dagLevelDistance', 'dagNodeFilter', 'onDagError', 'd3AlphaMin', 'd3AlphaDecay', 'd3VelocityDecay', 'warmupTicks', 'cooldownTicks', 'cooldownTime', 'onEngineTick', 'onEngineStop'].map(function (p) {
1049
+ return _defineProperty({}, p, bindFG.linkProp(p));
1050
+ })).concat(_toConsumableArray(['nodeRelSize', 'nodeId', 'nodeVal', 'nodeVisibility', 'linkSource', 'linkTarget', 'linkVisibility', 'linkCurvature'].map(function (p) {
1051
+ return _defineProperty({}, p, bindBoth.linkProp(p));
1052
+ }))));
1053
+ var linkedMethods = Object.assign.apply(Object, _toConsumableArray(['d3Force', 'd3ReheatSimulation', 'emitParticle'].map(function (p) {
1054
+ return _defineProperty({}, p, bindFG.linkMethod(p));
1055
+ })));
1056
+
1057
+ function adjustCanvasSize(state) {
1058
+ if (state.canvas) {
1059
+ var curWidth = state.canvas.width;
1060
+ var curHeight = state.canvas.height;
1061
+
1062
+ if (curWidth === 300 && curHeight === 150) {
1063
+ // Default canvas dimensions
1064
+ curWidth = curHeight = 0;
1065
+ }
1066
+
1067
+ var pxScale = window.devicePixelRatio; // 2 on retina displays
1068
+
1069
+ curWidth /= pxScale;
1070
+ curHeight /= pxScale; // Resize canvases
1071
+
1072
+ [state.canvas, state.shadowCanvas].forEach(function (canvas) {
1073
+ // Element size
1074
+ canvas.style.width = "".concat(state.width, "px");
1075
+ canvas.style.height = "".concat(state.height, "px"); // Memory size (scaled to avoid blurriness)
1076
+
1077
+ canvas.width = state.width * pxScale;
1078
+ canvas.height = state.height * pxScale; // Normalize coordinate system to use css pixels (on init only)
1079
+
1080
+ if (!curWidth && !curHeight) {
1081
+ canvas.getContext('2d').scale(pxScale, pxScale);
1082
+ }
1083
+ }); // Relative center panning based on 0,0
1084
+
1085
+ var k = d3Zoom.zoomTransform(state.canvas).k;
1086
+ state.zoom.translateBy(state.zoom.__baseElem, (state.width - curWidth) / 2 / k, (state.height - curHeight) / 2 / k);
1087
+ state.needsRedraw = true;
1088
+ }
1089
+ }
1090
+
1091
+ function resetTransform(ctx) {
1092
+ var pxRatio = window.devicePixelRatio;
1093
+ ctx.setTransform(pxRatio, 0, 0, pxRatio, 0, 0);
1094
+ }
1095
+
1096
+ function clearCanvas(ctx, width, height) {
1097
+ ctx.save();
1098
+ resetTransform(ctx); // reset transform
1099
+
1100
+ ctx.clearRect(0, 0, width, height);
1101
+ ctx.restore(); //restore transforms
1102
+ } //
1103
+
1104
+
1105
+ var forceGraph = Kapsule__default["default"]({
1106
+ props: _objectSpread2({
1107
+ width: {
1108
+ "default": window.innerWidth,
1109
+ onChange: function onChange(_, state) {
1110
+ return adjustCanvasSize(state);
1111
+ },
1112
+ triggerUpdate: false
1113
+ },
1114
+ height: {
1115
+ "default": window.innerHeight,
1116
+ onChange: function onChange(_, state) {
1117
+ return adjustCanvasSize(state);
1118
+ },
1119
+ triggerUpdate: false
1120
+ },
1121
+ graphData: {
1122
+ "default": {
1123
+ nodes: [],
1124
+ links: []
1125
+ },
1126
+ onChange: function onChange(d, state) {
1127
+ [{
1128
+ type: 'Node',
1129
+ objs: d.nodes
1130
+ }, {
1131
+ type: 'Link',
1132
+ objs: d.links
1133
+ }].forEach(hexIndex);
1134
+ state.forceGraph.graphData(d);
1135
+ state.shadowGraph.graphData(d);
1136
+
1137
+ function hexIndex(_ref4) {
1138
+ var type = _ref4.type,
1139
+ objs = _ref4.objs;
1140
+ objs.filter(function (d) {
1141
+ if (!d.hasOwnProperty('__indexColor')) return true;
1142
+ var cur = state.colorTracker.lookup(d.__indexColor);
1143
+ return !cur || !cur.hasOwnProperty('d') || cur.d !== d;
1144
+ }).forEach(function (d) {
1145
+ // store object lookup color
1146
+ d.__indexColor = state.colorTracker.register({
1147
+ type: type,
1148
+ d: d
1149
+ });
1150
+ });
1151
+ }
1152
+ },
1153
+ triggerUpdate: false
1154
+ },
1155
+ backgroundColor: {
1156
+ onChange: function onChange(color, state) {
1157
+ state.canvas && color && (state.canvas.style.background = color);
1158
+ },
1159
+ triggerUpdate: false
1160
+ },
1161
+ nodeLabel: {
1162
+ "default": 'name',
1163
+ triggerUpdate: false
1164
+ },
1165
+ nodePointerAreaPaint: {
1166
+ onChange: function onChange(paintFn, state) {
1167
+ state.shadowGraph.nodeCanvasObject(!paintFn ? null : function (node, ctx, globalScale) {
1168
+ return paintFn(node, node.__indexColor, ctx, globalScale);
1169
+ });
1170
+ state.flushShadowCanvas && state.flushShadowCanvas();
1171
+ },
1172
+ triggerUpdate: false
1173
+ },
1174
+ linkPointerAreaPaint: {
1175
+ onChange: function onChange(paintFn, state) {
1176
+ state.shadowGraph.linkCanvasObject(!paintFn ? null : function (link, ctx, globalScale) {
1177
+ return paintFn(link, link.__indexColor, ctx, globalScale);
1178
+ });
1179
+ state.flushShadowCanvas && state.flushShadowCanvas();
1180
+ },
1181
+ triggerUpdate: false
1182
+ },
1183
+ linkLabel: {
1184
+ "default": 'name',
1185
+ triggerUpdate: false
1186
+ },
1187
+ linkHoverPrecision: {
1188
+ "default": 4,
1189
+ triggerUpdate: false
1190
+ },
1191
+ minZoom: {
1192
+ "default": 0.01,
1193
+ onChange: function onChange(minZoom, state) {
1194
+ state.zoom.scaleExtent([minZoom, state.zoom.scaleExtent()[1]]);
1195
+ },
1196
+ triggerUpdate: false
1197
+ },
1198
+ maxZoom: {
1199
+ "default": 1000,
1200
+ onChange: function onChange(maxZoom, state) {
1201
+ state.zoom.scaleExtent([state.zoom.scaleExtent()[0], maxZoom]);
1202
+ },
1203
+ triggerUpdate: false
1204
+ },
1205
+ enableNodeDrag: {
1206
+ "default": true,
1207
+ triggerUpdate: false
1208
+ },
1209
+ enableZoomInteraction: {
1210
+ "default": true,
1211
+ triggerUpdate: false
1212
+ },
1213
+ enablePanInteraction: {
1214
+ "default": true,
1215
+ triggerUpdate: false
1216
+ },
1217
+ enableZoomPanInteraction: {
1218
+ "default": true,
1219
+ triggerUpdate: false
1220
+ },
1221
+ // to be deprecated
1222
+ enablePointerInteraction: {
1223
+ "default": true,
1224
+ onChange: function onChange(_, state) {
1225
+ state.hoverObj = null;
1226
+ },
1227
+ triggerUpdate: false
1228
+ },
1229
+ autoPauseRedraw: {
1230
+ "default": true,
1231
+ triggerUpdate: false
1232
+ },
1233
+ onNodeDrag: {
1234
+ "default": function _default() {},
1235
+ triggerUpdate: false
1236
+ },
1237
+ onNodeDragEnd: {
1238
+ "default": function _default() {},
1239
+ triggerUpdate: false
1240
+ },
1241
+ onNodeClick: {
1242
+ triggerUpdate: false
1243
+ },
1244
+ onNodeRightClick: {
1245
+ triggerUpdate: false
1246
+ },
1247
+ onNodeHover: {
1248
+ triggerUpdate: false
1249
+ },
1250
+ onLinkClick: {
1251
+ triggerUpdate: false
1252
+ },
1253
+ onLinkRightClick: {
1254
+ triggerUpdate: false
1255
+ },
1256
+ onLinkHover: {
1257
+ triggerUpdate: false
1258
+ },
1259
+ onBackgroundClick: {
1260
+ triggerUpdate: false
1261
+ },
1262
+ onBackgroundRightClick: {
1263
+ triggerUpdate: false
1264
+ },
1265
+ onZoom: {
1266
+ "default": function _default() {},
1267
+ triggerUpdate: false
1268
+ },
1269
+ onZoomEnd: {
1270
+ "default": function _default() {},
1271
+ triggerUpdate: false
1272
+ },
1273
+ onRenderFramePre: {
1274
+ triggerUpdate: false
1275
+ },
1276
+ onRenderFramePost: {
1277
+ triggerUpdate: false
1278
+ }
1279
+ }, linkedProps),
1280
+ aliases: {
1281
+ // Prop names supported for backwards compatibility
1282
+ stopAnimation: 'pauseAnimation'
1283
+ },
1284
+ methods: _objectSpread2({
1285
+ graph2ScreenCoords: function graph2ScreenCoords(state, x, y) {
1286
+ var t = d3Zoom.zoomTransform(state.canvas);
1287
+ return {
1288
+ x: x * t.k + t.x,
1289
+ y: y * t.k + t.y
1290
+ };
1291
+ },
1292
+ screen2GraphCoords: function screen2GraphCoords(state, x, y) {
1293
+ var t = d3Zoom.zoomTransform(state.canvas);
1294
+ return {
1295
+ x: (x - t.x) / t.k,
1296
+ y: (y - t.y) / t.k
1297
+ };
1298
+ },
1299
+ centerAt: function centerAt(state, x, y, transitionDuration) {
1300
+ if (!state.canvas) return null; // no canvas yet
1301
+ // setter
1302
+
1303
+ if (x !== undefined || y !== undefined) {
1304
+ var finalPos = Object.assign({}, x !== undefined ? {
1305
+ x: x
1306
+ } : {}, y !== undefined ? {
1307
+ y: y
1308
+ } : {});
1309
+
1310
+ if (!transitionDuration) {
1311
+ // no animation
1312
+ setCenter(finalPos);
1313
+ } else {
1314
+ new TWEEN__default["default"].Tween(getCenter()).to(finalPos, transitionDuration).easing(TWEEN__default["default"].Easing.Quadratic.Out).onUpdate(setCenter).start();
1315
+ }
1316
+
1317
+ return this;
1318
+ } // getter
1319
+
1320
+
1321
+ return getCenter(); //
1322
+
1323
+ function getCenter() {
1324
+ var t = d3Zoom.zoomTransform(state.canvas);
1325
+ return {
1326
+ x: (state.width / 2 - t.x) / t.k,
1327
+ y: (state.height / 2 - t.y) / t.k
1328
+ };
1329
+ }
1330
+
1331
+ function setCenter(_ref5) {
1332
+ var x = _ref5.x,
1333
+ y = _ref5.y;
1334
+ state.zoom.translateTo(state.zoom.__baseElem, x === undefined ? getCenter().x : x, y === undefined ? getCenter().y : y);
1335
+ state.needsRedraw = true;
1336
+ }
1337
+ },
1338
+ zoom: function zoom(state, k, transitionDuration) {
1339
+ if (!state.canvas) return null; // no canvas yet
1340
+ // setter
1341
+
1342
+ if (k !== undefined) {
1343
+ if (!transitionDuration) {
1344
+ // no animation
1345
+ setZoom(k);
1346
+ } else {
1347
+ new TWEEN__default["default"].Tween({
1348
+ k: getZoom()
1349
+ }).to({
1350
+ k: k
1351
+ }, transitionDuration).easing(TWEEN__default["default"].Easing.Quadratic.Out).onUpdate(function (_ref6) {
1352
+ var k = _ref6.k;
1353
+ return setZoom(k);
1354
+ }).start();
1355
+ }
1356
+
1357
+ return this;
1358
+ } // getter
1359
+
1360
+
1361
+ return getZoom(); //
1362
+
1363
+ function getZoom() {
1364
+ return d3Zoom.zoomTransform(state.canvas).k;
1365
+ }
1366
+
1367
+ function setZoom(k) {
1368
+ state.zoom.scaleTo(state.zoom.__baseElem, k);
1369
+ state.needsRedraw = true;
1370
+ }
1371
+ },
1372
+ zoomToFit: function zoomToFit(state) {
1373
+ var transitionDuration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
1374
+ var padding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
1375
+
1376
+ for (var _len = arguments.length, bboxArgs = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
1377
+ bboxArgs[_key - 3] = arguments[_key];
1378
+ }
1379
+
1380
+ var bbox = this.getGraphBbox.apply(this, bboxArgs);
1381
+
1382
+ if (bbox) {
1383
+ var center = {
1384
+ x: (bbox.x[0] + bbox.x[1]) / 2,
1385
+ y: (bbox.y[0] + bbox.y[1]) / 2
1386
+ };
1387
+ var zoomK = Math.max(1e-12, Math.min(1e12, (state.width - padding * 2) / (bbox.x[1] - bbox.x[0]), (state.height - padding * 2) / (bbox.y[1] - bbox.y[0])));
1388
+ this.centerAt(center.x, center.y, transitionDuration);
1389
+ this.zoom(zoomK, transitionDuration);
1390
+ }
1391
+
1392
+ return this;
1393
+ },
1394
+ getGraphBbox: function getGraphBbox(state) {
1395
+ var nodeFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
1396
+ return true;
1397
+ };
1398
+ var getVal = accessorFn__default["default"](state.nodeVal);
1399
+
1400
+ var getR = function getR(node) {
1401
+ return Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize;
1402
+ };
1403
+
1404
+ var nodesPos = state.graphData.nodes.filter(nodeFilter).map(function (node) {
1405
+ return {
1406
+ x: node.x,
1407
+ y: node.y,
1408
+ r: getR(node)
1409
+ };
1410
+ });
1411
+ return !nodesPos.length ? null : {
1412
+ x: [d3Array.min(nodesPos, function (node) {
1413
+ return node.x - node.r;
1414
+ }), d3Array.max(nodesPos, function (node) {
1415
+ return node.x + node.r;
1416
+ })],
1417
+ y: [d3Array.min(nodesPos, function (node) {
1418
+ return node.y - node.r;
1419
+ }), d3Array.max(nodesPos, function (node) {
1420
+ return node.y + node.r;
1421
+ })]
1422
+ };
1423
+ },
1424
+ pauseAnimation: function pauseAnimation(state) {
1425
+ if (state.animationFrameRequestId) {
1426
+ cancelAnimationFrame(state.animationFrameRequestId);
1427
+ state.animationFrameRequestId = null;
1428
+ }
1429
+
1430
+ return this;
1431
+ },
1432
+ resumeAnimation: function resumeAnimation(state) {
1433
+ if (!state.animationFrameRequestId) {
1434
+ this._animationCycle();
1435
+ }
1436
+
1437
+ return this;
1438
+ },
1439
+ _destructor: function _destructor() {
1440
+ this.pauseAnimation();
1441
+ this.graphData({
1442
+ nodes: [],
1443
+ links: []
1444
+ });
1445
+ }
1446
+ }, linkedMethods),
1447
+ stateInit: function stateInit() {
1448
+ return {
1449
+ lastSetZoom: 1,
1450
+ zoom: d3Zoom.zoom(),
1451
+ forceGraph: new CanvasForceGraph(),
1452
+ shadowGraph: new CanvasForceGraph().cooldownTicks(0).nodeColor('__indexColor').linkColor('__indexColor').isShadow(true),
1453
+ colorTracker: new ColorTracker__default["default"]() // indexed objects for rgb lookup
1454
+
1455
+ };
1456
+ },
1457
+ init: function init(domNode, state) {
1458
+ // Wipe DOM
1459
+ domNode.innerHTML = ''; // Container anchor for canvas and tooltip
1460
+
1461
+ var container = document.createElement('div');
1462
+ container.classList.add('force-graph-container');
1463
+ container.style.position = 'relative';
1464
+ domNode.appendChild(container);
1465
+ state.canvas = document.createElement('canvas');
1466
+ if (state.backgroundColor) state.canvas.style.background = state.backgroundColor;
1467
+ container.appendChild(state.canvas);
1468
+ state.shadowCanvas = document.createElement('canvas'); // Show shadow canvas
1469
+ //state.shadowCanvas.style.position = 'absolute';
1470
+ //state.shadowCanvas.style.top = '0';
1471
+ //state.shadowCanvas.style.left = '0';
1472
+ //container.appendChild(state.shadowCanvas);
1473
+
1474
+ var ctx = state.canvas.getContext('2d');
1475
+ var shadowCtx = state.shadowCanvas.getContext('2d');
1476
+ var pointerPos = {
1477
+ x: -1e12,
1478
+ y: -1e12
1479
+ };
1480
+
1481
+ var getObjUnderPointer = function getObjUnderPointer() {
1482
+ var obj = null;
1483
+ var pxScale = window.devicePixelRatio;
1484
+ var px = pointerPos.x > 0 && pointerPos.y > 0 ? shadowCtx.getImageData(pointerPos.x * pxScale, pointerPos.y * pxScale, 1, 1) : null; // Lookup object per pixel color
1485
+
1486
+ px && (obj = state.colorTracker.lookup(px.data));
1487
+ return obj;
1488
+ }; // Setup node drag interaction
1489
+
1490
+
1491
+ d3Selection.select(state.canvas).call(d3Drag.drag().subject(function () {
1492
+ if (!state.enableNodeDrag) {
1493
+ return null;
1494
+ }
1495
+
1496
+ var obj = getObjUnderPointer();
1497
+ return obj && obj.type === 'Node' ? obj.d : null; // Only drag nodes
1498
+ }).on('start', function (ev) {
1499
+ var obj = ev.subject;
1500
+ obj.__initialDragPos = {
1501
+ x: obj.x,
1502
+ y: obj.y,
1503
+ fx: obj.fx,
1504
+ fy: obj.fy
1505
+ }; // keep engine running at low intensity throughout drag
1506
+
1507
+ if (!ev.active) {
1508
+ obj.fx = obj.x;
1509
+ obj.fy = obj.y; // Fix points
1510
+ } // drag cursor
1511
+
1512
+
1513
+ state.canvas.classList.add('grabbable');
1514
+ }).on('drag', function (ev) {
1515
+ var obj = ev.subject;
1516
+ var initPos = obj.__initialDragPos;
1517
+ var dragPos = ev;
1518
+ var k = d3Zoom.zoomTransform(state.canvas).k;
1519
+ var translate = {
1520
+ x: initPos.x + (dragPos.x - initPos.x) / k - obj.x,
1521
+ y: initPos.y + (dragPos.y - initPos.y) / k - obj.y
1522
+ }; // Move fx/fy (and x/y) of nodes based on the scaled drag distance since the drag start
1523
+
1524
+ ['x', 'y'].forEach(function (c) {
1525
+ return obj["f".concat(c)] = obj[c] = initPos[c] + (dragPos[c] - initPos[c]) / k;
1526
+ }); // prevent freeze while dragging
1527
+
1528
+ state.forceGraph.d3AlphaTarget(0.3) // keep engine running at low intensity throughout drag
1529
+ .resetCountdown(); // prevent freeze while dragging
1530
+
1531
+ state.isPointerDragging = true;
1532
+ obj.__dragged = true;
1533
+ state.onNodeDrag(obj, translate);
1534
+ }).on('end', function (ev) {
1535
+ var obj = ev.subject;
1536
+ var initPos = obj.__initialDragPos;
1537
+ var translate = {
1538
+ x: obj.x - initPos.x,
1539
+ y: obj.y - initPos.y
1540
+ };
1541
+
1542
+ if (initPos.fx === undefined) {
1543
+ obj.fx = undefined;
1544
+ }
1545
+
1546
+ if (initPos.fy === undefined) {
1547
+ obj.fy = undefined;
1548
+ }
1549
+
1550
+ delete obj.__initialDragPos;
1551
+
1552
+ if (state.forceGraph.d3AlphaTarget()) {
1553
+ state.forceGraph.d3AlphaTarget(0) // release engine low intensity
1554
+ .resetCountdown(); // let the engine readjust after releasing fixed nodes
1555
+ } // drag cursor
1556
+
1557
+
1558
+ state.canvas.classList.remove('grabbable');
1559
+ state.isPointerDragging = false;
1560
+
1561
+ if (obj.__dragged) {
1562
+ delete obj.__dragged;
1563
+ state.onNodeDragEnd(obj, translate);
1564
+ }
1565
+ })); // Setup zoom / pan interaction
1566
+
1567
+ state.zoom(state.zoom.__baseElem = d3Selection.select(state.canvas)); // Attach controlling elem for easy access
1568
+
1569
+ state.zoom.__baseElem.on('dblclick.zoom', null); // Disable double-click to zoom
1570
+
1571
+
1572
+ state.zoom.filter(function (ev) {
1573
+ return (// disable zoom interaction
1574
+ !ev.button && state.enableZoomPanInteraction && (state.enableZoomInteraction || ev.type !== 'wheel') && (state.enablePanInteraction || ev.type === 'wheel')
1575
+ );
1576
+ }).on('zoom', function (ev) {
1577
+ var t = ev.transform;
1578
+ [ctx, shadowCtx].forEach(function (c) {
1579
+ resetTransform(c);
1580
+ c.translate(t.x, t.y);
1581
+ c.scale(t.k, t.k);
1582
+ });
1583
+ state.onZoom(_objectSpread2({}, t));
1584
+ state.needsRedraw = true;
1585
+ }).on('end', function (ev) {
1586
+ return state.onZoomEnd(_objectSpread2({}, ev.transform));
1587
+ });
1588
+ adjustCanvasSize(state);
1589
+ state.forceGraph.onNeedsRedraw(function () {
1590
+ return state.needsRedraw = true;
1591
+ }).onFinishUpdate(function () {
1592
+ // re-zoom, if still in default position (not user modified)
1593
+ if (d3Zoom.zoomTransform(state.canvas).k === state.lastSetZoom && state.graphData.nodes.length) {
1594
+ state.zoom.scaleTo(state.zoom.__baseElem, state.lastSetZoom = ZOOM2NODES_FACTOR / Math.cbrt(state.graphData.nodes.length));
1595
+ state.needsRedraw = true;
1596
+ }
1597
+ }); // Setup tooltip
1598
+
1599
+ var toolTipElem = document.createElement('div');
1600
+ toolTipElem.classList.add('graph-tooltip');
1601
+ container.appendChild(toolTipElem); // Capture pointer coords on move or touchstart
1602
+
1603
+ ['pointermove', 'pointerdown'].forEach(function (evType) {
1604
+ return container.addEventListener(evType, function (ev) {
1605
+ if (evType === 'pointerdown') {
1606
+ state.isPointerPressed = true; // track click state
1607
+
1608
+ state.pointerDownEvent = ev;
1609
+ } // detect pointer drag on canvas pan
1610
+
1611
+
1612
+ !state.isPointerDragging && ev.type === 'pointermove' && state.onBackgroundClick // only bother detecting drags this way if background clicks are enabled (so they don't trigger accidentally on canvas panning)
1613
+ && (ev.pressure > 0 || state.isPointerPressed) // ev.pressure always 0 on Safari, so we use the isPointerPressed tracker
1614
+ && (ev.pointerType !== 'touch' || ev.movementX === undefined || [ev.movementX, ev.movementY].some(function (m) {
1615
+ return Math.abs(m) > 1;
1616
+ })) // relax drag trigger sensitivity on touch events
1617
+ && (state.isPointerDragging = true); // update the pointer pos
1618
+
1619
+ var offset = getOffset(container);
1620
+ pointerPos.x = ev.pageX - offset.left;
1621
+ pointerPos.y = ev.pageY - offset.top; // Move tooltip
1622
+
1623
+ toolTipElem.style.top = "".concat(pointerPos.y, "px");
1624
+ toolTipElem.style.left = "".concat(pointerPos.x, "px"); //
1625
+
1626
+ function getOffset(el) {
1627
+ var rect = el.getBoundingClientRect(),
1628
+ scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
1629
+ scrollTop = window.pageYOffset || document.documentElement.scrollTop;
1630
+ return {
1631
+ top: rect.top + scrollTop,
1632
+ left: rect.left + scrollLeft
1633
+ };
1634
+ }
1635
+ }, {
1636
+ passive: true
1637
+ });
1638
+ }); // Handle click/touch events on nodes/links
1639
+
1640
+ container.addEventListener('pointerup', function (ev) {
1641
+ state.isPointerPressed = false;
1642
+
1643
+ if (state.isPointerDragging) {
1644
+ state.isPointerDragging = false;
1645
+ return; // don't trigger click events after pointer drag (pan / node drag functionality)
1646
+ }
1647
+
1648
+ var cbEvents = [ev, state.pointerDownEvent];
1649
+ requestAnimationFrame(function () {
1650
+ // trigger click events asynchronously, to allow hoverObj to be set (on frame)
1651
+ if (ev.button === 0) {
1652
+ // mouse left-click or touch
1653
+ if (state.hoverObj) {
1654
+ var fn = state["on".concat(state.hoverObj.type, "Click")];
1655
+ fn && fn.apply(void 0, [state.hoverObj.d].concat(cbEvents));
1656
+ } else {
1657
+ state.onBackgroundClick && state.onBackgroundClick.apply(state, cbEvents);
1658
+ }
1659
+ }
1660
+
1661
+ if (ev.button === 2) {
1662
+ // mouse right-click
1663
+ if (state.hoverObj) {
1664
+ var _fn = state["on".concat(state.hoverObj.type, "RightClick")];
1665
+ _fn && _fn.apply(void 0, [state.hoverObj.d].concat(cbEvents));
1666
+ } else {
1667
+ state.onBackgroundRightClick && state.onBackgroundRightClick.apply(state, cbEvents);
1668
+ }
1669
+ }
1670
+ });
1671
+ }, {
1672
+ passive: true
1673
+ });
1674
+ container.addEventListener('contextmenu', function (ev) {
1675
+ if (!state.onBackgroundRightClick && !state.onNodeRightClick && !state.onLinkRightClick) return true; // default contextmenu behavior
1676
+
1677
+ ev.preventDefault();
1678
+ return false;
1679
+ });
1680
+ state.forceGraph(ctx);
1681
+ state.shadowGraph(shadowCtx); //
1682
+
1683
+ var refreshShadowCanvas = throttle__default["default"](function () {
1684
+ // wipe canvas
1685
+ clearCanvas(shadowCtx, state.width, state.height); // Adjust link hover area
1686
+
1687
+ state.shadowGraph.linkWidth(function (l) {
1688
+ return accessorFn__default["default"](state.linkWidth)(l) + state.linkHoverPrecision;
1689
+ }); // redraw
1690
+
1691
+ var t = d3Zoom.zoomTransform(state.canvas);
1692
+ state.shadowGraph.globalScale(t.k).tickFrame();
1693
+ }, HOVER_CANVAS_THROTTLE_DELAY);
1694
+ state.flushShadowCanvas = refreshShadowCanvas.flush; // hook to immediately invoke shadow canvas paint
1695
+ // Kick-off renderer
1696
+
1697
+ (this._animationCycle = function animate() {
1698
+ // IIFE
1699
+ var doRedraw = !state.autoPauseRedraw || !!state.needsRedraw || state.forceGraph.isEngineRunning() || state.graphData.links.some(function (d) {
1700
+ return d.__photons && d.__photons.length;
1701
+ });
1702
+ state.needsRedraw = false;
1703
+
1704
+ if (state.enablePointerInteraction) {
1705
+ // Update tooltip and trigger onHover events
1706
+ var obj = !state.isPointerDragging ? getObjUnderPointer() : null; // don't hover during drag
1707
+
1708
+ if (obj !== state.hoverObj) {
1709
+ var prevObj = state.hoverObj;
1710
+ var prevObjType = prevObj ? prevObj.type : null;
1711
+ var objType = obj ? obj.type : null;
1712
+
1713
+ if (prevObjType && prevObjType !== objType) {
1714
+ // Hover out
1715
+ var fn = state["on".concat(prevObjType, "Hover")];
1716
+ fn && fn(null, prevObj.d);
1717
+ }
1718
+
1719
+ if (objType) {
1720
+ // Hover in
1721
+ var _fn2 = state["on".concat(objType, "Hover")];
1722
+ _fn2 && _fn2(obj.d, prevObjType === objType ? prevObj.d : null);
1723
+ }
1724
+
1725
+ var tooltipContent = obj ? accessorFn__default["default"](state["".concat(obj.type.toLowerCase(), "Label")])(obj.d) || '' : '';
1726
+ toolTipElem.style.visibility = tooltipContent ? 'visible' : 'hidden';
1727
+ toolTipElem.innerHTML = tooltipContent; // set pointer if hovered object is clickable
1728
+
1729
+ state.canvas.classList[obj && state["on".concat(objType, "Click")] || !obj && state.onBackgroundClick ? 'add' : 'remove']('clickable');
1730
+ state.hoverObj = obj;
1731
+ }
1732
+
1733
+ doRedraw && refreshShadowCanvas();
1734
+ }
1735
+
1736
+ if (doRedraw) {
1737
+ // Wipe canvas
1738
+ clearCanvas(ctx, state.width, state.height); // Frame cycle
1739
+
1740
+ var globalScale = d3Zoom.zoomTransform(state.canvas).k;
1741
+ state.onRenderFramePre && state.onRenderFramePre(ctx, globalScale);
1742
+ state.forceGraph.globalScale(globalScale).tickFrame();
1743
+ state.onRenderFramePost && state.onRenderFramePost(ctx, globalScale);
1744
+ }
1745
+
1746
+ TWEEN__default["default"].update(); // update canvas animation tweens
1747
+
1748
+ state.animationFrameRequestId = requestAnimationFrame(animate);
1749
+ })();
1750
+ },
1751
+ update: function updateFn(state) {}
1752
+ });
1753
+
1754
+ module.exports = forceGraph;