@hpcc-js/graph 2.83.0 → 2.83.2

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