diff --git a/dist/vis.js b/dist/vis.js index 722f07f1..66eeadf7 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -16193,8 +16193,8 @@ return /******/ (function(modules) { // webpackBootstrap Network.prototype.getScale = function () { return this.view.getScale.apply(this.view, arguments); }; - Network.prototype.getPosition = function () { - return this.view.getPosition.apply(this.view, arguments); + Network.prototype.getViewPosition = function () { + return this.view.getViewPosition.apply(this.view, arguments); }; Network.prototype.fit = function () { return this.view.fit.apply(this.view, arguments); @@ -22575,28 +22575,41 @@ return /******/ (function(modules) { // webpackBootstrap var childNodesObj = {}; var childEdgesObj = {}; var nodeId = this.body.nodeIndices[i]; - if (this.body.nodes[nodeId].edges.length === 1) { + var visibleEdges = 0; + var edge = undefined; + for (var j = 0; j < this.body.nodes[nodeId].edges.length; j++) { + if (this.body.nodes[nodeId].edges[j].options.hidden === false) { + visibleEdges++; + edge = this.body.nodes[nodeId].edges[j]; + } + } + + if (visibleEdges === 1) { // this is an outlier - var edge = this.body.nodes[nodeId].edges[0]; var childNodeId = this._getConnectedId(edge, nodeId); if (childNodeId !== nodeId) { if (options.joinCondition === undefined) { - childEdgesObj[edge.id] = edge; - childNodesObj[nodeId] = this.body.nodes[nodeId]; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + if (this._checkIfUsed(clusters, nodeId, edge.id) === false && this._checkIfUsed(clusters, childNodeId, edge.id) === false) { + childEdgesObj[edge.id] = edge; + childNodesObj[nodeId] = this.body.nodes[nodeId]; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } } else { var clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters, nodeId, edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = this.body.nodes[nodeId]; } clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters, nodeId, edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } - clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); + + if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) { + clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); + } } } } @@ -22609,6 +22622,17 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_dataChanged'); } } + }, { + key: '_checkIfUsed', + value: function _checkIfUsed(clusters, nodeId, edgeId) { + for (var i = 0; i < clusters.length; i++) { + var cluster = clusters[i]; + if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) { + return true; + } + } + return false; + } }, { key: 'clusterByConnection', @@ -22792,12 +22816,6 @@ return /******/ (function(modules) { // webpackBootstrap var clusterNodeProperties = util.deepExtend({}, options.clusterNodeProperties); - // check if we have an unique id; - if (clusterNodeProperties.id === undefined) { - clusterNodeProperties.id = 'cluster:' + util.randomUUID(); - } - var clusterId = clusterNodeProperties.id; - // construct the clusterNodeProperties if (options.processProperties !== undefined) { // get the childNode options @@ -22820,6 +22838,12 @@ return /******/ (function(modules) { // webpackBootstrap } } + // check if we have an unique id; + if (clusterNodeProperties.id === undefined) { + clusterNodeProperties.id = 'cluster:' + util.randomUUID(); + } + var clusterId = clusterNodeProperties.id; + if (clusterNodeProperties.label === undefined) { clusterNodeProperties.label = 'cluster'; } @@ -22988,7 +23012,8 @@ return /******/ (function(modules) { // webpackBootstrap 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. + + // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { var fromId = undefined, toId = undefined; @@ -22997,22 +23022,25 @@ return /******/ (function(modules) { // webpackBootstrap var _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; - } + // if both from and to nodes are visible, we create a new temporary edge + if (edge.from.options.hidden !== true && edge.to.options.hidden !== true) { + if (this.clusteredNodes[edge.fromId] !== undefined) { + fromId = clusterId; + toId = edge.toId; + } else { + fromId = edge.fromId; + toId = clusterId; + } - var clonedOptions = this._cloneOptions(edge, 'edge'); - var id = 'clusterEdge:' + util.randomUUID(); - util.deepExtend(clonedOptions, _clusterNode.clusterEdgeProperties); - util.deepExtend(clonedOptions, { from: fromId, to: toId, hidden: false, physics: true, id: id }); - var newEdge = this.body.functions.createEdge(clonedOptions); + var clonedOptions = this._cloneOptions(edge, 'edge'); + var id = 'clusterEdge:' + util.randomUUID(); + util.deepExtend(clonedOptions, _clusterNode.clusterEdgeProperties); + util.deepExtend(clonedOptions, { from: fromId, to: toId, hidden: false, physics: true, id: id }); + var newEdge = this.body.functions.createEdge(clonedOptions); - this.body.edges[id] = newEdge; - this.body.edges[id].connect(); + this.body.edges[id] = newEdge; + this.body.edges[id].connect(); + } } else { edge.options.hidden = false; edge.togglePhysics(true); @@ -24289,8 +24317,8 @@ return /******/ (function(modules) { // webpackBootstrap return this.body.view.scale; } }, { - key: "getPosition", - value: function getPosition() { + key: "getViewPosition", + value: function getViewPosition() { return { x: this.body.view.translation.x, y: this.body.view.translation.y }; } }]); @@ -24756,9 +24784,9 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('_requestRedraw'); if (scaleOld < scale) { - this.body.emitter.emit('zoom', { direction: '+' }); + this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale }); } else { - this.body.emitter.emit('zoom', { direction: '-' }); + this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale }); } } } diff --git a/docs/network/index.html b/docs/network/index.html index 1ea95686..a00277f8 100644 --- a/docs/network/index.html +++ b/docs/network/index.html @@ -466,6 +466,33 @@ var locales = { container with the module name to contain its options. +
String event name, Function callback
)
+ String event name, Function callback
)
+ String event name, Function callback
)
+ {direction:'+'/'-'}
{direction:'+'/'-', scale: Number}
+ You can zoom in and out to cluster/decluster. +
+ + + + + + + diff --git a/lib/network/Network.js b/lib/network/Network.js index 00adb0e6..f0bae773 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -464,7 +464,7 @@ Network.prototype.selectEdges = function() {return this.selectionHandler Network.prototype.unselectAll = function() {return this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);}; Network.prototype.redraw = function() {return this.renderer.redraw.apply(this.renderer,arguments);}; Network.prototype.getScale = function() {return this.view.getScale.apply(this.view,arguments);}; -Network.prototype.getPosition = function() {return this.view.getPosition.apply(this.view,arguments);}; +Network.prototype.getViewPosition = function() {return this.view.getViewPosition.apply(this.view,arguments);}; Network.prototype.fit = function() {return this.view.fit.apply(this.view,arguments);}; Network.prototype.moveTo = function() {return this.view.moveTo.apply(this.view,arguments);}; Network.prototype.focus = function() {return this.view.focus.apply(this.view,arguments);}; diff --git a/lib/network/modules/Clustering.js b/lib/network/modules/Clustering.js index 031d48db..e68234cc 100644 --- a/lib/network/modules/Clustering.js +++ b/lib/network/modules/Clustering.js @@ -96,29 +96,42 @@ class ClusterEngine { let childNodesObj = {}; let childEdgesObj = {}; let nodeId = this.body.nodeIndices[i]; - if (this.body.nodes[nodeId].edges.length === 1) { + let visibleEdges = 0; + let edge; + for (let j = 0; j < this.body.nodes[nodeId].edges.length; j++) { + if (this.body.nodes[nodeId].edges[j].options.hidden === false) { + visibleEdges++; + edge = this.body.nodes[nodeId].edges[j]; + } + } + + if (visibleEdges === 1) { // this is an outlier - let edge = this.body.nodes[nodeId].edges[0]; let childNodeId = this._getConnectedId(edge, nodeId); if (childNodeId !== nodeId) { if (options.joinCondition === undefined) { - childEdgesObj[edge.id] = edge; - childNodesObj[nodeId] = this.body.nodes[nodeId]; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + if (this._checkIfUsed(clusters,nodeId,edge.id) === false && this._checkIfUsed(clusters,childNodeId,edge.id) === false) { + childEdgesObj[edge.id] = edge; + childNodesObj[nodeId] = this.body.nodes[nodeId]; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } } else { let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = this.body.nodes[nodeId]; } clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(clonedOptions) === true) { + if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } - clusters.push({nodes:childNodesObj, edges:childEdgesObj}) + + if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) { + clusters.push({nodes: childNodesObj, edges: childEdgesObj}) + } } } } @@ -132,6 +145,17 @@ class ClusterEngine { } } + + _checkIfUsed(clusters, nodeId, edgeId) { + for (let i = 0; i < clusters.length; i++) { + let cluster = clusters[i]; + if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) { + return true; + } + } + return false; + } + /** * suck all connected nodes of a node into the node. * @param nodeId @@ -285,10 +309,6 @@ class ClusterEngine { let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties); - // check if we have an unique id; - if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();} - let clusterId = clusterNodeProperties.id; - // construct the clusterNodeProperties if (options.processProperties !== undefined) { // get the childNode options @@ -311,6 +331,10 @@ class ClusterEngine { } } + // check if we have an unique id; + if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();} + let clusterId = clusterNodeProperties.id; + if (clusterNodeProperties.label === undefined) { clusterNodeProperties.label = 'cluster'; } @@ -474,7 +498,8 @@ class ClusterEngine { 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. + + // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { let fromId, toId; let clusteredNode = this.clusteredNodes[edge.fromId] || this.clusteredNodes[edge.toId]; @@ -482,23 +507,26 @@ class ClusterEngine { let clusterNode = this.body.nodes[clusterId]; clusterNode.containedEdges[edgeId] = edge; - if (this.clusteredNodes[edge.fromId] !== undefined) { - fromId = clusterId; - toId = edge.toId; + // if both from and to nodes are visible, we create a new temporary edge + if (edge.from.options.hidden !== true && edge.to.options.hidden !== true) { + 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 { - 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; diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index bf610b14..f79ae7c5 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -407,10 +407,10 @@ class InteractionHandler { this.body.emitter.emit('_requestRedraw'); if (scaleOld < scale) { - this.body.emitter.emit('zoom', {direction: '+'}); + this.body.emitter.emit('zoom', {direction: '+', scale: this.body.view.scale}); } else { - this.body.emitter.emit('zoom', {direction: '-'}); + this.body.emitter.emit('zoom', {direction: '-', scale: this.body.view.scale}); } } } diff --git a/lib/network/modules/View.js b/lib/network/modules/View.js index 68464b40..f7f301c2 100644 --- a/lib/network/modules/View.js +++ b/lib/network/modules/View.js @@ -325,7 +325,7 @@ class View { return this.body.view.scale; } - getPosition() { + getViewPosition() { return {x:this.body.view.translation.x, y:this.body.view.translation.y}; }