|
|
@ -1,4 +1,4 @@ |
|
|
|
'use strict' |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
let util = require('../../util'); |
|
|
|
import NetworkUtil from '../NetworkUtil'; |
|
|
@ -335,7 +335,7 @@ class LayoutEngine { |
|
|
|
this._placeNodesByHierarchy(distribution); |
|
|
|
|
|
|
|
// Todo: condense the whitespace.
|
|
|
|
this._condenseHierarchy(); |
|
|
|
this._condenseHierarchy(distribution); |
|
|
|
|
|
|
|
// shift to center so gravity does not have to do much
|
|
|
|
this._shiftToCenter(); |
|
|
@ -347,8 +347,7 @@ class LayoutEngine { |
|
|
|
* TODO: implement. Clear whitespace after positioning. |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_condenseHierarchy() { |
|
|
|
|
|
|
|
_condenseHierarchy(distribution) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -359,28 +358,39 @@ class LayoutEngine { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_placeNodesByHierarchy(distribution) { |
|
|
|
let nodeId, node; |
|
|
|
this.positionedNodes = {}; |
|
|
|
// start placing all the level 0 nodes first. Then recursively position their branches.
|
|
|
|
for (let level in distribution) { |
|
|
|
if (distribution.hasOwnProperty(level)) { |
|
|
|
// sort nodes in level by position:
|
|
|
|
let nodeArray = Object.keys(distribution[level]); |
|
|
|
this._sortNodeArray(nodeArray) |
|
|
|
nodeArray = this._indexArrayToNodes(nodeArray); |
|
|
|
this._sortNodeArray(nodeArray); |
|
|
|
|
|
|
|
for (let i = 0; i < nodeArray.length; i++) { |
|
|
|
nodeId = nodeArray[i]; |
|
|
|
node = distribution[level][nodeId]; |
|
|
|
if (this.positionedNodes[nodeId] === undefined) { |
|
|
|
this._setPositionForHierarchy(node, this.nodeSpacing * i) |
|
|
|
this.positionedNodes[nodeId] = true; |
|
|
|
this._placeBranchNodes(nodeId, level); |
|
|
|
let node = nodeArray[i]; |
|
|
|
if (this.positionedNodes[node.id] === undefined) { |
|
|
|
this._setPositionForHierarchy(node, this.nodeSpacing * i); |
|
|
|
this.positionedNodes[node.id] = true; |
|
|
|
this._placeBranchNodes(node.id, level); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Receives an array with node indices and returns an array with the actual node references. Used for sorting based on |
|
|
|
* node properties. |
|
|
|
* @param idArray |
|
|
|
*/ |
|
|
|
_indexArrayToNodes(idArray) { |
|
|
|
let array = []; |
|
|
|
for (let i = 0; i < idArray.length; i++) { |
|
|
|
array.push(this.body.nodes[idArray[i]]) |
|
|
|
} |
|
|
|
return array; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* This function get the distribution of levels based on hubsize |
|
|
@ -454,7 +464,7 @@ class LayoutEngine { |
|
|
|
// set level
|
|
|
|
this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] + 1; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
while (hubSize > 0) { |
|
|
|
// determine hubs
|
|
|
@ -483,7 +493,7 @@ class LayoutEngine { |
|
|
|
// TODO: this should come from options.
|
|
|
|
let customCallback = function(nodeA, nodeB, edge) { |
|
|
|
|
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let levelByDirection = (nodeA, nodeB, edge) => { |
|
|
|
let levelA = this.hierarchicalLevels[nodeA.id]; |
|
|
@ -497,7 +507,7 @@ class LayoutEngine { |
|
|
|
); |
|
|
|
|
|
|
|
this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] + diff; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
this._crawlNetwork(levelByDirection); |
|
|
|
this._setMinLevelToZero(); |
|
|
@ -521,8 +531,8 @@ class LayoutEngine { |
|
|
|
else { |
|
|
|
this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] - 1; |
|
|
|
} |
|
|
|
} |
|
|
|
this._crawlNetwork(levelByDirection) |
|
|
|
}; |
|
|
|
this._crawlNetwork(levelByDirection); |
|
|
|
this._setMinLevelToZero(); |
|
|
|
} |
|
|
|
|
|
|
@ -569,7 +579,7 @@ class LayoutEngine { |
|
|
|
} |
|
|
|
this.hierarchicalChildren[childNodeId].parents.push(parentNodeId); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
this._crawlNetwork(fillInRelations); |
|
|
|
} |
|
|
@ -597,7 +607,7 @@ class LayoutEngine { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// we can crawl from a specific node or over all nodes.
|
|
|
@ -720,7 +730,7 @@ class LayoutEngine { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_findCommonParent(childA,childB) { |
|
|
|
let parents = {} |
|
|
|
let parents = {}; |
|
|
|
let iterateParents = (parents,child) => { |
|
|
|
if (this.hierarchicalChildren[child] !== undefined) { |
|
|
|
for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { |
|
|
@ -729,7 +739,7 @@ class LayoutEngine { |
|
|
|
iterateParents(parents, parent) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
let findParent = (parents, child) => { |
|
|
|
if (this.hierarchicalChildren[child] !== undefined) { |
|
|
|
for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { |
|
|
@ -737,14 +747,17 @@ class LayoutEngine { |
|
|
|
if (parents[parent] !== undefined) { |
|
|
|
return {foundParent:parent, withChild:child}; |
|
|
|
} |
|
|
|
return findParent(parents, parent); |
|
|
|
let branch = findParent(parents, parent); |
|
|
|
if (branch.foundParent !== null) { |
|
|
|
return branch; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return {foundParent:null, withChild:child}; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
iterateParents(parents, childA); |
|
|
|
return findParent(parents, childB) |
|
|
|
return findParent(parents, childB); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -783,11 +796,17 @@ class LayoutEngine { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_sortNodeArray(nodeArray) { |
|
|
|
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { |
|
|
|
nodeArray.sort(function (a,b) {return a.x - b.x;}) |
|
|
|
} |
|
|
|
else { |
|
|
|
nodeArray.sort(function (a,b) {return a.y - b.y;}) |
|
|
|
if (nodeArray.length > 1) { |
|
|
|
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { |
|
|
|
nodeArray.sort(function (a, b) { |
|
|
|
return a.x - b.x; |
|
|
|
}) |
|
|
|
} |
|
|
|
else { |
|
|
|
nodeArray.sort(function (a, b) { |
|
|
|
return a.y - b.y; |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|