|
@ -95,7 +95,7 @@ class ClusterEngine { |
|
|
options = this._checkOptions(options); |
|
|
options = this._checkOptions(options); |
|
|
let clusters = []; |
|
|
let clusters = []; |
|
|
let usedNodes = {}; |
|
|
let usedNodes = {}; |
|
|
let edge, edges, node, nodeId, visibleEdges; |
|
|
|
|
|
|
|
|
let edge, edges, node, nodeId, relevantEdgeCount; |
|
|
// 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 childNodesObj = {}; |
|
|
let childNodesObj = {}; |
|
@ -104,50 +104,45 @@ class ClusterEngine { |
|
|
|
|
|
|
|
|
// if this node is already used in another cluster this session, we do not have to re-evaluate it.
|
|
|
// if this node is already used in another cluster this session, we do not have to re-evaluate it.
|
|
|
if (usedNodes[nodeId] === undefined) { |
|
|
if (usedNodes[nodeId] === undefined) { |
|
|
visibleEdges = 0; |
|
|
|
|
|
|
|
|
relevantEdgeCount = 0; |
|
|
node = this.body.nodes[nodeId]; |
|
|
node = this.body.nodes[nodeId]; |
|
|
edges = []; |
|
|
edges = []; |
|
|
for (let j = 0; j < node.edges.length; j++) { |
|
|
for (let j = 0; j < node.edges.length; j++) { |
|
|
edge = node.edges[j]; |
|
|
edge = node.edges[j]; |
|
|
if (edge.hiddenByCluster !== true) { |
|
|
if (edge.hiddenByCluster !== true) { |
|
|
|
|
|
if (edge.toId !== edge.fromId) { |
|
|
|
|
|
relevantEdgeCount++; |
|
|
|
|
|
} |
|
|
edges.push(edge); |
|
|
edges.push(edge); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// this node qualifies, we collect its neighbours to start the clustering process.
|
|
|
// this node qualifies, we collect its neighbours to start the clustering process.
|
|
|
if (edges.length === edgeCount) { |
|
|
|
|
|
|
|
|
if (relevantEdgeCount === edgeCount) { |
|
|
let gatheringSuccessful = true; |
|
|
let gatheringSuccessful = true; |
|
|
for (let j = 0; j < edges.length; j++) { |
|
|
for (let j = 0; j < edges.length; j++) { |
|
|
edge = edges[j]; |
|
|
edge = edges[j]; |
|
|
let childNodeId = this._getConnectedId(edge, nodeId); |
|
|
let childNodeId = this._getConnectedId(edge, nodeId); |
|
|
// if unused and if not referencing itself
|
|
|
|
|
|
if (childNodeId !== nodeId && usedNodes[nodeId] === undefined) { |
|
|
|
|
|
// add the nodes to the list by the join condition.
|
|
|
|
|
|
if (options.joinCondition === undefined) { |
|
|
|
|
|
|
|
|
// add the nodes to the list by the join condition.
|
|
|
|
|
|
if (options.joinCondition === undefined) { |
|
|
|
|
|
childEdgesObj[edge.id] = edge; |
|
|
|
|
|
childNodesObj[nodeId] = this.body.nodes[nodeId]; |
|
|
|
|
|
childNodesObj[childNodeId] = this.body.nodes[childNodeId]; |
|
|
|
|
|
usedNodes[nodeId] = true; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); |
|
|
|
|
|
if (options.joinCondition(clonedOptions) === true) { |
|
|
childEdgesObj[edge.id] = edge; |
|
|
childEdgesObj[edge.id] = edge; |
|
|
childNodesObj[nodeId] = this.body.nodes[nodeId]; |
|
|
childNodesObj[nodeId] = this.body.nodes[nodeId]; |
|
|
childNodesObj[childNodeId] = this.body.nodes[childNodeId]; |
|
|
|
|
|
usedNodes[nodeId] = true; |
|
|
usedNodes[nodeId] = true; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); |
|
|
|
|
|
if (options.joinCondition(clonedOptions) === true) { |
|
|
|
|
|
childEdgesObj[edge.id] = edge; |
|
|
|
|
|
childNodesObj[nodeId] = this.body.nodes[nodeId]; |
|
|
|
|
|
usedNodes[nodeId] = true; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
// this node does not qualify after all.
|
|
|
|
|
|
gatheringSuccessful = false; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// this node does not qualify after all.
|
|
|
|
|
|
gatheringSuccessful = false; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else { |
|
|
|
|
|
// this node does not qualify after all.
|
|
|
|
|
|
gatheringSuccessful = false; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// add to the cluster queue
|
|
|
// add to the cluster queue
|
|
@ -280,7 +275,7 @@ class ClusterEngine { |
|
|
* @param options |
|
|
* @param options |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_createClusterEdges (childNodesObj, clusterNodeProperties, clusterEdgeProperties) { |
|
|
|
|
|
|
|
|
_createClusterEdges (childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { |
|
|
let edge, childNodeId, childNode, toId, fromId, otherNodeId; |
|
|
let edge, childNodeId, childNode, toId, fromId, otherNodeId; |
|
|
|
|
|
|
|
|
// loop over all child nodes and their edges to find edges going out of the cluster
|
|
|
// loop over all child nodes and their edges to find edges going out of the cluster
|
|
@ -296,16 +291,22 @@ class ClusterEngine { |
|
|
edge = childNode.edges[j]; |
|
|
edge = childNode.edges[j]; |
|
|
// we only handle edges that are visible to the system, not the disabled ones from the clustering process.
|
|
|
// we only handle edges that are visible to the system, not the disabled ones from the clustering process.
|
|
|
if (edge.hiddenByCluster !== true) { |
|
|
if (edge.hiddenByCluster !== true) { |
|
|
// set up the from and to.
|
|
|
|
|
|
if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here.
|
|
|
|
|
|
toId = clusterNodeProperties.id; |
|
|
|
|
|
fromId = edge.fromId; |
|
|
|
|
|
otherNodeId = fromId; |
|
|
|
|
|
|
|
|
// self-referencing edges will be added to the "hidden" list
|
|
|
|
|
|
if (edge.toId == edge.fromId) { |
|
|
|
|
|
childEdgesObj[edge.id] = edge; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
toId = edge.toId; |
|
|
|
|
|
fromId = clusterNodeProperties.id; |
|
|
|
|
|
otherNodeId = toId; |
|
|
|
|
|
|
|
|
// set up the from and to.
|
|
|
|
|
|
if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here.
|
|
|
|
|
|
toId = clusterNodeProperties.id; |
|
|
|
|
|
fromId = edge.fromId; |
|
|
|
|
|
otherNodeId = fromId; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
toId = edge.toId; |
|
|
|
|
|
fromId = clusterNodeProperties.id; |
|
|
|
|
|
otherNodeId = toId; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Only edges from the cluster outwards are being replaced.
|
|
|
// Only edges from the cluster outwards are being replaced.
|
|
@ -446,8 +447,8 @@ class ClusterEngine { |
|
|
// 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
|
|
|
|
|
|
this._createClusterEdges(childNodesObj, clusterNodeProperties, options.clusterEdgeProperties); |
|
|
|
|
|
|
|
|
// create the new edges that will connect to the cluster, all self-referencing edges will be added to childEdgesObject here.
|
|
|
|
|
|
this._createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties); |
|
|
|
|
|
|
|
|
// disable the childEdges
|
|
|
// disable the childEdges
|
|
|
for (let edgeId in childEdgesObj) { |
|
|
for (let edgeId in childEdgesObj) { |
|
|