@ -160,23 +160,19 @@ class ClusterEngine {
let childEdgesObj = { } ;
let childEdgesObj = { } ;
// collect the nodes that will be in the cluster
// collect the nodes that will be in the cluster
for ( let nodeId in this . body . nodes ) {
if ( ! this . body . nodes . hasOwnProperty ( nodeId ) ) continue ;
let node = this . body . nodes [ nodeId ] ;
util . forEach ( this . body . nodes , ( node , nodeId ) => {
let clonedOptions = NetworkUtil . cloneOptions ( node ) ;
let clonedOptions = NetworkUtil . cloneOptions ( node ) ;
if ( options . joinCondition ( clonedOptions ) === true ) {
if ( options . joinCondition ( clonedOptions ) === true ) {
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
childNodesObj [ nodeId ] = node ;
// collect the edges that will be in the cluster
// collect the edges that will be in the cluster
for ( let i = 0 ; i < node . edges . length ; i ++ ) {
let edge = node . edges [ i ] ;
util . forEach ( node . edges , ( edge ) => {
if ( this . clusteredEdges [ edge . id ] === undefined ) {
if ( this . clusteredEdges [ edge . id ] === undefined ) {
childEdgesObj [ edge . id ] = edge ;
childEdgesObj [ edge . id ] = edge ;
}
}
}
} ) ;
}
}
}
} ) ;
this . _cluster ( childNodesObj , childEdgesObj , options , refreshData ) ;
this . _cluster ( childNodesObj , childEdgesObj , options , refreshData ) ;
}
}
@ -249,7 +245,7 @@ class ClusterEngine {
* @ returns { Boolean } true if no joinCondition , otherwise return value of joinCondition
* @ returns { Boolean } true if no joinCondition , otherwise return value of joinCondition
* /
* /
var findClusterData = function ( ) {
var findClusterData = function ( ) {
for ( var n in clusters ) {
for ( let n = 0 ; n < clusters . length ; ++ n ) {
// Search for a cluster containing any of the node id's
// Search for a cluster containing any of the node id's
for ( var m in childNodesObj ) {
for ( var m in childNodesObj ) {
if ( clusters [ n ] . nodes [ m ] !== undefined ) {
if ( clusters [ n ] . nodes [ m ] !== undefined ) {
@ -379,6 +375,8 @@ class ClusterEngine {
} )
} )
for ( childNode in childNodesObj ) {
for ( childNode in childNodesObj ) {
if ( ! childNodesObj . hasOwnProperty ( childNode ) ) continue ;
var childNode = childNodesObj [ childNode ] ;
var childNode = childNodesObj [ childNode ] ;
for ( var y = 0 ; y < childNode . edges . length ; y ++ ) {
for ( var y = 0 ; y < childNode . edges . length ; y ++ ) {
var childEdge = childNode . edges [ y ] ;
var childEdge = childNode . edges [ y ] ;
@ -1197,11 +1195,11 @@ class ClusterEngine {
_filter ( arr , callback ) {
_filter ( arr , callback ) {
let ret = [ ] ;
let ret = [ ] ;
for ( var n in arr ) {
if ( callback ( arr [ n ] ) ) {
ret . push ( arr [ n ] ) ;
util . forEach ( arr , ( item ) => {
if ( callback ( item ) ) {
ret . push ( item ) ;
}
}
}
} ) ;
return ret ;
return ret ;
}
}
@ -1211,18 +1209,15 @@ class ClusterEngine {
* Scan all edges for changes in clustering and adjust this if necessary .
* Scan all edges for changes in clustering and adjust this if necessary .
*
*
* Call this ( internally ) after there has been a change in node or edge data .
* Call this ( internally ) after there has been a change in node or edge data .
*
* Pre : States of this . body . nodes and this . body . edges consistent
* Pre : this . clusteredNodes and this . clusteredEdge consistent with containedNodes and containedEdges
* of cluster nodes .
* /
* /
_updateState ( ) {
_updateState ( ) {
// Pre: States of this.body.nodes and this.body.edges consistent
// Pre: this.clusteredNodes and this.clusteredEdge consistent with containedNodes and containedEdges
// of cluster nodes.
let nodeId ;
let nodeId ;
let edgeId ;
let m , n ;
let deletedNodeIds = [ ] ;
let deletedNodeIds = [ ] ;
let deletedEdgeIds = [ ] ;
let deletedEdgeIds = [ ] ;
let self = this ;
/ * *
/ * *
* Utility function to iterate over clustering nodes only
* Utility function to iterate over clustering nodes only
@ -1230,12 +1225,11 @@ class ClusterEngine {
* @ param { Function } callback function to call for each cluster node
* @ param { Function } callback function to call for each cluster node
* /
* /
let eachClusterNode = ( callback ) => {
let eachClusterNode = ( callback ) => {
for ( nodeId in this . body . nodes ) {
let node = this . body . nodes [ nodeId ] ;
if ( node . isCluster !== true ) continue ;
callback ( node ) ;
}
util . forEach ( this . body . nodes , ( node ) => {
if ( node . isCluster === true ) {
callback ( node ) ;
}
} ) ;
} ;
} ;
@ -1245,6 +1239,7 @@ class ClusterEngine {
// Determine the deleted nodes
// Determine the deleted nodes
for ( nodeId in this . clusteredNodes ) {
for ( nodeId in this . clusteredNodes ) {
if ( ! this . clusteredNodes . hasOwnProperty ( nodeId ) ) continue ;
let node = this . body . nodes [ nodeId ] ;
let node = this . body . nodes [ nodeId ] ;
if ( node === undefined ) {
if ( node === undefined ) {
@ -1254,13 +1249,13 @@ class ClusterEngine {
// Remove nodes from cluster nodes
// Remove nodes from cluster nodes
eachClusterNode ( function ( clusterNode ) {
eachClusterNode ( function ( clusterNode ) {
for ( n in deletedNodeIds ) {
for ( let n = 0 ; n < deletedNodeIds . length ; n ++ ) {
delete clusterNode . containedNodes [ deletedNodeIds [ n ] ] ;
delete clusterNode . containedNodes [ deletedNodeIds [ n ] ] ;
}
}
} ) ;
} ) ;
// Remove nodes from cluster list
// Remove nodes from cluster list
for ( n in deletedNodeIds ) {
for ( let n = 0 ; n < deletedNodeIds . length ; n ++ ) {
delete this . clusteredNodes [ deletedNodeIds [ n ] ] ;
delete this . clusteredNodes [ deletedNodeIds [ n ] ] ;
}
}
@ -1270,44 +1265,40 @@ class ClusterEngine {
//
//
// Add the deleted clustered edges to the list
// Add the deleted clustered edges to the list
for ( edgeId in this . clusteredEdges ) {
util . forEach ( this . clusteredEdges , ( edgeId ) => {
let edge = this . body . edges [ edgeId ] ;
let edge = this . body . edges [ edgeId ] ;
if ( edge === undefined || ! edge . endPointsValid ( ) ) {
if ( edge === undefined || ! edge . endPointsValid ( ) ) {
deletedEdgeIds . push ( edgeId ) ;
deletedEdgeIds . push ( edgeId ) ;
}
}
}
} ) ;
// Cluster nodes can also contain edges which are not clustered,
// Cluster nodes can also contain edges which are not clustered,
// i.e. nodes 1-2 within cluster with an edge in between.
// i.e. nodes 1-2 within cluster with an edge in between.
// So the cluster nodes also need to be scanned for invalid edges
// So the cluster nodes also need to be scanned for invalid edges
eachClusterNode ( function ( clusterNode ) {
eachClusterNode ( function ( clusterNode ) {
for ( edgeId in clusterNode . containedEdges ) {
let edge = clusterNode . containedEdges [ edgeId ] ;
util . forEach ( clusterNode . containedEdges , ( edge , edgeId ) => {
if ( ! edge . endPointsValid ( ) && deletedEdgeIds . indexOf ( edgeId ) === - 1 ) {
if ( ! edge . endPointsValid ( ) && deletedEdgeIds . indexOf ( edgeId ) === - 1 ) {
deletedEdgeIds . push ( edgeId ) ;
deletedEdgeIds . push ( edgeId ) ;
}
}
}
} ) ;
} ) ;
} ) ;
// Also scan for cluster edges which need to be removed in the active list.
// Also scan for cluster edges which need to be removed in the active list.
// Regular edges have been removed beforehand, so this only picks up the cluster edges.
// Regular edges have been removed beforehand, so this only picks up the cluster edges.
for ( edgeId in this . body . edges ) {
let edge = this . body . edges [ edgeId ] ;
util . forEach ( this . body . edges , ( edge , edgeId ) => {
// Explicitly scan the contained edges for validity
// Explicitly scan the contained edges for validity
let isValid = true ;
let isValid = true ;
let replacedIds = edge . clusteringEdgeReplacingIds ;
let replacedIds = edge . clusteringEdgeReplacingIds ;
if ( replacedIds !== undefined ) {
if ( replacedIds !== undefined ) {
let numValid = 0 ;
let numValid = 0 ;
for ( let n in replacedIds ) {
let containedEdgeId = replacedIds [ n ] ;
util . forEach ( replacedIds , ( containedEdgeId ) => {
let containedEdge = this . body . edges [ containedEdgeId ] ;
let containedEdge = this . body . edges [ containedEdgeId ] ;
if ( containedEdge !== undefined && containedEdge . endPointsValid ( ) ) {
if ( containedEdge !== undefined && containedEdge . endPointsValid ( ) ) {
numValid += 1 ;
numValid += 1 ;
}
}
}
} ) ;
isValid = ( numValid > 0 ) ;
isValid = ( numValid > 0 ) ;
}
}
@ -1315,43 +1306,41 @@ class ClusterEngine {
if ( ! edge . endPointsValid ( ) || ! isValid ) {
if ( ! edge . endPointsValid ( ) || ! isValid ) {
deletedEdgeIds . push ( edgeId ) ;
deletedEdgeIds . push ( edgeId ) ;
}
}
}
} ) ;
// Remove edges from cluster nodes
// Remove edges from cluster nodes
eachClusterNode ( function ( clusterNode ) {
for ( n in deletedEdgeIds ) {
let deletedEdgeId = deletedEdgeIds [ n ] ;
eachClusterNode ( ( clusterNode ) => {
util . forEach ( deletedEdgeIds , ( deletedEdgeId ) => {
delete clusterNode . containedEdges [ deletedEdgeId ] ;
delete clusterNode . containedEdges [ deletedEdgeId ] ;
for ( m in clusterNode . edges ) {
let edge = clusterNode . edges [ m ] ;
util . forEach ( clusterNode . edges , ( edge , m ) => {
if ( edge . id === deletedEdgeId ) {
if ( edge . id === deletedEdgeId ) {
clusterNode . edges [ m ] = null ; // Don't want to directly delete here, because in the loop
clusterNode . edges [ m ] = null ; // Don't want to directly delete here, because in the loop
continue ;
return ;
}
}
edge . clusteringEdgeReplacingIds = self . _filter ( edge . clusteringEdgeReplacingIds , function ( id ) {
edge . clusteringEdgeReplacingIds = this . _filter ( edge . clusteringEdgeReplacingIds , function ( id ) {
return deletedEdgeIds . indexOf ( id ) === - 1 ;
return deletedEdgeIds . indexOf ( id ) === - 1 ;
} ) ;
} ) ;
}
} ) ;
// Clean up the nulls
// Clean up the nulls
clusterNode . edges = self . _filter ( clusterNode . edges , function ( item ) { return item !== null } ) ;
}
clusterNode . edges = this . _filter ( clusterNode . edges , function ( item ) { return item !== null } ) ;
} ) ;
} ) ;
} ) ;
// Remove from cluster list
// Remove from cluster list
for ( n in deletedEdgeIds ) {
delete this . clusteredEdges [ delet edEd geIds [ n ] ] ;
}
util . forEach ( deletedEdgeIds , ( edgeId ) => {
delete this . clusteredEdges [ edgeId ] ;
} ) ;
// Remove cluster edges from active list (this.body.edges).
// Remove cluster edges from active list (this.body.edges).
// deletedEdgeIds still contains id of regular edges, but these should all
// deletedEdgeIds still contains id of regular edges, but these should all
// be gone when you reach here.
// be gone when you reach here.
for ( n in deletedEdgeIds ) {
delete this . body . edges [ delet edEd geIds [ n ] ] ;
}
util . forEach ( deletedEdgeIds , ( edgeId ) => {
delete this . body . edges [ edgeId ] ;
} ) ;
//
//
@ -1360,13 +1349,12 @@ class ClusterEngine {
// Iterating over keys here, because edges may be removed in the loop
// Iterating over keys here, because edges may be removed in the loop
let ids = Object . keys ( this . body . edges ) ;
let ids = Object . keys ( this . body . edges ) ;
for ( n in ids ) {
let edgeId = ids [ n ] ;
util . forEach ( ids , ( edgeId ) => {
let edge = this . body . edges [ edgeId ] ;
let edge = this . body . edges [ edgeId ] ;
let shouldBeClustered = this . _isClusteredNode ( edge . fromId ) || this . _isClusteredNode ( edge . toId ) ;
let shouldBeClustered = this . _isClusteredNode ( edge . fromId ) || this . _isClusteredNode ( edge . toId ) ;
if ( shouldBeClustered === this . _isClusteredEdge ( edge . id ) ) {
if ( shouldBeClustered === this . _isClusteredEdge ( edge . id ) ) {
continue ; // all is well
return ; // all is well
}
}
if ( shouldBeClustered ) {
if ( shouldBeClustered ) {
@ -1382,11 +1370,16 @@ class ClusterEngine {
}
}
// TODO: check that it works for both edges clustered
// TODO: check that it works for both edges clustered
// (This might be paranoia)
} else {
} else {
// undo clustering for this edge
// This should not be happening, the state should
// be properly updated at this point.
//
// If it *is* reached during normal operation, then we have to implement
// undo clustering for this edge here.
throw new Error ( 'remove edge from clustering not implemented!' ) ;
throw new Error ( 'remove edge from clustering not implemented!' ) ;
}
}
}
} ) ;
// Clusters may be nested to any level. Keep on opening until nothing to open
// Clusters may be nested to any level. Keep on opening until nothing to open
@ -1405,7 +1398,7 @@ class ClusterEngine {
} ) ;
} ) ;
// Open them
// Open them
for ( let n in clustersToOpe n) {
for ( let n = 0 ; n < clustersToOpen . length ; ++ n ) {
this . openCluster ( clustersToOpen [ n ] , { } , false /* Don't refresh, we're in an refresh/update already */ ) ;
this . openCluster ( clustersToOpen [ n ] , { } , false /* Don't refresh, we're in an refresh/update already */ ) ;
}
}