@deck.gl-community/graph-layers 9.0.2 → 9.1.0-beta.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 (118) hide show
  1. package/LICENSE +1 -1
  2. package/dist/core/graph-engine.d.ts +16 -7
  3. package/dist/core/graph-engine.d.ts.map +1 -1
  4. package/dist/core/graph-engine.js +13 -4
  5. package/dist/core/graph-layout.d.ts +69 -0
  6. package/dist/core/graph-layout.d.ts.map +1 -0
  7. package/dist/core/{base-layout.js → graph-layout.js} +63 -80
  8. package/dist/core/interaction-manager.d.ts +1 -1
  9. package/dist/core/interaction-manager.d.ts.map +1 -1
  10. package/dist/{core → graph}/edge.d.ts +18 -17
  11. package/dist/graph/edge.d.ts.map +1 -0
  12. package/dist/{core → graph}/edge.js +12 -15
  13. package/dist/{core → graph}/graph.d.ts +34 -31
  14. package/dist/graph/graph.d.ts.map +1 -0
  15. package/dist/{core → graph}/graph.js +43 -36
  16. package/dist/{core → graph}/node.d.ts +20 -20
  17. package/dist/graph/node.d.ts.map +1 -0
  18. package/dist/{core → graph}/node.js +16 -18
  19. package/dist/index.cjs +1181 -434
  20. package/dist/index.cjs.map +4 -4
  21. package/dist/index.d.ts +16 -14
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +19 -18
  24. package/dist/layers/graph-layer.d.ts +45 -5
  25. package/dist/layers/graph-layer.d.ts.map +1 -1
  26. package/dist/layers/graph-layer.js +80 -38
  27. package/dist/layers/node-layers/{path-rounded-rectange-layer.d.ts → path-rounded-rectangle-layer.d.ts} +1 -1
  28. package/dist/layers/node-layers/path-rounded-rectangle-layer.d.ts.map +1 -0
  29. package/dist/layers/node-layers/rounded-rectangle-layer-fragment.d.ts +1 -1
  30. package/dist/layers/node-layers/rounded-rectangle-layer-fragment.d.ts.map +1 -1
  31. package/dist/layers/node-layers/rounded-rectangle-layer-fragment.js +1 -3
  32. package/dist/layers/node-layers/rounded-rectangle-layer.d.ts +12 -3
  33. package/dist/layers/node-layers/rounded-rectangle-layer.d.ts.map +1 -1
  34. package/dist/layers/node-layers/rounded-rectangle-layer.js +25 -11
  35. package/dist/layouts/d3-force/d3-force-layout.d.ts +12 -3
  36. package/dist/layouts/d3-force/d3-force-layout.d.ts.map +1 -1
  37. package/dist/layouts/d3-force/d3-force-layout.js +11 -11
  38. package/dist/layouts/d3-force/worker.d.ts.map +1 -1
  39. package/dist/layouts/experimental/force-multi-graph-layout.d.ts +43 -0
  40. package/dist/layouts/experimental/force-multi-graph-layout.d.ts.map +1 -0
  41. package/dist/layouts/experimental/force-multi-graph-layout.js +226 -0
  42. package/dist/layouts/experimental/hive-plot-layout.d.ts +34 -0
  43. package/dist/layouts/experimental/hive-plot-layout.d.ts.map +1 -0
  44. package/dist/layouts/experimental/hive-plot-layout.js +142 -0
  45. package/dist/layouts/experimental/radial-layout.d.ts +28 -0
  46. package/dist/layouts/experimental/radial-layout.d.ts.map +1 -0
  47. package/dist/layouts/experimental/radial-layout.js +164 -0
  48. package/dist/layouts/gpu-force/gpu-force-layout.d.ts +15 -3
  49. package/dist/layouts/gpu-force/gpu-force-layout.d.ts.map +1 -1
  50. package/dist/layouts/gpu-force/gpu-force-layout.js +20 -18
  51. package/dist/layouts/gpu-force/worker.d.ts.map +1 -1
  52. package/dist/layouts/simple-layout.d.ts +42 -0
  53. package/dist/layouts/simple-layout.d.ts.map +1 -0
  54. package/dist/layouts/{simple-layout/simple-layout.js → simple-layout.js} +8 -7
  55. package/dist/loaders/create-graph.d.ts +13 -0
  56. package/dist/loaders/create-graph.d.ts.map +1 -0
  57. package/dist/{utils → loaders}/create-graph.js +9 -4
  58. package/dist/loaders/edge-parsers.d.ts +2 -6
  59. package/dist/loaders/edge-parsers.d.ts.map +1 -1
  60. package/dist/loaders/json-loader.js +1 -1
  61. package/dist/loaders/node-parsers.d.ts +2 -3
  62. package/dist/loaders/node-parsers.d.ts.map +1 -1
  63. package/dist/loaders/simple-json-graph-loader.d.ts +12 -0
  64. package/dist/loaders/simple-json-graph-loader.d.ts.map +1 -0
  65. package/dist/loaders/simple-json-graph-loader.js +20 -0
  66. package/dist/loaders/table-graph-loader.d.ts +17 -0
  67. package/dist/loaders/table-graph-loader.d.ts.map +1 -0
  68. package/dist/loaders/table-graph-loader.js +91 -0
  69. package/dist/utils/log.d.ts +1 -1
  70. package/dist/utils/log.d.ts.map +1 -1
  71. package/dist/utils/log.js +3 -3
  72. package/dist/widgets/long-press-button.d.ts +13 -0
  73. package/dist/widgets/long-press-button.d.ts.map +1 -0
  74. package/dist/widgets/long-press-button.js +31 -0
  75. package/dist/widgets/view-control-widget.d.ts +78 -0
  76. package/dist/widgets/view-control-widget.d.ts.map +1 -0
  77. package/dist/widgets/view-control-widget.js +194 -0
  78. package/package.json +8 -6
  79. package/src/core/graph-engine.ts +30 -10
  80. package/src/core/graph-layout.ts +146 -0
  81. package/src/core/interaction-manager.ts +2 -2
  82. package/src/{core → graph}/edge.ts +19 -17
  83. package/src/{core → graph}/graph.ts +51 -36
  84. package/src/{core → graph}/node.ts +21 -20
  85. package/src/index.ts +28 -28
  86. package/src/layers/graph-layer.ts +133 -46
  87. package/src/layers/node-layers/rounded-rectangle-layer-fragment.ts +1 -3
  88. package/src/layers/node-layers/rounded-rectangle-layer.ts +34 -10
  89. package/src/layouts/d3-force/d3-force-layout.ts +21 -11
  90. package/src/layouts/experimental/force-multi-graph-layout.ts +268 -0
  91. package/src/layouts/experimental/hive-plot-layout.ts +182 -0
  92. package/src/layouts/experimental/radial-layout.ts +210 -0
  93. package/src/layouts/gpu-force/gpu-force-layout.ts +32 -17
  94. package/src/layouts/{simple-layout/simple-layout.ts → simple-layout.ts} +34 -19
  95. package/src/{utils → loaders}/create-graph.ts +9 -4
  96. package/src/loaders/edge-parsers.ts +2 -1
  97. package/src/loaders/json-loader.ts +1 -1
  98. package/src/loaders/node-parsers.ts +2 -1
  99. package/src/loaders/simple-json-graph-loader.ts +28 -0
  100. package/src/loaders/table-graph-loader.ts +124 -0
  101. package/src/utils/log.ts +3 -3
  102. package/src/widgets/long-press-button.tsx +50 -0
  103. package/src/widgets/view-control-widget.tsx +337 -0
  104. package/dist/core/base-layout.d.ts +0 -72
  105. package/dist/core/base-layout.d.ts.map +0 -1
  106. package/dist/core/edge.d.ts.map +0 -1
  107. package/dist/core/graph.d.ts.map +0 -1
  108. package/dist/core/node.d.ts.map +0 -1
  109. package/dist/layers/node-layers/path-rounded-rectange-layer.d.ts.map +0 -1
  110. package/dist/layouts/simple-layout/simple-layout.d.ts +0 -23
  111. package/dist/layouts/simple-layout/simple-layout.d.ts.map +0 -1
  112. package/dist/utils/create-graph.d.ts +0 -9
  113. package/dist/utils/create-graph.d.ts.map +0 -1
  114. package/src/core/base-layout.ts +0 -154
  115. /package/dist/layers/node-layers/{path-rounded-rectange-layer.js → path-rounded-rectangle-layer.js} +0 -0
  116. /package/src/layers/node-layers/{path-rounded-rectange-layer.ts → path-rounded-rectangle-layer.ts} +0 -0
  117. /package/src/layouts/d3-force/{worker.ts → worker.js} +0 -0
  118. /package/src/layouts/gpu-force/{worker.ts → worker.js} +0 -0
