@mui/x-charts-pro 8.19.0 → 8.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BarChartPro/BarChartPro.js +18 -0
- package/CHANGELOG.md +174 -0
- package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/ChartZoomSlider/internals/ChartAxisZoomSliderThumb.js +20 -6
- package/ChartZoomSlider/internals/previews/AreaPreviewPlot.js +1 -1
- package/ChartZoomSlider/internals/previews/BarPreviewPlot.js +3 -3
- package/Heatmap/Heatmap.js +2 -0
- package/LineChartPro/LineChartPro.js +18 -0
- package/SankeyChart/SankeyPlot.js +12 -16
- package/SankeyChart/calculateSankeyLayout.d.ts +2 -3
- package/SankeyChart/calculateSankeyLayout.js +3 -2
- package/SankeyChart/d3Sankey/align.d.ts +33 -0
- package/SankeyChart/d3Sankey/align.js +63 -0
- package/SankeyChart/d3Sankey/index.d.ts +4 -0
- package/SankeyChart/d3Sankey/index.js +49 -0
- package/SankeyChart/d3Sankey/sankey.d.ts +42 -0
- package/SankeyChart/d3Sankey/sankey.js +454 -0
- package/SankeyChart/d3Sankey/sankey.types.d.ts +344 -0
- package/SankeyChart/d3Sankey/sankey.types.js +5 -0
- package/SankeyChart/d3Sankey/sankeyLinkHorizontal.d.ts +22 -0
- package/SankeyChart/d3Sankey/sankeyLinkHorizontal.js +37 -0
- package/SankeyChart/plugins/useSankeyHighlight.selectors.js +2 -2
- package/SankeyChart/sankey.types.d.ts +1 -1
- package/SankeyChart/seriesConfig/index.js +15 -0
- package/SankeyChart/seriesConfig/tooltipPosition.d.ts +3 -0
- package/SankeyChart/seriesConfig/tooltipPosition.js +96 -0
- package/SankeyChart/utils.d.ts +2 -2
- package/SankeyChart/utils.js +1 -1
- package/ScatterChartPro/ScatterChartPro.js +18 -0
- package/esm/BarChartPro/BarChartPro.js +18 -0
- package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderThumb.js +20 -6
- package/esm/ChartZoomSlider/internals/previews/AreaPreviewPlot.js +1 -1
- package/esm/ChartZoomSlider/internals/previews/BarPreviewPlot.js +3 -3
- package/esm/Heatmap/Heatmap.js +2 -0
- package/esm/LineChartPro/LineChartPro.js +18 -0
- package/esm/SankeyChart/SankeyPlot.js +14 -18
- package/esm/SankeyChart/calculateSankeyLayout.d.ts +2 -3
- package/esm/SankeyChart/calculateSankeyLayout.js +3 -2
- package/esm/SankeyChart/d3Sankey/align.d.ts +33 -0
- package/esm/SankeyChart/d3Sankey/align.js +54 -0
- package/esm/SankeyChart/d3Sankey/index.d.ts +4 -0
- package/esm/SankeyChart/d3Sankey/index.js +4 -0
- package/esm/SankeyChart/d3Sankey/sankey.d.ts +42 -0
- package/esm/SankeyChart/d3Sankey/sankey.js +448 -0
- package/esm/SankeyChart/d3Sankey/sankey.types.d.ts +344 -0
- package/esm/SankeyChart/d3Sankey/sankey.types.js +1 -0
- package/esm/SankeyChart/d3Sankey/sankeyLinkHorizontal.d.ts +22 -0
- package/esm/SankeyChart/d3Sankey/sankeyLinkHorizontal.js +31 -0
- package/esm/SankeyChart/plugins/useSankeyHighlight.selectors.js +2 -2
- package/esm/SankeyChart/sankey.types.d.ts +1 -1
- package/esm/SankeyChart/seriesConfig/index.js +14 -0
- package/esm/SankeyChart/seriesConfig/tooltipPosition.d.ts +3 -0
- package/esm/SankeyChart/seriesConfig/tooltipPosition.js +90 -0
- package/esm/SankeyChart/utils.d.ts +2 -2
- package/esm/SankeyChart/utils.js +1 -1
- package/esm/ScatterChartPro/ScatterChartPro.js +18 -0
- package/esm/hooks/useSankeySeries.d.ts +9 -2
- package/esm/hooks/useSankeySeries.js +15 -1
- package/esm/index.js +1 -1
- package/esm/typeOverloads/modules.d.ts +6 -1
- package/hooks/useSankeySeries.d.ts +9 -2
- package/hooks/useSankeySeries.js +15 -0
- package/index.js +1 -1
- package/package.json +5 -5
- package/typeOverloads/modules.d.ts +6 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SankeyNode } from "./sankey.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the horizontal node position of a node in a Sankey layout with left alignment.
|
|
4
|
+
* Returns (node.depth) to indicate the desired horizontal position of the node in the generated Sankey diagram.
|
|
5
|
+
*
|
|
6
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sankeyLeft(node: SankeyNode<{}, {}>): number;
|
|
9
|
+
/**
|
|
10
|
+
* Compute the horizontal node position of a node in a Sankey layout with right alignment.
|
|
11
|
+
* Returns (n - 1 - node.height) to indicate the desired horizontal position of the node in the generated Sankey diagram.
|
|
12
|
+
*
|
|
13
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
14
|
+
* @param n Total depth n of the graph (one plus the maximum node.depth)
|
|
15
|
+
*/
|
|
16
|
+
export declare function sankeyRight(node: SankeyNode<{}, {}>, n: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Compute the horizontal node position of a node in a Sankey layout with justified alignment.
|
|
19
|
+
* Like d3.sankeyLeft, except that nodes without any outgoing links are moved to the far right.
|
|
20
|
+
* Returns an integer between 0 and n - 1 that indicates the desired horizontal position of the node in the generated Sankey diagram.
|
|
21
|
+
*
|
|
22
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
23
|
+
* @param n Total depth n of the graph (one plus the maximum node.depth)
|
|
24
|
+
*/
|
|
25
|
+
export declare function sankeyJustify(node: SankeyNode<{}, {}>, n: number): number;
|
|
26
|
+
/**
|
|
27
|
+
* Compute the horizontal node position of a node in a Sankey layout with center alignment.
|
|
28
|
+
* Like d3.sankeyLeft, except that nodes without any incoming links are moved as right as possible.
|
|
29
|
+
* Returns an integer between 0 and n - 1 that indicates the desired horizontal position of the node in the generated Sankey diagram.
|
|
30
|
+
*
|
|
31
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sankeyCenter(node: SankeyNode<{}, {}>): number;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { min } from '@mui/x-charts-vendor/d3-array';
|
|
2
|
+
function targetDepth(d) {
|
|
3
|
+
return d.target.depth ?? 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Compute the horizontal node position of a node in a Sankey layout with left alignment.
|
|
8
|
+
* Returns (node.depth) to indicate the desired horizontal position of the node in the generated Sankey diagram.
|
|
9
|
+
*
|
|
10
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
11
|
+
*/
|
|
12
|
+
export function sankeyLeft(node) {
|
|
13
|
+
return node.depth ?? 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compute the horizontal node position of a node in a Sankey layout with right alignment.
|
|
18
|
+
* Returns (n - 1 - node.height) to indicate the desired horizontal position of the node in the generated Sankey diagram.
|
|
19
|
+
*
|
|
20
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
21
|
+
* @param n Total depth n of the graph (one plus the maximum node.depth)
|
|
22
|
+
*/
|
|
23
|
+
export function sankeyRight(node, n) {
|
|
24
|
+
return n - 1 - (node.height ?? 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compute the horizontal node position of a node in a Sankey layout with justified alignment.
|
|
29
|
+
* Like d3.sankeyLeft, except that nodes without any outgoing links are moved to the far right.
|
|
30
|
+
* Returns an integer between 0 and n - 1 that indicates the desired horizontal position of the node in the generated Sankey diagram.
|
|
31
|
+
*
|
|
32
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
33
|
+
* @param n Total depth n of the graph (one plus the maximum node.depth)
|
|
34
|
+
*/
|
|
35
|
+
export function sankeyJustify(node, n) {
|
|
36
|
+
return node.sourceLinks?.length ? node.depth ?? 0 : n - 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compute the horizontal node position of a node in a Sankey layout with center alignment.
|
|
41
|
+
* Like d3.sankeyLeft, except that nodes without any incoming links are moved as right as possible.
|
|
42
|
+
* Returns an integer between 0 and n - 1 that indicates the desired horizontal position of the node in the generated Sankey diagram.
|
|
43
|
+
*
|
|
44
|
+
* @param node Sankey node for which to calculate the horizontal node position.
|
|
45
|
+
*/
|
|
46
|
+
export function sankeyCenter(node) {
|
|
47
|
+
if (node.targetLinks?.length) {
|
|
48
|
+
return node.depth ?? 0;
|
|
49
|
+
}
|
|
50
|
+
if (node.sourceLinks?.length) {
|
|
51
|
+
return (min(node.sourceLinks, targetDepth) ?? 0) - 1;
|
|
52
|
+
}
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SankeyExtraProperties, SankeyGraph, SankeyLayout } from "./sankey.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get a Sankey layout generator.
|
|
4
|
+
*
|
|
5
|
+
* Invoking sankey() without generics, means the node type and link type assume no user-defined attributes, i.e.
|
|
6
|
+
* only the attributes internally used by the Sankey layout generator.
|
|
7
|
+
*
|
|
8
|
+
* Default nodes/links accessors are assumed.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sankey(): SankeyLayout<SankeyGraph<{}, {}>, {}, {}>;
|
|
11
|
+
/**
|
|
12
|
+
* Get a Sankey layout generator.
|
|
13
|
+
*
|
|
14
|
+
* Default nodes/links accessors are assumed.
|
|
15
|
+
*
|
|
16
|
+
* The first generic N refers to user-defined properties contained in the node data passed into
|
|
17
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
18
|
+
* SankeyNodeMinimal interface.
|
|
19
|
+
*
|
|
20
|
+
* The second generic L refers to user-defined properties contained in the link data passed into
|
|
21
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
22
|
+
* SankeyLinkMinimal interface.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sankey<N extends SankeyExtraProperties, L extends SankeyExtraProperties>(): SankeyLayout<SankeyGraph<N, L>, N, L>;
|
|
25
|
+
/**
|
|
26
|
+
* Get a Sankey layout generator.
|
|
27
|
+
*
|
|
28
|
+
* The nodes/links accessors need to be configured to work with the data type of the first argument passed
|
|
29
|
+
* in when invoking the Sankey layout generator.
|
|
30
|
+
*
|
|
31
|
+
* The first generic corresponds to the data type of the first argument passed in when invoking the Sankey layout generator,
|
|
32
|
+
* and its nodes/links accessors.
|
|
33
|
+
*
|
|
34
|
+
* The second generic N refers to user-defined properties contained in the node data passed into
|
|
35
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
36
|
+
* SankeyNodeMinimal interface.
|
|
37
|
+
*
|
|
38
|
+
* The third generic L refers to user-defined properties contained in the link data passed into
|
|
39
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
40
|
+
* SankeyLinkMinimal interface.
|
|
41
|
+
*/
|
|
42
|
+
export declare function sankey<Data, N extends SankeyExtraProperties, L extends SankeyExtraProperties>(): SankeyLayout<Data, N, L>;
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { max, min, sum } from '@mui/x-charts-vendor/d3-array';
|
|
3
|
+
import { sankeyJustify } from "./align.js";
|
|
4
|
+
|
|
5
|
+
// Helper types for internal use within the Sankey layout generator.
|
|
6
|
+
|
|
7
|
+
function constant(x) {
|
|
8
|
+
return function () {
|
|
9
|
+
return x;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function ascendingSourceBreadth(a, b) {
|
|
13
|
+
return ascendingBreadth(a.source, b.source) || a.index - b.index;
|
|
14
|
+
}
|
|
15
|
+
function ascendingTargetBreadth(a, b) {
|
|
16
|
+
return ascendingBreadth(a.target, b.target) || a.index - b.index;
|
|
17
|
+
}
|
|
18
|
+
function ascendingBreadth(a, b) {
|
|
19
|
+
return a.y0 - b.y0;
|
|
20
|
+
}
|
|
21
|
+
function value(d) {
|
|
22
|
+
return d.value;
|
|
23
|
+
}
|
|
24
|
+
function defaultId(d, e, f) {
|
|
25
|
+
return d.index;
|
|
26
|
+
}
|
|
27
|
+
function defaultNodes(graph) {
|
|
28
|
+
return graph.nodes;
|
|
29
|
+
}
|
|
30
|
+
function defaultLinks(graph) {
|
|
31
|
+
return graph.links;
|
|
32
|
+
}
|
|
33
|
+
function find(nodeById, id) {
|
|
34
|
+
const node = nodeById.get(id);
|
|
35
|
+
if (!node) {
|
|
36
|
+
throw new Error('missing: ' + id);
|
|
37
|
+
}
|
|
38
|
+
return node;
|
|
39
|
+
}
|
|
40
|
+
function computeLinkBreadths({
|
|
41
|
+
nodes
|
|
42
|
+
}) {
|
|
43
|
+
for (const node of nodes) {
|
|
44
|
+
let y0 = node.y0;
|
|
45
|
+
let y1 = y0;
|
|
46
|
+
for (const link of node.sourceLinks) {
|
|
47
|
+
link.y0 = y0 + link.width / 2;
|
|
48
|
+
y0 += link.width;
|
|
49
|
+
}
|
|
50
|
+
for (const link of node.targetLinks) {
|
|
51
|
+
link.y1 = y1 + link.width / 2;
|
|
52
|
+
y1 += link.width;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get a Sankey layout generator.
|
|
59
|
+
*
|
|
60
|
+
* Invoking sankey() without generics, means the node type and link type assume no user-defined attributes, i.e.
|
|
61
|
+
* only the attributes internally used by the Sankey layout generator.
|
|
62
|
+
*
|
|
63
|
+
* Default nodes/links accessors are assumed.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get a Sankey layout generator.
|
|
68
|
+
*
|
|
69
|
+
* Default nodes/links accessors are assumed.
|
|
70
|
+
*
|
|
71
|
+
* The first generic N refers to user-defined properties contained in the node data passed into
|
|
72
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
73
|
+
* SankeyNodeMinimal interface.
|
|
74
|
+
*
|
|
75
|
+
* The second generic L refers to user-defined properties contained in the link data passed into
|
|
76
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
77
|
+
* SankeyLinkMinimal interface.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get a Sankey layout generator.
|
|
82
|
+
*
|
|
83
|
+
* The nodes/links accessors need to be configured to work with the data type of the first argument passed
|
|
84
|
+
* in when invoking the Sankey layout generator.
|
|
85
|
+
*
|
|
86
|
+
* The first generic corresponds to the data type of the first argument passed in when invoking the Sankey layout generator,
|
|
87
|
+
* and its nodes/links accessors.
|
|
88
|
+
*
|
|
89
|
+
* The second generic N refers to user-defined properties contained in the node data passed into
|
|
90
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
91
|
+
* SankeyNodeMinimal interface.
|
|
92
|
+
*
|
|
93
|
+
* The third generic L refers to user-defined properties contained in the link data passed into
|
|
94
|
+
* Sankey layout generator. These properties are IN EXCESS to the properties explicitly identified in the
|
|
95
|
+
* SankeyLinkMinimal interface.
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
export function sankey() {
|
|
99
|
+
let x0 = 0,
|
|
100
|
+
y0 = 0,
|
|
101
|
+
x1 = 1,
|
|
102
|
+
y1 = 1; // extent
|
|
103
|
+
let dx = 24; // nodeWidth
|
|
104
|
+
let dy = 8,
|
|
105
|
+
py; // nodePadding
|
|
106
|
+
let id = defaultId;
|
|
107
|
+
let align = sankeyJustify;
|
|
108
|
+
let sort;
|
|
109
|
+
let linkSort;
|
|
110
|
+
let nodes = defaultNodes;
|
|
111
|
+
let links = defaultLinks;
|
|
112
|
+
let iterations = 6;
|
|
113
|
+
function sankey() {
|
|
114
|
+
const graph = {
|
|
115
|
+
nodes: nodes.apply(null, arguments),
|
|
116
|
+
links: links.apply(null, arguments)
|
|
117
|
+
// It is not really complete at this point, but will mostly be once
|
|
118
|
+
// computeNodeLinks(graph) is called.
|
|
119
|
+
};
|
|
120
|
+
computeNodeLinks(graph);
|
|
121
|
+
computeNodeValues(graph);
|
|
122
|
+
computeNodeDepths(graph);
|
|
123
|
+
computeNodeHeights(graph);
|
|
124
|
+
computeNodeBreadths(graph);
|
|
125
|
+
computeLinkBreadths(graph);
|
|
126
|
+
return graph;
|
|
127
|
+
}
|
|
128
|
+
sankey.update = function (graph) {
|
|
129
|
+
computeLinkBreadths(graph);
|
|
130
|
+
return graph;
|
|
131
|
+
};
|
|
132
|
+
sankey.nodeId = function (_) {
|
|
133
|
+
return arguments.length ? (id = typeof _ === 'function' ? _ : constant(_), sankey) : id;
|
|
134
|
+
};
|
|
135
|
+
sankey.nodeAlign = function (_) {
|
|
136
|
+
return arguments.length ? (align = typeof _ === 'function' ? _ : constant(_), sankey) : align;
|
|
137
|
+
};
|
|
138
|
+
sankey.nodeSort = function (_) {
|
|
139
|
+
return arguments.length ? (sort = _, sankey) : sort;
|
|
140
|
+
};
|
|
141
|
+
sankey.nodeWidth = function (_) {
|
|
142
|
+
return arguments.length ? (dx = +_, sankey) : dx;
|
|
143
|
+
};
|
|
144
|
+
sankey.nodePadding = function (_) {
|
|
145
|
+
return arguments.length ? (dy = py = +_, sankey) : dy;
|
|
146
|
+
};
|
|
147
|
+
sankey.nodes = function (_) {
|
|
148
|
+
return arguments.length ? (nodes = typeof _ === 'function' ? _ : constant(_), sankey) : nodes;
|
|
149
|
+
};
|
|
150
|
+
sankey.links = function (_) {
|
|
151
|
+
return arguments.length ? (links = typeof _ === 'function' ? _ : constant(_), sankey) : links;
|
|
152
|
+
};
|
|
153
|
+
sankey.linkSort = function (_) {
|
|
154
|
+
return arguments.length ? (linkSort = _, sankey) : linkSort;
|
|
155
|
+
};
|
|
156
|
+
sankey.size = function (_) {
|
|
157
|
+
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
|
|
158
|
+
};
|
|
159
|
+
sankey.extent = function (_) {
|
|
160
|
+
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
|
|
161
|
+
};
|
|
162
|
+
sankey.iterations = function (_) {
|
|
163
|
+
return arguments.length ? (iterations = +_, sankey) : iterations;
|
|
164
|
+
};
|
|
165
|
+
function computeNodeLinks({
|
|
166
|
+
nodes,
|
|
167
|
+
links
|
|
168
|
+
}) {
|
|
169
|
+
for (const [i, node] of nodes.entries()) {
|
|
170
|
+
node.index = i;
|
|
171
|
+
node.sourceLinks = [];
|
|
172
|
+
node.targetLinks = [];
|
|
173
|
+
}
|
|
174
|
+
const nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d]));
|
|
175
|
+
for (const [i, link] of links.entries()) {
|
|
176
|
+
link.index = i;
|
|
177
|
+
let {
|
|
178
|
+
source,
|
|
179
|
+
target
|
|
180
|
+
} = link;
|
|
181
|
+
if (typeof source !== 'object') source = link.source = find(nodeById, source);
|
|
182
|
+
if (typeof target !== 'object') target = link.target = find(nodeById, target);
|
|
183
|
+
source.sourceLinks.push(link);
|
|
184
|
+
target.targetLinks.push(link);
|
|
185
|
+
}
|
|
186
|
+
if (linkSort != null) {
|
|
187
|
+
for (const {
|
|
188
|
+
sourceLinks,
|
|
189
|
+
targetLinks
|
|
190
|
+
} of nodes) {
|
|
191
|
+
sourceLinks.sort(linkSort);
|
|
192
|
+
targetLinks.sort(linkSort);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function computeNodeValues({
|
|
197
|
+
nodes
|
|
198
|
+
}) {
|
|
199
|
+
for (const node of nodes) {
|
|
200
|
+
node.value = node.fixedValue === undefined ? Math.max(sum(node.sourceLinks, value), sum(node.targetLinks, value)) : node.fixedValue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function computeNodeDepths({
|
|
204
|
+
nodes
|
|
205
|
+
}) {
|
|
206
|
+
const n = nodes.length;
|
|
207
|
+
let current = new Set(nodes);
|
|
208
|
+
let next = new Set();
|
|
209
|
+
let x = 0;
|
|
210
|
+
while (current.size) {
|
|
211
|
+
for (const node of current) {
|
|
212
|
+
node.depth = x;
|
|
213
|
+
for (const {
|
|
214
|
+
target
|
|
215
|
+
} of node.sourceLinks) {
|
|
216
|
+
next.add(target);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (++x > n) throw new Error('circular link');
|
|
220
|
+
current = next;
|
|
221
|
+
next = new Set();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function computeNodeHeights({
|
|
225
|
+
nodes
|
|
226
|
+
}) {
|
|
227
|
+
const n = nodes.length;
|
|
228
|
+
let current = new Set(nodes);
|
|
229
|
+
let next = new Set();
|
|
230
|
+
let x = 0;
|
|
231
|
+
while (current.size) {
|
|
232
|
+
for (const node of current) {
|
|
233
|
+
node.height = x;
|
|
234
|
+
for (const {
|
|
235
|
+
source
|
|
236
|
+
} of node.targetLinks) {
|
|
237
|
+
next.add(source);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (++x > n) throw new Error('circular link');
|
|
241
|
+
current = next;
|
|
242
|
+
next = new Set();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function computeNodeLayers({
|
|
246
|
+
nodes
|
|
247
|
+
}) {
|
|
248
|
+
const x = (max(nodes, d => d.depth) ?? 0) + 1;
|
|
249
|
+
const kx = (x1 - x0 - dx) / (x - 1);
|
|
250
|
+
const columns = new Array(x);
|
|
251
|
+
for (const node of nodes) {
|
|
252
|
+
const i = Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x))));
|
|
253
|
+
node.layer = i;
|
|
254
|
+
node.x0 = x0 + i * kx;
|
|
255
|
+
node.x1 = node.x0 + dx;
|
|
256
|
+
if (columns[i]) columns[i].push(node);else columns[i] = [node];
|
|
257
|
+
}
|
|
258
|
+
if (sort) for (const column of columns) {
|
|
259
|
+
column.sort(sort);
|
|
260
|
+
}
|
|
261
|
+
return columns;
|
|
262
|
+
}
|
|
263
|
+
function initializeNodeBreadths(columns) {
|
|
264
|
+
const ky = min(columns, c => (y1 - y0 - (c.length - 1) * py) / sum(c, value));
|
|
265
|
+
for (const nodes of columns) {
|
|
266
|
+
let y = y0;
|
|
267
|
+
for (const node of nodes) {
|
|
268
|
+
node.y0 = y;
|
|
269
|
+
node.y1 = y + node.value * ky;
|
|
270
|
+
y = node.y1 + py;
|
|
271
|
+
for (const link of node.sourceLinks) {
|
|
272
|
+
link.width = link.value * ky;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
y = (y1 - y + py) / (nodes.length + 1);
|
|
276
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
277
|
+
const node = nodes[i];
|
|
278
|
+
node.y0 += y * (i + 1);
|
|
279
|
+
node.y1 += y * (i + 1);
|
|
280
|
+
}
|
|
281
|
+
reorderLinks(nodes);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function computeNodeBreadths(graph) {
|
|
285
|
+
const columns = computeNodeLayers(graph);
|
|
286
|
+
py = Math.min(dy, (y1 - y0) / (max(columns, c => c.length) - 1));
|
|
287
|
+
initializeNodeBreadths(columns);
|
|
288
|
+
for (let i = 0; i < iterations; ++i) {
|
|
289
|
+
const alpha = Math.pow(0.99, i);
|
|
290
|
+
const beta = Math.max(1 - alpha, (i + 1) / iterations);
|
|
291
|
+
relaxRightToLeft(columns, alpha, beta);
|
|
292
|
+
relaxLeftToRight(columns, alpha, beta);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Reposition each node based on its incoming (target) links.
|
|
297
|
+
function relaxLeftToRight(columns, alpha, beta) {
|
|
298
|
+
for (let i = 1, n = columns.length; i < n; ++i) {
|
|
299
|
+
const column = columns[i];
|
|
300
|
+
for (const target of column) {
|
|
301
|
+
let y = 0;
|
|
302
|
+
let w = 0;
|
|
303
|
+
for (const {
|
|
304
|
+
source,
|
|
305
|
+
value
|
|
306
|
+
} of target.targetLinks) {
|
|
307
|
+
let v = value * (target.layer - source.layer);
|
|
308
|
+
y += targetTop(source, target) * v;
|
|
309
|
+
w += v;
|
|
310
|
+
}
|
|
311
|
+
if (!(w > 0)) continue;
|
|
312
|
+
let dy = (y / w - target.y0) * alpha;
|
|
313
|
+
target.y0 += dy;
|
|
314
|
+
target.y1 += dy;
|
|
315
|
+
reorderNodeLinks(target);
|
|
316
|
+
}
|
|
317
|
+
if (sort === undefined) column.sort(ascendingBreadth);
|
|
318
|
+
resolveCollisions(column, beta);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Reposition each node based on its outgoing (source) links.
|
|
323
|
+
function relaxRightToLeft(columns, alpha, beta) {
|
|
324
|
+
for (let n = columns.length, i = n - 2; i >= 0; --i) {
|
|
325
|
+
const column = columns[i];
|
|
326
|
+
for (const source of column) {
|
|
327
|
+
let y = 0;
|
|
328
|
+
let w = 0;
|
|
329
|
+
for (const {
|
|
330
|
+
target,
|
|
331
|
+
value
|
|
332
|
+
} of source.sourceLinks) {
|
|
333
|
+
let v = value * (target.layer - source.layer);
|
|
334
|
+
y += sourceTop(source, target) * v;
|
|
335
|
+
w += v;
|
|
336
|
+
}
|
|
337
|
+
if (!(w > 0)) continue;
|
|
338
|
+
let dy = (y / w - source.y0) * alpha;
|
|
339
|
+
source.y0 += dy;
|
|
340
|
+
source.y1 += dy;
|
|
341
|
+
reorderNodeLinks(source);
|
|
342
|
+
}
|
|
343
|
+
if (sort === undefined) column.sort(ascendingBreadth);
|
|
344
|
+
resolveCollisions(column, beta);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function resolveCollisions(nodes, alpha) {
|
|
348
|
+
const i = nodes.length >> 1;
|
|
349
|
+
const subject = nodes[i];
|
|
350
|
+
resolveCollisionsBottomToTop(nodes, subject.y0 - py, i - 1, alpha);
|
|
351
|
+
resolveCollisionsTopToBottom(nodes, subject.y1 + py, i + 1, alpha);
|
|
352
|
+
resolveCollisionsBottomToTop(nodes, y1, nodes.length - 1, alpha);
|
|
353
|
+
resolveCollisionsTopToBottom(nodes, y0, 0, alpha);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Push any overlapping nodes down.
|
|
357
|
+
function resolveCollisionsTopToBottom(nodes, y, i, alpha) {
|
|
358
|
+
for (; i < nodes.length; ++i) {
|
|
359
|
+
const node = nodes[i];
|
|
360
|
+
const dy = (y - node.y0) * alpha;
|
|
361
|
+
if (dy > 1e-6) node.y0 += dy, node.y1 += dy;
|
|
362
|
+
y = node.y1 + py;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Push any overlapping nodes up.
|
|
367
|
+
function resolveCollisionsBottomToTop(nodes, y, i, alpha) {
|
|
368
|
+
for (; i >= 0; --i) {
|
|
369
|
+
const node = nodes[i];
|
|
370
|
+
const dy = (node.y1 - y) * alpha;
|
|
371
|
+
if (dy > 1e-6) node.y0 -= dy, node.y1 -= dy;
|
|
372
|
+
y = node.y0 - py;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function reorderNodeLinks({
|
|
376
|
+
sourceLinks,
|
|
377
|
+
targetLinks
|
|
378
|
+
}) {
|
|
379
|
+
if (linkSort === undefined) {
|
|
380
|
+
for (const {
|
|
381
|
+
source: {
|
|
382
|
+
sourceLinks
|
|
383
|
+
}
|
|
384
|
+
} of targetLinks) {
|
|
385
|
+
sourceLinks.sort(ascendingTargetBreadth);
|
|
386
|
+
}
|
|
387
|
+
for (const {
|
|
388
|
+
target: {
|
|
389
|
+
targetLinks
|
|
390
|
+
}
|
|
391
|
+
} of sourceLinks) {
|
|
392
|
+
targetLinks.sort(ascendingSourceBreadth);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function reorderLinks(nodes) {
|
|
397
|
+
if (linkSort === undefined) {
|
|
398
|
+
for (const {
|
|
399
|
+
sourceLinks,
|
|
400
|
+
targetLinks
|
|
401
|
+
} of nodes) {
|
|
402
|
+
sourceLinks?.sort(ascendingTargetBreadth);
|
|
403
|
+
targetLinks?.sort(ascendingSourceBreadth);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Returns the target.y0 that would produce an ideal link from source to target.
|
|
409
|
+
function targetTop(source, target) {
|
|
410
|
+
let y = source.y0 - (source.sourceLinks.length - 1) * py / 2;
|
|
411
|
+
for (const {
|
|
412
|
+
target: node,
|
|
413
|
+
width
|
|
414
|
+
} of source.sourceLinks) {
|
|
415
|
+
if (node === target) break;
|
|
416
|
+
y += width + py;
|
|
417
|
+
}
|
|
418
|
+
for (const {
|
|
419
|
+
source: node,
|
|
420
|
+
width
|
|
421
|
+
} of target.targetLinks) {
|
|
422
|
+
if (node === source) break;
|
|
423
|
+
y -= width;
|
|
424
|
+
}
|
|
425
|
+
return y;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Returns the source.y0 that would produce an ideal link from source to target.
|
|
429
|
+
function sourceTop(source, target) {
|
|
430
|
+
let y = target.y0 - (target.targetLinks.length - 1) * py / 2;
|
|
431
|
+
for (const {
|
|
432
|
+
source: node,
|
|
433
|
+
width
|
|
434
|
+
} of target.targetLinks) {
|
|
435
|
+
if (node === source) break;
|
|
436
|
+
y += width + py;
|
|
437
|
+
}
|
|
438
|
+
for (const {
|
|
439
|
+
target: node,
|
|
440
|
+
width
|
|
441
|
+
} of source.sourceLinks) {
|
|
442
|
+
if (node === target) break;
|
|
443
|
+
y -= width;
|
|
444
|
+
}
|
|
445
|
+
return y;
|
|
446
|
+
}
|
|
447
|
+
return sankey;
|
|
448
|
+
}
|