Browse Source

Network: make 'hidden' and 'clustered' play nice together (#3274)

Fix for #3164

- `network.clustering.cluster()` now handles all nodes, not just the visible ones
- Changing ivisibility of nodes now explicitly takes clustering into account, see `Network._updateVisibleIndices()`
- `network.clustering` does not change `hidden` status any more.

The important part of this PR is the realization that 'hidden' and 'clustered' are two distinct things and should be handled separately.
In particular, clustering should **not** change the `hidden` state in any way.
revert-3409-performance
wimrijnders 7 years ago
committed by yotamberk
parent
commit
af4e1fa9ed
2 changed files with 50 additions and 16 deletions
  1. +15
    -4
      lib/network/Network.js
  2. +35
    -12
      lib/network/modules/Clustering.js

+ 15
- 4
lib/network/Network.js View File

@ -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 * @private
*/ */
Network.prototype._updateVisibleIndices = function () { Network.prototype._updateVisibleIndices = function () {
@ -258,7 +263,7 @@ Network.prototype._updateVisibleIndices = function () {
for (let nodeId in nodes) { for (let nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) { 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); this.body.nodeIndices.push(nodes[nodeId].id);
} }
} }
@ -266,8 +271,14 @@ Network.prototype._updateVisibleIndices = function () {
for (let edgeId in edges) { for (let edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) { 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);
} }
} }
} }

+ 35
- 12
lib/network/modules/Clustering.js View File

@ -5,8 +5,8 @@ var Cluster = require('./components/nodes/Cluster').default;
class ClusterEngine { class ClusterEngine {
constructor(body) { constructor(body) {
this.body = 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.options = {};
this.defaultOptions = {}; this.defaultOptions = {};
@ -60,14 +60,15 @@ 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 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 node = this.body.nodes[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] = 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++) { for (let i = 0; i < node.edges.length; i++) {
let edge = node.edges[i]; let edge = node.edges[i];
if (this.clusteredEdges[edge.id] === undefined) { if (this.clusteredEdges[edge.id] === undefined) {
@ -332,7 +333,7 @@ class ClusterEngine {
// hide the replaced edge // hide the replaced edge
this._backupEdgeOptions(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 // cache the options before changing
this._backupEdgeOptions(edge); this._backupEdgeOptions(edge);
// disable physics and hide the 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) { for (let nodeId in childNodesObj) {
if (childNodesObj.hasOwnProperty(nodeId)) { if (childNodesObj.hasOwnProperty(nodeId)) {
this.clusteredNodes[nodeId] = {clusterId:clusterNodeProperties.id, node: this.body.nodes[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) { _backupEdgeOptions(edge) {
if (this.clusteredEdges[edge.id] === undefined) { 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) { _restoreEdge(edge) {
let originalOptions = this.clusteredEdges[edge.id]; let originalOptions = this.clusteredEdges[edge.id];
if (originalOptions !== undefined) { if (originalOptions !== undefined) {
edge.setOptions({physics: originalOptions.physics, hidden: originalOptions.hidden});
edge.setOptions({physics: originalOptions.physics});
delete this.clusteredEdges[edge.id]; delete this.clusteredEdges[edge.id];
} }
} }
@ -591,8 +592,7 @@ class ClusterEngine {
containedNode.vx = clusterNode.vx; containedNode.vx = clusterNode.vx;
containedNode.vy = clusterNode.vy; 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]; delete this.clusteredNodes[nodeId];
} }
@ -845,6 +845,29 @@ class ClusterEngine {
return hubThreshold; 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;
}
} }

Loading…
Cancel
Save