@@ -23,6 +23,8 @@ var repeat = gup.repeat;
2323var unwrap = gup . unwrap ;
2424var interpolateNumber = require ( 'd3-interpolate' ) . interpolateNumber ;
2525
26+ var Plotly = require ( '../../plot_api/plot_api' ) ;
27+
2628// view models
2729
2830function sankeyModel ( layout , d , traceIndex ) {
@@ -163,6 +165,25 @@ function sankeyModel(layout, d, traceIndex) {
163165 }
164166 computeLinkConcentrations ( ) ;
165167
168+ // Force node position
169+ if ( trace . node . x . length !== 0 && trace . node . y . length !== 0 ) {
170+ var i ;
171+ for ( i = 0 ; i < Math . min ( trace . node . x . length , trace . node . y . length , graph . nodes . length ) ; i ++ ) {
172+ if ( trace . node . x [ i ] && trace . node . y [ i ] ) {
173+ var pos = [ trace . node . x [ i ] * width , trace . node . y [ i ] * height ] ;
174+ graph . nodes [ i ] . x0 = pos [ 0 ] - nodeThickness / 2 ;
175+ graph . nodes [ i ] . x1 = pos [ 0 ] + nodeThickness / 2 ;
176+
177+ var nodeHeight = graph . nodes [ i ] . y1 - graph . nodes [ i ] . y0 ;
178+ graph . nodes [ i ] . y0 = pos [ 1 ] - nodeHeight / 2 ;
179+ graph . nodes [ i ] . y1 = pos [ 1 ] + nodeHeight / 2 ;
180+ }
181+ }
182+ // Update links
183+ sankey . update ( graph ) ;
184+ }
185+
186+
166187 return {
167188 circular : circular ,
168189 key : traceIndex ,
@@ -399,6 +420,7 @@ function nodeModel(d, n) {
399420 partOfGroup : n . partOfGroup || false ,
400421 group : n . group ,
401422 traceId : d . key ,
423+ trace : d . trace ,
402424 node : n ,
403425 nodePad : d . nodePad ,
404426 nodeLineColor : d . nodeLineColor ,
@@ -425,7 +447,8 @@ function nodeModel(d, n) {
425447 graph : d . graph ,
426448 arrangement : d . arrangement ,
427449 uniqueNodeLabelPathId : [ d . guid , d . key , key ] . join ( '_' ) ,
428- interactionState : d . interactionState
450+ interactionState : d . interactionState ,
451+ figure : d
429452 } ;
430453}
431454
@@ -509,7 +532,7 @@ function attachPointerEvents(selection, sankey, eventSet) {
509532 } ) ;
510533}
511534
512- function attachDragHandler ( sankeyNode , sankeyLink , callbacks ) {
535+ function attachDragHandler ( sankeyNode , sankeyLink , callbacks , gd ) {
513536 var dragBehavior = d3 . behavior . drag ( )
514537 . origin ( function ( d ) {
515538 return {
@@ -520,6 +543,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
520543
521544 . on ( 'dragstart' , function ( d ) {
522545 if ( d . arrangement === 'fixed' ) return ;
546+ Lib . ensureSingle ( gd . _fullLayout . _infolayer , 'g' , 'dragcover' , function ( s ) {
547+ gd . _fullLayout . _dragCover = s ;
548+ } ) ;
523549 Lib . raiseToTop ( this ) ;
524550 d . interactionState . dragInProgress = d . node ;
525551
@@ -533,9 +559,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
533559 if ( d . forceLayouts [ forceKey ] ) {
534560 d . forceLayouts [ forceKey ] . alpha ( 1 ) ;
535561 } else { // make a forceLayout if needed
536- attachForce ( sankeyNode , forceKey , d ) ;
562+ attachForce ( sankeyNode , forceKey , d , gd ) ;
537563 }
538- startForce ( sankeyNode , sankeyLink , d , forceKey ) ;
564+ startForce ( sankeyNode , sankeyLink , d , forceKey , gd ) ;
539565 }
540566 } )
541567
@@ -553,8 +579,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
553579 d . node . x0 = x - d . visibleWidth / 2 ;
554580 d . node . x1 = x + d . visibleWidth / 2 ;
555581 }
556- d . node . y0 = Math . max ( 0 , Math . min ( d . size - d . visibleHeight , y ) ) ;
557- d . node . y1 = d . node . y0 + d . visibleHeight ;
582+ y = Math . max ( 0 , Math . min ( d . size - d . visibleHeight / 2 , y ) ) ;
583+ d . node . y0 = y - d . visibleHeight / 2 ;
584+ d . node . y1 = y + d . visibleHeight / 2 ;
558585 }
559586
560587 saveCurrentDragPosition ( d . node ) ;
@@ -570,14 +597,15 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
570597 d . node . childrenNodes [ i ] . x = d . node . x ;
571598 d . node . childrenNodes [ i ] . y = d . node . y ;
572599 }
600+ if ( d . arrangement !== 'snap' ) persistFinalNodePositions ( d , gd ) ;
573601 } ) ;
574602
575603 sankeyNode
576604 . on ( '.drag' , null ) // remove possible previous handlers
577605 . call ( dragBehavior ) ;
578606}
579607
580- function attachForce ( sankeyNode , forceKey , d ) {
608+ function attachForce ( sankeyNode , forceKey , d , gd ) {
581609 // Attach force to nodes in the same column (same x coordinate)
582610 switchToForceFormat ( d . graph . nodes ) ;
583611 var nodes = d . graph . nodes
@@ -590,11 +618,11 @@ function attachForce(sankeyNode, forceKey, d) {
590618 . radius ( function ( n ) { return n . dy / 2 + d . nodePad / 2 ; } )
591619 . strength ( 1 )
592620 . iterations ( c . forceIterations ) )
593- . force ( 'constrain' , snappingForce ( sankeyNode , forceKey , nodes , d ) )
621+ . force ( 'constrain' , snappingForce ( sankeyNode , forceKey , nodes , d , gd ) )
594622 . stop ( ) ;
595623}
596624
597- function startForce ( sankeyNode , sankeyLink , d , forceKey ) {
625+ function startForce ( sankeyNode , sankeyLink , d , forceKey , gd ) {
598626 window . requestAnimationFrame ( function faster ( ) {
599627 var i ;
600628 for ( i = 0 ; i < c . forceTicksPerFrame ; i ++ ) {
@@ -609,6 +637,14 @@ function startForce(sankeyNode, sankeyLink, d, forceKey) {
609637
610638 if ( d . forceLayouts [ forceKey ] . alpha ( ) > 0 ) {
611639 window . requestAnimationFrame ( faster ) ;
640+ } else {
641+ // Make sure the final x position is equal to its original value
642+ // necessary because the force simulation will have numerical error
643+ var x = d . node . originalX ;
644+ d . node . x0 = x - d . visibleWidth / 2 ;
645+ d . node . x1 = x + d . visibleWidth / 2 ;
646+
647+ persistFinalNodePositions ( d , gd ) ;
612648 }
613649 } ) ;
614650}
@@ -628,13 +664,31 @@ function snappingForce(sankeyNode, forceKey, nodes, d) {
628664 maxVelocity = Math . max ( maxVelocity , Math . abs ( n . vx ) , Math . abs ( n . vy ) ) ;
629665 }
630666 if ( ! d . interactionState . dragInProgress && maxVelocity < 0.1 && d . forceLayouts [ forceKey ] . alpha ( ) > 0 ) {
631- d . forceLayouts [ forceKey ] . alpha ( 0 ) ;
667+ d . forceLayouts [ forceKey ] . alpha ( 0 ) ; // This will stop the animation loop
632668 }
633669 } ;
634670}
635671
636672// basic data utilities
637673
674+ function persistFinalNodePositions ( d , gd ) {
675+ var x = [ ] ;
676+ var y = [ ] ;
677+ for ( var i = 0 ; i < d . graph . nodes . length ; i ++ ) {
678+ var nodeX = ( d . graph . nodes [ i ] . x0 + d . graph . nodes [ i ] . x1 ) / 2 ;
679+ var nodeY = ( d . graph . nodes [ i ] . y0 + d . graph . nodes [ i ] . y1 ) / 2 ;
680+ x . push ( nodeX / d . figure . width ) ;
681+ y . push ( nodeY / d . figure . height ) ;
682+ }
683+ Plotly . restyle ( gd , {
684+ 'node.x' : [ x ] ,
685+ 'node.y' : [ y ]
686+ } , d . trace . index )
687+ . then ( function ( ) {
688+ if ( gd . _fullLayout . _dragCover ) gd . _fullLayout . _dragCover . remove ( ) ;
689+ } ) ;
690+ }
691+
638692function persistOriginalPlace ( nodes ) {
639693 var distinctLayerPositions = [ ] ;
640694 var i ;
@@ -795,7 +849,7 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
795849
796850 sankeyNode
797851 . call ( attachPointerEvents , sankey , callbacks . nodeEvents )
798- . call ( attachDragHandler , sankeyLink , callbacks ) ; // has to be here as it binds sankeyLink
852+ . call ( attachDragHandler , sankeyLink , callbacks , gd ) ; // has to be here as it binds sankeyLink
799853
800854 sankeyNode . transition ( )
801855 . ease ( c . ease ) . duration ( c . duration )
0 commit comments