@hpcc-js/graph 2.87.2 → 2.87.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 (57) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +256 -256
  3. package/dist/index.es6.js +7 -7
  4. package/dist/index.es6.js.map +1 -1
  5. package/dist/index.js +7 -7
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.min.js +1 -1
  8. package/dist/index.min.js.map +1 -1
  9. package/package.json +8 -8
  10. package/src/AdjacencyGraph.ts +224 -224
  11. package/src/Edge.css +23 -23
  12. package/src/Edge.ts +257 -257
  13. package/src/Graph.css +18 -18
  14. package/src/Graph.ts +1075 -1075
  15. package/src/GraphData.ts +187 -187
  16. package/src/GraphLayouts.ts +173 -173
  17. package/src/Sankey.css +46 -46
  18. package/src/Sankey.ts +291 -291
  19. package/src/Subgraph.css +10 -10
  20. package/src/Subgraph.ts +165 -165
  21. package/src/Vertex.css +3 -3
  22. package/src/Vertex.ts +282 -282
  23. package/src/__package__.ts +3 -3
  24. package/src/__tests__/data.ts +444 -444
  25. package/src/__tests__/index.ts +1 -1
  26. package/src/__tests__/test1.ts +18 -18
  27. package/src/__tests__/test2.ts +80 -80
  28. package/src/__tests__/test3.ts +46 -46
  29. package/src/__tests__/test4.ts +66 -66
  30. package/src/__tests__/test5.ts +85 -85
  31. package/src/graph2/dataGraph.ts +305 -305
  32. package/src/graph2/graph.css +34 -34
  33. package/src/graph2/graph.ts +135 -135
  34. package/src/graph2/graphReactT.ts +44 -44
  35. package/src/graph2/graphT.ts +1330 -1330
  36. package/src/graph2/index.ts +7 -7
  37. package/src/graph2/layouts/circle.ts +37 -37
  38. package/src/graph2/layouts/dagre.ts +132 -132
  39. package/src/graph2/layouts/dagreWorker.ts +35 -35
  40. package/src/graph2/layouts/forceDirected.ts +117 -117
  41. package/src/graph2/layouts/forceDirectedWorker.ts +30 -30
  42. package/src/graph2/layouts/geoForceDirected.ts +112 -112
  43. package/src/graph2/layouts/graphviz.ts +124 -124
  44. package/src/graph2/layouts/graphvizWorker.ts +71 -71
  45. package/src/graph2/layouts/index.ts +7 -7
  46. package/src/graph2/layouts/layout.ts +105 -105
  47. package/src/graph2/layouts/null.ts +35 -35
  48. package/src/graph2/layouts/placeholders.ts +103 -103
  49. package/src/graph2/layouts/tree.ts +328 -328
  50. package/src/graph2/liteMap.ts +72 -72
  51. package/src/graph2/liteSVGZooom.ts +61 -61
  52. package/src/graph2/sankeyGraph.css +45 -45
  53. package/src/graph2/sankeyGraph.ts +316 -316
  54. package/src/graph2/subgraph.tsx +30 -30
  55. package/src/graph2/vertex.tsx +31 -31
  56. package/src/index.ts +8 -8
  57. package/src/test.ts +649 -649
