@ -40,8 +40,7 @@ class ClusterEngine {
}
}
for ( let i = 0 ; i < nodesToCluster . length ; i ++ ) {
for ( let i = 0 ; i < nodesToCluster . length ; i ++ ) {
let node = this . body . nodes [ nodesToCluster [ i ] ] ;
this . clusterByConnection ( node , options , false ) ;
this . clusterByConnection ( nodesToCluster [ i ] , options , false ) ;
}
}
this . body . emitter . emit ( '_dataChanged' ) ;
this . body . emitter . emit ( '_dataChanged' ) ;
}
}
@ -64,9 +63,16 @@ class ClusterEngine {
// collect the nodes that will be in the cluster
// collect the nodes that will be in the cluster
for ( let i = 0 ; i < this . body . nodeIndices . length ; i ++ ) {
for ( let i = 0 ; i < this . body . nodeIndices . length ; i ++ ) {
let nodeId = this . body . nodeIndices [ i ] ;
let nodeId = this . body . nodeIndices [ i ] ;
let clonedOptions = this . _cloneOptions ( nodeId ) ;
let node = this . body . nodes [ nodeId ] ;
let clonedOptions = this . _cloneOptions ( node ) ;
if ( options . joinCondition ( clonedOptions ) === true ) {
if ( options . joinCondition ( clonedOptions ) === true ) {
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
// collect the nodes that will be in the cluster
for ( let i = 0 ; i < node . edges . length ; i ++ ) {
let edge = node . edges [ i ] ;
childEdgesObj [ edge . id ] = edge ;
}
}
}
}
}
@ -89,20 +95,24 @@ class ClusterEngine {
let childEdgesObj = { } ;
let childEdgesObj = { } ;
let nodeId = this . body . nodeIndices [ i ] ;
let nodeId = this . body . nodeIndices [ i ] ;
if ( this . body . nodes [ nodeId ] . edges . length === 1 ) {
if ( this . body . nodes [ nodeId ] . edges . length === 1 ) {
// this is an outlier
let edge = this . body . nodes [ nodeId ] . edges [ 0 ] ;
let edge = this . body . nodes [ nodeId ] . edges [ 0 ] ;
let childNodeId = this . _getConnectedId ( edge , nodeId ) ;
let childNodeId = this . _getConnectedId ( edge , nodeId ) ;
if ( childNodeId != nodeId ) {
if ( childNodeId !== nodeId ) {
if ( options . joinCondition === undefined ) {
if ( options . joinCondition === undefined ) {
childEdgesObj [ edge . id ] = edge ;
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
}
}
else {
else {
let clonedOptions = this . _cloneOptions ( nodeId ) ;
let clonedOptions = this . _cloneOptions ( this . body . nodes [ nodeId ] ) ;
if ( options . joinCondition ( clonedOptions ) === true ) {
if ( options . joinCondition ( clonedOptions ) === true ) {
childEdgesObj [ edge . id ] = edge ;
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
childNodesObj [ nodeId ] = this . body . nodes [ nodeId ] ;
}
}
clonedOptions = this . _cloneOptions ( childNodeId ) ;
clonedOptions = this . _cloneOptions ( this . body . nodes [ childNodeId ] ) ;
if ( options . joinCondition ( clonedOptions ) === true ) {
if ( options . joinCondition ( clonedOptions ) === true ) {
childEdgesObj [ edge . id ] = edge ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
}
}
}
}
@ -145,7 +155,7 @@ class ClusterEngine {
let childNodesObj = { } ;
let childNodesObj = { } ;
let childEdgesObj = { } ;
let childEdgesObj = { } ;
let parentNodeId = node . id ;
let parentNodeId = node . id ;
let parentClonedOptions = this . _cloneOptions ( pare ntN odeId ) ;
let parentClonedOptions = this . _cloneOptions ( node ) ;
childNodesObj [ parentNodeId ] = node ;
childNodesObj [ parentNodeId ] = node ;
// collect the nodes that will be in the cluster
// collect the nodes that will be in the cluster
@ -160,7 +170,7 @@ class ClusterEngine {
}
}
else {
else {
// clone the options and insert some additional parameters that could be interesting.
// clone the options and insert some additional parameters that could be interesting.
let childClonedOptions = this . _cloneOptions ( childNodeId ) ;
let childClonedOptions = this . _cloneOptions ( this . body . nodes [ childNodeId ] ) ;
if ( options . joinCondition ( parentClonedOptions , childClonedOptions ) === true ) {
if ( options . joinCondition ( parentClonedOptions , childClonedOptions ) === true ) {
childEdgesObj [ edge . id ] = edge ;
childEdgesObj [ edge . id ] = edge ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
childNodesObj [ childNodeId ] = this . body . nodes [ childNodeId ] ;
@ -183,16 +193,16 @@ class ClusterEngine {
* @ returns { { } }
* @ returns { { } }
* @ private
* @ private
* /
* /
_cloneOptions ( objId , type ) {
_cloneOptions ( item , type ) {
let clonedOptions = { } ;
let clonedOptions = { } ;
if ( type === undefined || type === 'node' ) {
if ( type === undefined || type === 'node' ) {
util . deepExtend ( clonedOptions , this . body . nodes [ objId ] . options , true ) ;
clonedOptions . x = this . body . nodes [ objId ] . x ;
clonedOptions . y = this . body . nodes [ objId ] . y ;
clonedOptions . amountOfConnections = this . body . nodes [ objId ] . edges . length ;
util . deepExtend ( clonedOptions , item . options , true ) ;
clonedOptions . x = item . x ;
clonedOptions . y = item . y ;
clonedOptions . amountOfConnections = item . edges . length ;
}
}
else {
else {
util . deepExtend ( clonedOptions , this . body . edges [ objId ] . options , true ) ;
util . deepExtend ( clonedOptions , item . options , true ) ;
}
}
return clonedOptions ;
return clonedOptions ;
}
}
@ -207,41 +217,37 @@ class ClusterEngine {
* @ param options
* @ param options
* @ private
* @ private
* /
* /
_createClusterEdges ( childNodesObj , childEdgesObj , newEdges , option s) {
let edge , childNodeId , childNode ;
_createClusterEdges ( childNodesObj , childEdgesObj , newEdges , clusterNodeProperties , clusterEdgePropertie s) {
let edge , childNodeId , childNode , toId , fromId , otherNodeId ;
let childKeys = Object . keys ( childNodesObj ) ;
let childKeys = Object . keys ( childNodesObj ) ;
for ( let i = 0 ; i < childKeys . length ; i ++ ) {
for ( let i = 0 ; i < childKeys . length ; i ++ ) {
childNodeId = childKeys [ i ] ;
childNodeId = childKeys [ i ] ;
childNode = childNodesObj [ childNodeId ] ;
childNode = childNodesObj [ childNodeId ] ;
// mark all edges for removal from global and construct new edges from the cluster to others
// construct new edges from the cluster to others
for ( let j = 0 ; j < childNode . edges . length ; j ++ ) {
for ( let j = 0 ; j < childNode . edges . length ; j ++ ) {
edge = childNode . edges [ j ] ;
edge = childNode . edges [ j ] ;
childEdgesObj [ edge . id ] = edge ;
childEdgesObj [ edge . id ] = edge ;
let otherNodeId = edge . toId ;
let otherOnTo = true ;
if ( edge . toId != childNodeId ) {
otherNode Id = edge . to Id;
otherOnTo = true ;
// childNodeId position will be replaced by the cluster.
if ( edge . toId == childNodeId ) { // this is a double equals because ints and strings can be interchanged here.
toId = clusterNodeProperties . id ;
from Id = edge . from Id;
otherNodeId = fromId ;
}
}
else if ( edge . fromId != childNodeId ) {
otherNodeId = edge . fromId ;
otherOnTo = false ;
else {
toId = edge . toId ;
fromId = clusterNodeProperties . id ;
otherNodeId = toId ;
}
}
// if the node connected to the cluster is also in the cluster we do not need a new edge.
if ( childNodesObj [ otherNodeId ] === undefined ) {
if ( childNodesObj [ otherNodeId ] === undefined ) {
let clonedOptions = this . _cloneOptions ( edge . id , 'edge' ) ;
util . deepExtend ( clonedOptions , options . clusterEdgeProperties ) ;
if ( otherOnTo === true ) {
clonedOptions . from = options . clusterNodeProperties . id ;
clonedOptions . to = otherNodeId ;
}
else {
clonedOptions . from = otherNodeId ;
clonedOptions . to = options . clusterNodeProperties . id ;
}
let clonedOptions = this . _cloneOptions ( edge , 'edge' ) ;
util . deepExtend ( clonedOptions , clusterEdgeProperties ) ;
clonedOptions . from = fromId ;
clonedOptions . to = toId ;
clonedOptions . id = 'clusterEdge:' + util . randomUUID ( ) ;
clonedOptions . id = 'clusterEdge:' + util . randomUUID ( ) ;
newEdges . push ( this . body . functions . createEdge ( clonedOptions ) )
newEdges . push ( this . body . functions . createEdge ( clonedOptions ) )
}
}
@ -249,7 +255,6 @@ class ClusterEngine {
}
}
}
}
/ * *
/ * *
* This function checks the options that can be supplied to the different cluster functions
* This function checks the options that can be supplied to the different cluster functions
* for certain fields and inserts defaults if needed
* for certain fields and inserts defaults if needed
@ -276,32 +281,34 @@ class ClusterEngine {
// kill condition: no children so cant cluster
// kill condition: no children so cant cluster
if ( Object . keys ( childNodesObj ) . length === 0 ) { return ; }
if ( Object . keys ( childNodesObj ) . length === 0 ) { return ; }
let clusterNodeProperties = util . deepExtend ( { } , options . clusterNodeProperties ) ;
// check if we have an unique id;
// check if we have an unique id;
if ( options . clusterNodeProperties . id === undefined ) { options . clusterNodeProperties . id = 'cluster:' + util . randomUUID ( ) ; }
let clusterId = options . clusterNodeProperties . id ;
if ( clusterNodeProperties . id === undefined ) { clusterNodeProperties . id = 'cluster:' + util . randomUUID ( ) ; }
let clusterId = clusterNodeProperties . id ;
// construct the clusterNodeProperties
// construct the clusterNodeProperties
let clusterNodeProperties = options . clusterNodeProperties ;
if ( options . processProperties !== undefined ) {
if ( options . processProperties !== undefined ) {
// get the childNode options
// get the childNode options
let childNodesOptions = [ ] ;
let childNodesOptions = [ ] ;
for ( let nodeId in childNodesObj ) {
for ( let nodeId in childNodesObj ) {
let clonedOptions = this . _cloneOptions ( nodeId ) ;
let clonedOptions = this . _cloneOptions ( childNodesObj [ nodeId ] ) ;
childNodesOptions . push ( clonedOptions ) ;
childNodesOptions . push ( clonedOptions ) ;
}
}
// get clusterproperties based on childNodes
// get clusterproperties based on childNodes
let childEdgesOptions = [ ] ;
let childEdgesOptions = [ ] ;
for ( let edgeId in childEdgesObj ) {
for ( let edgeId in childEdgesObj ) {
let clonedOptions = this . _cloneOptions ( edgeId , 'edge' ) ;
let clonedOptions = this . _cloneOptions ( childEdgesObj [ edgeId ] , 'edge' ) ;
childEdgesOptions . push ( clonedOptions ) ;
childEdgesOptions . push ( clonedOptions ) ;
}
}
clusterNodeProperties = options . processProperties ( clusterNodeProperties , childNodesOptions , childEdgesOptions ) ;
clusterNodeProperties = options . processProperties ( clusterNodeProperties , childNodesOptions , childEdgesOptions ) ;
if ( ! clusterNodeProperties ) {
if ( ! clusterNodeProperties ) {
throw new Error ( "The processCluster Properties function does not return properties!" ) ;
throw new Error ( "The processProperties function does not return properties!" ) ;
}
}
}
}
if ( clusterNodeProperties . label === undefined ) {
if ( clusterNodeProperties . label === undefined ) {
clusterNodeProperties . label = 'cluster' ;
clusterNodeProperties . label = 'cluster' ;
}
}
@ -320,25 +327,23 @@ class ClusterEngine {
clusterNodeProperties . y = pos . y ;
clusterNodeProperties . y = pos . y ;
}
}
// force the ID to remain the same
// force the ID to remain the same
clusterNodeProperties . id = clusterId ;
clusterNodeProperties . id = clusterId ;
// create the clusterNode
// create the clusterNode
let clusterNode = this . body . functions . createNode ( clusterNodeProperties , Cluster ) ;
let clusterNode = this . body . functions . createNode ( clusterNodeProperties , Cluster ) ;
clusterNode . isCluster = true ;
clusterNode . isCluster = true ;
clusterNode . containedNodes = childNodesObj ;
clusterNode . containedNodes = childNodesObj ;
clusterNode . containedEdges = childEdgesObj ;
clusterNode . containedEdges = childEdgesObj ;
// cache a copy from the cluster edge properties if we have to reconnect others later on
clusterNode . clusterEdgeProperties = options . clusterEdgeProperties ;
// finally put the cluster node into global
// finally put the cluster node into global
this . body . nodes [ clusterNodeProperties . id ] = clusterNode ;
this . body . nodes [ clusterNodeProperties . id ] = clusterNode ;
// create the new edges that will connect to the cluster
// create the new edges that will connect to the cluster
let newEdges = [ ] ;
let newEdges = [ ] ;
this . _createClusterEdges ( childNodesObj , childEdgesObj , newEdges , options ) ;
this . _createClusterEdges ( childNodesObj , childEdgesObj , newEdges , clusterNodeProperties , options . clusterEdgeProperties ) ;
// disable the childEdges
// disable the childEdges
for ( let edgeId in childEdgesObj ) {
for ( let edgeId in childEdgesObj ) {
@ -360,7 +365,6 @@ class ClusterEngine {
}
}
}
}
// push new edges to global
// push new edges to global
for ( let i = 0 ; i < newEdges . length ; i ++ ) {
for ( let i = 0 ; i < newEdges . length ; i ++ ) {
this . body . edges [ newEdges [ i ] . id ] = newEdges [ i ] ;
this . body . edges [ newEdges [ i ] . id ] = newEdges [ i ] ;
@ -370,7 +374,6 @@ class ClusterEngine {
// set ID to undefined so no duplicates arise
// set ID to undefined so no duplicates arise
clusterNodeProperties . id = undefined ;
clusterNodeProperties . id = undefined ;
// wrap up
// wrap up
if ( refreshData === true ) {
if ( refreshData === true ) {
this . body . emitter . emit ( '_dataChanged' ) ;
this . body . emitter . emit ( '_dataChanged' ) ;
@ -406,17 +409,20 @@ class ClusterEngine {
let minY = childNodesObj [ childKeys [ 0 ] ] . y ;
let minY = childNodesObj [ childKeys [ 0 ] ] . y ;
let maxY = childNodesObj [ childKeys [ 0 ] ] . y ;
let maxY = childNodesObj [ childKeys [ 0 ] ] . y ;
let node ;
let node ;
for ( let i = 0 ; i < childKeys . lengh t ; i ++ ) {
node = childNodesObj [ childKeys [ 0 ] ] ;
for ( let i = 1 ; i < childKeys . length ; i ++ ) {
node = childNodesObj [ childKeys [ i ] ] ;
minX = node . x < minX ? node . x : minX ;
minX = node . x < minX ? node . x : minX ;
maxX = node . x > maxX ? node . x : maxX ;
maxX = node . x > maxX ? node . x : maxX ;
minY = node . y < minY ? node . y : minY ;
minY = node . y < minY ? node . y : minY ;
maxY = node . y > maxY ? node . y : maxY ;
maxY = node . y > maxY ? node . y : maxY ;
}
}
return { x : 0.5 * ( minX + maxX ) , y : 0.5 * ( minY + maxY ) } ;
return { x : 0.5 * ( minX + maxX ) , y : 0.5 * ( minY + maxY ) } ;
}
}
/ * *
/ * *
* Open a cluster by calling this function .
* Open a cluster by calling this function .
* @ param { String } clusterNodeId | the ID of the cluster node
* @ param { String } clusterNodeId | the ID of the cluster node
@ -457,9 +463,46 @@ class ClusterEngine {
// release edges
// release edges
for ( let edgeId in containedEdges ) {
for ( let edgeId in containedEdges ) {
if ( containedEdges . hasOwnProperty ( edgeId ) ) {
if ( containedEdges . hasOwnProperty ( edgeId ) ) {
let edge = this . body . edges [ edgeId ] ;
edge . options . hidden = false ;
edge . togglePhysics ( true ) ;
let edge = containedEdges [ edgeId ] ;
// if this edge was a temporary edge and it's connected nodes do not exist anymore, we remove it from the data
if ( this . body . nodes [ edge . fromId ] === undefined || this . body . nodes [ edge . toId ] === undefined ) {
edge . edgeType . cleanup ( ) ;
// this removes the edge from node.edges, which is why edgeIds is formed
edge . disconnect ( ) ;
delete this . body . edges [ edgeId ] ;
}
else {
// one of the nodes connected to this edge is in a cluster. We give the edge to that cluster and make a new temporary edge.
if ( this . clusteredNodes [ edge . fromId ] !== undefined || this . clusteredNodes [ edge . toId ] !== undefined ) {
let fromId , toId ;
let clusteredNode = this . clusteredNodes [ edge . fromId ] || this . clusteredNodes [ edge . toId ] ;
let clusterId = clusteredNode . clusterId ;
let clusterNode = this . body . nodes [ clusterId ] ;
clusterNode . containedEdges [ edgeId ] = edge ;
if ( this . clusteredNodes [ edge . fromId ] !== undefined ) {
fromId = clusterId ;
toId = edge . toId ;
}
else {
fromId = edge . fromId ;
toId = clusterId ;
}
let clonedOptions = this . _cloneOptions ( edge , 'edge' ) ;
let id = 'clusterEdge:' + util . randomUUID ( ) ;
util . deepExtend ( clonedOptions , clusterNode . clusterEdgeProperties ) ;
util . deepExtend ( clonedOptions , { from : fromId , to : toId , hidden : false , physics : true , id : id } ) ;
let newEdge = this . body . functions . createEdge ( clonedOptions ) ;
this . body . edges [ id ] = newEdge ;
this . body . edges [ id ] . connect ( ) ;
}
else {
edge . options . hidden = false ;
edge . togglePhysics ( true ) ;
}
}
}
}
}
}