diff --git a/lib/network/Network.js b/lib/network/Network.js index d7528f9a..1df33bdf 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -247,7 +247,12 @@ Network.prototype.setOptions = function (options) { /** - * Update the this.body.nodeIndices with the most recent node index list + * Update the visible nodes and edges list with the most recent node state. + * + * Visible nodes are stored in this.body.nodeIndices. + * Visible edges are stored in this.body.edgeIndices. + * A node or edges is visible if it is not hidden or clustered. + * * @private */ Network.prototype._updateVisibleIndices = function () { @@ -258,7 +263,7 @@ Network.prototype._updateVisibleIndices = function () { for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].options.hidden === false) { + if (!this.clustering._isClusteredNode(nodeId) && nodes[nodeId].options.hidden === false) { this.body.nodeIndices.push(nodes[nodeId].id); } } @@ -266,8 +271,14 @@ Network.prototype._updateVisibleIndices = function () { for (let edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].options.hidden === false) { - this.body.edgeIndices.push(edges[edgeId].id); + let edge = edges[edgeId]; + + if (!this.clustering._isClusteredEdge(edgeId) + && edge.options.hidden === false + // Also hidden if any of its connecting nodes are hidden + && nodes[edge.fromId].options.hidden === false + && nodes[edge.toId ].options.hidden === false) { + this.body.edgeIndices.push(edge.id); } } } diff --git a/lib/network/modules/Clustering.js b/lib/network/modules/Clustering.js index f295c162..41692f8f 100644 --- a/lib/network/modules/Clustering.js +++ b/lib/network/modules/Clustering.js @@ -5,8 +5,8 @@ var Cluster = require('./components/nodes/Cluster').default; class ClusterEngine { constructor(body) { this.body = body; - this.clusteredNodes = {}; - this.clusteredEdges = {}; + this.clusteredNodes = {}; // Set of all nodes which are in a cluster + this.clusteredEdges = {}; // Set of all edges replaced by a clustering edge this.options = {}; this.defaultOptions = {}; @@ -60,14 +60,15 @@ class ClusterEngine { let childEdgesObj = {}; // collect the nodes that will be in the cluster - for (let i = 0; i < this.body.nodeIndices.length; i++) { - let nodeId = this.body.nodeIndices[i]; + for (let nodeId in this.body.nodes) { + if (!this.body.nodes.hasOwnProperty(nodeId)) continue; + let node = this.body.nodes[nodeId]; let clonedOptions = NetworkUtil.cloneOptions(node); if (options.joinCondition(clonedOptions) === true) { childNodesObj[nodeId] = this.body.nodes[nodeId]; - // collect the nodes 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]; if (this.clusteredEdges[edge.id] === undefined) { @@ -332,7 +333,7 @@ class ClusterEngine { // hide the replaced edge this._backupEdgeOptions(edge); - edge.setOptions({physics:false, hidden:true}); + edge.setOptions({physics:false}); } } @@ -451,7 +452,7 @@ class ClusterEngine { // cache the options before changing this._backupEdgeOptions(edge); // disable physics and hide the edge - edge.setOptions({physics:false, hidden:true}); + edge.setOptions({physics:false}); } } } @@ -460,7 +461,7 @@ class ClusterEngine { for (let nodeId in childNodesObj) { if (childNodesObj.hasOwnProperty(nodeId)) { this.clusteredNodes[nodeId] = {clusterId:clusterNodeProperties.id, node: this.body.nodes[nodeId]}; - this.body.nodes[nodeId].setOptions({hidden:true, physics:false}); + this.body.nodes[nodeId].setOptions({physics:false}); } } @@ -475,14 +476,14 @@ class ClusterEngine { _backupEdgeOptions(edge) { if (this.clusteredEdges[edge.id] === undefined) { - this.clusteredEdges[edge.id] = {physics: edge.options.physics, hidden: edge.options.hidden}; + this.clusteredEdges[edge.id] = {physics: edge.options.physics}; } } _restoreEdge(edge) { let originalOptions = this.clusteredEdges[edge.id]; if (originalOptions !== undefined) { - edge.setOptions({physics: originalOptions.physics, hidden: originalOptions.hidden}); + edge.setOptions({physics: originalOptions.physics}); delete this.clusteredEdges[edge.id]; } } @@ -591,8 +592,7 @@ class ClusterEngine { containedNode.vx = clusterNode.vx; containedNode.vy = clusterNode.vy; - // we use these methods to avoid re-instantiating the shape, which happens with setOptions. - containedNode.setOptions({hidden:false, physics:true}); + containedNode.setOptions({physics:true}); delete this.clusteredNodes[nodeId]; } @@ -845,6 +845,29 @@ class ClusterEngine { return hubThreshold; }; + + /** + * Determine if node with given id is part of a cluster. + * + * @return {boolean} true if part of a cluster. + */ + _isClusteredNode(nodeId) { + return this.clusteredNodes[nodeId] !== undefined; + } + + + /** + * Determine if edge with given id is not visible due to clustering. + * + * An edge is considered clustered if: + * - it is directly replaced by a clustering edge + * - any of its connecting nodes is in a cluster + * + * @return {boolean} true if part of a cluster. + */ + _isClusteredEdge(edgeId) { + return this.clusteredEdges[edgeId] !== undefined; + } }