@@ -1,1330 +1,1330 @@
1
- import { d3Event, drag as d3Drag, Palette, select as d3Select, Selection, Spacer, SVGGlowFilter, SVGZoomWidget, ToggleButton, Utility, Widget } from "@hpcc-js/common";
2
- import { IconEx, Icons, render } from "@hpcc-js/react";
3
- import { Graph2 as GraphCollection, hashSum } from "@hpcc-js/util";
4
- import { HTMLTooltip } from "@hpcc-js/html";
5
- import { interpolateNumberArray as d3InterpolateNumberArray } from "d3-interpolate";
6
- import "d3-transition";
7
- import { interpolatePath as d3InterpolatePath } from "d3-interpolate-path";
8
- import { Circle, Dagre, ForceDirected, ForceDirectedAnimated, Graphviz, ILayout, Null } from "./layouts/index";
9
- import { Options as FDOptions } from "./layouts/forceDirectedWorker";
10
- import type { VertexBaseProps, EdgeBaseProps, GraphDataProps, HierarchyBase, SubgraphBaseProps } from "./layouts/placeholders";
11
- import { EdgePlaceholder, SubgraphPlaceholder, VertexPlaceholder, isEdgePlaceholder } from "./layouts/placeholders";
12
- import { Engine, graphviz as gvWorker } from "./layouts/graphvizWorker";
13
- import { Tree, RadialTree, Dendrogram, RadialDendrogram } from "./layouts/tree";
14
-
15
- import "../../src/graph2/graph.css";
16
-
17
- let scriptDir = (globalThis?.document?.currentScript as HTMLScriptElement)?.src ?? "./dummy.js";
18
- scriptDir = scriptDir.substring(0, scriptDir.replace(/[?#].*/, "").lastIndexOf("/") + 1);
19
-
20
- export {
21
- GraphDataProps,
22
- SubgraphBaseProps,
23
- VertexBaseProps,
24
- EdgeBaseProps,
25
- HierarchyBase
26
- };
27
-
28
- type GraphLayoutType = "Hierarchy" | "DOT" | "Tree" | "Dendrogram" | "RadialTree" | "RadialDendrogram" | "ForceDirected" | "ForceDirected2" | "ForceDirectedHybrid" | "Neato" | "FDP" | "Circle" | "TwoPI" | "Circo" | "None";
29
- const GraphLayoutTypeSet = ["Hierarchy", "DOT", "Tree", "Dendrogram", "RadialTree", "RadialDendrogram", "ForceDirected", "ForceDirected2", "ForceDirectedHybrid", "Neato", "FDP", "Circle", "TwoPI", "Circo", "None"];
30
-
31
- function dragStart<V extends VertexBaseProps>(n: VertexPlaceholder<V>) {
32
- n.fx = n.sx = n.x;
33
- n.fy = n.sy = n.y;
34
- }
35
- function dragTick(n: VertexPlaceholder<VertexBaseProps>, d: VertexPlaceholder<VertexBaseProps>) {
36
- n.fx = n.sx + d.fx - d.sx;
37
- n.fy = n.sy + d.fy - d.sy;
38
- }
39
- function dragEnd<V extends VertexBaseProps>(n: VertexPlaceholder<V>) {
40
- n.x = n.fx;
41
- n.y = n.fy;
42
- n.fx = n.sx = undefined;
43
- n.fy = n.sy = undefined;
44
- }
45
-
46
- export type RendererT<T> = (props: T, element: SVGGElement) => void;
47
-
48
- export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E extends EdgeBaseProps<V>> extends SVGZoomWidget {
49
-
50
- protected _centroidFilter: SVGGlowFilter;
51
-
52
- private _toggleHierarchy = new ToggleButton().faChar("fa-sitemap").tooltip("Hierarchy").on("click", () => this.layoutClick("Hierarchy"));
53
- private _toggleForceDirected = new ToggleButton().faChar("fa-expand").tooltip("Force Directed").on("click", () => this.layoutClick("ForceDirected"));
54
- private _toggleForceDirected2 = new ToggleButton().faChar("fa-arrows").tooltip("Spring").on("click", () => this.layoutClick("ForceDirected2"));
55
- private _toggleCircle = new ToggleButton().faChar("fa-circle-o").tooltip("Circle").on("click", () => this.layoutClick("Circle"));
56
- private _toggleDot = new ToggleButton().faChar("fa-angle-double-down").tooltip("DOT").on("click", () => this.layoutClick("DOT"));
57
- private _toggleNeato = new ToggleButton().faChar("fa-sun-o").tooltip("Neato").on("click", () => this.layoutClick("Neato"));
58
- private _toggleFDP = new ToggleButton().faChar("fa-asterisk").tooltip("FDP").on("click", () => this.layoutClick("FDP"));
59
- private _toggleTwoPI = new ToggleButton().faChar("fa-bullseye").tooltip("TwoPI").on("click", () => this.layoutClick("TwoPI"));
60
- private _toggleCirco = new ToggleButton().faChar("fa-cogs").tooltip("Circo").on("click", () => this.layoutClick("Circo"));
61
- private _toggleT = new ToggleButton().faChar("fa-sitemap fa-rotate-270").tooltip("Tree").on("click", () => this.layoutClick("Tree"));
62
- private _toggleRT = new ToggleButton().faChar("fa-sun-o").tooltip("Radial Tree").on("click", () => this.layoutClick("RadialTree"));
63
- private _toggleD = new ToggleButton().faChar("fa-sitemap fa-rotate-270").tooltip("Dendrogram").on("click", () => this.layoutClick("Dendrogram"));
64
- private _toggleRD = new ToggleButton().faChar("fa-asterisk").tooltip("Radial Dendrogram").on("click", () => this.layoutClick("RadialDendrogram"));
65
-
66
- protected _graphData = new GraphCollection<VertexPlaceholder<V>, EdgePlaceholder<V, E>, SubgraphPlaceholder<SG>>()
67
- .idFunc(d => d.id)
68
- .sourceFunc(e => e.source.id)
69
- .targetFunc(e => e.target.id)
70
- .updateFunc((b: VertexPlaceholder<V> | EdgePlaceholder<V, E> | SubgraphPlaceholder<SG>, a: VertexPlaceholder<V> | EdgePlaceholder<V, E> | SubgraphPlaceholder<SG>) => {
71
- b.props = a.props;
72
- if (isEdgePlaceholder(a) && isEdgePlaceholder(b)) {
73
- b.source = a.source;
74
- b.target = a.target;
75
- }
76
- return b;
77
- })
78
- ;
79
-
80
- protected _prevDoClickTime: number = 0;
81
-
82
- protected _svgDefsAnn: any;
83
- protected _svgDefsCat: any;
84
- protected _subgraphG: Selection<SVGGElement, any, SVGGElement, any>;
85
- protected _edgeG: Selection<SVGGElement, any, SVGGElement, any>;
86
- protected _vertexG: Selection<SVGGElement, any, SVGGElement, any>;
87
-
88
- protected _tooltip: HTMLTooltip = new HTMLTooltip();
89
-
90
- protected _selection = new Utility.Selection(this);
91
- private _dragHandler = d3Drag<Element, VertexPlaceholder<V>>();
92
-
93
- protected _catPalette = Palette.ordinal("hpcc10");
94
- _svgDefs: any;
95
-
96
- constructor(subgraphRenderer: RendererT<SG>, vertexRenderer: RendererT<V>, edgeRenderer: RendererT<E>) {
97
- super();
98
- this._subgraphRenderer = subgraphRenderer;
99
- this._vertexRenderer = vertexRenderer;
100
- this._edgeRenderer = edgeRenderer;
101
- const context = this;
102
- this._drawStartPos = "origin";
103
-
104
- const buttons: Widget[] = [
105
- this._toggleHierarchy,
106
- this._toggleForceDirected,
107
- this._toggleForceDirected2,
108
- this._toggleCircle,
109
- new Spacer(),
110
- this._toggleDot,
111
- this._toggleNeato,
112
- this._toggleFDP,
113
- this._toggleTwoPI,
114
- this._toggleCirco,
115
- new Spacer(),
116
- this._toggleT,
117
- this._toggleRT,
118
- this._toggleD,
119
- this._toggleRD,
120
- new Spacer()
121
- ];
122
- this._iconBar.buttons(buttons.concat(this._iconBar.buttons()));
123
-
124
- this._dragHandler
125
- .on("start", function (d) {
126
- if (context.allowDragging()) {
127
- d3Select(this).classed("grabbed", true);
128
- dragStart(d);
129
- Utility.safeRaise(this);
130
- context.moveVertexPlaceholder(d, false, true);
131
-
132
- const selection = context.selection();
133
- const isSelected = context.selected(d.props, selection as V[]);
134
- if (isSelected) {
135
- selection
136
- .filter(v => v.id !== d.props.id)
137
- .forEach(v => {
138
- const n = context._graphData.vertex(v.id);
139
- dragStart(n);
140
- });
141
- } else if (context.dragSingleNeighbors()) {
142
- context._graphData.singleNeighbors(d.id).forEach(n => {
143
- dragStart(n);
144
- });
145
- }
146
- }
147
- })
148
- .on("drag", function (d) {
149
- if (context.allowDragging()) {
150
- d.fx = d.sx + context.rproject(d3Event().x - d.sx);
151
- d.fy = d.sy + context.rproject(d3Event().y - d.sy);
152
- context._graphData.vertexEdges(d.id).forEach(e => delete e.points);
153
- context.moveVertexPlaceholder(d, false, true);
154
- const selection = context.selection();
155
- const isSelected = context.selected(d.props, selection as V[]);
156
-
157
- if (isSelected) {
158
- selection
159
- .filter(v => v.id !== d.props.id)
160
- .forEach(v => {
161
- const n = context._graphData.vertex(v.id);
162
- dragTick(n, d);
163
- context.moveVertexPlaceholder(n, false, true);
164
- });
165
- } else if (context.dragSingleNeighbors()) {
166
- context._graphData.singleNeighbors(d.id).forEach(n => {
167
- dragTick(n, d);
168
- context.moveVertexPlaceholder(n, false, true);
169
- });
170
- }
171
- }
172
- })
173
- .on("end", function (d) {
174
- let doClick = true;
175
- if (context.allowDragging()) {
176
- doClick = Math.abs(d.sx - d.fx) < 1 && Math.abs(d.sy - d.fy) < 1;
177
- dragEnd(d);
178
-
179
- const selection = context.selection();
180
- const isSelected = context.selected(d.props, selection as V[]);
181
- if (isSelected) {
182
- selection
183
- .filter(v => v.id !== d.props.id)
184
- .forEach(v => {
185
- const n = context._graphData.vertex(v.id);
186
- dragEnd(n);
187
- });
188
- } else if (context.dragSingleNeighbors()) {
189
- context._graphData.singleNeighbors(d.id).forEach(dragEnd);
190
- }
191
-
192
- d3Select(this).classed("grabbed", false);
193
- }
194
- if (doClick) {
195
- const event = d3Event();
196
- context._selection.click({
197
- _id: String(d.id),
198
- element: () => d.element
199
- }, event.sourceEvent);
200
- context.selectionChanged();
201
- const selected = d.element.classed("selected");
202
- const eventOrigin = context.resolveEventOrigin();
203
- if (event?.sourceEvent?.button === 2) {
204
- context.vertex_contextmenu(d.props.origData || d.props, "", selected, eventOrigin);
205
- } else {
206
- context.vertex_click(d.props.origData || d.props, "", selected, eventOrigin);
207
- }
208
- const doClickTime = Date.now();
209
- if (doClickTime - context._prevDoClickTime < context.doubleClickMaxDelay()) {
210
- context.vertex_dblclick(d.props.origData || d.props, "", selected, eventOrigin);
211
- }
212
- context._prevDoClickTime = doClickTime;
213
- }
214
- })
215
- .filter(() => true)
216
- ;
217
- }
218
-
219
- resolveEventOrigin(): { origin: string, data?: SG | V | E } {
220
- const d3evt = d3Event();
221
- const eventPath = d3evt?.sourceEvent?.path ?? d3evt?.sourceEvent?.composedPath() ?? d3evt?.path ?? d3evt?.composedPath();
222
- const element = eventPath?.find(n => n?.hasAttribute && n?.hasAttribute("data-click"));
223
- const origin = element ? element.getAttribute("data-click") : "";
224
- const dataStr = element ? element.getAttribute("data-click-data") : "";
225
- let data = undefined;
226
- if (dataStr) {
227
- try {
228
- data = JSON.parse(dataStr);
229
- } catch (e) {
230
- console.warn("Unexpected annotation data:", dataStr);
231
- }
232
- }
233
-
234
- return {
235
- origin,
236
- data
237
- };
238
- }
239
-
240
- iconBarButtons(): Widget[] {
241
- return this._iconBar.buttons();
242
- }
243
-
244
- protected _categories: IconEx[] = [];
245
- categories(): IconEx[];
246
- categories(_: IconEx[]): this;
247
- categories(_?: IconEx[]): IconEx[] | this {
248
- if (_ === void 0) return this._categories;
249
- this._categories = _;
250
- return this;
251
- }
252
-
253
- protected _annotations: IconEx[] = [];
254
- annotations(): IconEx[];
255
- annotations(_: IconEx[]): this;
256
- annotations(_?: IconEx[]): IconEx[] | this {
257
- if (_ === void 0) return this._annotations;
258
- this._annotations = _;
259
- return this;
260
- }
261
-
262
- private _origData: GraphDataProps<SG, V, E> = {
263
- subgraphs: [],
264
- vertices: [],
265
- edges: [],
266
- hierarchy: []
267
- };
268
- data(): GraphDataProps<SG, V, E>;
269
- data(_: GraphDataProps<SG, V, E>, merge?: boolean): this;
270
- data(_?: GraphDataProps<SG, V, E>, merge?: boolean): GraphDataProps<SG, V, E> | this {
271
- if (_ === void 0) return this._origData;
272
- this._origData = _;
273
-
274
- this._graphData.mergeSubgraphs((_.subgraphs || []).map(sg => ({
275
- id: sg.id,
276
- props: sg
277
- })));
278
-
279
- this._graphData.mergeVertices((_.vertices || []).map(v => ({
280
- id: v.id,
281
- centroid: v.centroid,
282
- props: v
283
- })));
284
-
285
- this._graphData.mergeEdges(
286
- (_.edges || [])
287
- .filter(e => {
288
- return this._graphData.vertexExists(e.source.id) && this._graphData.vertexExists(e.target.id);
289
- })
290
- .map(e => ({
291
- id: e.id,
292
- props: e,
293
- source: this._graphData.vertex(e.source.id),
294
- target: this._graphData.vertex(e.target.id)
295
- }))
296
- );
297
-
298
- this._graphData.clearParents();
299
- (_.hierarchy ? _.hierarchy : []).forEach(h => {
300
- if (this._graphData.subgraphExists(h.child.id)) {
301
- this._graphData.subgraphParent(h.child.id, h.parent.id);
302
- } else if (this._graphData.vertexExists(h.child.id)) {
303
- this._graphData.vertexParent(h.child.id, h.parent.id);
304
- }
305
- });
306
-
307
- return this;
308
- }
309
-
310
- selected(vertex: V, _?: Array<V>): boolean {
311
- return (_ || this.selection()).some(n => n.id === vertex.id);
312
- }
313
-
314
- selection(_: Array<V | SG | E>): this;
315
- selection(): Array<V | SG | E>;
316
- selection(_?: Array<V | SG | E>): Array<SG | V | E> | this {
317
- if (!arguments.length) return this._selection.get().map(item => this._graphData.item(item._id).props);
318
- this._selection.set(_.map(item => {
319
- const vp = this._graphData.item(item.id);
320
- return {
321
- _id: String(vp.id),
322
- element: () => vp.element
323
- };
324
- }));
325
- return this;
326
- }
327
-
328
- graphData(): GraphCollection<VertexPlaceholder<V>, EdgePlaceholder<V, E>> {
329
- return this._graphData;
330
- }
331
-
332
- resetLayout() {
333
- delete this._prevLayout;
334
- return this;
335
- }
336
-
337
- layoutRunning() {
338
- return this._layoutAlgo && this._layoutAlgo.running();
339
- }
340
-
341
- protected _layoutAlgo: ILayout = new Null(this);
342
- layoutAlgo(layout: ILayout) {
343
- if (this._layoutAlgo) {
344
- this._layoutAlgo.stop();
345
- }
346
- this._layoutAlgo = layout;
347
- return this._layoutAlgo.start().then(() => {
348
- this.updateIconBar();
349
- if (this.applyScaleOnLayout()) {
350
- // Wait for any transitions to finish ---
351
- setTimeout(() => {
352
- this.zoomToFit(this.transitionDuration());
353
- }, this.transitionDuration());
354
- }
355
- });
356
- }
357
-
358
- layoutClick(layout: GraphLayoutType) {
359
- if (this.layoutRunning()) {
360
- this._layoutAlgo.stop();
361
- this.updateIconBar();
362
- } else {
363
- delete this._prevLayout;
364
- this
365
- .layout(layout)
366
- .render()
367
- ;
368
- }
369
- }
370
-
371
- updateIconBarItem(tb: ToggleButton, tbLayout: GraphLayoutType) {
372
- const layout = this.layout();
373
- const running = this._layoutAlgo && this._layoutAlgo.running();
374
- tb.enabled(!running || layout === tbLayout).selected(running && layout === tbLayout).render();
375
- }
376
-
377
- updateIconBar() {
378
- super.updateIconBar();
379
- this.updateIconBarItem(this._toggleHierarchy, "Hierarchy");
380
- this.updateIconBarItem(this._toggleDot, "DOT");
381
- this.updateIconBarItem(this._toggleForceDirected, "ForceDirected");
382
- this.updateIconBarItem(this._toggleNeato, "Neato");
383
- this.updateIconBarItem(this._toggleFDP, "FDP");
384
- this.updateIconBarItem(this._toggleForceDirected2, "ForceDirected2");
385
- this.updateIconBarItem(this._toggleCircle, "Circle");
386
- this.updateIconBarItem(this._toggleTwoPI, "TwoPI");
387
- this.updateIconBarItem(this._toggleCirco, "Circo");
388
- this.updateIconBarItem(this._toggleT, "Tree");
389
- this.updateIconBarItem(this._toggleRT, "RadialTree");
390
- this.updateIconBarItem(this._toggleD, "Dendrogram");
391
- this.updateIconBarItem(this._toggleRD, "RadialDendrogram");
392
- }
393
-
394
- getNeighborMap(vertex: VertexPlaceholder<V>) {
395
- const vertices = {};
396
- const edges = {};
397
-
398
- if (vertex) {
399
- const nedges = this._graphData.vertexEdges(vertex.id);
400
- for (let i = 0; i < nedges.length; ++i) {
401
- const edge = this._graphData.edge(nedges[i].id);
402
- edges[edge.id] = edge;
403
- if (edge.source.id !== vertex.id) {
404
- vertices[edge.source.id] = edge.source;
405
- }
406
- if (edge.target.id !== vertex.id) {
407
- vertices[edge.target.id] = edge.target;
408
- }
409
- }
410
- }
411
-
412
- return {
413
- vertices,
414
- edges
415
- };
416
- }
417
-
418
- centerOnItem(id: string) {
419
- const item = this._graphData.item(id);
420
- let x;
421
- let y;
422
- if (this._graphData.isSubgraph(item) || this._graphData.isVertex(item)) {
423
- x = item.x;
424
- y = item.y;
425
- }
426
- if (this._graphData.isEdge(item)) {
427
- [x, y] = [0, 0]; // center(item.points);
428
- }
429
- if (x !== undefined && y !== undefined) {
430
- const bbox = item.element.node().getBBox();
431
- if (this._graphData.isVertex(item)) {
432
- const proj = this.projectPlacholder(item);
433
- x = proj.x;
434
- y = proj.y;
435
- } else if (this._graphData.isSubgraph(item)) {
436
- x = this.project(item.x);
437
- y = this.project(item.y);
438
- }
439
- const deltaX = bbox.x + bbox.width / 2;
440
- const deltaY = bbox.y + bbox.height / 2;
441
- const itemBBox = {
442
- x: x + deltaX - bbox.width / 2,
443
- y: y + deltaY - bbox.height / 2,
444
- width: bbox.width,
445
- height: bbox.height
446
- };
447
- this.centerOnBBox(itemBBox);
448
- }
449
- }
450
-
451
- hideVertex(id: string): this {
452
- const item = this._graphData.item(id);
453
- if (this._graphData.isVertex(item)) {
454
- item.props.hidden = true;
455
- }
456
- return this;
457
- }
458
-
459
- showVertex(id: string): this {
460
- const item = this._graphData.item(id);
461
- if (this._graphData.isVertex(item)) {
462
- item.props.hidden = false;
463
- }
464
- return this;
465
- }
466
-
467
- protected highlight = {
468
- zoom: 1.1,
469
- opacity: 0.33,
470
- edge: "1.25px"
471
- };
472
-
473
- highlightVerticies(vertexMap?: { [id: string]: boolean }) {
474
- const context = this;
475
- const vertexElements = this._vertexG.selectAll<SVGGElement, VertexPlaceholder<V>>(".graphVertex");
476
- const forceLabelOnHighlight = !context.showVertexLabels() && context.showVertexLabelsOnHighlight();
477
- vertexElements
478
- .classed("graphVertex-highlighted", d => !vertexMap || vertexMap[d.id])
479
- .style("filter", d => vertexMap && vertexMap[d.id] ? "url(#" + this.id() + "_glow)" : null)
480
- .each(function (d) {
481
- if (forceLabelOnHighlight) {
482
- const props = context.calcProps(
483
- {
484
- showLabel: !!(vertexMap && vertexMap[d.id]),
485
- ...context.vertexMapper(d.props, d.props.origData)
486
- }
487
- );
488
- context._vertexRenderer(props, this);
489
- }
490
- })
491
- .transition().duration(this.transitionDuration())
492
- .on("end", function (d) {
493
- if (vertexMap && vertexMap[d.id]) {
494
- if (d.element.node() && d.element.node().parentNode) {
495
- d.element.node().parentNode.appendChild(d.element.node());
496
- }
497
- }
498
- })
499
- .style("opacity", function (d) {
500
- if (d.props.hidden) return 0;
501
- if (!vertexMap || vertexMap[d.id]) {
502
- return 1;
503
- }
504
- return context.highlight.opacity;
505
- })
506
- ;
507
- return this;
508
- }
509
-
510
- highlightEdges(edgeMap?: { [id: string]: boolean }) {
511
- const context = this;
512
- const edgeElements = this._edgeG.selectAll<SVGGElement, EdgePlaceholder<V, E>>(".graphEdge");
513
- edgeElements
514
- .classed("graphEdge-highlighted", d => !edgeMap || edgeMap[d.id])
515
- .style("stroke-width", function (o) {
516
- if (edgeMap && edgeMap[o.id]) {
517
- return context.highlight.edge;
518
- }
519
- return context.edgeStrokeWidth() + "px";
520
- }).transition().duration(this.transitionDuration())
521
- .style("opacity", function (o) {
522
- if (o.source.props.hidden || o.target.props.hidden) return 0;
523
- if (!edgeMap || edgeMap[o.id]) {
524
- return 1;
525
- }
526
- return context.highlight.opacity;
527
- })
528
- ;
529
- return this;
530
- }
531
-
532
- highlightVertex(_element, d?: VertexPlaceholder<V>) {
533
- if (this.highlightOnMouseOverVertex()) {
534
- if (d) {
535
- const highlight = this.getNeighborMap(d);
536
- highlight.vertices[d.id] = d;
537
- this.highlightVerticies(highlight.vertices);
538
- this.highlightEdges(highlight.edges);
539
- } else {
540
- this.highlightVerticies();
541
- this.highlightEdges();
542
- }
543
- }
544
- }
545
-
546
- highlightEdge(_element, d?: EdgePlaceholder<V, E>) {
547
- if (this.highlightOnMouseOverEdge()) {
548
- if (d) {
549
- const vertices = {};
550
- vertices[d.source.id] = d.source;
551
- vertices[d.target.id] = d.target;
552
- const edges = {};
553
- edges[d.id] = d;
554
- this.highlightVerticies(vertices);
555
- this.highlightEdges(edges);
556
- } else {
557
- this.highlightVerticies();
558
- this.highlightEdges();
559
- }
560
- }
561
- }
562
-
563
- moveSubgraphPlaceholder(sp: SubgraphPlaceholder<SG>, transition: boolean): this {
564
- const context = this;
565
- const x = this.project(sp.x);
566
- const y = this.project(sp.y);
567
- const width = this.project(sp.props.width, true);
568
- const height = this.project(sp.props.height, true);
569
- if (sp.element) {
570
- sp.element.transition().duration(transition ? this.transitionDuration() : 0)
571
- .attr("transform", `translate(${x} ${y})`)
572
- .each(function (d) {
573
- context._subgraphRenderer({ ...d.props, width, height }, this);
574
- })
575
- ;
576
- }
577
- return this;
578
- }
579
-
580
- moveEdgePlaceholder(ep: EdgePlaceholder<V, E>, transition: boolean): this {
581
- const edgeLayout = {
582
- ...this._layoutAlgo.edgePath(ep, this.edgeArcDepth())
583
- };
584
- const context = this;
585
- if (this._edgeRenderer && ep.element) {
586
- const previousEdgeLayout = (ep as any).previousEdgeLayout ?? edgeLayout;
587
- (ep as any).previousEdgeLayout = edgeLayout;
588
- const pathInterpolator = d3InterpolatePath(previousEdgeLayout.path, edgeLayout.path);
589
- const labelPosInterpolator = d3InterpolateNumberArray(previousEdgeLayout.labelPos, edgeLayout.labelPos);
590
- ep.element.transition().duration(transition ? this.transitionDuration() : 0)
591
- .tween("some.path", function () {
592
- return function (t) {
593
- const updated = {
594
- path: pathInterpolator(t),
595
- labelPos: labelPosInterpolator(t),
596
- strokeWidth: ep.props.strokeWidth ?? context.edgeStrokeWidth(),
597
- color: ep.props.stroke ?? context.edgeColor()
598
- };
599
- context._edgeRenderer({ ...edgeLayout, ...ep.props, ...updated }, ep.element.node());
600
- };
601
- });
602
- }
603
- return this;
604
- }
605
-
606
- moveVertexPlaceholder(vp: VertexPlaceholder<V>, transition: boolean, moveNeighbours: boolean): this {
607
- const { x, y } = this.projectPlacholder(vp);
608
- vp.element && (transition ? vp.element.transition().duration(this.transitionDuration()) as unknown as Selection<SVGPathElement, EdgePlaceholder<V, E>, SVGGElement, any> : vp.element)
609
- .attr("transform", `translate(${x} ${y})`)
610
- ;
611
- if (moveNeighbours) {
612
- this._graphData.vertexEdges(vp.id).forEach(e => this.moveEdgePlaceholder(e, transition));
613
- }
614
- return this;
615
- }
616
-
617
- moveSubgraphs(transition: boolean): this {
618
- this._graphData.allSubgraphs().forEach(s => this.moveSubgraphPlaceholder(s, transition));
619
- return this;
620
- }
621
-
622
- moveEdges(transition: boolean): this {
623
- this._graphData.allEdges().forEach(e => this.moveEdgePlaceholder(e, transition));
624
- return this;
625
- }
626
-
627
- moveVertices(transition: boolean): this {
628
- this._graphData.allVertices().forEach(v => this.moveVertexPlaceholder(v, transition, false));
629
- return this;
630
- }
631
-
632
- project(pos: number, clip: boolean = false) {
633
- const rf = 10;
634
- pos = pos !== undefined ? pos : 0;
635
- let scale = this._transformScale;
636
- if (clip) {
637
- if (this._transformScale > this.maxScale() + (this._transformScale - this.maxScale()) / 2) {
638
- scale = this.maxScale() + (this._transformScale - this.maxScale()) / 2;
639
- } else if (this._transformScale < this.minScale() - (this._transformScale - this.minScale()) / 13) {
640
- // scale = this.minScale() - (this._transformScale - this.minScale()) / 13;
641
- }
642
- }
643
- return Math.round(pos * scale * rf) / rf;
644
- }
645
-
646
- rproject(pos: number) {
647
- const rf = 10;
648
- pos = pos !== undefined ? pos : 0;
649
- return Math.round(pos / this._transformScale * rf) / rf;
650
- }
651
-
652
- projectPlacholder(vp: VertexPlaceholder<V>) {
653
- return {
654
- x: this.project(vp.fx !== undefined ? vp.fx : vp.x),
655
- y: this.project(vp.fy !== undefined ? vp.fy : vp.y)
656
- };
657
- }
658
-
659
- categoryID(id: string | number, prefix: "cat" | "ann" = "cat"): string {
660
- return id === undefined || id === "" ? "" : `${prefix}${this.id()}_${id}`;
661
- }
662
-
663
- updateCategories() {
664
- render(Icons, {
665
- icons: this._categories.map((c): IconEx => ({
666
- ...c,
667
- id: this.categoryID(c.id),
668
- fill: c.fill || "transparent",
669
- imageCharFill: c.imageCharFill || this._catPalette(c.id)
670
- }))
671
- }, this._svgDefsCat.node());
672
- }
673
-
674
- updateAnnotations() {
675
- render(Icons, {
676
- icons: this._annotations.map((c): IconEx => ({
677
- ...c,
678
- id: this.categoryID(c.id, "ann"),
679
- shape: c.shape || "square",
680
- height: c.height || 12,
681
- fill: c.fill || this._catPalette(c.id)
682
- }))
683
- }, this._svgDefsAnn.node());
684
- }
685
-
686
- private _edgeRenderer: RendererT<E>;
687
- edgeRenderer(): RendererT<E>;
688
- edgeRenderer(_: RendererT<E>): this;
689
- edgeRenderer(_?: RendererT<E>): this | RendererT<E> {
690
- if (!arguments.length) return this._edgeRenderer;
691
- this._edgeRenderer = _;
692
- return this;
693
- }
694
-
695
- updateEdges(): this {
696
- const context = this;
697
- this._edgeG.selectAll(".graphEdge")
698
- .data(this._graphData.allEdges(), (d: EdgePlaceholder<V, E>) => d.id)
699
- .join(
700
- enter => enter.append("g")
701
- .attr("class", "graphEdge")
702
- .on("click.selectionBag", function (d) {
703
- context._selection.click({
704
- _id: String(d.id),
705
- element: () => d.element
706
- }, d3Event());
707
- context.selectionChanged();
708
- })
709
- .on("click", function (this: SVGElement, d) {
710
- const selected = d.element.classed("selected");
711
- context.edge_click(d.props.origData || d.props, "", selected);
712
- })
713
- .on("mouseover", function (d) {
714
- Utility.safeRaise(this);
715
- context.edge_mouseover(d3Select(this), d);
716
- })
717
- .on("mouseout", function (d) {
718
- context.edge_mouseout(d3Select(this), d);
719
- })
720
- .each(function (d) {
721
- d.element = d3Select(this);
722
- if (!context._edgeRenderer) {
723
- d.elementPath = d.element.append("path");
724
- d.elementText = d.element.append("text")
725
- .attr("text-anchor", "middle")
726
- ;
727
- }
728
- })
729
- ,
730
- update => update
731
- .attr("opacity", d => d.source.props.hidden || d.target.props.hidden ? 0 : 1)
732
- .classed("hide-text", !context.showEdgeLabels())
733
- ,
734
- exit => exit
735
- .each(function (d) {
736
- delete d.element;
737
- })
738
- .remove()
739
- )
740
- ;
741
- return this;
742
- }
743
-
744
- private _vertexRenderer: RendererT<V>;
745
- vertexRenderer(): RendererT<V>;
746
- vertexRenderer(_: RendererT<V>): this;
747
- vertexRenderer(_?: RendererT<V>): this | RendererT<V> {
748
- if (!arguments.length) return this._vertexRenderer;
749
- this._vertexRenderer = _;
750
- return this;
751
- }
752
-
753
- vertexMapper(props: V, origRow: V): V {
754
- return {
755
- ...props,
756
- categoryID: this.categoryID(props.categoryID),
757
- annotationIDs: props.annotationIDs ? props.annotationIDs.map(a => this.categoryID(a, "ann")) : []
758
- };
759
- }
760
-
761
- updateVertices(): this {
762
- const context = this;
763
- this._vertexG.selectAll(".graphVertex")
764
- .data(this._graphData.allVertices(), (d: VertexPlaceholder<V>) => d.id)
765
- .join(
766
- enter => enter.append("g")
767
- .attr("class", "graphVertex")
768
- .on("dblclick", function (this: SVGElement, d) {
769
- d3Event().stopPropagation();
770
- })
771
- .on("contextmenu", function (this: SVGElement, d) {
772
- d3Event().preventDefault();
773
- })
774
- .on("mousein", function (d) {
775
- Utility.safeRaise(this);
776
- context.highlightVertex(d3Select(this), d);
777
- const selected = d.element.classed("selected");
778
- const eventOrigin = context.resolveEventOrigin();
779
- context.vertex_mousein(d.props.origData || d.props, "", selected, eventOrigin);
780
- })
781
- .on("mouseover", function (d) {
782
- Utility.safeRaise(this);
783
- context.highlightVertex(d3Select(this), d);
784
- const selected = d.element.classed("selected");
785
- if (d.props.tooltip) {
786
- context._tooltip
787
- .tooltipHTML(context.tooltipHTML.bind(context))
788
- .triggerElement(d.element)
789
- .tooltipWidth(context.tooltipWidth())
790
- .tooltipHeight(context.tooltipHeight())
791
- .enablePointerEvents(context.enableTooltipPointerEvents())
792
- .closeDelay(context.tooltipCloseDelay())
793
- .direction("n")
794
- .data(d)
795
- .visible(true)
796
- .render()
797
- ;
798
- }
799
- const eventOrigin = context.resolveEventOrigin();
800
- context.vertex_mouseover(d.props.origData || d.props, "", selected, eventOrigin);
801
- })
802
- .on("mouseout", function (d) {
803
- context.highlightVertex(null, null);
804
- const selected = d.element.classed("selected");
805
- const eventOrigin = context.resolveEventOrigin();
806
- context.vertex_mouseout(d.props.origData || d.props, "", selected, eventOrigin);
807
- if (d.props.tooltip) {
808
- context._tooltip.mouseout();
809
- }
810
- })
811
- .call(this._dragHandler)
812
- .each(function (d) {
813
- d.element = d3Select(this);
814
- }),
815
- update => update,
816
- exit => exit
817
- .each(function (d) {
818
- delete d.element;
819
- })
820
- .remove()
821
- )
822
- .classed("centroid", d => d.props.centroid)
823
- .attr("opacity", d => d.props.hidden ? 0 : 1)
824
- .attr("filter", d => d.props.centroid ? "url(#" + this.id() + "_glow)" : null)
825
- .each(function (this: SVGGElement, d) {
826
- const props = context.calcProps(
827
- {
828
- showLabel: context.showVertexLabels(),
829
- ...context.vertexMapper(d.props, d.props.origData)
830
- }
831
- );
832
- context._vertexRenderer(props, this);
833
- })
834
- ;
835
- return this;
836
- }
837
-
838
- calcProps(props: V): V {
839
- return { ...props };
840
- }
841
-
842
- hasSubgraphs() {
843
- switch (this.layout()) {
844
- case "DOT":
845
- case "Hierarchy":
846
- return true;
847
- }
848
- return false;
849
- }
850
-
851
- private _subgraphRenderer: RendererT<SG>;
852
- subgraphRenderer(): RendererT<SG>;
853
- subgraphRenderer(_: RendererT<SG>): this;
854
- subgraphRenderer(_?: RendererT<SG>): this | RendererT<SG> {
855
- if (!arguments.length) return this._subgraphRenderer;
856
- this._subgraphRenderer = _;
857
- return this;
858
- }
859
-
860
- updateSubgraphs(): this {
861
- const context = this;
862
- this._subgraphG.selectAll(".subgraphPlaceholder")
863
- .data(this.hasSubgraphs() ? this._graphData.allSubgraphs() : [], (d: SubgraphPlaceholder<SG>) => d.id)
864
- .join(
865
- enter => enter.append("g")
866
- .attr("class", "subgraphPlaceholder")
867
- .on("click.selectionBag", function (d) {
868
- context._selection.click({
869
- _id: String(d.id),
870
- element: () => d.element
871
- }, d3Event());
872
- context.selectionChanged();
873
- })
874
- .on("click", function (this: SVGElement, d) {
875
- const selected = d.element.classed("selected");
876
- context.subgraph_click(d.props.origData || d.props, "", selected);
877
- })
878
- .on("mouseover", function () {
879
- Utility.safeRaise(this);
880
- })
881
- .each(function (d) {
882
- d.element = d3Select(this);
883
- })
884
- ,
885
- update => update,
886
- exit => exit
887
- .each(function (d) {
888
- delete d.element;
889
- })
890
- .transition()
891
- .style("opacity", 0)
892
- .remove()
893
- )
894
- .each(function (d) {
895
- context.moveSubgraphPlaceholder(d, false);
896
- })
897
- ;
898
- return this;
899
- }
900
-
901
- enter(domNode, element) {
902
- super.enter(domNode, element);
903
-
904
- const svg = this.locateSVGNode(domNode);
905
- this._svgDefs = d3Select(svg).select<SVGDefsElement>("defs");
906
- this._centroidFilter = new SVGGlowFilter(this._svgDefs, this._id + "_glow");
907
-
908
- this._svgDefsCat = this._svgDefs.append("g");
909
- this._svgDefsAnn = this._svgDefs.append("g");
910
- this._subgraphG = this._renderElement.append("g");
911
- this._edgeG = this._renderElement.append("g");
912
- this._vertexG = this._renderElement.append("g");
913
-
914
- this._tooltip.target(domNode);
915
-
916
- this.on("startMarqueeSelection", () => {
917
- }).on("updateMarqueeSelection", rect => {
918
- const vertices: VertexPlaceholder<V>[] = this._graphData.allVertices().filter(v => v.x >= rect.x && v.x <= rect.x + rect.width && v.y >= rect.y && v.y <= rect.y + rect.height);
919
- this.selection(vertices.map(v => v.props));
920
- }).on("endMarqueeSelection", () => {
921
- this.selectionChanged();
922
- });
923
- }
924
-
925
- protected forceDirectedOptions(): FDOptions {
926
- return {
927
- alpha: this.forceDirectedAlpha(),
928
- alphaMin: this.forceDirectedAlphaMin(),
929
- alphaDecay: this.forceDirectedAlphaDecay(),
930
- velocityDecay: this.forceDirectedVelocityDecay(),
931
- repulsionStrength: this.forceDirectedRepulsionStrength(),
932
- iterations: this.forceDirectedIterations(),
933
- linkDistance: this.forceDirectedLinkDistance(),
934
- linkStrength: this.forceDirectedLinkStrength(),
935
- pinCentroid: this.forceDirectedPinCentroid(),
936
- forceStrength: this.forceDirectedForceStrength(),
937
- distanceMin: this.forceDirectedMinDistance(),
938
- distanceMax: this.forceDirectedMaxDistance(),
939
- };
940
- }
941
-
942
- private layoutOptions(layout: GraphLayoutType) {
943
- switch (layout) {
944
- case "ForceDirected":
945
- case "ForceDirected2":
946
- case "ForceDirectedHybrid":
947
- return this.forceDirectedOptions();
948
- case "Hierarchy":
949
- return {
950
- rankdir: this.hierarchyRankDirection(),
951
- nodesep: this.hierarchyNodeSeparation(),
952
- edgesep: this.hierarchyEdgeSeparation(),
953
- ranksep: this.hierarchyRankSeparation(),
954
- digraph: this.hierarchyDigraph()
955
- };
956
- case "Tree":
957
- case "Dendrogram":
958
- return { rankdir: this.treeRankDirection() };
959
- case "DOT":
960
- case "Neato":
961
- case "FDP":
962
- case "TwoPI":
963
- case "Circo":
964
- return new URL(this.wasmFolder() || scriptDir, document.baseURI).href;
965
- case "None":
966
- case "Circle":
967
- case "RadialTree":
968
- case "RadialDendrogram":
969
- default:
970
- return undefined;
971
- }
972
- }
973
-
974
- private _prevLayout: string;
975
- updateLayout() {
976
- const layout = this.layout();
977
- const options: any = this.layoutOptions(layout);
978
- const hash = hashSum([layout, options]);
979
- if (this._prevLayout !== hash) {
980
- this._prevLayout = hash;
981
- switch (layout) {
982
- case "None":
983
- this.layoutAlgo(new Null(this));
984
- break;
985
- case "Circle":
986
- this.layoutAlgo(new Circle(this));
987
- break;
988
- case "ForceDirected":
989
- this.layoutAlgo(new ForceDirected(this, options));
990
- break;
991
- case "ForceDirected2":
992
- this.layoutAlgo(new ForceDirectedAnimated(this, options));
993
- break;
994
- case "ForceDirectedHybrid":
995
- this.layoutAlgo(new ForceDirected(this, options)).then(() => {
996
- this.layoutAlgo(new ForceDirectedAnimated(this, options));
997
- });
998
- break;
999
- case "Hierarchy":
1000
- this.layoutAlgo(new Dagre(this, options));
1001
- break;
1002
- case "DOT":
1003
- this.layoutAlgo(new Graphviz(this, "dot", options));
1004
- break;
1005
- case "Tree":
1006
- this.layoutAlgo(new Tree(this, options));
1007
- break;
1008
- case "RadialTree":
1009
- this.layoutAlgo(new RadialTree(this));
1010
- break;
1011
- case "Dendrogram":
1012
- this.layoutAlgo(new Dendrogram(this, options));
1013
- break;
1014
- case "RadialDendrogram":
1015
- this.layoutAlgo(new RadialDendrogram(this));
1016
- break;
1017
- case "Neato":
1018
- this.layoutAlgo(new Graphviz(this, "neato", options));
1019
- break;
1020
- case "FDP":
1021
- this.layoutAlgo(new Graphviz(this, "fdp", options));
1022
- break;
1023
- case "TwoPI":
1024
- this.layoutAlgo(new Graphviz(this, "twopi", options));
1025
- break;
1026
- case "Circo":
1027
- this.layoutAlgo(new Graphviz(this, "circo", options));
1028
- break;
1029
- }
1030
- }
1031
- }
1032
-
1033
- update(domNode, element) {
1034
- super.update(domNode, element);
1035
-
1036
- this._renderElement.classed("allowDragging", this.allowDragging());
1037
-
1038
- this.updateCategories();
1039
- this.updateAnnotations();
1040
-
1041
- this.updateSubgraphs();
1042
- this.updateVertices();
1043
- this.updateEdges();
1044
- this.moveEdges(false);
1045
-
1046
- this.updateLayout();
1047
-
1048
- this.updateIconBar();
1049
- }
1050
-
1051
- exit(domNode, element) {
1052
- super.exit(domNode, element);
1053
- this._tooltip.target(null);
1054
- }
1055
-
1056
- render(callback?: (w: Widget) => void): this {
1057
- this.progress("start");
1058
- super.render(w => {
1059
- this.progress("stop");
1060
- if (callback) {
1061
- callback(w);
1062
- }
1063
- });
1064
- return this;
1065
- }
1066
-
1067
- private _prevWidth;
1068
- private _prevHeight;
1069
- private _prevTransformScale;
1070
- private _transformScale = 1;
1071
- zoomed(transform) {
1072
- super.zoomed(transform);
1073
- const { width, height } = this.size();
1074
-
1075
- if (transform.k < this.minScale()) {
1076
- this._edgeG.attr("transform", `scale(${this.minScale() / transform.k})`);
1077
- this._subgraphG.attr("transform", `scale(${this.minScale() / transform.k})`);
1078
- this._vertexG.attr("transform", `scale(${this.minScale() / transform.k})`);
1079
- this._transformScale = transform.k / this.minScale();
1080
- } else if (transform.k > this.maxScale()) {
1081
- this._edgeG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1082
- this._subgraphG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1083
- this._vertexG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1084
- this._transformScale = transform.k / this.maxScale();
1085
- } else {
1086
- this._transformScale = 1;
1087
- this._edgeG.attr("transform", null);
1088
- this._subgraphG.attr("transform", null);
1089
- this._vertexG.attr("transform", null);
1090
- }
1091
-
1092
- if (this._prevTransformScale !== this._transformScale ||
1093
- this._prevWidth !== width ||
1094
- this._prevHeight !== height) {
1095
- this._prevTransformScale = this._transformScale;
1096
- this._prevWidth = width;
1097
- this._prevHeight = height;
1098
- this
1099
- .moveSubgraphs(false)
1100
- .moveVertices(false)
1101
- .moveEdges(false)
1102
- ;
1103
- }
1104
- }
1105
-
1106
- // Events ---
1107
- centroids(): VertexPlaceholder<V>[] {
1108
- return this._graphData.allVertices().filter(vp => !!vp.props.centroid);
1109
- }
1110
-
1111
- selectionChanged() {
1112
- if (this.highlightSelectedPathToCentroid()) {
1113
- const highlightedVertices = {};
1114
- this.centroids().forEach(centroid => {
1115
- this.selection().forEach(selection => {
1116
- const { ids, len } = this._graphData.dijkstra(centroid.id, selection.id);
1117
- if (len) {
1118
- ids.forEach(id => {
1119
- highlightedVertices[id] = true;
1120
- });
1121
- }
1122
- });
1123
- });
1124
- this._edgeG.selectAll(".graphEdge")
1125
- // .classed("shortest-path", d => highlightedEdges[d.id()] === true)
1126
- ;
1127
- }
1128
- }
1129
-
1130
- tooltipHTML(data) {
1131
- return data.props.tooltip;
1132
- }
1133
-
1134
- subgraph_click(row, _col, sel) {
1135
- }
1136
-
1137
- vertex_click(row, _col, sel, data) {
1138
- }
1139
-
1140
- vertex_dblclick(row, _col, sel, data) {
1141
- }
1142
-
1143
- vertex_contextmenu(row, _col, sel, data) {
1144
- }
1145
-
1146
- vertex_mousein(row, _col, sel, data) {
1147
- }
1148
-
1149
- vertex_mouseover(row, _col, sel, data) {
1150
- }
1151
-
1152
- vertex_mouseout(row, _col, sel, data) {
1153
- }
1154
-
1155
- edge_click(row, _col, sel) {
1156
- }
1157
-
1158
- edge_mouseover(element, d) {
1159
- this.highlightEdge(element, d);
1160
- }
1161
-
1162
- edge_mouseout(_element, _d) {
1163
- this.highlightEdge(null, null);
1164
- }
1165
-
1166
- progress(what: "start" | "stop" | "layout-start" | "layout-tick" | "layout-stop") {
1167
- }
1168
- }
1169
- GraphT.prototype._class += " graph_GraphT";
1170
-
1171
- export interface GraphT<SG extends SubgraphBaseProps = any, V extends VertexBaseProps = any, E extends EdgeBaseProps<V> = any> {
1172
- allowDragging(): boolean;
1173
- allowDragging(_: boolean): this;
1174
- dragSingleNeighbors(): boolean;
1175
- dragSingleNeighbors(_: boolean): this;
1176
- layout(): GraphLayoutType;
1177
- layout(_: GraphLayoutType): this;
1178
- applyScaleOnLayout(): boolean;
1179
- applyScaleOnLayout(_: boolean): this;
1180
- highlightOnMouseOverVertex(): boolean;
1181
- highlightOnMouseOverVertex(_: boolean): this;
1182
- highlightOnMouseOverEdge(): boolean;
1183
- highlightOnMouseOverEdge(_: boolean): this;
1184
- transitionDuration(): number;
1185
- transitionDuration(_: number): this;
1186
-
1187
- highlightSelectedPathToCentroid(): boolean;
1188
- highlightSelectedPathToCentroid(_: boolean): this;
1189
- edgeArcDepth(): number;
1190
- edgeArcDepth(_: number): this;
1191
- minScale(): number;
1192
- minScale(_: number): this;
1193
- maxScale(): number;
1194
- maxScale(_: number): this;
1195
- showEdgeLabels(): boolean;
1196
- showEdgeLabels(_: boolean): this;
1197
- showEdgeLabelsOnHighlight(): boolean;
1198
- showEdgeLabelsOnHighlight(_: boolean): this;
1199
- showVertexLabels(): boolean;
1200
- showVertexLabels(_: boolean): this;
1201
- showVertexLabelsOnHighlight(): boolean;
1202
- showVertexLabelsOnHighlight(_: boolean): this;
1203
-
1204
- hierarchyRankDirection(): "TB" | "BT" | "LR" | "RL";
1205
- hierarchyRankDirection(_: "TB" | "BT" | "LR" | "RL"): this;
1206
- hierarchyNodeSeparation(): number;
1207
- hierarchyNodeSeparation(_: number): this;
1208
- hierarchyEdgeSeparation(): number;
1209
- hierarchyEdgeSeparation(_: number): this;
1210
- hierarchyRankSeparation(): number;
1211
- hierarchyRankSeparation(_: number): this;
1212
- hierarchyDigraph(): boolean;
1213
- hierarchyDigraph(_: boolean): this;
1214
-
1215
- forceDirectedAlpha(): number;
1216
- forceDirectedAlpha(_: number): this;
1217
- forceDirectedAlphaMin(): number;
1218
- forceDirectedAlphaMin(_: number): this;
1219
- forceDirectedAlphaDecay(): number;
1220
- forceDirectedAlphaDecay(_: number): this;
1221
- forceDirectedRepulsionStrength(): number;
1222
- forceDirectedRepulsionStrength(_: number): this;
1223
- forceDirectedVelocityDecay(): number;
1224
- forceDirectedVelocityDecay(_: number): this;
1225
- forceDirectedIterations(): number;
1226
- forceDirectedIterations(_: number): this;
1227
- forceDirectedLinkDistance(): number;
1228
- forceDirectedLinkDistance(_: number): this;
1229
- forceDirectedLinkStrength(): number;
1230
- forceDirectedLinkStrength(_: number): this;
1231
- forceDirectedPinCentroid(): boolean;
1232
- forceDirectedPinCentroid(_: boolean): this;
1233
- forceDirectedForceStrength(): number;
1234
- forceDirectedForceStrength(_: number): this;
1235
- forceDirectedMinDistance(): number;
1236
- forceDirectedMinDistance(_: number): this;
1237
- forceDirectedMaxDistance(): number;
1238
- forceDirectedMaxDistance(_: number): this;
1239
-
1240
- treeRankDirection(): "TB" | "LR";
1241
- treeRankDirection(_: "TB" | "LR"): this;
1242
-
1243
- edgeColor(): string;
1244
- edgeColor(_: string): this;
1245
- edgeStrokeWidth(): number;
1246
- edgeStrokeWidth(_: number): this;
1247
- tooltipWidth(): number;
1248
- tooltipWidth(_: number): this;
1249
- tooltipHeight(): number;
1250
- tooltipHeight(_: number): this;
1251
- enableTooltipPointerEvents(): boolean;
1252
- enableTooltipPointerEvents(_: boolean): this;
1253
- tooltipCloseDelay(): number;
1254
- tooltipCloseDelay(_: number): this;
1255
- doubleClickMaxDelay(): number;
1256
- doubleClickMaxDelay(_: number): this;
1257
-
1258
- wasmFolder(): string;
1259
- wasmFolder(_: string): this;
1260
- }
1261
-
1262
- GraphT.prototype.publish("allowDragging", true, "boolean", "Allow Dragging of Vertices");
1263
- GraphT.prototype.publish("dragSingleNeighbors", true, "boolean", "Dragging a Vertex also moves its singleton neighbors");
1264
- GraphT.prototype.publish("layout", "ForceDirectedHybrid", "set", "Default Layout", GraphLayoutTypeSet);
1265
- GraphT.prototype.publish("scale", "100%", "set", "Zoom Level", ["all", "width", "selection", "100%", "90%", "75%", "50%", "25%", "10%"]);
1266
- GraphT.prototype.publish("applyScaleOnLayout", false, "boolean", "Shrink to fit on Layout");
1267
- GraphT.prototype.publish("highlightOnMouseOverVertex", true, "boolean", "Highlight Vertex on Mouse Over");
1268
- GraphT.prototype.publish("highlightOnMouseOverEdge", true, "boolean", "Highlight Edge on Mouse Over");
1269
- GraphT.prototype.publish("transitionDuration", 250, "number", "Transition Duration");
1270
- GraphT.prototype.publish("showEdges", true, "boolean", "Show Edges");
1271
- GraphT.prototype.publish("showEdgeLabels", true, "boolean", "Show Edge labels");
1272
- GraphT.prototype.publish("showEdgeLabelsOnHighlight", true, "boolean", "Show Edge labels when highlighted");
1273
- GraphT.prototype.publish("showVertexLabels", true, "boolean", "Show Vertex labels");
1274
- GraphT.prototype.publish("showVertexLabelsOnHighlight", true, "boolean", "Show Vertex labels when highlighted");
1275
- GraphT.prototype.publish("snapToGrid", 0, "number", "Snap to Grid");
1276
- GraphT.prototype.publish("selectionClearOnBackgroundClick", false, "boolean", "Clear selection on background click");
1277
- GraphT.prototype.publish("edgeArcDepth", 8, "number", "Edge Arc Depth");
1278
- GraphT.prototype.publish("edgeColor", null, "html-color", "Edge line stroke color", null, { optional: true });
1279
- GraphT.prototype.publish("edgeStrokeWidth", 1, "number", "Edge line stroke width (pixels)");
1280
- GraphT.prototype.publish("minScale", 0.6, "number", "Min scale size for text");
1281
- GraphT.prototype.publish("maxScale", 1.0, "number", "Max scale size for text");
1282
- GraphT.prototype.publish("tooltipWidth", 256, "number", "Tooltip width (pixels)");
1283
- GraphT.prototype.publish("tooltipHeight", 128, "number", "Tooltip width (pixels)");
1284
- GraphT.prototype.publish("enableTooltipPointerEvents", false, "boolean", "If true, tooltip will use the style: 'pointer-events: all'");
1285
- GraphT.prototype.publish("tooltipCloseDelay", 0, "number", "Number of milliseconds to wait before closing tooltip (cancelled on tooltip mouseover event)");
1286
- GraphT.prototype.publish("doubleClickMaxDelay", 300, "number", "Number of milliseconds to wait before a subsequent click is not considered a double click");
1287
- GraphT.prototype.publish("highlightSelectedPathToCentroid", true, "boolean", "Highlight path to Center Vertex (for selected vertices)");
1288
-
1289
- GraphT.prototype.publish("hierarchyRankDirection", "TB", "set", "Direction for Rank Nodes", ["TB", "BT", "LR", "RL"], { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1290
- GraphT.prototype.publish("hierarchyNodeSeparation", 50, "number", "Number of pixels that separate nodes horizontally in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1291
- GraphT.prototype.publish("hierarchyEdgeSeparation", 10, "number", "Number of pixels that separate edges horizontally in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1292
- GraphT.prototype.publish("hierarchyRankSeparation", 50, "number", "Number of pixels between each rank in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1293
- GraphT.prototype.publish("hierarchyDigraph", true, "boolean", "Directional GraphBase", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1294
-
1295
- GraphT.prototype.publish("forceDirectedAlpha", 1, "number", "Alpha", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1296
- GraphT.prototype.publish("forceDirectedAlphaMin", 0.001, "number", "Min Alpha", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1297
- GraphT.prototype.publish("forceDirectedAlphaDecay", 0.0228, "number", "Defaults to 1 - pow(alphaMin, 1 / 300)", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1298
- GraphT.prototype.publish("forceDirectedRepulsionStrength", -350, "number", "Charge strength ", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1299
- GraphT.prototype.publish("forceDirectedVelocityDecay", 0.4, "number", "Velocity Decay ", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1300
- GraphT.prototype.publish("forceDirectedIterations", 300, "number", "Iterations", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1301
- GraphT.prototype.publish("forceDirectedLinkDistance", 300, "number", "Target distance between linked nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1302
- GraphT.prototype.publish("forceDirectedLinkStrength", 1, "number", "Strength (rigidity) of links", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1303
- GraphT.prototype.publish("forceDirectedPinCentroid", false, "boolean", "Pin centroid to center", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1304
- GraphT.prototype.publish("forceDirectedForceStrength", 0, "number", "Strength of center force", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1305
- GraphT.prototype.publish("forceDirectedMinDistance", 1, "number", "Min distance between nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1306
- GraphT.prototype.publish("forceDirectedMaxDistance", Infinity, "number", "Max distance between nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1307
-
1308
- GraphT.prototype.publish("treeRankDirection", "LR", "set", "Direction for Rank Nodes", ["TB", "LR"], { disable: (w: GraphT) => w.layout() !== "Tree" && w.layout() !== "Dendrogram" });
1309
-
1310
- GraphT.prototype.publish("wasmFolder", null, "string", "WASM Folder", null, { optional: true, disable: (w: GraphT) => ["DOT", "Neato", "FDP", "TwoPI", "Circo"].indexOf(w.layout()) < 0 });
1311
-
1312
- const _origScale = GraphT.prototype.scale;
1313
- GraphT.prototype.scale = function (_?, transitionDuration?) {
1314
- const retVal = _origScale.apply(this, arguments);
1315
- if (arguments.length) {
1316
- this.zoomTo(_, transitionDuration);
1317
- }
1318
- return retVal;
1319
- };
1320
-
1321
- export function graphviz(dot: string, engine: Engine = "dot", _scriptDir: string = scriptDir) {
1322
-
1323
- return gvWorker({
1324
- items: [],
1325
- links: [],
1326
- raw: dot
1327
- }, {
1328
- engine: engine
1329
- });
1330
- }
1
+ import { d3Event, drag as d3Drag, Palette, select as d3Select, Selection, Spacer, SVGGlowFilter, SVGZoomWidget, ToggleButton, Utility, Widget } from "@hpcc-js/common";
2
+ import { IconEx, Icons, render } from "@hpcc-js/react";
3
+ import { Graph2 as GraphCollection, hashSum } from "@hpcc-js/util";
4
+ import { HTMLTooltip } from "@hpcc-js/html";
5
+ import { interpolateNumberArray as d3InterpolateNumberArray } from "d3-interpolate";
6
+ import "d3-transition";
7
+ import { interpolatePath as d3InterpolatePath } from "d3-interpolate-path";
8
+ import { Circle, Dagre, ForceDirected, ForceDirectedAnimated, Graphviz, ILayout, Null } from "./layouts/index";
9
+ import { Options as FDOptions } from "./layouts/forceDirectedWorker";
10
+ import type { VertexBaseProps, EdgeBaseProps, GraphDataProps, HierarchyBase, SubgraphBaseProps } from "./layouts/placeholders";
11
+ import { EdgePlaceholder, SubgraphPlaceholder, VertexPlaceholder, isEdgePlaceholder } from "./layouts/placeholders";
12
+ import { Engine, graphviz as gvWorker } from "./layouts/graphvizWorker";
13
+ import { Tree, RadialTree, Dendrogram, RadialDendrogram } from "./layouts/tree";
14
+
15
+ import "../../src/graph2/graph.css";
16
+
17
+ let scriptDir = (globalThis?.document?.currentScript as HTMLScriptElement)?.src ?? "./dummy.js";
18
+ scriptDir = scriptDir.substring(0, scriptDir.replace(/[?#].*/, "").lastIndexOf("/") + 1);
19
+
20
+ export {
21
+ GraphDataProps,
22
+ SubgraphBaseProps,
23
+ VertexBaseProps,
24
+ EdgeBaseProps,
25
+ HierarchyBase
26
+ };
27
+
28
+ type GraphLayoutType = "Hierarchy" | "DOT" | "Tree" | "Dendrogram" | "RadialTree" | "RadialDendrogram" | "ForceDirected" | "ForceDirected2" | "ForceDirectedHybrid" | "Neato" | "FDP" | "Circle" | "TwoPI" | "Circo" | "None";
29
+ const GraphLayoutTypeSet = ["Hierarchy", "DOT", "Tree", "Dendrogram", "RadialTree", "RadialDendrogram", "ForceDirected", "ForceDirected2", "ForceDirectedHybrid", "Neato", "FDP", "Circle", "TwoPI", "Circo", "None"];
30
+
31
+ function dragStart<V extends VertexBaseProps>(n: VertexPlaceholder<V>) {
32
+ n.fx = n.sx = n.x;
33
+ n.fy = n.sy = n.y;
34
+ }
35
+ function dragTick(n: VertexPlaceholder<VertexBaseProps>, d: VertexPlaceholder<VertexBaseProps>) {
36
+ n.fx = n.sx + d.fx - d.sx;
37
+ n.fy = n.sy + d.fy - d.sy;
38
+ }
39
+ function dragEnd<V extends VertexBaseProps>(n: VertexPlaceholder<V>) {
40
+ n.x = n.fx;
41
+ n.y = n.fy;
42
+ n.fx = n.sx = undefined;
43
+ n.fy = n.sy = undefined;
44
+ }
45
+
46
+ export type RendererT<T> = (props: T, element: SVGGElement) => void;
47
+
48
+ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E extends EdgeBaseProps<V>> extends SVGZoomWidget {
49
+
50
+ protected _centroidFilter: SVGGlowFilter;
51
+
52
+ private _toggleHierarchy = new ToggleButton().faChar("fa-sitemap").tooltip("Hierarchy").on("click", () => this.layoutClick("Hierarchy"));
53
+ private _toggleForceDirected = new ToggleButton().faChar("fa-expand").tooltip("Force Directed").on("click", () => this.layoutClick("ForceDirected"));
54
+ private _toggleForceDirected2 = new ToggleButton().faChar("fa-arrows").tooltip("Spring").on("click", () => this.layoutClick("ForceDirected2"));
55
+ private _toggleCircle = new ToggleButton().faChar("fa-circle-o").tooltip("Circle").on("click", () => this.layoutClick("Circle"));
56
+ private _toggleDot = new ToggleButton().faChar("fa-angle-double-down").tooltip("DOT").on("click", () => this.layoutClick("DOT"));
57
+ private _toggleNeato = new ToggleButton().faChar("fa-sun-o").tooltip("Neato").on("click", () => this.layoutClick("Neato"));
58
+ private _toggleFDP = new ToggleButton().faChar("fa-asterisk").tooltip("FDP").on("click", () => this.layoutClick("FDP"));
59
+ private _toggleTwoPI = new ToggleButton().faChar("fa-bullseye").tooltip("TwoPI").on("click", () => this.layoutClick("TwoPI"));
60
+ private _toggleCirco = new ToggleButton().faChar("fa-cogs").tooltip("Circo").on("click", () => this.layoutClick("Circo"));
61
+ private _toggleT = new ToggleButton().faChar("fa-sitemap fa-rotate-270").tooltip("Tree").on("click", () => this.layoutClick("Tree"));
62
+ private _toggleRT = new ToggleButton().faChar("fa-sun-o").tooltip("Radial Tree").on("click", () => this.layoutClick("RadialTree"));
63
+ private _toggleD = new ToggleButton().faChar("fa-sitemap fa-rotate-270").tooltip("Dendrogram").on("click", () => this.layoutClick("Dendrogram"));
64
+ private _toggleRD = new ToggleButton().faChar("fa-asterisk").tooltip("Radial Dendrogram").on("click", () => this.layoutClick("RadialDendrogram"));
65
+
66
+ protected _graphData = new GraphCollection<VertexPlaceholder<V>, EdgePlaceholder<V, E>, SubgraphPlaceholder<SG>>()
67
+ .idFunc(d => d.id)
68
+ .sourceFunc(e => e.source.id)
69
+ .targetFunc(e => e.target.id)
70
+ .updateFunc((b: VertexPlaceholder<V> | EdgePlaceholder<V, E> | SubgraphPlaceholder<SG>, a: VertexPlaceholder<V> | EdgePlaceholder<V, E> | SubgraphPlaceholder<SG>) => {
71
+ b.props = a.props;
72
+ if (isEdgePlaceholder(a) && isEdgePlaceholder(b)) {
73
+ b.source = a.source;
74
+ b.target = a.target;
75
+ }
76
+ return b;
77
+ })
78
+ ;
79
+
80
+ protected _prevDoClickTime: number = 0;
81
+
82
+ protected _svgDefsAnn: any;
83
+ protected _svgDefsCat: any;
84
+ protected _subgraphG: Selection<SVGGElement, any, SVGGElement, any>;
85
+ protected _edgeG: Selection<SVGGElement, any, SVGGElement, any>;
86
+ protected _vertexG: Selection<SVGGElement, any, SVGGElement, any>;
87
+
88
+ protected _tooltip: HTMLTooltip = new HTMLTooltip();
89
+
90
+ protected _selection = new Utility.Selection(this);
91
+ private _dragHandler = d3Drag<Element, VertexPlaceholder<V>>();
92
+
93
+ protected _catPalette = Palette.ordinal("hpcc10");
94
+ _svgDefs: any;
95
+
96
+ constructor(subgraphRenderer: RendererT<SG>, vertexRenderer: RendererT<V>, edgeRenderer: RendererT<E>) {
97
+ super();
98
+ this._subgraphRenderer = subgraphRenderer;
99
+ this._vertexRenderer = vertexRenderer;
100
+ this._edgeRenderer = edgeRenderer;
101
+ const context = this;
102
+ this._drawStartPos = "origin";
103
+
104
+ const buttons: Widget[] = [
105
+ this._toggleHierarchy,
106
+ this._toggleForceDirected,
107
+ this._toggleForceDirected2,
108
+ this._toggleCircle,
109
+ new Spacer(),
110
+ this._toggleDot,
111
+ this._toggleNeato,
112
+ this._toggleFDP,
113
+ this._toggleTwoPI,
114
+ this._toggleCirco,
115
+ new Spacer(),
116
+ this._toggleT,
117
+ this._toggleRT,
118
+ this._toggleD,
119
+ this._toggleRD,
120
+ new Spacer()
121
+ ];
122
+ this._iconBar.buttons(buttons.concat(this._iconBar.buttons()));
123
+
124
+ this._dragHandler
125
+ .on("start", function (d) {
126
+ if (context.allowDragging()) {
127
+ d3Select(this).classed("grabbed", true);
128
+ dragStart(d);
129
+ Utility.safeRaise(this);
130
+ context.moveVertexPlaceholder(d, false, true);
131
+
132
+ const selection = context.selection();
133
+ const isSelected = context.selected(d.props, selection as V[]);
134
+ if (isSelected) {
135
+ selection
136
+ .filter(v => v.id !== d.props.id)
137
+ .forEach(v => {
138
+ const n = context._graphData.vertex(v.id);
139
+ dragStart(n);
140
+ });
141
+ } else if (context.dragSingleNeighbors()) {
142
+ context._graphData.singleNeighbors(d.id).forEach(n => {
143
+ dragStart(n);
144
+ });
145
+ }
146
+ }
147
+ })
148
+ .on("drag", function (d) {
149
+ if (context.allowDragging()) {
150
+ d.fx = d.sx + context.rproject(d3Event().x - d.sx);
151
+ d.fy = d.sy + context.rproject(d3Event().y - d.sy);
152
+ context._graphData.vertexEdges(d.id).forEach(e => delete e.points);
153
+ context.moveVertexPlaceholder(d, false, true);
154
+ const selection = context.selection();
155
+ const isSelected = context.selected(d.props, selection as V[]);
156
+
157
+ if (isSelected) {
158
+ selection
159
+ .filter(v => v.id !== d.props.id)
160
+ .forEach(v => {
161
+ const n = context._graphData.vertex(v.id);
162
+ dragTick(n, d);
163
+ context.moveVertexPlaceholder(n, false, true);
164
+ });
165
+ } else if (context.dragSingleNeighbors()) {
166
+ context._graphData.singleNeighbors(d.id).forEach(n => {
167
+ dragTick(n, d);
168
+ context.moveVertexPlaceholder(n, false, true);
169
+ });
170
+ }
171
+ }
172
+ })
173
+ .on("end", function (d) {
174
+ let doClick = true;
175
+ if (context.allowDragging()) {
176
+ doClick = Math.abs(d.sx - d.fx) < 1 && Math.abs(d.sy - d.fy) < 1;
177
+ dragEnd(d);
178
+
179
+ const selection = context.selection();
180
+ const isSelected = context.selected(d.props, selection as V[]);
181
+ if (isSelected) {
182
+ selection
183
+ .filter(v => v.id !== d.props.id)
184
+ .forEach(v => {
185
+ const n = context._graphData.vertex(v.id);
186
+ dragEnd(n);
187
+ });
188
+ } else if (context.dragSingleNeighbors()) {
189
+ context._graphData.singleNeighbors(d.id).forEach(dragEnd);
190
+ }
191
+
192
+ d3Select(this).classed("grabbed", false);
193
+ }
194
+ if (doClick) {
195
+ const event = d3Event();
196
+ context._selection.click({
197
+ _id: String(d.id),
198
+ element: () => d.element
199
+ }, event.sourceEvent);
200
+ context.selectionChanged();
201
+ const selected = d.element.classed("selected");
202
+ const eventOrigin = context.resolveEventOrigin();
203
+ if (event?.sourceEvent?.button === 2) {
204
+ context.vertex_contextmenu(d.props.origData || d.props, "", selected, eventOrigin);
205
+ } else {
206
+ context.vertex_click(d.props.origData || d.props, "", selected, eventOrigin);
207
+ }
208
+ const doClickTime = Date.now();
209
+ if (doClickTime - context._prevDoClickTime < context.doubleClickMaxDelay()) {
210
+ context.vertex_dblclick(d.props.origData || d.props, "", selected, eventOrigin);
211
+ }
212
+ context._prevDoClickTime = doClickTime;
213
+ }
214
+ })
215
+ .filter(() => true)
216
+ ;
217
+ }
218
+
219
+ resolveEventOrigin(): { origin: string, data?: SG | V | E } {
220
+ const d3evt = d3Event();
221
+ const eventPath = d3evt?.sourceEvent?.path ?? d3evt?.sourceEvent?.composedPath() ?? d3evt?.path ?? d3evt?.composedPath();
222
+ const element = eventPath?.find(n => n?.hasAttribute && n?.hasAttribute("data-click"));
223
+ const origin = element ? element.getAttribute("data-click") : "";
224
+ const dataStr = element ? element.getAttribute("data-click-data") : "";
225
+ let data = undefined;
226
+ if (dataStr) {
227
+ try {
228
+ data = JSON.parse(dataStr);
229
+ } catch (e) {
230
+ console.warn("Unexpected annotation data:", dataStr);
231
+ }
232
+ }
233
+
234
+ return {
235
+ origin,
236
+ data
237
+ };
238
+ }
239
+
240
+ iconBarButtons(): Widget[] {
241
+ return this._iconBar.buttons();
242
+ }
243
+
244
+ protected _categories: IconEx[] = [];
245
+ categories(): IconEx[];
246
+ categories(_: IconEx[]): this;
247
+ categories(_?: IconEx[]): IconEx[] | this {
248
+ if (_ === void 0) return this._categories;
249
+ this._categories = _;
250
+ return this;
251
+ }
252
+
253
+ protected _annotations: IconEx[] = [];
254
+ annotations(): IconEx[];
255
+ annotations(_: IconEx[]): this;
256
+ annotations(_?: IconEx[]): IconEx[] | this {
257
+ if (_ === void 0) return this._annotations;
258
+ this._annotations = _;
259
+ return this;
260
+ }
261
+
262
+ private _origData: GraphDataProps<SG, V, E> = {
263
+ subgraphs: [],
264
+ vertices: [],
265
+ edges: [],
266
+ hierarchy: []
267
+ };
268
+ data(): GraphDataProps<SG, V, E>;
269
+ data(_: GraphDataProps<SG, V, E>, merge?: boolean): this;
270
+ data(_?: GraphDataProps<SG, V, E>, merge?: boolean): GraphDataProps<SG, V, E> | this {
271
+ if (_ === void 0) return this._origData;
272
+ this._origData = _;
273
+
274
+ this._graphData.mergeSubgraphs((_.subgraphs || []).map(sg => ({
275
+ id: sg.id,
276
+ props: sg
277
+ })));
278
+
279
+ this._graphData.mergeVertices((_.vertices || []).map(v => ({
280
+ id: v.id,
281
+ centroid: v.centroid,
282
+ props: v
283
+ })));
284
+
285
+ this._graphData.mergeEdges(
286
+ (_.edges || [])
287
+ .filter(e => {
288
+ return this._graphData.vertexExists(e.source.id) && this._graphData.vertexExists(e.target.id);
289
+ })
290
+ .map(e => ({
291
+ id: e.id,
292
+ props: e,
293
+ source: this._graphData.vertex(e.source.id),
294
+ target: this._graphData.vertex(e.target.id)
295
+ }))
296
+ );
297
+
298
+ this._graphData.clearParents();
299
+ (_.hierarchy ? _.hierarchy : []).forEach(h => {
300
+ if (this._graphData.subgraphExists(h.child.id)) {
301
+ this._graphData.subgraphParent(h.child.id, h.parent.id);
302
+ } else if (this._graphData.vertexExists(h.child.id)) {
303
+ this._graphData.vertexParent(h.child.id, h.parent.id);
304
+ }
305
+ });
306
+
307
+ return this;
308
+ }
309
+
310
+ selected(vertex: V, _?: Array<V>): boolean {
311
+ return (_ || this.selection()).some(n => n.id === vertex.id);
312
+ }
313
+
314
+ selection(_: Array<V | SG | E>): this;
315
+ selection(): Array<V | SG | E>;
316
+ selection(_?: Array<V | SG | E>): Array<SG | V | E> | this {
317
+ if (!arguments.length) return this._selection.get().map(item => this._graphData.item(item._id).props);
318
+ this._selection.set(_.map(item => {
319
+ const vp = this._graphData.item(item.id);
320
+ return {
321
+ _id: String(vp.id),
322
+ element: () => vp.element
323
+ };
324
+ }));
325
+ return this;
326
+ }
327
+
328
+ graphData(): GraphCollection<VertexPlaceholder<V>, EdgePlaceholder<V, E>> {
329
+ return this._graphData;
330
+ }
331
+
332
+ resetLayout() {
333
+ delete this._prevLayout;
334
+ return this;
335
+ }
336
+
337
+ layoutRunning() {
338
+ return this._layoutAlgo && this._layoutAlgo.running();
339
+ }
340
+
341
+ protected _layoutAlgo: ILayout = new Null(this);
342
+ layoutAlgo(layout: ILayout) {
343
+ if (this._layoutAlgo) {
344
+ this._layoutAlgo.stop();
345
+ }
346
+ this._layoutAlgo = layout;
347
+ return this._layoutAlgo.start().then(() => {
348
+ this.updateIconBar();
349
+ if (this.applyScaleOnLayout()) {
350
+ // Wait for any transitions to finish ---
351
+ setTimeout(() => {
352
+ this.zoomToFit(this.transitionDuration());
353
+ }, this.transitionDuration());
354
+ }
355
+ });
356
+ }
357
+
358
+ layoutClick(layout: GraphLayoutType) {
359
+ if (this.layoutRunning()) {
360
+ this._layoutAlgo.stop();
361
+ this.updateIconBar();
362
+ } else {
363
+ delete this._prevLayout;
364
+ this
365
+ .layout(layout)
366
+ .render()
367
+ ;
368
+ }
369
+ }
370
+
371
+ updateIconBarItem(tb: ToggleButton, tbLayout: GraphLayoutType) {
372
+ const layout = this.layout();
373
+ const running = this._layoutAlgo && this._layoutAlgo.running();
374
+ tb.enabled(!running || layout === tbLayout).selected(running && layout === tbLayout).render();
375
+ }
376
+
377
+ updateIconBar() {
378
+ super.updateIconBar();
379
+ this.updateIconBarItem(this._toggleHierarchy, "Hierarchy");
380
+ this.updateIconBarItem(this._toggleDot, "DOT");
381
+ this.updateIconBarItem(this._toggleForceDirected, "ForceDirected");
382
+ this.updateIconBarItem(this._toggleNeato, "Neato");
383
+ this.updateIconBarItem(this._toggleFDP, "FDP");
384
+ this.updateIconBarItem(this._toggleForceDirected2, "ForceDirected2");
385
+ this.updateIconBarItem(this._toggleCircle, "Circle");
386
+ this.updateIconBarItem(this._toggleTwoPI, "TwoPI");
387
+ this.updateIconBarItem(this._toggleCirco, "Circo");
388
+ this.updateIconBarItem(this._toggleT, "Tree");
389
+ this.updateIconBarItem(this._toggleRT, "RadialTree");
390
+ this.updateIconBarItem(this._toggleD, "Dendrogram");
391
+ this.updateIconBarItem(this._toggleRD, "RadialDendrogram");
392
+ }
393
+
394
+ getNeighborMap(vertex: VertexPlaceholder<V>) {
395
+ const vertices = {};
396
+ const edges = {};
397
+
398
+ if (vertex) {
399
+ const nedges = this._graphData.vertexEdges(vertex.id);
400
+ for (let i = 0; i < nedges.length; ++i) {
401
+ const edge = this._graphData.edge(nedges[i].id);
402
+ edges[edge.id] = edge;
403
+ if (edge.source.id !== vertex.id) {
404
+ vertices[edge.source.id] = edge.source;
405
+ }
406
+ if (edge.target.id !== vertex.id) {
407
+ vertices[edge.target.id] = edge.target;
408
+ }
409
+ }
410
+ }
411
+
412
+ return {
413
+ vertices,
414
+ edges
415
+ };
416
+ }
417
+
418
+ centerOnItem(id: string) {
419
+ const item = this._graphData.item(id);
420
+ let x;
421
+ let y;
422
+ if (this._graphData.isSubgraph(item) || this._graphData.isVertex(item)) {
423
+ x = item.x;
424
+ y = item.y;
425
+ }
426
+ if (this._graphData.isEdge(item)) {
427
+ [x, y] = [0, 0]; // center(item.points);
428
+ }
429
+ if (x !== undefined && y !== undefined) {
430
+ const bbox = item.element.node().getBBox();
431
+ if (this._graphData.isVertex(item)) {
432
+ const proj = this.projectPlacholder(item);
433
+ x = proj.x;
434
+ y = proj.y;
435
+ } else if (this._graphData.isSubgraph(item)) {
436
+ x = this.project(item.x);
437
+ y = this.project(item.y);
438
+ }
439
+ const deltaX = bbox.x + bbox.width / 2;
440
+ const deltaY = bbox.y + bbox.height / 2;
441
+ const itemBBox = {
442
+ x: x + deltaX - bbox.width / 2,
443
+ y: y + deltaY - bbox.height / 2,
444
+ width: bbox.width,
445
+ height: bbox.height
446
+ };
447
+ this.centerOnBBox(itemBBox);
448
+ }
449
+ }
450
+
451
+ hideVertex(id: string): this {
452
+ const item = this._graphData.item(id);
453
+ if (this._graphData.isVertex(item)) {
454
+ item.props.hidden = true;
455
+ }
456
+ return this;
457
+ }
458
+
459
+ showVertex(id: string): this {
460
+ const item = this._graphData.item(id);
461
+ if (this._graphData.isVertex(item)) {
462
+ item.props.hidden = false;
463
+ }
464
+ return this;
465
+ }
466
+
467
+ protected highlight = {
468
+ zoom: 1.1,
469
+ opacity: 0.33,
470
+ edge: "1.25px"
471
+ };
472
+
473
+ highlightVerticies(vertexMap?: { [id: string]: boolean }) {
474
+ const context = this;
475
+ const vertexElements = this._vertexG.selectAll<SVGGElement, VertexPlaceholder<V>>(".graphVertex");
476
+ const forceLabelOnHighlight = !context.showVertexLabels() && context.showVertexLabelsOnHighlight();
477
+ vertexElements
478
+ .classed("graphVertex-highlighted", d => !vertexMap || vertexMap[d.id])
479
+ .style("filter", d => vertexMap && vertexMap[d.id] ? "url(#" + this.id() + "_glow)" : null)
480
+ .each(function (d) {
481
+ if (forceLabelOnHighlight) {
482
+ const props = context.calcProps(
483
+ {
484
+ showLabel: !!(vertexMap && vertexMap[d.id]),
485
+ ...context.vertexMapper(d.props, d.props.origData)
486
+ }
487
+ );
488
+ context._vertexRenderer(props, this);
489
+ }
490
+ })
491
+ .transition().duration(this.transitionDuration())
492
+ .on("end", function (d) {
493
+ if (vertexMap && vertexMap[d.id]) {
494
+ if (d.element.node() && d.element.node().parentNode) {
495
+ d.element.node().parentNode.appendChild(d.element.node());
496
+ }
497
+ }
498
+ })
499
+ .style("opacity", function (d) {
500
+ if (d.props.hidden) return 0;
501
+ if (!vertexMap || vertexMap[d.id]) {
502
+ return 1;
503
+ }
504
+ return context.highlight.opacity;
505
+ })
506
+ ;
507
+ return this;
508
+ }
509
+
510
+ highlightEdges(edgeMap?: { [id: string]: boolean }) {
511
+ const context = this;
512
+ const edgeElements = this._edgeG.selectAll<SVGGElement, EdgePlaceholder<V, E>>(".graphEdge");
513
+ edgeElements
514
+ .classed("graphEdge-highlighted", d => !edgeMap || edgeMap[d.id])
515
+ .style("stroke-width", function (o) {
516
+ if (edgeMap && edgeMap[o.id]) {
517
+ return context.highlight.edge;
518
+ }
519
+ return context.edgeStrokeWidth() + "px";
520
+ }).transition().duration(this.transitionDuration())
521
+ .style("opacity", function (o) {
522
+ if (o.source.props.hidden || o.target.props.hidden) return 0;
523
+ if (!edgeMap || edgeMap[o.id]) {
524
+ return 1;
525
+ }
526
+ return context.highlight.opacity;
527
+ })
528
+ ;
529
+ return this;
530
+ }
531
+
532
+ highlightVertex(_element, d?: VertexPlaceholder<V>) {
533
+ if (this.highlightOnMouseOverVertex()) {
534
+ if (d) {
535
+ const highlight = this.getNeighborMap(d);
536
+ highlight.vertices[d.id] = d;
537
+ this.highlightVerticies(highlight.vertices);
538
+ this.highlightEdges(highlight.edges);
539
+ } else {
540
+ this.highlightVerticies();
541
+ this.highlightEdges();
542
+ }
543
+ }
544
+ }
545
+
546
+ highlightEdge(_element, d?: EdgePlaceholder<V, E>) {
547
+ if (this.highlightOnMouseOverEdge()) {
548
+ if (d) {
549
+ const vertices = {};
550
+ vertices[d.source.id] = d.source;
551
+ vertices[d.target.id] = d.target;
552
+ const edges = {};
553
+ edges[d.id] = d;
554
+ this.highlightVerticies(vertices);
555
+ this.highlightEdges(edges);
556
+ } else {
557
+ this.highlightVerticies();
558
+ this.highlightEdges();
559
+ }
560
+ }
561
+ }
562
+
563
+ moveSubgraphPlaceholder(sp: SubgraphPlaceholder<SG>, transition: boolean): this {
564
+ const context = this;
565
+ const x = this.project(sp.x);
566
+ const y = this.project(sp.y);
567
+ const width = this.project(sp.props.width, true);
568
+ const height = this.project(sp.props.height, true);
569
+ if (sp.element) {
570
+ sp.element.transition().duration(transition ? this.transitionDuration() : 0)
571
+ .attr("transform", `translate(${x} ${y})`)
572
+ .each(function (d) {
573
+ context._subgraphRenderer({ ...d.props, width, height }, this);
574
+ })
575
+ ;
576
+ }
577
+ return this;
578
+ }
579
+
580
+ moveEdgePlaceholder(ep: EdgePlaceholder<V, E>, transition: boolean): this {
581
+ const edgeLayout = {
582
+ ...this._layoutAlgo.edgePath(ep, this.edgeArcDepth())
583
+ };
584
+ const context = this;
585
+ if (this._edgeRenderer && ep.element) {
586
+ const previousEdgeLayout = (ep as any).previousEdgeLayout ?? edgeLayout;
587
+ (ep as any).previousEdgeLayout = edgeLayout;
588
+ const pathInterpolator = d3InterpolatePath(previousEdgeLayout.path, edgeLayout.path);
589
+ const labelPosInterpolator = d3InterpolateNumberArray(previousEdgeLayout.labelPos, edgeLayout.labelPos);
590
+ ep.element.transition().duration(transition ? this.transitionDuration() : 0)
591
+ .tween("some.path", function () {
592
+ return function (t) {
593
+ const updated = {
594
+ path: pathInterpolator(t),
595
+ labelPos: labelPosInterpolator(t),
596
+ strokeWidth: ep.props.strokeWidth ?? context.edgeStrokeWidth(),
597
+ color: ep.props.stroke ?? context.edgeColor()
598
+ };
599
+ context._edgeRenderer({ ...edgeLayout, ...ep.props, ...updated }, ep.element.node());
600
+ };
601
+ });
602
+ }
603
+ return this;
604
+ }
605
+
606
+ moveVertexPlaceholder(vp: VertexPlaceholder<V>, transition: boolean, moveNeighbours: boolean): this {
607
+ const { x, y } = this.projectPlacholder(vp);
608
+ vp.element && (transition ? vp.element.transition().duration(this.transitionDuration()) as unknown as Selection<SVGPathElement, EdgePlaceholder<V, E>, SVGGElement, any> : vp.element)
609
+ .attr("transform", `translate(${x} ${y})`)
610
+ ;
611
+ if (moveNeighbours) {
612
+ this._graphData.vertexEdges(vp.id).forEach(e => this.moveEdgePlaceholder(e, transition));
613
+ }
614
+ return this;
615
+ }
616
+
617
+ moveSubgraphs(transition: boolean): this {
618
+ this._graphData.allSubgraphs().forEach(s => this.moveSubgraphPlaceholder(s, transition));
619
+ return this;
620
+ }
621
+
622
+ moveEdges(transition: boolean): this {
623
+ this._graphData.allEdges().forEach(e => this.moveEdgePlaceholder(e, transition));
624
+ return this;
625
+ }
626
+
627
+ moveVertices(transition: boolean): this {
628
+ this._graphData.allVertices().forEach(v => this.moveVertexPlaceholder(v, transition, false));
629
+ return this;
630
+ }
631
+
632
+ project(pos: number, clip: boolean = false) {
633
+ const rf = 10;
634
+ pos = pos !== undefined ? pos : 0;
635
+ let scale = this._transformScale;
636
+ if (clip) {
637
+ if (this._transformScale > this.maxScale() + (this._transformScale - this.maxScale()) / 2) {
638
+ scale = this.maxScale() + (this._transformScale - this.maxScale()) / 2;
639
+ } else if (this._transformScale < this.minScale() - (this._transformScale - this.minScale()) / 13) {
640
+ // scale = this.minScale() - (this._transformScale - this.minScale()) / 13;
641
+ }
642
+ }
643
+ return Math.round(pos * scale * rf) / rf;
644
+ }
645
+
646
+ rproject(pos: number) {
647
+ const rf = 10;
648
+ pos = pos !== undefined ? pos : 0;
649
+ return Math.round(pos / this._transformScale * rf) / rf;
650
+ }
651
+
652
+ projectPlacholder(vp: VertexPlaceholder<V>) {
653
+ return {
654
+ x: this.project(vp.fx !== undefined ? vp.fx : vp.x),
655
+ y: this.project(vp.fy !== undefined ? vp.fy : vp.y)
656
+ };
657
+ }
658
+
659
+ categoryID(id: string | number, prefix: "cat" | "ann" = "cat"): string {
660
+ return id === undefined || id === "" ? "" : `${prefix}${this.id()}_${id}`;
661
+ }
662
+
663
+ updateCategories() {
664
+ render(Icons, {
665
+ icons: this._categories.map((c): IconEx => ({
666
+ ...c,
667
+ id: this.categoryID(c.id),
668
+ fill: c.fill || "transparent",
669
+ imageCharFill: c.imageCharFill || this._catPalette(c.id)
670
+ }))
671
+ }, this._svgDefsCat.node());
672
+ }
673
+
674
+ updateAnnotations() {
675
+ render(Icons, {
676
+ icons: this._annotations.map((c): IconEx => ({
677
+ ...c,
678
+ id: this.categoryID(c.id, "ann"),
679
+ shape: c.shape || "square",
680
+ height: c.height || 12,
681
+ fill: c.fill || this._catPalette(c.id)
682
+ }))
683
+ }, this._svgDefsAnn.node());
684
+ }
685
+
686
+ private _edgeRenderer: RendererT<E>;
687
+ edgeRenderer(): RendererT<E>;
688
+ edgeRenderer(_: RendererT<E>): this;
689
+ edgeRenderer(_?: RendererT<E>): this | RendererT<E> {
690
+ if (!arguments.length) return this._edgeRenderer;
691
+ this._edgeRenderer = _;
692
+ return this;
693
+ }
694
+
695
+ updateEdges(): this {
696
+ const context = this;
697
+ this._edgeG.selectAll(".graphEdge")
698
+ .data(this._graphData.allEdges(), (d: EdgePlaceholder<V, E>) => d.id)
699
+ .join(
700
+ enter => enter.append("g")
701
+ .attr("class", "graphEdge")
702
+ .on("click.selectionBag", function (d) {
703
+ context._selection.click({
704
+ _id: String(d.id),
705
+ element: () => d.element
706
+ }, d3Event());
707
+ context.selectionChanged();
708
+ })
709
+ .on("click", function (this: SVGElement, d) {
710
+ const selected = d.element.classed("selected");
711
+ context.edge_click(d.props.origData || d.props, "", selected);
712
+ })
713
+ .on("mouseover", function (d) {
714
+ Utility.safeRaise(this);
715
+ context.edge_mouseover(d3Select(this), d);
716
+ })
717
+ .on("mouseout", function (d) {
718
+ context.edge_mouseout(d3Select(this), d);
719
+ })
720
+ .each(function (d) {
721
+ d.element = d3Select(this);
722
+ if (!context._edgeRenderer) {
723
+ d.elementPath = d.element.append("path");
724
+ d.elementText = d.element.append("text")
725
+ .attr("text-anchor", "middle")
726
+ ;
727
+ }
728
+ })
729
+ ,
730
+ update => update
731
+ .attr("opacity", d => d.source.props.hidden || d.target.props.hidden ? 0 : 1)
732
+ .classed("hide-text", !context.showEdgeLabels())
733
+ ,
734
+ exit => exit
735
+ .each(function (d) {
736
+ delete d.element;
737
+ })
738
+ .remove()
739
+ )
740
+ ;
741
+ return this;
742
+ }
743
+
744
+ private _vertexRenderer: RendererT<V>;
745
+ vertexRenderer(): RendererT<V>;
746
+ vertexRenderer(_: RendererT<V>): this;
747
+ vertexRenderer(_?: RendererT<V>): this | RendererT<V> {
748
+ if (!arguments.length) return this._vertexRenderer;
749
+ this._vertexRenderer = _;
750
+ return this;
751
+ }
752
+
753
+ vertexMapper(props: V, origRow: V): V {
754
+ return {
755
+ ...props,
756
+ categoryID: this.categoryID(props.categoryID),
757
+ annotationIDs: props.annotationIDs ? props.annotationIDs.map(a => this.categoryID(a, "ann")) : []
758
+ };
759
+ }
760
+
761
+ updateVertices(): this {
762
+ const context = this;
763
+ this._vertexG.selectAll(".graphVertex")
764
+ .data(this._graphData.allVertices(), (d: VertexPlaceholder<V>) => d.id)
765
+ .join(
766
+ enter => enter.append("g")
767
+ .attr("class", "graphVertex")
768
+ .on("dblclick", function (this: SVGElement, d) {
769
+ d3Event().stopPropagation();
770
+ })
771
+ .on("contextmenu", function (this: SVGElement, d) {
772
+ d3Event().preventDefault();
773
+ })
774
+ .on("mousein", function (d) {
775
+ Utility.safeRaise(this);
776
+ context.highlightVertex(d3Select(this), d);
777
+ const selected = d.element.classed("selected");
778
+ const eventOrigin = context.resolveEventOrigin();
779
+ context.vertex_mousein(d.props.origData || d.props, "", selected, eventOrigin);
780
+ })
781
+ .on("mouseover", function (d) {
782
+ Utility.safeRaise(this);
783
+ context.highlightVertex(d3Select(this), d);
784
+ const selected = d.element.classed("selected");
785
+ if (d.props.tooltip) {
786
+ context._tooltip
787
+ .tooltipHTML(context.tooltipHTML.bind(context))
788
+ .triggerElement(d.element)
789
+ .tooltipWidth(context.tooltipWidth())
790
+ .tooltipHeight(context.tooltipHeight())
791
+ .enablePointerEvents(context.enableTooltipPointerEvents())
792
+ .closeDelay(context.tooltipCloseDelay())
793
+ .direction("n")
794
+ .data(d)
795
+ .visible(true)
796
+ .render()
797
+ ;
798
+ }
799
+ const eventOrigin = context.resolveEventOrigin();
800
+ context.vertex_mouseover(d.props.origData || d.props, "", selected, eventOrigin);
801
+ })
802
+ .on("mouseout", function (d) {
803
+ context.highlightVertex(null, null);
804
+ const selected = d.element.classed("selected");
805
+ const eventOrigin = context.resolveEventOrigin();
806
+ context.vertex_mouseout(d.props.origData || d.props, "", selected, eventOrigin);
807
+ if (d.props.tooltip) {
808
+ context._tooltip.mouseout();
809
+ }
810
+ })
811
+ .call(this._dragHandler)
812
+ .each(function (d) {
813
+ d.element = d3Select(this);
814
+ }),
815
+ update => update,
816
+ exit => exit
817
+ .each(function (d) {
818
+ delete d.element;
819
+ })
820
+ .remove()
821
+ )
822
+ .classed("centroid", d => d.props.centroid)
823
+ .attr("opacity", d => d.props.hidden ? 0 : 1)
824
+ .attr("filter", d => d.props.centroid ? "url(#" + this.id() + "_glow)" : null)
825
+ .each(function (this: SVGGElement, d) {
826
+ const props = context.calcProps(
827
+ {
828
+ showLabel: context.showVertexLabels(),
829
+ ...context.vertexMapper(d.props, d.props.origData)
830
+ }
831
+ );
832
+ context._vertexRenderer(props, this);
833
+ })
834
+ ;
835
+ return this;
836
+ }
837
+
838
+ calcProps(props: V): V {
839
+ return { ...props };
840
+ }
841
+
842
+ hasSubgraphs() {
843
+ switch (this.layout()) {
844
+ case "DOT":
845
+ case "Hierarchy":
846
+ return true;
847
+ }
848
+ return false;
849
+ }
850
+
851
+ private _subgraphRenderer: RendererT<SG>;
852
+ subgraphRenderer(): RendererT<SG>;
853
+ subgraphRenderer(_: RendererT<SG>): this;
854
+ subgraphRenderer(_?: RendererT<SG>): this | RendererT<SG> {
855
+ if (!arguments.length) return this._subgraphRenderer;
856
+ this._subgraphRenderer = _;
857
+ return this;
858
+ }
859
+
860
+ updateSubgraphs(): this {
861
+ const context = this;
862
+ this._subgraphG.selectAll(".subgraphPlaceholder")
863
+ .data(this.hasSubgraphs() ? this._graphData.allSubgraphs() : [], (d: SubgraphPlaceholder<SG>) => d.id)
864
+ .join(
865
+ enter => enter.append("g")
866
+ .attr("class", "subgraphPlaceholder")
867
+ .on("click.selectionBag", function (d) {
868
+ context._selection.click({
869
+ _id: String(d.id),
870
+ element: () => d.element
871
+ }, d3Event());
872
+ context.selectionChanged();
873
+ })
874
+ .on("click", function (this: SVGElement, d) {
875
+ const selected = d.element.classed("selected");
876
+ context.subgraph_click(d.props.origData || d.props, "", selected);
877
+ })
878
+ .on("mouseover", function () {
879
+ Utility.safeRaise(this);
880
+ })
881
+ .each(function (d) {
882
+ d.element = d3Select(this);
883
+ })
884
+ ,
885
+ update => update,
886
+ exit => exit
887
+ .each(function (d) {
888
+ delete d.element;
889
+ })
890
+ .transition()
891
+ .style("opacity", 0)
892
+ .remove()
893
+ )
894
+ .each(function (d) {
895
+ context.moveSubgraphPlaceholder(d, false);
896
+ })
897
+ ;
898
+ return this;
899
+ }
900
+
901
+ enter(domNode, element) {
902
+ super.enter(domNode, element);
903
+
904
+ const svg = this.locateSVGNode(domNode);
905
+ this._svgDefs = d3Select(svg).select<SVGDefsElement>("defs");
906
+ this._centroidFilter = new SVGGlowFilter(this._svgDefs, this._id + "_glow");
907
+
908
+ this._svgDefsCat = this._svgDefs.append("g");
909
+ this._svgDefsAnn = this._svgDefs.append("g");
910
+ this._subgraphG = this._renderElement.append("g");
911
+ this._edgeG = this._renderElement.append("g");
912
+ this._vertexG = this._renderElement.append("g");
913
+
914
+ this._tooltip.target(domNode);
915
+
916
+ this.on("startMarqueeSelection", () => {
917
+ }).on("updateMarqueeSelection", rect => {
918
+ const vertices: VertexPlaceholder<V>[] = this._graphData.allVertices().filter(v => v.x >= rect.x && v.x <= rect.x + rect.width && v.y >= rect.y && v.y <= rect.y + rect.height);
919
+ this.selection(vertices.map(v => v.props));
920
+ }).on("endMarqueeSelection", () => {
921
+ this.selectionChanged();
922
+ });
923
+ }
924
+
925
+ protected forceDirectedOptions(): FDOptions {
926
+ return {
927
+ alpha: this.forceDirectedAlpha(),
928
+ alphaMin: this.forceDirectedAlphaMin(),
929
+ alphaDecay: this.forceDirectedAlphaDecay(),
930
+ velocityDecay: this.forceDirectedVelocityDecay(),
931
+ repulsionStrength: this.forceDirectedRepulsionStrength(),
932
+ iterations: this.forceDirectedIterations(),
933
+ linkDistance: this.forceDirectedLinkDistance(),
934
+ linkStrength: this.forceDirectedLinkStrength(),
935
+ pinCentroid: this.forceDirectedPinCentroid(),
936
+ forceStrength: this.forceDirectedForceStrength(),
937
+ distanceMin: this.forceDirectedMinDistance(),
938
+ distanceMax: this.forceDirectedMaxDistance(),
939
+ };
940
+ }
941
+
942
+ private layoutOptions(layout: GraphLayoutType) {
943
+ switch (layout) {
944
+ case "ForceDirected":
945
+ case "ForceDirected2":
946
+ case "ForceDirectedHybrid":
947
+ return this.forceDirectedOptions();
948
+ case "Hierarchy":
949
+ return {
950
+ rankdir: this.hierarchyRankDirection(),
951
+ nodesep: this.hierarchyNodeSeparation(),
952
+ edgesep: this.hierarchyEdgeSeparation(),
953
+ ranksep: this.hierarchyRankSeparation(),
954
+ digraph: this.hierarchyDigraph()
955
+ };
956
+ case "Tree":
957
+ case "Dendrogram":
958
+ return { rankdir: this.treeRankDirection() };
959
+ case "DOT":
960
+ case "Neato":
961
+ case "FDP":
962
+ case "TwoPI":
963
+ case "Circo":
964
+ return new URL(this.wasmFolder() || scriptDir, document.baseURI).href;
965
+ case "None":
966
+ case "Circle":
967
+ case "RadialTree":
968
+ case "RadialDendrogram":
969
+ default:
970
+ return undefined;
971
+ }
972
+ }
973
+
974
+ private _prevLayout: string;
975
+ updateLayout() {
976
+ const layout = this.layout();
977
+ const options: any = this.layoutOptions(layout);
978
+ const hash = hashSum([layout, options]);
979
+ if (this._prevLayout !== hash) {
980
+ this._prevLayout = hash;
981
+ switch (layout) {
982
+ case "None":
983
+ this.layoutAlgo(new Null(this));
984
+ break;
985
+ case "Circle":
986
+ this.layoutAlgo(new Circle(this));
987
+ break;
988
+ case "ForceDirected":
989
+ this.layoutAlgo(new ForceDirected(this, options));
990
+ break;
991
+ case "ForceDirected2":
992
+ this.layoutAlgo(new ForceDirectedAnimated(this, options));
993
+ break;
994
+ case "ForceDirectedHybrid":
995
+ this.layoutAlgo(new ForceDirected(this, options)).then(() => {
996
+ this.layoutAlgo(new ForceDirectedAnimated(this, options));
997
+ });
998
+ break;
999
+ case "Hierarchy":
1000
+ this.layoutAlgo(new Dagre(this, options));
1001
+ break;
1002
+ case "DOT":
1003
+ this.layoutAlgo(new Graphviz(this, "dot", options));
1004
+ break;
1005
+ case "Tree":
1006
+ this.layoutAlgo(new Tree(this, options));
1007
+ break;
1008
+ case "RadialTree":
1009
+ this.layoutAlgo(new RadialTree(this));
1010
+ break;
1011
+ case "Dendrogram":
1012
+ this.layoutAlgo(new Dendrogram(this, options));
1013
+ break;
1014
+ case "RadialDendrogram":
1015
+ this.layoutAlgo(new RadialDendrogram(this));
1016
+ break;
1017
+ case "Neato":
1018
+ this.layoutAlgo(new Graphviz(this, "neato", options));
1019
+ break;
1020
+ case "FDP":
1021
+ this.layoutAlgo(new Graphviz(this, "fdp", options));
1022
+ break;
1023
+ case "TwoPI":
1024
+ this.layoutAlgo(new Graphviz(this, "twopi", options));
1025
+ break;
1026
+ case "Circo":
1027
+ this.layoutAlgo(new Graphviz(this, "circo", options));
1028
+ break;
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ update(domNode, element) {
1034
+ super.update(domNode, element);
1035
+
1036
+ this._renderElement.classed("allowDragging", this.allowDragging());
1037
+
1038
+ this.updateCategories();
1039
+ this.updateAnnotations();
1040
+
1041
+ this.updateSubgraphs();
1042
+ this.updateVertices();
1043
+ this.updateEdges();
1044
+ this.moveEdges(false);
1045
+
1046
+ this.updateLayout();
1047
+
1048
+ this.updateIconBar();
1049
+ }
1050
+
1051
+ exit(domNode, element) {
1052
+ super.exit(domNode, element);
1053
+ this._tooltip.target(null);
1054
+ }
1055
+
1056
+ render(callback?: (w: Widget) => void): this {
1057
+ this.progress("start");
1058
+ super.render(w => {
1059
+ this.progress("stop");
1060
+ if (callback) {
1061
+ callback(w);
1062
+ }
1063
+ });
1064
+ return this;
1065
+ }
1066
+
1067
+ private _prevWidth;
1068
+ private _prevHeight;
1069
+ private _prevTransformScale;
1070
+ private _transformScale = 1;
1071
+ zoomed(transform) {
1072
+ super.zoomed(transform);
1073
+ const { width, height } = this.size();
1074
+
1075
+ if (transform.k < this.minScale()) {
1076
+ this._edgeG.attr("transform", `scale(${this.minScale() / transform.k})`);
1077
+ this._subgraphG.attr("transform", `scale(${this.minScale() / transform.k})`);
1078
+ this._vertexG.attr("transform", `scale(${this.minScale() / transform.k})`);
1079
+ this._transformScale = transform.k / this.minScale();
1080
+ } else if (transform.k > this.maxScale()) {
1081
+ this._edgeG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1082
+ this._subgraphG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1083
+ this._vertexG.attr("transform", `scale(${this.maxScale() / transform.k})`);
1084
+ this._transformScale = transform.k / this.maxScale();
1085
+ } else {
1086
+ this._transformScale = 1;
1087
+ this._edgeG.attr("transform", null);
1088
+ this._subgraphG.attr("transform", null);
1089
+ this._vertexG.attr("transform", null);
1090
+ }
1091
+
1092
+ if (this._prevTransformScale !== this._transformScale ||
1093
+ this._prevWidth !== width ||
1094
+ this._prevHeight !== height) {
1095
+ this._prevTransformScale = this._transformScale;
1096
+ this._prevWidth = width;
1097
+ this._prevHeight = height;
1098
+ this
1099
+ .moveSubgraphs(false)
1100
+ .moveVertices(false)
1101
+ .moveEdges(false)
1102
+ ;
1103
+ }
1104
+ }
1105
+
1106
+ // Events ---
1107
+ centroids(): VertexPlaceholder<V>[] {
1108
+ return this._graphData.allVertices().filter(vp => !!vp.props.centroid);
1109
+ }
1110
+
1111
+ selectionChanged() {
1112
+ if (this.highlightSelectedPathToCentroid()) {
1113
+ const highlightedVertices = {};
1114
+ this.centroids().forEach(centroid => {
1115
+ this.selection().forEach(selection => {
1116
+ const { ids, len } = this._graphData.dijkstra(centroid.id, selection.id);
1117
+ if (len) {
1118
+ ids.forEach(id => {
1119
+ highlightedVertices[id] = true;
1120
+ });
1121
+ }
1122
+ });
1123
+ });
1124
+ this._edgeG.selectAll(".graphEdge")
1125
+ // .classed("shortest-path", d => highlightedEdges[d.id()] === true)
1126
+ ;
1127
+ }
1128
+ }
1129
+
1130
+ tooltipHTML(data) {
1131
+ return data.props.tooltip;
1132
+ }
1133
+
1134
+ subgraph_click(row, _col, sel) {
1135
+ }
1136
+
1137
+ vertex_click(row, _col, sel, data) {
1138
+ }
1139
+
1140
+ vertex_dblclick(row, _col, sel, data) {
1141
+ }
1142
+
1143
+ vertex_contextmenu(row, _col, sel, data) {
1144
+ }
1145
+
1146
+ vertex_mousein(row, _col, sel, data) {
1147
+ }
1148
+
1149
+ vertex_mouseover(row, _col, sel, data) {
1150
+ }
1151
+
1152
+ vertex_mouseout(row, _col, sel, data) {
1153
+ }
1154
+
1155
+ edge_click(row, _col, sel) {
1156
+ }
1157
+
1158
+ edge_mouseover(element, d) {
1159
+ this.highlightEdge(element, d);
1160
+ }
1161
+
1162
+ edge_mouseout(_element, _d) {
1163
+ this.highlightEdge(null, null);
1164
+ }
1165
+
1166
+ progress(what: "start" | "stop" | "layout-start" | "layout-tick" | "layout-stop") {
1167
+ }
1168
+ }
1169
+ GraphT.prototype._class += " graph_GraphT";
1170
+
1171
+ export interface GraphT<SG extends SubgraphBaseProps = any, V extends VertexBaseProps = any, E extends EdgeBaseProps<V> = any> {
1172
+ allowDragging(): boolean;
1173
+ allowDragging(_: boolean): this;
1174
+ dragSingleNeighbors(): boolean;
1175
+ dragSingleNeighbors(_: boolean): this;
1176
+ layout(): GraphLayoutType;
1177
+ layout(_: GraphLayoutType): this;
1178
+ applyScaleOnLayout(): boolean;
1179
+ applyScaleOnLayout(_: boolean): this;
1180
+ highlightOnMouseOverVertex(): boolean;
1181
+ highlightOnMouseOverVertex(_: boolean): this;
1182
+ highlightOnMouseOverEdge(): boolean;
1183
+ highlightOnMouseOverEdge(_: boolean): this;
1184
+ transitionDuration(): number;
1185
+ transitionDuration(_: number): this;
1186
+
1187
+ highlightSelectedPathToCentroid(): boolean;
1188
+ highlightSelectedPathToCentroid(_: boolean): this;
1189
+ edgeArcDepth(): number;
1190
+ edgeArcDepth(_: number): this;
1191
+ minScale(): number;
1192
+ minScale(_: number): this;
1193
+ maxScale(): number;
1194
+ maxScale(_: number): this;
1195
+ showEdgeLabels(): boolean;
1196
+ showEdgeLabels(_: boolean): this;
1197
+ showEdgeLabelsOnHighlight(): boolean;
1198
+ showEdgeLabelsOnHighlight(_: boolean): this;
1199
+ showVertexLabels(): boolean;
1200
+ showVertexLabels(_: boolean): this;
1201
+ showVertexLabelsOnHighlight(): boolean;
1202
+ showVertexLabelsOnHighlight(_: boolean): this;
1203
+
1204
+ hierarchyRankDirection(): "TB" | "BT" | "LR" | "RL";
1205
+ hierarchyRankDirection(_: "TB" | "BT" | "LR" | "RL"): this;
1206
+ hierarchyNodeSeparation(): number;
1207
+ hierarchyNodeSeparation(_: number): this;
1208
+ hierarchyEdgeSeparation(): number;
1209
+ hierarchyEdgeSeparation(_: number): this;
1210
+ hierarchyRankSeparation(): number;
1211
+ hierarchyRankSeparation(_: number): this;
1212
+ hierarchyDigraph(): boolean;
1213
+ hierarchyDigraph(_: boolean): this;
1214
+
1215
+ forceDirectedAlpha(): number;
1216
+ forceDirectedAlpha(_: number): this;
1217
+ forceDirectedAlphaMin(): number;
1218
+ forceDirectedAlphaMin(_: number): this;
1219
+ forceDirectedAlphaDecay(): number;
1220
+ forceDirectedAlphaDecay(_: number): this;
1221
+ forceDirectedRepulsionStrength(): number;
1222
+ forceDirectedRepulsionStrength(_: number): this;
1223
+ forceDirectedVelocityDecay(): number;
1224
+ forceDirectedVelocityDecay(_: number): this;
1225
+ forceDirectedIterations(): number;
1226
+ forceDirectedIterations(_: number): this;
1227
+ forceDirectedLinkDistance(): number;
1228
+ forceDirectedLinkDistance(_: number): this;
1229
+ forceDirectedLinkStrength(): number;
1230
+ forceDirectedLinkStrength(_: number): this;
1231
+ forceDirectedPinCentroid(): boolean;
1232
+ forceDirectedPinCentroid(_: boolean): this;
1233
+ forceDirectedForceStrength(): number;
1234
+ forceDirectedForceStrength(_: number): this;
1235
+ forceDirectedMinDistance(): number;
1236
+ forceDirectedMinDistance(_: number): this;
1237
+ forceDirectedMaxDistance(): number;
1238
+ forceDirectedMaxDistance(_: number): this;
1239
+
1240
+ treeRankDirection(): "TB" | "LR";
1241
+ treeRankDirection(_: "TB" | "LR"): this;
1242
+
1243
+ edgeColor(): string;
1244
+ edgeColor(_: string): this;
1245
+ edgeStrokeWidth(): number;
1246
+ edgeStrokeWidth(_: number): this;
1247
+ tooltipWidth(): number;
1248
+ tooltipWidth(_: number): this;
1249
+ tooltipHeight(): number;
1250
+ tooltipHeight(_: number): this;
1251
+ enableTooltipPointerEvents(): boolean;
1252
+ enableTooltipPointerEvents(_: boolean): this;
1253
+ tooltipCloseDelay(): number;
1254
+ tooltipCloseDelay(_: number): this;
1255
+ doubleClickMaxDelay(): number;
1256
+ doubleClickMaxDelay(_: number): this;
1257
+
1258
+ wasmFolder(): string;
1259
+ wasmFolder(_: string): this;
1260
+ }
1261
+
1262
+ GraphT.prototype.publish("allowDragging", true, "boolean", "Allow Dragging of Vertices");
1263
+ GraphT.prototype.publish("dragSingleNeighbors", true, "boolean", "Dragging a Vertex also moves its singleton neighbors");
1264
+ GraphT.prototype.publish("layout", "ForceDirectedHybrid", "set", "Default Layout", GraphLayoutTypeSet);
1265
+ GraphT.prototype.publish("scale", "100%", "set", "Zoom Level", ["all", "width", "selection", "100%", "90%", "75%", "50%", "25%", "10%"]);
1266
+ GraphT.prototype.publish("applyScaleOnLayout", false, "boolean", "Shrink to fit on Layout");
1267
+ GraphT.prototype.publish("highlightOnMouseOverVertex", true, "boolean", "Highlight Vertex on Mouse Over");
1268
+ GraphT.prototype.publish("highlightOnMouseOverEdge", true, "boolean", "Highlight Edge on Mouse Over");
1269
+ GraphT.prototype.publish("transitionDuration", 250, "number", "Transition Duration");
1270
+ GraphT.prototype.publish("showEdges", true, "boolean", "Show Edges");
1271
+ GraphT.prototype.publish("showEdgeLabels", true, "boolean", "Show Edge labels");
1272
+ GraphT.prototype.publish("showEdgeLabelsOnHighlight", true, "boolean", "Show Edge labels when highlighted");
1273
+ GraphT.prototype.publish("showVertexLabels", true, "boolean", "Show Vertex labels");
1274
+ GraphT.prototype.publish("showVertexLabelsOnHighlight", true, "boolean", "Show Vertex labels when highlighted");
1275
+ GraphT.prototype.publish("snapToGrid", 0, "number", "Snap to Grid");
1276
+ GraphT.prototype.publish("selectionClearOnBackgroundClick", false, "boolean", "Clear selection on background click");
1277
+ GraphT.prototype.publish("edgeArcDepth", 8, "number", "Edge Arc Depth");
1278
+ GraphT.prototype.publish("edgeColor", null, "html-color", "Edge line stroke color", null, { optional: true });
1279
+ GraphT.prototype.publish("edgeStrokeWidth", 1, "number", "Edge line stroke width (pixels)");
1280
+ GraphT.prototype.publish("minScale", 0.6, "number", "Min scale size for text");
1281
+ GraphT.prototype.publish("maxScale", 1.0, "number", "Max scale size for text");
1282
+ GraphT.prototype.publish("tooltipWidth", 256, "number", "Tooltip width (pixels)");
1283
+ GraphT.prototype.publish("tooltipHeight", 128, "number", "Tooltip width (pixels)");
1284
+ GraphT.prototype.publish("enableTooltipPointerEvents", false, "boolean", "If true, tooltip will use the style: 'pointer-events: all'");
1285
+ GraphT.prototype.publish("tooltipCloseDelay", 0, "number", "Number of milliseconds to wait before closing tooltip (cancelled on tooltip mouseover event)");
1286
+ GraphT.prototype.publish("doubleClickMaxDelay", 300, "number", "Number of milliseconds to wait before a subsequent click is not considered a double click");
1287
+ GraphT.prototype.publish("highlightSelectedPathToCentroid", true, "boolean", "Highlight path to Center Vertex (for selected vertices)");
1288
+
1289
+ GraphT.prototype.publish("hierarchyRankDirection", "TB", "set", "Direction for Rank Nodes", ["TB", "BT", "LR", "RL"], { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1290
+ GraphT.prototype.publish("hierarchyNodeSeparation", 50, "number", "Number of pixels that separate nodes horizontally in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1291
+ GraphT.prototype.publish("hierarchyEdgeSeparation", 10, "number", "Number of pixels that separate edges horizontally in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1292
+ GraphT.prototype.publish("hierarchyRankSeparation", 50, "number", "Number of pixels between each rank in the layout", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1293
+ GraphT.prototype.publish("hierarchyDigraph", true, "boolean", "Directional GraphBase", null, { disable: (w: GraphT) => w.layout() !== "Hierarchy" });
1294
+
1295
+ GraphT.prototype.publish("forceDirectedAlpha", 1, "number", "Alpha", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1296
+ GraphT.prototype.publish("forceDirectedAlphaMin", 0.001, "number", "Min Alpha", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1297
+ GraphT.prototype.publish("forceDirectedAlphaDecay", 0.0228, "number", "Defaults to 1 - pow(alphaMin, 1 / 300)", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1298
+ GraphT.prototype.publish("forceDirectedRepulsionStrength", -350, "number", "Charge strength ", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1299
+ GraphT.prototype.publish("forceDirectedVelocityDecay", 0.4, "number", "Velocity Decay ", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1300
+ GraphT.prototype.publish("forceDirectedIterations", 300, "number", "Iterations", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1301
+ GraphT.prototype.publish("forceDirectedLinkDistance", 300, "number", "Target distance between linked nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1302
+ GraphT.prototype.publish("forceDirectedLinkStrength", 1, "number", "Strength (rigidity) of links", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1303
+ GraphT.prototype.publish("forceDirectedPinCentroid", false, "boolean", "Pin centroid to center", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1304
+ GraphT.prototype.publish("forceDirectedForceStrength", 0, "number", "Strength of center force", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1305
+ GraphT.prototype.publish("forceDirectedMinDistance", 1, "number", "Min distance between nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1306
+ GraphT.prototype.publish("forceDirectedMaxDistance", Infinity, "number", "Max distance between nodes", null, { disable: (w: GraphT) => w.layout().indexOf("ForceDirected") !== 0 });
1307
+
1308
+ GraphT.prototype.publish("treeRankDirection", "LR", "set", "Direction for Rank Nodes", ["TB", "LR"], { disable: (w: GraphT) => w.layout() !== "Tree" && w.layout() !== "Dendrogram" });
1309
+
1310
+ GraphT.prototype.publish("wasmFolder", null, "string", "WASM Folder", null, { optional: true, disable: (w: GraphT) => ["DOT", "Neato", "FDP", "TwoPI", "Circo"].indexOf(w.layout()) < 0 });
1311
+
1312
+ const _origScale = GraphT.prototype.scale;
1313
+ GraphT.prototype.scale = function (_?, transitionDuration?) {
1314
+ const retVal = _origScale.apply(this, arguments);
1315
+ if (arguments.length) {
1316
+ this.zoomTo(_, transitionDuration);
1317
+ }
1318
+ return retVal;
1319
+ };
1320
+
1321
+ export function graphviz(dot: string, engine: Engine = "dot", _scriptDir: string = scriptDir) {
1322
+
1323
+ return gvWorker({
1324
+ items: [],
1325
+ links: [],
1326
+ raw: dot
1327
+ }, {
1328
+ engine: engine
1329
+ });
1330
+ }