diff --git a/docs/network/index.html b/docs/network/index.html
index 9bdf520a..7a438717 100644
--- a/docs/network/index.html
+++ b/docs/network/index.html
@@ -645,7 +645,20 @@ var locales = {
| Returns: Value |
When a clusteredEdgeId is available, this method will return the original baseEdgeId provided in data.edges
- ie. After clustering the 'SelectEdge' event is fired but provides only the clustered edge. This method can then be used to return the baseEdgeId.
+ ie. After clustering the 'SelectEdge' event is fired but provides only the clustered edge. This method can then be used to return the baseEdgeId.
+ This method is deprecated. Please use getBaseEdges() instead.
+ |
+
+
+
+ getBaseEdges(String clusteredEdgeId)
+ |
+
+
+ | Returns: Array |
+ For the given clusteredEdgeId, this method will return all the original base edge id's provided in data.edges.
+ For a non-clustered (i.e. 'base') edge, clusteredEdgeId is returned.
+ Only the base edge id's are returned. All clustered edges id's under clusteredEdgeId are skipped, but scanned recursively to return their base id's.
|
diff --git a/examples/network/other/changingClusteredEdgesNodes.html b/examples/network/other/changingClusteredEdgesNodes.html
index 0f079720..4bb324d9 100644
--- a/examples/network/other/changingClusteredEdgesNodes.html
+++ b/examples/network/other/changingClusteredEdgesNodes.html
@@ -27,9 +27,9 @@
-Demonstrating getBaseEdge, getClusteredEdges updateEdge and updateClusteredNode.
- Clicking on the cluster will change it to a star (updateClusteredNode).
+Demonstrating getBaseEdges, getClusteredEdges updateEdge and updateClusteredNode.
- Clicking on the cluster will change it to a star (updateClusteredNode).
- Clicking on an edge will make it red regardless of whether it is a clusteredEdge or not (updateEdge)
-- Clicking on an edge will also show the results of getBaseEdge and getClusteredEdge
+- Clicking on an edge will also show the results of getBaseEdges and getClusteredEdge
@@ -94,7 +94,7 @@ Demonstrating getBaseEdge, getClusteredEdges updateEdge and updateClusteredNode.
var obj = {};
obj.clicked_id = params.edges[0];
network.clustering.updateEdge(params.edges[0], {color : '#aa0000'});
- obj.base_edge = network.clustering.getBaseEdge(params.edges[0]);
+ obj.base_edges = network.clustering.getBaseEdges(params.edges[0]);
obj.all_clustered_edges = network.clustering.getClusteredEdges(params.edges[0]);
document.getElementById('eventSpan').innerHTML = 'selectEdge event:
' + JSON.stringify(obj, null, 4);
}
diff --git a/lib/network/modules/Clustering.js b/lib/network/modules/Clustering.js
index 41692f8f..50c7abf1 100644
--- a/lib/network/modules/Clustering.js
+++ b/lib/network/modules/Clustering.js
@@ -305,37 +305,62 @@ class ClusterEngine {
}
}
- // here we actually create the replacement edges. We could not do this in the loop above as the creation process
+
+ //
+ // Here we actually create the replacement edges.
+ //
+ // We could not do this in the loop above as the creation process
// would add an edge to the edges array we are iterating over.
+ //
+ // NOTE: a clustered edge can have multiple base edges!
+ //
+ var newEdges = [];
+
+ /**
+ * Find a cluster edge which matches the given created edge.
+ */
+ var getNewEdge = function(createdEdge) {
+ for (let j = 0; j < newEdges.length; j++) {
+ let newEdge = newEdges[j];
+
+ // We replace both to and from edges with a single cluster edge
+ let matchToDirection = (createdEdge.fromId === newEdge.fromId && createdEdge.toId === newEdge.toId);
+ let matchFromDirection = (createdEdge.fromId === newEdge.toId && createdEdge.toId === newEdge.fromId);
+
+ if (matchToDirection || matchFromDirection ) {
+ return newEdge;
+ }
+ }
+
+ return null;
+ };
+
+
for (let j = 0; j < createEdges.length; j++) {
- let edge = createEdges[j].edge;
- // copy the options of the edge we will replace
- let clonedOptions = NetworkUtil.cloneOptions(edge, 'edge');
- // make sure the properties of clusterEdges are superimposed on it
- util.deepExtend(clonedOptions, clusterEdgeProperties);
-
- // set up the edge
- clonedOptions.from = createEdges[j].fromId;
- clonedOptions.to = createEdges[j].toId;
- clonedOptions.id = 'clusterEdge:' + util.randomUUID();
- //clonedOptions.id = '(cf: ' + createEdges[j].fromId + " to: " + createEdges[j].toId + ")" + Math.random();
-
- // create the edge and give a reference to the one it replaced.
- let newEdge = this.body.functions.createEdge(clonedOptions);
- newEdge.clusteringEdgeReplacingId = edge.id;
+ let createdEdge = createEdges[j];
+ let edge = createdEdge.edge;
+ let newEdge = getNewEdge(createdEdge);
+
+ if (newEdge === null) {
+ // Create a clustered edge for this connection
+ newEdge = this._createClusteredEdge(
+ createdEdge.fromId,
+ createdEdge.toId,
+ edge,
+ clusterEdgeProperties);
+
+ newEdges.push(newEdge);
+ } else {
+ newEdge.clusteringEdgeReplacingIds.push(edge.id);
+ }
// also reference the new edge in the old edge
this.body.edges[edge.id].edgeReplacedById = newEdge.id;
- // connect the edge.
- this.body.edges[newEdge.id] = newEdge;
- newEdge.connect();
-
// hide the replaced edge
this._backupEdgeOptions(edge);
edge.setOptions({physics:false});
}
-
}
/**
@@ -606,52 +631,47 @@ class ClusterEngine {
// actually handling the deleting.
for (let i = 0; i < edgesToBeDeleted.length; i++) {
- let edge = edgesToBeDeleted[i];
-
- let otherNodeId = this._getConnectedId(edge, clusterNodeId);
- // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
- if (this.clusteredNodes[otherNodeId] !== undefined) {
- // transfer ownership:
- let otherCluster = this.body.nodes[this.clusteredNodes[otherNodeId].clusterId];
- let transferEdge = this.body.edges[edge.clusteringEdgeReplacingId];
- if (transferEdge !== undefined) {
+ let edge = edgesToBeDeleted[i];
+ let otherNodeId = this._getConnectedId(edge, clusterNodeId);
+ let otherNode = this.clusteredNodes[otherNodeId];
+
+ for (let j = 0; j < edge.clusteringEdgeReplacingIds.length; j++) {
+ let transferId = edge.clusteringEdgeReplacingIds[j];
+ let transferEdge = this.body.edges[transferId];
+ if (transferEdge === undefined) continue;
+
+ // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
+ if (otherNode !== undefined) {
+ // transfer ownership:
+ let otherCluster = this.body.nodes[otherNode.clusterId];
otherCluster.containedEdges[transferEdge.id] = transferEdge;
// delete local reference
delete containedEdges[transferEdge.id];
- // create new cluster edge from the otherCluster:
// get to and from
let fromId = transferEdge.fromId;
let toId = transferEdge.toId;
if (transferEdge.toId == otherNodeId) {
- toId = this.clusteredNodes[otherNodeId].clusterId;
+ toId = otherNode.clusterId;
}
else {
- fromId = this.clusteredNodes[otherNodeId].clusterId;
+ fromId = otherNode.clusterId;
}
- // clone the options and apply the cluster options to them
- let clonedOptions = NetworkUtil.cloneOptions(transferEdge, 'edge');
- util.deepExtend(clonedOptions, otherCluster.clusterEdgeProperties);
-
- // apply the edge specific options to it.
- let id = 'clusterEdge:' + util.randomUUID();
- util.deepExtend(clonedOptions, {from: fromId, to: toId, hidden: false, physics: true, id: id});
+ // create new cluster edge from the otherCluster
+ this._createClusteredEdge(
+ fromId,
+ toId,
+ transferEdge,
+ otherCluster.clusterEdgeProperties,
+ {hidden: false, physics: true});
- // create it
- let newEdge = this.body.functions.createEdge(clonedOptions);
- newEdge.clusteringEdgeReplacingId = transferEdge.id;
- this.body.edges[id] = newEdge;
- this.body.edges[id].connect();
- }
- }
- else {
- let replacedEdge = this.body.edges[edge.clusteringEdgeReplacingId];
- if (replacedEdge !== undefined) {
- this._restoreEdge(replacedEdge);
+ } else {
+ this._restoreEdge(transferEdge);
}
}
+
edge.cleanup();
// this removes the edge from node.edges, which is why edgeIds is formed
edge.disconnect();
@@ -773,22 +793,61 @@ class ClusterEngine {
* Get the base edge id of clusterEdgeId. cluster edge (clusteredEdgeId) -> cluster edge B -> cluster edge C -> base edge
* @param clusteredEdgeId
* @returns baseEdgeId
+ *
+ * TODO: deprecate in 5.0.0. Method getBaseEdges() is the correct one to use.
*/
getBaseEdge(clusteredEdgeId) {
- let baseEdgeId = clusteredEdgeId;
- let max = 100;
+ // Just kludge this by returning the first base edge id found
+ return this.getBaseEdges(clusteredEdgeId)[0];
+ }
+
+
+ /**
+ * Get all regular edges for this clustered edge id.
+ *
+ * @param {Number} clusteredEdgeId
+ * @returns {Array[Number} all baseEdgeId's under this clustered edge
+ */
+ getBaseEdges(clusteredEdgeId) {
+ let IdsToHandle = [clusteredEdgeId];
+ let doneIds = [];
+ let foundIds = [];
+ let max = 100;
let counter = 0;
- while (clusteredEdgeId !== undefined && this.body.edges[clusteredEdgeId] !== undefined && counter < max) {
- clusteredEdgeId = this.body.edges[clusteredEdgeId].clusteringEdgeReplacingId;
+ while (IdsToHandle.length > 0 && counter < max) {
+ let nextId = IdsToHandle.pop();
+ if (nextId === undefined) continue; // Paranoia here and onwards
+ let nextEdge = this.body.edges[nextId];
+ if (nextEdge === undefined) continue;
counter++;
- if (clusteredEdgeId !== undefined) {
- baseEdgeId = clusteredEdgeId;
+
+ let replacingIds = nextEdge.clusteringEdgeReplacingIds;
+ if (replacingIds === undefined) {
+ // nextId is a base id
+ foundIds.push(nextId);
+ } else {
+ // Another cluster edge, unravel this one as well
+ for (let i = 0; i < replacingIds.length; ++i) {
+ let replacingId = replacingIds[i];
+
+ // Don't add if already handled
+ // TODO: never triggers; find a test-case which does
+ if (IdsToHandle.indexOf(replacingIds) !== -1 || doneIds.indexOf(replacingIds) !== -1) {
+ continue;
+ }
+
+ IdsToHandle.push(replacingId);
+ }
}
+
+ doneIds.push(nextId);
}
- return baseEdgeId;
+
+ return foundIds;
}
+
/**
* Get the Id the node is connected to
* @param edge
@@ -846,6 +905,39 @@ class ClusterEngine {
};
+ /**
+ * Create an edge for the cluster representation.
+ *
+ * @return {Edge} newly created clustered edge
+ * @private
+ */
+ _createClusteredEdge(fromId, toId, baseEdge, clusterEdgeProperties, extraOptions) {
+ // copy the options of the edge we will replace
+ let clonedOptions = NetworkUtil.cloneOptions(baseEdge, 'edge');
+ // make sure the properties of clusterEdges are superimposed on it
+ util.deepExtend(clonedOptions, clusterEdgeProperties);
+
+ // set up the edge
+ clonedOptions.from = fromId;
+ clonedOptions.to = toId;
+ clonedOptions.id = 'clusterEdge:' + util.randomUUID();
+
+ // apply the edge specific options to it if specified
+ if (extraOptions !== undefined) {
+ util.deepExtend(clonedOptions, extraOptions);
+ }
+
+ let newEdge = this.body.functions.createEdge(clonedOptions);
+ newEdge.clusteringEdgeReplacingIds = [baseEdge.id];
+ newEdge.connect();
+
+ // Register the new edge
+ this.body.edges[newEdge.id] = newEdge;
+
+ return newEdge;
+ }
+
+
/**
* Determine if node with given id is part of a cluster.
*