@@ -34,7 +34,36 @@ function convertToD3Sankey(trace) {
3434 components [ cscale . label ] = scale ;
3535 }
3636
37- var nodeCount = nodeSpec . label . length ;
37+ var maxNodeId = 0 ;
38+ for ( i = 0 ; i < linkSpec . value . length ; i ++ ) {
39+ if ( linkSpec . source [ i ] > maxNodeId ) maxNodeId = linkSpec . source [ i ] ;
40+ if ( linkSpec . target [ i ] > maxNodeId ) maxNodeId = linkSpec . target [ i ] ;
41+ }
42+ var nodeCount = maxNodeId + 1 ;
43+
44+ // Group nodes
45+ var j ;
46+ var groups = trace . node . groups ;
47+ var groupLookup = { } ;
48+ for ( i = 0 ; i < groups . length ; i ++ ) {
49+ var group = groups [ i ] ;
50+ // Build a lookup table to quickly find in which group a node is
51+ for ( j = 0 ; j < group . length ; j ++ ) {
52+ var nodeIndex = group [ j ] ;
53+ var groupIndex = nodeCount + i ;
54+ if ( groupLookup . hasOwnProperty ( nodeIndex ) ) {
55+ Lib . warn ( 'Node ' + nodeIndex + ' is already part of a group.' ) ;
56+ } else {
57+ groupLookup [ nodeIndex ] = groupIndex ;
58+ }
59+ }
60+ }
61+
62+ // Process links
63+ var groupedLinks = {
64+ source : [ ] ,
65+ target : [ ]
66+ } ;
3867 for ( i = 0 ; i < linkSpec . value . length ; i ++ ) {
3968 var val = linkSpec . value [ i ] ;
4069 // remove negative values, but keep zeros with special treatment
@@ -44,6 +73,21 @@ function convertToD3Sankey(trace) {
4473 continue ;
4574 }
4675
76+ // Remove links that are within the same group
77+ if ( groupLookup . hasOwnProperty ( source ) && groupLookup . hasOwnProperty ( target ) && groupLookup [ source ] === groupLookup [ target ] ) {
78+ continue ;
79+ }
80+
81+ // if link targets a node in the group, relink target to that group
82+ if ( groupLookup . hasOwnProperty ( target ) ) {
83+ target = groupLookup [ target ] ;
84+ }
85+
86+ // if link originates from a node in a group, relink source to that group
87+ if ( groupLookup . hasOwnProperty ( source ) ) {
88+ source = groupLookup [ source ] ;
89+ }
90+
4791 source = + source ;
4892 target = + target ;
4993 linkedNodes [ source ] = linkedNodes [ target ] = true ;
@@ -63,42 +107,46 @@ function convertToD3Sankey(trace) {
63107 target : target ,
64108 value : + val
65109 } ) ;
110+
111+ groupedLinks . source . push ( source ) ;
112+ groupedLinks . target . push ( target ) ;
66113 }
67114
115+ // Process nodes
116+ var totalCount = nodeCount + groups . length ;
68117 var hasNodeColorArray = isArrayOrTypedArray ( nodeSpec . color ) ;
69118 var nodes = [ ] ;
70- var removedNodes = false ;
71- var nodeIndices = { } ;
72-
73- for ( i = 0 ; i < nodeCount ; i ++ ) {
74- if ( linkedNodes [ i ] ) {
75- var l = nodeSpec . label [ i ] ;
76- nodeIndices [ i ] = nodes . length ;
77- nodes . push ( {
78- pointNumber : i ,
79- label : l ,
80- color : hasNodeColorArray ? nodeSpec . color [ i ] : nodeSpec . color
81- } ) ;
82- } else removedNodes = true ;
119+ for ( i = 0 ; i < totalCount ; i ++ ) {
120+ if ( ! linkedNodes [ i ] ) continue ;
121+ var l = nodeSpec . label [ i ] ;
122+
123+ nodes . push ( {
124+ group : ( i > nodeCount - 1 ) ,
125+ childrenNodes : [ ] ,
126+ pointNumber : i ,
127+ label : l ,
128+ color : hasNodeColorArray ? nodeSpec . color [ i ] : nodeSpec . color
129+ } ) ;
83130 }
84131
85- // need to re-index links now, since we didn't put all the nodes in
86- if ( removedNodes ) {
87- for ( i = 0 ; i < links . length ; i ++ ) {
88- links [ i ] . source = nodeIndices [ links [ i ] . source ] ;
89- links [ i ] . target = nodeIndices [ links [ i ] . target ] ;
90- }
132+ // Check if we have circularity on the resulting graph
133+ var circular = false ;
134+ if ( circularityPresent ( totalCount , groupedLinks . source , groupedLinks . target ) ) {
135+ circular = true ;
91136 }
92137
93138 return {
139+ circular : circular ,
94140 links : links ,
95- nodes : nodes
141+ nodes : nodes ,
142+
143+ // Data structure for groups
144+ groups : groups ,
145+ groupLookup : groupLookup
96146 } ;
97147}
98148
99- function circularityPresent ( nodeList , sources , targets ) {
100-
101- var nodeLen = nodeList . length ;
149+ function circularityPresent ( nodeLen , sources , targets ) {
102150 var nodes = Lib . init2dArray ( nodeLen , 0 ) ;
103151
104152 for ( var i = 0 ; i < Math . min ( sources . length , targets . length ) ; i ++ ) {
@@ -120,16 +168,15 @@ function circularityPresent(nodeList, sources, targets) {
120168}
121169
122170module . exports = function calc ( gd , trace ) {
123- var circular = false ;
124- if ( circularityPresent ( trace . node . label , trace . link . source , trace . link . target ) ) {
125- circular = true ;
126- }
127-
128171 var result = convertToD3Sankey ( trace ) ;
129172
130173 return wrap ( {
131- circular : circular ,
174+ circular : result . circular ,
132175 _nodes : result . nodes ,
133- _links : result . links
176+ _links : result . links ,
177+
178+ // Data structure for grouping
179+ _groups : result . groups ,
180+ _groupLookup : result . groupLookup ,
134181 } ) ;
135182} ;
0 commit comments