Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/chart/sankey/SankeySeries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -318,6 +323,7 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
draggable: true,

layoutIterations: 32,
sortNodes: true,

// true | false | 'move' | 'scale', see module:component/helper/RoamController.
roam: false,
Expand Down
29 changes: 18 additions & 11 deletions src/chart/sankey/sankeyLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -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[],
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
Expand Down
97 changes: 97 additions & 0 deletions test/sankey-node-sorting.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.