@@ -0,0 +1,226 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { GraphLayout } from "../../core/graph-layout.js";
5
+ import { EDGE_TYPE } from "../../core/constants.js";
6
+ import * as d3 from 'd3-force';
7
+ export class ForceMultiGraphLayout extends GraphLayout {
8
+ static defaultOptions = {
9
+ alpha: 3,
10
+ nBodyStrength: -1200,
11
+ nBodyDistanceMin: 100,
12
+ nBodyDistanceMax: 1400
13
+ };
14
+ _name = 'ForceMultiGraphLayout';
15
+ _graph;
16
+ // d3 part
17
+ // custom graph data
18
+ _d3Graph = { nodes: [], edges: [] };
19
+ _nodeMap = {};
20
+ _edgeMap = {};
21
+ _simulator;
22
+ constructor(options = {}) {
23
+ super(options);
24
+ this._options = {
25
+ ...ForceMultiGraphLayout.defaultOptions,
26
+ ...options
27
+ };
28
+ }
29
+ initializeGraph(graph) {
30
+ this.updateGraph(graph);
31
+ }
32
+ _strength = (d3Edge) => {
33
+ if (d3Edge.isVirtual) {
34
+ return 1 / d3Edge.edgeCount;
35
+ }
36
+ const sourceDegree = this._graph.getDegree(d3Edge.source.id);
37
+ const targetDegree = this._graph.getDegree(d3Edge.target.id);
38
+ return 1 / Math.min(sourceDegree, targetDegree);
39
+ };
40
+ _generateSimulator() {
41
+ if (this._simulator) {
42
+ this._simulator.on('tick', null).on('end', null);
43
+ this._simulator = null;
44
+ }
45
+ const { alpha, nBodyStrength, nBodyDistanceMin, nBodyDistanceMax } = this._options;
46
+ const g = this._d3Graph;
47
+ this._simulator = d3
48
+ .forceSimulation(g.nodes)
49
+ .force('edge', d3
50
+ .forceLink(g.edges)
51
+ .id((n) => n.id)
52
+ .strength(this._strength))
53
+ .force('charge', d3
54
+ .forceManyBody()
55
+ .strength(nBodyStrength)
56
+ .distanceMin(nBodyDistanceMin)
57
+ .distanceMax(nBodyDistanceMax))
58
+ .force('center', d3.forceCenter())
59
+ .alpha(alpha);
60
+ // register event callbacks
61
+ this._simulator.on('tick', this._onLayoutChange).on('end', this._onLayoutDone);
62
+ }
63
+ start() {
64
+ this._generateSimulator();
65
+ this._simulator.restart();
66
+ }
67
+ resume() {
68
+ this._simulator.restart();
69
+ }
70
+ stop() {
71
+ this._simulator.stop();
72
+ }
73
+ updateGraph(graph) {
74
+ this._graph = graph;
75
+ // nodes
76
+ const newNodeMap = {};
77
+ const newD3Nodes = graph.getNodes().map((node) => {
78
+ const oldD3Node = this._nodeMap[node.id];
79
+ const newD3Node = oldD3Node ? oldD3Node : { id: node.id };
80
+ newNodeMap[node.id] = newD3Node;
81
+ return newD3Node;
82
+ });
83
+ // edges
84
+ // bucket edges between the same source/target node pairs.
85
+ const nodePairs = graph.getEdges().reduce((res, edge) => {
86
+ const nodes = [edge.getSourceNodeId(), edge.getTargetNodeId()];
87
+ // sort the node ids to count the edges with the same pair
88
+ // but different direction (a -> b or b -> a)
89
+ const pairId = nodes.sort().toString();
90
+ // push this edge into the bucket
91
+ if (!res[pairId]) {
92
+ res[pairId] = [edge];
93
+ }
94
+ else {
95
+ res[pairId].push(edge);
96
+ }
97
+ return res;
98
+ }, {});
99
+ // go through each pair of edges,
100
+ // if only one edge between two nodes, create a straight line
101
+ // otherwise, create one virtual node and two edges for each edge
102
+ const newD3Edges = [];
103
+ const newEdgeMap = {};
104
+ Object.keys(nodePairs).forEach((pairId) => {
105
+ const betweenEdges = nodePairs[pairId];
106
+ const firstEdge = betweenEdges[0];
107
+ if (betweenEdges.length === 1) {
108
+ // do nothing, this is a real edge
109
+ const newD3Edge = {
110
+ type: EDGE_TYPE.LINE,
111
+ id: firstEdge.getId(),
112
+ source: newNodeMap[firstEdge.getSourceNodeId()],
113
+ target: newNodeMap[firstEdge.getTargetNodeId()],
114
+ isVirtual: false
115
+ };
116
+ newEdgeMap[firstEdge.getId()] = newD3Edge;
117
+ newD3Edges.push(newD3Edge);
118
+ return;
119
+ }
120
+ // else reduce to one virtual edge
121
+ const newD3Edge = {
122
+ type: EDGE_TYPE.LINE,
123
+ id: pairId,
124
+ source: newNodeMap[firstEdge.getSourceNodeId()],
125
+ target: newNodeMap[firstEdge.getTargetNodeId()],
126
+ isVirtual: true,
127
+ edgeCount: betweenEdges.length
128
+ };
129
+ newEdgeMap[pairId] = newD3Edge;
130
+ newD3Edges.push(newD3Edge);
131
+ betweenEdges.forEach((e, idx) => {
132
+ newEdgeMap[e.id] = {
133
+ type: EDGE_TYPE.SPLINE_CURVE,
134
+ id: e.id,
135
+ source: newNodeMap[e.getSourceNodeId()],
136
+ target: newNodeMap[e.getTargetNodeId()],
137
+ virtualEdgeId: pairId,
138
+ isVirtual: true,
139
+ index: idx
140
+ };
141
+ });
142
+ });
143
+ this._nodeMap = newNodeMap;
144
+ this._d3Graph.nodes = newD3Nodes;
145
+ this._edgeMap = newEdgeMap;
146
+ this._d3Graph.edges = newD3Edges;
147
+ }
148
+ getNodePosition = (node) => {
149
+ const d3Node = this._nodeMap[node.id];
150
+ if (d3Node) {
151
+ return [d3Node.x, d3Node.y];
152
+ }
153
+ // default value
154
+ return [0, 0];
155
+ };
156
+ getEdgePosition = (edge) => {
157
+ const d3Edge = this._edgeMap[edge.id];
158
+ if (d3Edge) {
159
+ if (!d3Edge.isVirtual) {
160
+ return {
161
+ type: EDGE_TYPE.LINE,
162
+ sourcePosition: [d3Edge.source.x, d3Edge.source.y],
163
+ targetPosition: [d3Edge.target.x, d3Edge.target.y],
164
+ controlPoints: []
165
+ };
166
+ }
167
+ // else, check the referenced virtual edge
168
+ const virtualEdge = this._edgeMap[d3Edge.virtualEdgeId];
169
+ const edgeCount = virtualEdge.edgeCount;
170
+ // get the position of source and target nodes
171
+ const sourcePosition = [virtualEdge.source.x, virtualEdge.source.y];
172
+ const targetPosition = [virtualEdge.target.x, virtualEdge.target.y];
173
+ // calculate a symmetric curve
174
+ const distance = Math.hypot(sourcePosition[0] - targetPosition[0], sourcePosition[1] - targetPosition[1]);
175
+ const index = d3Edge.index;
176
+ // curve direction: inward vs. outward
177
+ const direction = index % 2 ? 1 : -1;
178
+ // if the number of the parallel edges is an even number => symmetric shape
179
+ // otherwise, the 0th node will be a staight line, and rest of them are symmetrical.
180
+ const symmetricShape = edgeCount % 2 === 0;
181
+ const offset = Math.max(distance / 10, 5) *
182
+ (symmetricShape ? Math.floor(index / 2 + 1) : Math.ceil(index / 2));
183
+ const controlPoint = computeControlPoint(sourcePosition, targetPosition, direction, offset);
184
+ return {
185
+ type: EDGE_TYPE.SPLINE_CURVE,
186
+ sourcePosition,
187
+ targetPosition,
188
+ controlPoints: [controlPoint]
189
+ };
190
+ }
191
+ // default value
192
+ return {
193
+ type: EDGE_TYPE.LINE,
194
+ sourcePosition: [0, 0],
195
+ targetPosition: [0, 0],
196
+ controlPoints: []
197
+ };
198
+ };
199
+ lockNodePosition = (node, x, y) => {
200
+ const d3Node = this._nodeMap[node.id];
201
+ d3Node.x = x;
202
+ d3Node.y = y;
203
+ this._onLayoutChange();
204
+ this._onLayoutDone();
205
+ };
206
+ }
207
+ /**
208
+ * A helper function to compute the control point of a curve
209
+ * @param {number[]} source - the coordinates of source point, ex: [x, y, z]
210
+ * @param {number[]} target - the coordinates of target point, ex: [x, y, z]
211
+ * @param {number} direction - the direction of the curve, 1 or -1
212
+ * @param {number} offset - offset from the midpoint
213
+ * @return {number[]} - the coordinates of the control point
214
+ */
215
+ function computeControlPoint(source, target, direction, offset) {
216
+ const midPoint = [(source[0] + target[0]) / 2, (source[1] + target[1]) / 2];
217
+ const dx = target[0] - source[0];
218
+ const dy = target[1] - source[1];
219
+ const normal = [dy, -dx];
220
+ const length = Math.sqrt(Math.pow(normal[0], 2.0) + Math.pow(normal[1], 2.0));
221
+ const normalized = [normal[0] / length, normal[1] / length];
222
+ return [
223
+ midPoint[0] + normalized[0] * offset * direction,
224
+ midPoint[1] + normalized[1] * offset * direction
225
+ ];
226
+ }
@@ -0,0 +1,34 @@
1
+ import { GraphLayout, GraphLayoutOptions } from "../../core/graph-layout.js";
2
+ import { Node } from "../../graph/node.js";
3
+ import { Graph } from "../../graph/graph.js";
4
+ export type HivePlotLayoutOptions = GraphLayoutOptions & {
5
+ innerRadius?: number;
6
+ outerRadius?: number;
7
+ getNodeAxis?: (node: Node) => any;
8
+ };
9
+ export declare class HivePlotLayout extends GraphLayout<HivePlotLayoutOptions> {
10
+ static defaultOptions: {
11
+ readonly innerRadius: 100;
12
+ readonly outerRadius: 500;
13
+ readonly getNodeAxis: (node: Node) => unknown;
14
+ };
15
+ _name: string;
16
+ _graph: Graph;
17
+ _totalAxis: number;
18
+ _axis: Record<string, any>;
19
+ _nodeMap: {};
20
+ _nodePositionMap: {};
21
+ constructor(options?: HivePlotLayoutOptions);
22
+ initializeGraph(graph: Graph): void;
23
+ updateGraph(graph: any): void;
24
+ start(): void;
25
+ getNodePosition: (node: any) => any;
26
+ getEdgePosition: (edge: any) => {
27
+ type: string;
28
+ sourcePosition: any;
29
+ targetPosition: any;
30
+ controlPoints: [number, number][];
31
+ };
32
+ lockNodePosition: (node: any, x: any, y: any) => void;
33
+ }
34
+ //# sourceMappingURL=hive-plot-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hive-plot-layout.d.ts","sourceRoot":"","sources":["../../../src/layouts/experimental/hive-plot-layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,mCAAgC;AACxE,OAAO,EAAC,IAAI,EAAC,4BAAyB;AAEtC,OAAO,EAAC,KAAK,EAAC,6BAA0B;AAExC,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,GAAG;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG,CAAC;CACnC,CAAC;AAEF,qBAAa,cAAe,SAAQ,WAAW,CAAC,qBAAqB,CAAC;IACpE,MAAM,CAAC,cAAc;;;qCAGC,IAAI;MACqC;IAE/D,KAAK,SAAc;IACnB,MAAM,EAAE,KAAK,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,QAAQ,KAAM;IACd,gBAAgB,KAAM;gBAEV,OAAO,GAAE,qBAA0B;IAQ/C,eAAe,CAAC,KAAK,EAAE,KAAK;IAI5B,WAAW,CAAC,KAAK,KAAA;IAsDjB,KAAK;IAKL,eAAe,qBAAiD;IAEhE,eAAe;;;;;MAqCb;IAEF,gBAAgB,sCAId;CACH"}
@@ -0,0 +1,142 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { GraphLayout } from "../../core/graph-layout.js";
5
+ import { EDGE_TYPE } from "../../core/constants.js";
6
+ export class HivePlotLayout extends GraphLayout {
7
+ static defaultOptions = {
8
+ innerRadius: 100,
9
+ outerRadius: 500,
10
+ getNodeAxis: (node) => node.getPropertyValue('group')
11
+ };
12
+ _name = 'HivePlot';
13
+ _graph;
14
+ _totalAxis;
15
+ _axis;
16
+ _nodeMap = {};
17
+ _nodePositionMap = {};
18
+ constructor(options = {}) {
19
+ super(options);
20
+ this._options = {
21
+ ...HivePlotLayout.defaultOptions,
22
+ ...options
23
+ };
24
+ }
25
+ initializeGraph(graph) {
26
+ this.updateGraph(graph);
27
+ }
28
+ updateGraph(graph) {
29
+ const { getNodeAxis, innerRadius, outerRadius } = this._options;
30
+ this._graph = graph;
31
+ this._nodeMap = graph.getNodes().reduce((res, node) => {
32
+ res[node.getId()] = node;
33
+ return res;
34
+ }, {});
35
+ // bucket nodes into few axis
36
+ this._axis = graph.getNodes().reduce((res, node) => {
37
+ const axis = getNodeAxis(node);
38
+ if (!res[axis]) {
39
+ res[axis] = [];
40
+ }
41
+ res[axis].push(node);
42
+ return res;
43
+ }, {});
44
+ // sort nodes along the same axis by degree
45
+ this._axis = Object.keys(this._axis).reduce((res, axis) => {
46
+ const bucketedNodes = this._axis[axis];
47
+ const sortedNodes = bucketedNodes.sort((a, b) => {
48
+ if (a.getDegree() > b.getDegree()) {
49
+ return 1;
50
+ }
51
+ if (a.getDegree() === b.getDegree()) {
52
+ return 0;
53
+ }
54
+ return -1;
55
+ });
56
+ res[axis] = sortedNodes;
57
+ return res;
58
+ }, {});
59
+ this._totalAxis = Object.keys(this._axis).length;
60
+ const center = [0, 0];
61
+ const angleInterval = 360 / Object.keys(this._axis).length;
62
+ // calculate positions
63
+ this._nodePositionMap = Object.keys(this._axis).reduce((res, axis, axisIdx) => {
64
+ const axisAngle = angleInterval * axisIdx;
65
+ const bucketedNodes = this._axis[axis];
66
+ const interval = (outerRadius - innerRadius) / bucketedNodes.length;
67
+ bucketedNodes.forEach((node, idx) => {
68
+ const radius = innerRadius + idx * interval;
69
+ const x = Math.cos((axisAngle / 180) * Math.PI) * radius + center[0];
70
+ const y = Math.sin((axisAngle / 180) * Math.PI) * radius + center[1];
71
+ res[node.getId()] = [x, y];
72
+ });
73
+ return res;
74
+ }, {});
75
+ }
76
+ start() {
77
+ this._onLayoutChange();
78
+ this._onLayoutDone();
79
+ }
80
+ getNodePosition = (node) => this._nodePositionMap[node.getId()];
81
+ getEdgePosition = (edge) => {
82
+ const { getNodeAxis } = this._options;
83
+ const sourceNodeId = edge.getSourceNodeId();
84
+ const targetNodeId = edge.getTargetNodeId();
85
+ const sourcePosition = this._nodePositionMap[sourceNodeId];
86
+ const targetPosition = this._nodePositionMap[targetNodeId];
87
+ const sourceNode = this._nodeMap[sourceNodeId];
88
+ const targetNode = this._nodeMap[targetNodeId];
89
+ const sourceNodeAxis = getNodeAxis(sourceNode);
90
+ const targetNodeAxis = getNodeAxis(targetNode);
91
+ if (sourceNodeAxis === targetNodeAxis) {
92
+ return {
93
+ type: EDGE_TYPE.LINE,
94
+ sourcePosition,
95
+ targetPosition,
96
+ controlPoints: []
97
+ };
98
+ }
99
+ const controlPoint = computeControlPoint({
100
+ sourcePosition,
101
+ sourceNodeAxis,
102
+ targetPosition,
103
+ targetNodeAxis,
104
+ totalAxis: this._totalAxis
105
+ });
106
+ return {
107
+ type: EDGE_TYPE.SPLINE_CURVE,
108
+ sourcePosition,
109
+ targetPosition,
110
+ controlPoints: [controlPoint]
111
+ };
112
+ };
113
+ lockNodePosition = (node, x, y) => {
114
+ this._nodePositionMap[node.id] = [x, y];
115
+ this._onLayoutChange();
116
+ this._onLayoutDone();
117
+ };
118
+ }
119
+ function computeControlPoint({ sourcePosition, sourceNodeAxis, targetPosition, targetNodeAxis, totalAxis }) {
120
+ const halfAxis = (totalAxis - 1) / 2;
121
+ // check whether the source/target are at the same side.
122
+ const sameSide = (sourceNodeAxis <= halfAxis && targetNodeAxis <= halfAxis) ||
123
+ (sourceNodeAxis > halfAxis && targetNodeAxis > halfAxis);
124
+ // curve direction
125
+ const direction = sameSide && sourceNodeAxis <= halfAxis && targetNodeAxis <= halfAxis ? 1 : -1;
126
+ // flip the source/target to follow the clockwise diretion
127
+ const source = sourceNodeAxis < targetNodeAxis && sameSide ? sourcePosition : targetPosition;
128
+ const target = sourceNodeAxis < targetNodeAxis && sameSide ? targetPosition : sourcePosition;
129
+ // calculate offset
130
+ const distance = Math.hypot(source[0] - target[0], source[1] - target[1]);
131
+ const offset = distance * 0.2;
132
+ const midPoint = [(source[0] + target[0]) / 2, (source[1] + target[1]) / 2];
133
+ const dx = target[0] - source[0];
134
+ const dy = target[1] - source[1];
135
+ const normal = [dy, -dx];
136
+ const length = Math.hypot(dy, -dx);
137
+ const normalized = [normal[0] / length, normal[1] / length];
138
+ return [
139
+ midPoint[0] + normalized[0] * offset * direction,
140
+ midPoint[1] + normalized[1] * offset * direction
141
+ ];
142
+ }
@@ -0,0 +1,28 @@
1
+ import { GraphLayout, GraphLayoutOptions } from "../../core/graph-layout.js";
2
+ import { Graph } from "../../graph/graph.js";
3
+ export type RadialLayoutOptions = GraphLayoutOptions & {
4
+ radius?: number;
5
+ tree?: any;
6
+ };
7
+ export declare class RadialLayout extends GraphLayout<RadialLayoutOptions> {
8
+ static defaultOptions: {
9
+ radius: number;
10
+ };
11
+ _name: string;
12
+ _graph: Graph;
13
+ _hierarchicalPoints: {};
14
+ nestedTree: any;
15
+ constructor(options?: RadialLayoutOptions);
16
+ initializeGraph(graph: Graph): void;
17
+ updateGraph(graph: Graph): void;
18
+ start(): void;
19
+ getNodePosition: (node: any) => any;
20
+ getEdgePosition: (edge: any) => {
21
+ type: string;
22
+ sourcePosition: any;
23
+ targetPosition: any;
24
+ controlPoints: any[];
25
+ };
26
+ lockNodePosition: (node: any, x: any, y: any) => void;
27
+ }
28
+ //# sourceMappingURL=radial-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radial-layout.d.ts","sourceRoot":"","sources":["../../../src/layouts/experimental/radial-layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,mCAAgC;AAGxE,OAAO,EAAC,KAAK,EAAC,6BAA0B;AAExC,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AA2CF,qBAAa,YAAa,SAAQ,WAAW,CAAC,mBAAmB,CAAC;IAChE,MAAM,CAAC,cAAc;;MAEnB;IAEF,KAAK,SAAkB;IACvB,MAAM,EAAE,KAAK,CAAQ;IAErB,mBAAmB,KAAM;IACzB,UAAU,MAAC;gBAEC,OAAO,GAAE,mBAAwB;IAQ7C,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAInC,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI/B,KAAK,IAAI,IAAI;IAsEb,eAAe,qBAEb;IAGF,eAAe;;;;;MAoCb;IAEF,gBAAgB,sCAId;CACH"}
@@ -0,0 +1,164 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { GraphLayout } from "../../core/graph-layout.js";
5
+ import { EDGE_TYPE } from "../../core/constants.js";
6
+ const traverseTree = (nodeId, nodeMap) => {
7
+ const node = nodeMap[nodeId];
8
+ if (node.isLeaf) {
9
+ return node;
10
+ }
11
+ return {
12
+ ...node,
13
+ children: node.children.map((nid) => traverseTree(nid, nodeMap))
14
+ };
15
+ };
16
+ const getLeafNodeCount = (node, count) => {
17
+ if (!node.children || node.children.length === 0) {
18
+ return count + 1;
19
+ }
20
+ const sum = node.children.reduce((res, c) => {
21
+ return res + getLeafNodeCount(c, 0);
22
+ }, 0);
23
+ return count + sum;
24
+ };
25
+ const getTreeDepth = (node, depth = 0) => {
26
+ if (node.isLeaf) {
27
+ return depth;
28
+ }
29
+ return getTreeDepth(node.children[0], depth + 1);
30
+ };
31
+ const getPath = (node, targetId, path) => {
32
+ if (node.id === targetId) {
33
+ path.push(node.id);
34
+ return true;
35
+ }
36
+ const inChildren = node.children && node.children.some((c) => getPath(c, targetId, path));
37
+ if (inChildren) {
38
+ path.push(node.id);
39
+ return true;
40
+ }
41
+ return false;
42
+ };
43
+ export class RadialLayout extends GraphLayout {
44
+ static defaultOptions = {
45
+ radius: 500
46
+ };
47
+ _name = 'RadialLayout';
48
+ _graph = null;
49
+ // custom layout data structure
50
+ _hierarchicalPoints = {};
51
+ nestedTree;
52
+ constructor(options = {}) {
53
+ super(options);
54
+ this._options = {
55
+ ...RadialLayout.defaultOptions,
56
+ ...options
57
+ };
58
+ }
59
+ initializeGraph(graph) {
60
+ this.updateGraph(graph);
61
+ }
62
+ updateGraph(graph) {
63
+ this._graph = graph;
64
+ }
65
+ start() {
66
+ const nodeCount = this._graph.getNodes().length;
67
+ if (nodeCount === 0) {
68
+ return;
69
+ }
70
+ const { tree } = this._options;
71
+ if (!tree || tree.length === 0) {
72
+ return;
73
+ }
74
+ const { radius } = this._options;
75
+ const unitAngle = 360 / nodeCount;
76
+ // hierarchical positions
77
+ const rootNode = tree[0];
78
+ const nodeMap = tree.reduce((res, node) => {
79
+ res[node.id] = {
80
+ ...node,
81
+ isLeaf: !node.children || node.children.length === 0
82
+ };
83
+ return res;
84
+ }, {});
85
+ // nested structure
86
+ this.nestedTree = traverseTree(rootNode.id, nodeMap);
87
+ const totalLevels = getTreeDepth(this.nestedTree, 0);
88
+ const distanceBetweenLevels = radius / (totalLevels - 1);
89
+ const calculatePosition = (node, level, startAngle, positionMap) => {
90
+ const isRoot = node.id === rootNode.id;
91
+ if (node.children && node.children.length !== 0) {
92
+ const groupSize = getLeafNodeCount(node, 0);
93
+ // center the pos
94
+ positionMap[node.id] = isRoot
95
+ ? [0, 0]
96
+ : rotate(0, 0, 0, distanceBetweenLevels * (level + 1), startAngle + unitAngle * (groupSize / 2));
97
+ // calculate children position
98
+ let tempAngle = startAngle;
99
+ node.children.forEach((n) => {
100
+ calculatePosition(n, level + 1, tempAngle, positionMap);
101
+ tempAngle += getLeafNodeCount(n, 0) * unitAngle;
102
+ });
103
+ }
104
+ else {
105
+ positionMap[node.id] = rotate(0, 0, 0, distanceBetweenLevels * (level + 1), startAngle + unitAngle);
106
+ }
107
+ };
108
+ this._hierarchicalPoints = {};
109
+ calculatePosition(this.nestedTree, 0, 0, this._hierarchicalPoints);
110
+ // layout completes: notifiy component to re-render
111
+ this._onLayoutChange();
112
+ this._onLayoutDone();
113
+ }
114
+ getNodePosition = (node) => {
115
+ return this._hierarchicalPoints[node.id];
116
+ };
117
+ // spline curve version
118
+ getEdgePosition = (edge) => {
119
+ const sourceNodeId = edge.getSourceNodeId();
120
+ const targetNodeId = edge.getTargetNodeId();
121
+ const sourceNodePos = this._hierarchicalPoints[sourceNodeId];
122
+ const targetNodePos = this._hierarchicalPoints[targetNodeId];
123
+ const sourcePath = [];
124
+ getPath(this.nestedTree, sourceNodeId, sourcePath);
125
+ const targetPath = [];
126
+ getPath(this.nestedTree, targetNodeId, targetPath);
127
+ const totalLevels = sourcePath.length;
128
+ let commonAncestorLevel = totalLevels - 1; // root
129
+ for (let i = 0; i < totalLevels; i++) {
130
+ if (sourcePath[i] === targetPath[i]) {
131
+ commonAncestorLevel = i;
132
+ break;
133
+ }
134
+ }
135
+ const wayPoints = [];
136
+ for (let i = 1; i <= commonAncestorLevel; i++) {
137
+ const nodeId = sourcePath[i];
138
+ wayPoints.push(this._hierarchicalPoints[nodeId]);
139
+ }
140
+ for (let i = commonAncestorLevel - 1; i > 0; i--) {
141
+ const nodeId = targetPath[i];
142
+ wayPoints.push(this._hierarchicalPoints[nodeId]);
143
+ }
144
+ return {
145
+ type: EDGE_TYPE.SPLINE_CURVE,
146
+ sourcePosition: sourceNodePos,
147
+ targetPosition: targetNodePos,
148
+ controlPoints: wayPoints
149
+ };
150
+ };
151
+ lockNodePosition = (node, x, y) => {
152
+ this._hierarchicalPoints[node.id] = [x, y];
153
+ this._onLayoutChange();
154
+ this._onLayoutDone();
155
+ };
156
+ }
157
+ function rotate(cx, cy, x, y, angle) {
158
+ const radians = (Math.PI / 180) * angle;
159
+ const cos = Math.cos(radians);
160
+ const sin = Math.sin(radians);
161
+ const nx = cos * (x - cx) + sin * (y - cy) + cx;
162
+ const ny = cos * (y - cy) - sin * (x - cx) + cy;
163
+ return [nx, ny];
164
+ }
@@ -1,5 +1,17 @@
1
- import { BaseLayout } from "../../core/base-layout.js";
2
- export declare class GPUForceLayout extends BaseLayout {
1
+ import { GraphLayout, GraphLayoutOptions } from "../../core/graph-layout.js";
2
+ export type GPUForceLayoutOptions = GraphLayoutOptions & {
3
+ alpha?: number;
4
+ resumeAlpha?: number;
5
+ nBodyStrength?: number;
6
+ nBodyDistanceMin?: number;
7
+ nBodyDistanceMax?: number;
8
+ getCollisionRadius?: number;
9
+ };
10
+ /**
11
+ * @todo this layout should be updated with the organizational and logic improvements made in d3-force
12
+ */
13
+ export declare class GPUForceLayout extends GraphLayout<GPUForceLayoutOptions> {
14
+ static defaultOptions: Required<GPUForceLayoutOptions>;
3
15
  protected readonly _name: string;
4
16
  private _d3Graph;
5
17
  private _nodeMap;
@@ -7,7 +19,7 @@ export declare class GPUForceLayout extends BaseLayout {
7
19
  private _graph;
8
20
  private _worker;
9
21
  private _callbacks;
10
- constructor(options: any);
22
+ constructor(options?: GPUForceLayoutOptions);
11
23
  initializeGraph(graph: any): void;
12
24
  start(): void;
13
25
  update(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"gpu-force-layout.d.ts","sourceRoot":"","sources":["../../../src/layouts/gpu-force/gpu-force-layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,UAAU,EAAC,kCAA+B;AAclD,qBAAa,cAAe,SAAQ,UAAU;IAC5C,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAM;gBACZ,OAAO,KAAA;IAanB,eAAe,CAAC,KAAK,KAAA;IAuCrB,KAAK;IAIL,MAAM;IAIN,aAAa;IAiCb,MAAM,CAAC,IAAI,KAAA;IACX,KAAK,CAAC,IAAI,KAAA;IAMV,MAAM;IAGN,IAAI;IAKJ,WAAW,CAAC,KAAK,KAAA;IA0CjB,aAAa,CAAC,KAAK,KAAA;IA6BnB,eAAe,iBAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxC;IAEF,eAAe;;;;;MAkBb;IAEF,gBAAgB,sCAQd;IAEF,kBAAkB,sBAIhB;CACH"}
1
+ {"version":3,"file":"gpu-force-layout.d.ts","sourceRoot":"","sources":["../../../src/layouts/gpu-force/gpu-force-layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,mCAAgC;AAIxE,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,GAAG;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,qBAAa,cAAe,SAAQ,WAAW,CAAC,qBAAqB,CAAC;IACpE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAOpD;IAEF,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAM;gBAEZ,OAAO,GAAE,qBAA0B;IAgB/C,eAAe,CAAC,KAAK,KAAA;IAuCrB,KAAK;IAIL,MAAM;IAIN,aAAa;IAiCb,MAAM,CAAC,IAAI,KAAA;IACX,KAAK,CAAC,IAAI,KAAA;IAMV,MAAM;IAGN,IAAI;IAKJ,WAAW,CAAC,KAAK,KAAA;IA0CjB,aAAa,CAAC,KAAK,KAAA;IA6BnB,eAAe,iBAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxC;IAEF,eAAe;;;;;MAkBb;IAEF,gBAAgB,sCAQd;IAEF,kBAAkB,sBAIhB;CACH"}