diff --git a/src/chart/sankey/SankeySeries.ts b/src/chart/sankey/SankeySeries.ts index ebd85227ab..a3ca46f19e 100644 --- a/src/chart/sankey/SankeySeries.ts +++ b/src/chart/sankey/SankeySeries.ts @@ -136,6 +136,11 @@ export interface SankeySeriesOption * The number of iterations to change the position of the node */ layoutIterations?: number + /** + * Whether to sort nodes by current coordinate when resolving collisions. + * Set to false to preserve the original node order in each depth column. + */ + sortNodes?: boolean nodeAlign?: 'justify' | 'left' | 'right' // TODO justify should be auto @@ -318,6 +323,7 @@ class SankeySeriesModel extends SeriesModel { draggable: true, layoutIterations: 32, + sortNodes: true, // true | false | 'move' | 'scale', see module:component/helper/RoamController. roam: false, diff --git a/src/chart/sankey/sankeyLayout.ts b/src/chart/sankey/sankeyLayout.ts index 8e9c8d22bb..8e0ce9e619 100644 --- a/src/chart/sankey/sankeyLayout.ts +++ b/src/chart/sankey/sankeyLayout.ts @@ -57,8 +57,9 @@ export default function sankeyLayout(ecModel: GlobalModel, api: ExtensionAPI) { const orient = seriesModel.get('orient'); const nodeAlign = seriesModel.get('nodeAlign'); + const sortNodes = seriesModel.get('sortNodes'); - layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign); + layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign, sortNodes); }); } @@ -71,10 +72,11 @@ function layoutSankey( height: number, iterations: number, orient: LayoutOrient, - nodeAlign: SankeySeriesOption['nodeAlign'] + nodeAlign: SankeySeriesOption['nodeAlign'], + sortNodes: boolean ) { computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign); - computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient); + computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient, sortNodes); computeEdgeDepths(nodes, orient); } @@ -257,6 +259,7 @@ function scaleNodeBreadths(nodes: GraphNode[], kx: number, orient: LayoutOrient) * @param nodeGap the vertical distance between two nodes * in the same column. * @param iterations the number of iterations for the algorithm + * @param sortNodes whether to sort the nodes by y-position */ function computeNodeDepths( nodes: GraphNode[], @@ -265,21 +268,22 @@ function computeNodeDepths( width: number, nodeGap: number, iterations: number, - orient: LayoutOrient + orient: LayoutOrient, + sortNodes: boolean ) { const nodesByBreadth = prepareNodesByBreadth(nodes, orient); initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); for (let alpha = 1; iterations > 0; iterations--) { // 0.99 is a experience parameter, ensure that each iterations of // changes as small as possible. alpha *= 0.99; relaxRightToLeft(nodesByBreadth, alpha, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); relaxLeftToRight(nodesByBreadth, alpha, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); } } @@ -355,13 +359,16 @@ function resolveCollisions( nodeGap: number, height: number, width: number, - orient: LayoutOrient + orient: LayoutOrient, + sortNodes: boolean ) { const keyAttr = orient === 'vertical' ? 'x' : 'y'; zrUtil.each(nodesByBreadth, function (nodes) { - nodes.sort(function (a, b) { - return a.getLayout()[keyAttr] - b.getLayout()[keyAttr]; - }); + if (sortNodes !== false) { + nodes.sort(function (a, b) { + return a.getLayout()[keyAttr] - b.getLayout()[keyAttr]; + }); + } let nodeX; let node; let dy; diff --git a/test/sankey-node-sorting.html b/test/sankey-node-sorting.html new file mode 100644 index 0000000000..2ceb98ac4e --- /dev/null +++ b/test/sankey-node-sorting.html @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + +