@ -32,6 +32,7 @@
* /
let util = require ( '../../util' ) ;
var NetworkUtil = require ( '../NetworkUtil' ) . default ;
var { HorizontalStrategy , VerticalStrategy } = require ( './components/DirectionStrategy.js' ) ;
/ * *
@ -109,6 +110,8 @@ class HierarchicalStatus {
* @ param { string | number } treeId
* /
setTreeIndex ( node , treeId ) {
if ( treeId === undefined ) return ; // Don't bother
if ( this . trees [ node . id ] === undefined ) {
this . trees [ node . id ] = treeId ;
this . treeIndex = Math . max ( treeId , this . treeIndex ) ;
@ -367,7 +370,7 @@ class LayoutEngine {
return ;
}
// get the type of static smooth curve in case it is required
let type = this . getStatic Type( ) ;
let type = this . direction . curve Type( ) ;
// force all edges into static smooth curves.
this . body . emitter . emit ( '_forceDisableDynamicCurves' , type , false ) ;
@ -406,6 +409,8 @@ class LayoutEngine {
}
}
this . setDirectionStrategy ( ) ;
this . body . emitter . emit ( '_resetHierarchicalLayout' ) ;
// because the hierarchical system needs it's own physics and smooth curve settings,
// we adapt the other options if needed.
@ -451,7 +456,7 @@ class LayoutEngine {
}
// get the type of static smooth curve in case it is required
let type = this . getStatic Type( ) ;
let type = this . direction . curve Type( ) ;
// disable smooth curves if nothing is defined. If smooth curves have been turned on,
// turn them into static smooth curves.
@ -770,29 +775,17 @@ class LayoutEngine {
for ( let nodeId in trees ) {
if ( trees . hasOwnProperty ( nodeId ) ) {
if ( trees [ nodeId ] === index ) {
let node = this . body . nodes [ nodeId ] ;
let pos = this . _getPositionForHierarchy ( node ) ;
this . _setPositionForHierarchy ( node , pos + offset , undefined , true ) ;
this . direction . shift ( nodeId , offset ) ;
}
}
}
} ;
// get the width of a tree
let getTreeSize = ( index ) => {
let res = this . hierarchical . getTreeSize ( this . body . nodes , index ) ;
if ( this . _isVertical ( ) ) {
return { min : res . min_x , max : res . max_x } ;
} else {
return { min : res . min_y , max : res . max_y } ;
}
} ;
// get the width of all trees
let getTreeSizes = ( ) => {
let treeWidths = [ ] ;
for ( let i = 0 ; i <= this . hierarchical . numTrees ( ) ; i ++ ) {
treeWidths . push ( getTreeSize ( i ) ) ;
for ( let i = 0 ; i < this . hierarchical . numTrees ( ) ; i ++ ) {
treeWidths . push ( this . direction . getTreeSize ( i ) ) ;
}
return treeWidths ;
} ;
@ -825,7 +818,7 @@ class LayoutEngine {
if ( branchMap . hasOwnProperty ( branchNode ) ) {
let node = this . body . nodes [ branchNode ] ;
let level = this . hierarchical . levels [ node . id ] ;
let position = this . _getPositionForHierarchy ( node ) ;
let position = this . direction . getPosition ( node ) ;
// get the space around the node.
let [ minSpaceNode , maxSpaceNode ] = this . _getSpaceAroundNode ( node , branchMap ) ;
@ -884,8 +877,8 @@ class LayoutEngine {
// callback for shifting branches
let branchShiftCallback = ( node1 , node2 , centerParent = false ) => {
//window.CALLBACKS.push(() => {
let pos1 = this . _getPositionForHierarchy ( node1 ) ;
let pos2 = this . _getPositionForHierarchy ( node2 ) ;
let pos1 = this . direction . getPosition ( node1 ) ;
let pos2 = this . direction . getPosition ( node2 ) ;
let diffAbs = Math . abs ( pos2 - pos1 ) ;
let nodeSpacing = this . options . hierarchical . nodeSpacing ;
//console.log("NOW CHECKING:", node1.id, node2.id, diffAbs);
@ -954,7 +947,7 @@ class LayoutEngine {
let sum = 0 ;
for ( let i = 0 ; i < edges . length ; i ++ ) {
if ( referenceNodes [ edges [ i ] . id ] !== undefined ) {
let a = this . _getPositionForHierarchy ( referenceNodes [ edges [ i ] . id ] ) - point ;
let a = this . direction . getPosition ( referenceNodes [ edges [ i ] . id ] ) - point ;
sum += a / Math . sqrt ( a * a + C2 ) ;
}
}
@ -966,7 +959,7 @@ class LayoutEngine {
let sum = 0 ;
for ( let i = 0 ; i < edges . length ; i ++ ) {
if ( referenceNodes [ edges [ i ] . id ] !== undefined ) {
let a = this . _getPositionForHierarchy ( referenceNodes [ edges [ i ] . id ] ) - point ;
let a = this . direction . getPosition ( referenceNodes [ edges [ i ] . id ] ) - point ;
sum -= ( C2 * Math . pow ( a * a + C2 , - 1.5 ) ) ;
}
}
@ -974,7 +967,7 @@ class LayoutEngine {
} ;
let getGuess = ( iterations , edges ) => {
let guess = this . _getPositionForHierarchy ( node ) ;
let guess = this . direction . getPosition ( node ) ;
// Newton's method for optimization
let guessMap = { } ;
for ( let i = 0 ; i < iterations ; i ++ ) {
@ -996,7 +989,7 @@ class LayoutEngine {
let moveBranch = ( guess ) => {
// position node if there is space
let nodePosition = this . _getPositionForHierarchy ( node ) ;
let nodePosition = this . direction . getPosition ( node ) ;
// check movable area of the branch
if ( branches [ node . id ] === undefined ) {
@ -1028,7 +1021,7 @@ class LayoutEngine {
} ;
let moveNode = ( guess ) => {
let nodePosition = this . _getPositionForHierarchy ( node ) ;
let nodePosition = this . direction . getPosition ( node ) ;
// position node if there is space
let [ minSpace , maxSpace ] = this . _getSpaceAroundNode ( node ) ;
@ -1044,7 +1037,7 @@ class LayoutEngine {
if ( newPosition !== nodePosition ) {
//console.log("moving Node:",diff, minSpace, maxSpace);
this . _setPositionForHierarchy ( node , newPosition , undefined , true ) ;
this . direction . setPosition ( node , newPosition ) ;
//this.body.emitter.emit("_redraw");
stillShifting = true ;
}
@ -1146,14 +1139,14 @@ class LayoutEngine {
let level = this . hierarchical . levels [ node . id ] ;
if ( level !== undefined ) {
let index = this . hierarchical . distributionIndex [ node . id ] ;
let position = this . _getPositionForHierarchy ( node ) ;
let position = this . direction . getPosition ( node ) ;
let ordering = this . hierarchical . distributionOrdering [ level ] ;
let minSpace = 1 e9 ;
let maxSpace = 1 e9 ;
if ( index !== 0 ) {
let prevNode = ordering [ index - 1 ] ;
if ( ( useMap === true && map [ prevNode . id ] === undefined ) || useMap === false ) {
let prevPos = this . _getPositionForHierarchy ( prevNode ) ;
let prevPos = this . direction . getPosition ( prevNode ) ;
minSpace = position - prevPos ;
}
}
@ -1161,7 +1154,7 @@ class LayoutEngine {
if ( index != ordering . length - 1 ) {
let nextNode = ordering [ index + 1 ] ;
if ( ( useMap === true && map [ nextNode . id ] === undefined ) || useMap === false ) {
let nextPos = this . _getPositionForHierarchy ( nextNode ) ;
let nextPos = this . direction . getPosition ( nextNode ) ;
maxSpace = Math . min ( maxSpace , nextPos - position ) ;
}
}
@ -1191,12 +1184,12 @@ class LayoutEngine {
// get the range of the children
let newPosition = this . _getCenterPosition ( children ) ;
let position = this . _getPositionForHierarchy ( parentNode ) ;
let position = this . direction . getPosition ( parentNode ) ;
let [ minSpace , maxSpace ] = this . _getSpaceAroundNode ( parentNode ) ;
let diff = position - newPosition ;
if ( ( diff < 0 && Math . abs ( diff ) < maxSpace - this . options . hierarchical . nodeSpacing ) ||
( diff > 0 && Math . abs ( diff ) < minSpace - this . options . hierarchical . nodeSpacing ) ) {
this . _setPositionForHierarchy ( parentNode , newPosition , undefined , true ) ;
this . direction . setPosition ( parentNode , newPosition ) ;
}
}
}
@ -1218,7 +1211,7 @@ class LayoutEngine {
// sort nodes in level by position:
let nodeArray = Object . keys ( distribution [ level ] ) ;
nodeArray = this . _indexArrayToNodes ( nodeArray ) ;
this . _sortNodeArray ( nodeArray ) ;
this . direction . sort ( nodeArray ) ;
let handledNodeCount = 0 ;
for ( let i = 0 ; i < nodeArray . length ; i ++ ) {
@ -1229,9 +1222,9 @@ class LayoutEngine {
// We get the X or Y values we need and store them in pos and previousPos.
// The get and set make sure we get X or Y
if ( handledNodeCount > 0 ) {
pos = this . _getPositionForHierarchy ( nodeArray [ i - 1 ] ) + spacing ;
pos = this . direction . getPosition ( nodeArray [ i - 1 ] ) + spacing ;
}
this . _setPositionForHierarchy ( node , pos , level ) ;
this . direction . setPosition ( node , pos , level ) ;
this . _validatePositionAndContinue ( node , level , pos ) ;
handledNodeCount ++ ;
@ -1265,7 +1258,7 @@ class LayoutEngine {
}
// use the positions to order the nodes.
this . _sortNodeArray ( childNodes ) ;
this . direction . sort ( childNodes ) ;
// position the childNodes
for ( let i = 0 ; i < childNodes . length ; i ++ ) {
@ -1279,9 +1272,12 @@ class LayoutEngine {
// we get the X or Y values we need and store them in pos and previousPos.
// The get and set make sure we get X or Y
if ( i === 0 ) { pos = this . _getPositionForHierarchy ( this . body . nodes [ parentId ] ) ; }
else { pos = this . _getPositionForHierarchy ( childNodes [ i - 1 ] ) + spacing ; }
this . _setPositionForHierarchy ( childNode , pos , childNodeLevel ) ;
if ( i === 0 ) {
pos = this . direction . getPosition ( this . body . nodes [ parentId ] ) ;
} else {
pos = this . direction . getPosition ( childNodes [ i - 1 ] ) + spacing ;
}
this . direction . setPosition ( childNode , pos , childNodeLevel ) ;
this . _validatePositionAndContinue ( childNode , childNodeLevel , pos ) ;
}
else {
@ -1291,7 +1287,7 @@ class LayoutEngine {
// center the parent nodes.
let center = this . _getCenterPosition ( childNodes ) ;
this . _setPositionForHierarchy ( this . body . nodes [ parentId ] , center , parentLevel ) ;
this . direction . setPosition ( this . body . nodes [ parentId ] , center , parentLevel ) ;
}
@ -1310,7 +1306,7 @@ class LayoutEngine {
// if overlap has been detected, we shift the branch
if ( this . lastNodeOnLevel [ level ] !== undefined ) {
let previousPos = this . _getPositionForHierarchy ( this . body . nodes [ this . lastNodeOnLevel [ level ] ] ) ;
let previousPos = this . direction . getPosition ( this . body . nodes [ this . lastNodeOnLevel [ level ] ] ) ;
if ( pos - previousPos < this . options . hierarchical . nodeSpacing ) {
let diff = ( previousPos + this . options . hierarchical . nodeSpacing ) - pos ;
let sharedParent = this . _findCommonParent ( this . lastNodeOnLevel [ level ] , node . id ) ;
@ -1354,14 +1350,7 @@ class LayoutEngine {
if ( this . body . nodes . hasOwnProperty ( nodeId ) ) {
node = this . body . nodes [ nodeId ] ;
let level = this . hierarchical . levels [ nodeId ] === undefined ? 0 : this . hierarchical . levels [ nodeId ] ;
if ( this . _isVertical ( ) ) {
node . y = this . options . hierarchical . levelSeparation * level ;
node . options . fixed . y = true ;
}
else {
node . x = this . options . hierarchical . levelSeparation * level ;
node . options . fixed . x = true ;
}
this . direction . fix ( node , level ) ;
if ( distribution [ level ] === undefined ) {
distribution [ level ] = { } ;
}
@ -1624,12 +1613,7 @@ class LayoutEngine {
return ;
}
progress [ parentId ] = true ;
if ( this . _isVertical ( ) ) {
this . body . nodes [ parentId ] . x += diff ;
}
else {
this . body . nodes [ parentId ] . y += diff ;
}
this . direction . shift ( parentId , diff ) ;
let childRef = this . hierarchical . childrenReference [ parentId ] ;
if ( childRef !== undefined ) {
@ -1682,93 +1666,27 @@ class LayoutEngine {
return findParent ( parents , childB ) ;
}
/ * *
* Abstract the getting of the position so we won ' t have to repeat the check for direction all the time
* Set the strategy pattern for handling the coordinates given the current direction .
*
* The individual instances contain all the operations and data specific to a layout direction .
*
* @ param { Node } node
* @ param { { x : number , y : number } } position
* @ param { number } level
* @ param { boolean } [ doNotUpdate = false ]
* @ private
* /
_setPositionForHierarchy ( node , position , level , doNotUpdate = false ) {
//console.log('_setPositionForHierarchy',node.id, position)
if ( doNotUpdate !== true ) {
this . hierarchical . addToOrdering ( node , level ) ;
}
if ( this . _isVertical ( ) ) {
node . x = position ;
setDirectionStrategy ( ) {
var isVertical = ( this . options . hierarchical . direction === 'UD'
|| this . options . hierarchical . direction === 'DU' ) ;
if ( isVertical ) {
this . direction = new VerticalStrategy ( this ) ;
} else {
this . direction = new HorizontalStrategy ( this ) ;
}
else {
node . y = position ;
}
}
/ * *
* Utility function to cut down on typing this all the time .
*
* TODO : use this in all applicable situations in this class .
* @ returns { boolean }
* @ private
* /
_isVertical ( ) {
return ( this . options . hierarchical . direction === 'UD' || this . options . hierarchical . direction === 'DU' ) ;
}
/ * *
* Abstract the getting of the position of a node so we do not have to repeat the direction check all the time .
* @ param { Node } node
* @ returns { number }
* @ private
* /
_getPositionForHierarchy ( node ) {
if ( this . _isVertical ( ) ) {
return node . x ;
}
else {
return node . y ;
}
}
/ * *
* Use the x or y value to sort the array , allowing users to specify order .
*
* @ param { Array . < Node > } nodeArray
* @ private
* /
_sortNodeArray ( nodeArray ) {
if ( nodeArray . length > 1 ) {
if ( this . _isVertical ( ) ) {
nodeArray . sort ( function ( a , b ) {
return a . x - b . x ;
} )
}
else {
nodeArray . sort ( function ( a , b ) {
return a . y - b . y ;
} )
}
}
}
/ * *
* Get the type of static smooth curve in case it is required .
*
* The return value is the type to use to translate dynamic curves to
* another type , in the case of hierarchical layout . Dynamic curves do
* not work for that layout type .
* @ returns { 'horizontal' | 'vertical' }
* /
getStaticType ( ) {
// Node that 'type' is the edge type, and therefore 'orthogonal' to the layout type.
let type = 'horizontal' ;
if ( ! this . _isVertical ( ) ) {
type = 'vertical' ;
}
return type ;
}
@ -1793,7 +1711,7 @@ class LayoutEngine {
childNode = this . body . nodes [ childNodeId ] ;
}
let position = this . _getPositionForHierarchy ( childNode ) ;
let position = this . direction . getPosition ( childNode ) ;
minPos = Math . min ( minPos , position ) ;
maxPos = Math . max ( maxPos , position ) ;
}