|
@ -1194,50 +1194,78 @@ class LayoutEngine { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get the hubsize from all remaining unlevelled nodes. |
|
|
|
|
|
|
|
|
* Return the active (i.e. visible) edges for this node |
|
|
* |
|
|
* |
|
|
* @returns {number} |
|
|
|
|
|
|
|
|
* @returns {array} Array of edge instances |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_getHubSize() { |
|
|
|
|
|
let hubSize = 0; |
|
|
|
|
|
for (let nodeId in this.body.nodes) { |
|
|
|
|
|
if (this.body.nodes.hasOwnProperty(nodeId)) { |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
if (this.hierarchical.levels[nodeId] === undefined) { |
|
|
|
|
|
hubSize = node.edges.length < hubSize ? hubSize : node.edges.length; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
_getActiveEdges(node) { |
|
|
|
|
|
let result = []; |
|
|
|
|
|
|
|
|
|
|
|
for (let j in node.edges) { |
|
|
|
|
|
let edge = node.edges[j]; |
|
|
|
|
|
if (this.body.edgeIndices.indexOf(edge.id) !== -1) { |
|
|
|
|
|
result.push(edge); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return hubSize; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Get the hubsizes for all active nodes. |
|
|
|
|
|
* |
|
|
|
|
|
* @returns {number} |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
|
|
|
_getHubSizes() { |
|
|
|
|
|
let hubSizes = {}; |
|
|
|
|
|
let nodeIds = this.body.nodeIndices; |
|
|
|
|
|
|
|
|
|
|
|
for (let i in nodeIds) { |
|
|
|
|
|
let nodeId = nodeIds[i]; |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
let hubSize = this._getActiveEdges(node).length; |
|
|
|
|
|
hubSizes[hubSize] = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Make an array of the size sorted descending
|
|
|
|
|
|
let result = []; |
|
|
|
|
|
for (let size in hubSizes) { |
|
|
|
|
|
result.push(Number(size)); |
|
|
|
|
|
} |
|
|
|
|
|
result.sort(function(a, b) { |
|
|
|
|
|
return b - a; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* this function allocates nodes in levels based on the recursive branching from the largest hubs. |
|
|
* this function allocates nodes in levels based on the recursive branching from the largest hubs. |
|
|
* |
|
|
* |
|
|
* @param hubsize |
|
|
|
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_determineLevelsByHubsize() { |
|
|
_determineLevelsByHubsize() { |
|
|
let hubSize = 1; |
|
|
|
|
|
|
|
|
|
|
|
let levelDownstream = (nodeA, nodeB) => { |
|
|
let levelDownstream = (nodeA, nodeB) => { |
|
|
this.hierarchical.levelDownstream(nodeA, nodeB); |
|
|
this.hierarchical.levelDownstream(nodeA, nodeB); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
while (hubSize > 0) { |
|
|
|
|
|
// determine hubs
|
|
|
|
|
|
hubSize = this._getHubSize(); |
|
|
|
|
|
if (hubSize === 0) |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
let hubSizes = this._getHubSizes(); |
|
|
|
|
|
|
|
|
for (let nodeId in this.body.nodes) { |
|
|
|
|
|
if (this.body.nodes.hasOwnProperty(nodeId)) { |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
if (node.edges.length === hubSize) { |
|
|
|
|
|
this._crawlNetwork(levelDownstream,nodeId); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (let i = 0; i < hubSizes.length; ++i ) { |
|
|
|
|
|
let hubSize = hubSizes[i]; |
|
|
|
|
|
if (hubSize === 0) break; |
|
|
|
|
|
|
|
|
|
|
|
let nodeIds = this.body.nodeIndices; |
|
|
|
|
|
for (let j in nodeIds) { |
|
|
|
|
|
let nodeId = nodeIds[j]; |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
|
|
|
|
|
|
if (hubSize === this._getActiveEdges(node).length) { |
|
|
|
|
|
this._crawlNetwork(levelDownstream, nodeId); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -1322,7 +1350,7 @@ class LayoutEngine { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Crawl over the entire network and use a callback on each node couple that is connected to each other. |
|
|
* Crawl over the entire network and use a callback on each node couple that is connected to each other. |
|
|
* @param callback | will receive nodeA nodeB and the connecting edge. A and B are unique. |
|
|
|
|
|
|
|
|
* @param callback | will receive nodeA, nodeB and the connecting edge. A and B are distinct. |
|
|
* @param startingNodeId |
|
|
* @param startingNodeId |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
@ -1340,18 +1368,19 @@ class LayoutEngine { |
|
|
|
|
|
|
|
|
progress[node.id] = true; |
|
|
progress[node.id] = true; |
|
|
let childNode; |
|
|
let childNode; |
|
|
for (let i = 0; i < node.edges.length; i++) { |
|
|
|
|
|
let edges = node.edges[i]; |
|
|
|
|
|
if (edges.connected === true) { |
|
|
|
|
|
if (edges.toId === node.id) { |
|
|
|
|
|
childNode = edges.from; |
|
|
|
|
|
|
|
|
let edges = this._getActiveEdges(node); |
|
|
|
|
|
for (let i = 0; i < edges.length; i++) { |
|
|
|
|
|
let edge = edges[i]; |
|
|
|
|
|
if (edge.connected === true) { |
|
|
|
|
|
if (edge.toId == node.id) { // '==' because id's can be string and numeric
|
|
|
|
|
|
childNode = edge.from; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
childNode = edges.to; |
|
|
|
|
|
|
|
|
childNode = edge.to; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (node.id !== childNode.id) { |
|
|
|
|
|
callback(node, childNode, edges); |
|
|
|
|
|
|
|
|
if (node.id != childNode.id) { // '!=' because id's can be string and numeric
|
|
|
|
|
|
callback(node, childNode, edge); |
|
|
crawler(childNode, tree); |
|
|
crawler(childNode, tree); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|