Browse Source

clustering added comments, fixed bugs

css_transitions
Alex de Mulder 10 years ago
parent
commit
c875715ee4
3 changed files with 150 additions and 72 deletions
  1. +69
    -31
      src/graph/Graph.js
  2. +5
    -4
      src/graph/Node.js
  3. +76
    -37
      vis.js

+ 69
- 31
src/graph/Graph.js View File

@ -72,8 +72,9 @@ function Graph (container, data, options) {
} }
}, },
clustering: { clustering: {
clusterLength: 30, // threshold length for clustering
zoomOffset: 0.1,
clusterLength: 50, // threshold length for clustering
fontSizeMultiplier: 2, // how much the fontsize grows
edgeGrowth: 10,
widthGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * widthGrowthFactor widthGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * widthGrowthFactor
heightGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * heightGrowthFactor heightGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * heightGrowthFactor
massTransferCoefficient: 0.1 // parent.mass += massTransferCoefficient * child.mass massTransferCoefficient: 0.1 // parent.mass += massTransferCoefficient * child.mass
@ -88,7 +89,7 @@ function Graph (container, data, options) {
this.nodes = {}; // object with Node objects this.nodes = {}; // object with Node objects
this.edges = {}; // object with Edge objects this.edges = {}; // object with Edge objects
this.scale = 1; // defining the global scale variable in the constructor this.scale = 1; // defining the global scale variable in the constructor
this.previous_scale = 1; // use this to check if the zoom operation is in or out
this.previous_scale = this.scale; // this is used to check if the zoom operation is zooming in or out
// TODO: create a counter to keep track on the number of nodes having values // TODO: create a counter to keep track on the number of nodes having values
// TODO: create a counter to keep track on the number of nodes currently moving // TODO: create a counter to keep track on the number of nodes currently moving
// TODO: create a counter to keep track on the number of edges having values // TODO: create a counter to keep track on the number of edges having values
@ -149,6 +150,7 @@ function Graph (container, data, options) {
this.setData(data); this.setData(data);
}; };
/** /**
* This function checks if the zoom action is in or out. * This function checks if the zoom action is in or out.
* If out, check if we can form clusters, if in, check if we can open clusters. * If out, check if we can form clusters, if in, check if we can open clusters.
@ -157,22 +159,28 @@ function Graph (container, data, options) {
* @private * @private
*/ */
Graph.prototype._updateClusters = function() { Graph.prototype._updateClusters = function() {
var moving_before_clustering = this.moving;
if (this.previous_scale > this.scale) { // zoom out if (this.previous_scale > this.scale) { // zoom out
this._formClusters();
this._formClusters(false);
} }
else if (this.previous_scale < this.scale) { // zoom out else if (this.previous_scale < this.scale) { // zoom out
this._openClusters(); this._openClusters();
} }
this._updateNodeIndexList();
for (var nid in this.nodes) { for (var nid in this.nodes) {
var node = this.nodes[nid]; var node = this.nodes[nid];
node.label = String(node.remaining_edges);
node.label = String(node.remaining_edges).concat(":",String(node.cluster_size));
} }
this.previous_scale = this.scale; this.previous_scale = this.scale;
if (this.moving != moving_before_clustering) {
this.start();
}
}; };
/** /**
* This function loops over all nodes in the node_indices list. For each node it checks if it is a cluster and if it * This function loops over all nodes in the node_indices list. For each node it checks if it is a cluster and if it
* has to be opened based on the current zoom level. * has to be opened based on the current zoom level.
@ -182,10 +190,13 @@ Graph.prototype._updateClusters = function() {
Graph.prototype._openClusters = function() { Graph.prototype._openClusters = function() {
for (var i = 0; i < this.node_indices.length; i++) { for (var i = 0; i < this.node_indices.length; i++) {
var node = this.nodes[this.node_indices[i]]; var node = this.nodes[this.node_indices[i]];
this._expandClusterNode(node,true);
this._expandClusterNode(node,false);
} }
this._updateNodeIndexList();
}; };
/** /**
* This function checks if a node has to be opened. This is done by checking the zoom level. * This function checks if a node has to be opened. This is done by checking the zoom level.
* If the node contains child nodes, this function is recursively called on the child nodes as well. * If the node contains child nodes, this function is recursively called on the child nodes as well.
@ -197,6 +208,7 @@ Graph.prototype._openClusters = function() {
*/ */
Graph.prototype._expandClusterNode = function(node, recursive) { Graph.prototype._expandClusterNode = function(node, recursive) {
if (node.formation_scale != undefined) { if (node.formation_scale != undefined) {
console.log(this.scale,node.formation_scale)
if (this.scale > node.formation_scale) { if (this.scale > node.formation_scale) {
for (var contained_node_id in node.contained_nodes) { for (var contained_node_id in node.contained_nodes) {
if (node.contained_nodes.hasOwnProperty(contained_node_id)) { if (node.contained_nodes.hasOwnProperty(contained_node_id)) {
@ -209,11 +221,9 @@ Graph.prototype._expandClusterNode = function(node, recursive) {
node.mass -= this.constants.clustering.massTransferCoefficient * this.nodes[contained_node_id].mass; node.mass -= this.constants.clustering.massTransferCoefficient * this.nodes[contained_node_id].mass;
// decrease the size again // decrease the size again
node.cluster_size -= child_node.cluster_size;
var grow_coefficient = this.constants.clustering.zoomOffset + (node.cluster_size + child_node.cluster_size) / node.cluster_size;
node.width -= grow_coefficient * this.constants.clustering.widthGrowth;
node.height -= grow_coefficient * this.constants.clustering.heightGrowth;
node.fontSize -= 1 * child_node.cluster_size;
node.width -= child_node.cluster_size * this.constants.clustering.widthGrowth;
node.height -= child_node.cluster_size * this.constants.clustering.heightGrowth;
node.fontSize -= this.constants.clustering.fontSizeMultiplier * child_node.cluster_size;
// check if a further expansion step is possible if recursivity is enabled // check if a further expansion step is possible if recursivity is enabled
if (recursive == true) { if (recursive == true) {
@ -232,17 +242,23 @@ Graph.prototype._expandClusterNode = function(node, recursive) {
// reset the cluster settings of this node // reset the cluster settings of this node
node.resetCluster(); node.resetCluster();
// restart the simulation to reorganise all nodes
this.moving = true;
} }
} }
}; };
/** /**
* This function checks if any nodes at the end of their trees have edges below a threshold length * This function checks if any nodes at the end of their trees have edges below a threshold length
* This function is called only from _updateClusters() * This function is called only from _updateClusters()
* forceLevelCollapse ignores the length and
* *
* @private * @private
* @param force_level_collapse | Boolean
*/ */
Graph.prototype._formClusters = function() {
Graph.prototype._formClusters = function(force_level_collapse) {
var min_length = this.constants.clustering.clusterLength/this.scale; var min_length = this.constants.clustering.clusterLength/this.scale;
var dx,dy,length, var dx,dy,length,
@ -266,8 +282,8 @@ Graph.prototype._formClusters = function() {
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
if (length < min_length) {
// checking for clustering possiblities
if (length < min_length || force_level_collapse == true) {
// checking for clustering possibilities
// first check which node is larger // first check which node is larger
if (edge.to.mass > edge.from.mass) { if (edge.to.mass > edge.from.mass) {
@ -284,18 +300,24 @@ Graph.prototype._formClusters = function() {
// clusters. This will also have to be altered in the force calculation and rendering. // clusters. This will also have to be altered in the force calculation and rendering.
// This method is non-destructive and does not require a second set of data. // This method is non-destructive and does not require a second set of data.
if (child_node.remaining_edges == 1) { if (child_node.remaining_edges == 1) {
this._addToCluster(parent_node,child_node,edge);
this._addToCluster(parent_node,child_node,edge,force_level_collapse);
delete this.edges[edges_id_array[i]]; delete this.edges[edges_id_array[i]];
} }
else if (parent_node.remaining_edges == 1) { else if (parent_node.remaining_edges == 1) {
this._addToCluster(child_node,parent_node,edge);
this._addToCluster(child_node,parent_node,edge,force_level_collapse);
delete this.edges[edges_id_array[i]]; delete this.edges[edges_id_array[i]];
} }
} }
} }
} }
this._updateNodeIndexList();
if (force_level_collapse == true)
this._applyClusterLevel();
}; };
/** /**
* This function adds the childnode to the parentnode, creating a cluster if it is not already. * This function adds the childnode to the parentnode, creating a cluster if it is not already.
* This function is called only from _updateClusters() * This function is called only from _updateClusters()
@ -305,7 +327,7 @@ Graph.prototype._formClusters = function() {
* @param edge | Edge object: this edge will be deleted from the global this.edges and stored in the parent node * @param edge | Edge object: this edge will be deleted from the global this.edges and stored in the parent node
* @private * @private
*/ */
Graph.prototype._addToCluster = function(parent_node, child_node, edge) {
Graph.prototype._addToCluster = function(parent_node, child_node, edge, force_level_collapse) {
// join child node and edge in parent node // join child node and edge in parent node
parent_node.contained_nodes[child_node.id] = child_node; parent_node.contained_nodes[child_node.id] = child_node;
parent_node.contained_edges[child_node.id] = edge; parent_node.contained_edges[child_node.id] = edge;
@ -315,14 +337,28 @@ Graph.prototype._addToCluster = function(parent_node, child_node, edge) {
} }
var grow_coefficient = this.constants.clustering.zoomOffset + (parent_node.cluster_size + child_node.cluster_size) / parent_node.cluster_size;
//var grow_coefficient = (parent_node.cluster_size + child_node.cluster_size) / parent_node.cluster_size;
parent_node.mass += this.constants.clustering.massTransferCoefficient * child_node.mass; parent_node.mass += this.constants.clustering.massTransferCoefficient * child_node.mass;
parent_node.width += grow_coefficient * this.constants.clustering.widthGrowth;
parent_node.height += grow_coefficient * this.constants.clustering.heightGrowth;
parent_node.remaining_edges -= 1;
parent_node.width += child_node.cluster_size * this.constants.clustering.widthGrowth;
parent_node.height += child_node.cluster_size * this.constants.clustering.heightGrowth;
parent_node.formation_scale = this.scale; parent_node.formation_scale = this.scale;
parent_node.cluster_size += child_node.cluster_size; parent_node.cluster_size += child_node.cluster_size;
parent_node.fontSize += 1 * child_node.cluster_size;
parent_node.fontSize += this.constants.clustering.fontSizeMultiplier * child_node.cluster_size;
if (force_level_collapse == true)
parent_node.remaining_edges_unapplied -= 1;
else
parent_node.remaining_edges -= 1;
// restart the simulation to reorganise all nodes
this.moving = true;
};
Graph.prototype._applyClusterLevel = function() {
for (var i = 0; i < this.node_indices.length; i++) {
var node = this.nodes[this.node_indices[i]];
node.remaining_edges = node.remaining_edges_unapplied;
}
}; };
@ -1832,6 +1868,8 @@ Graph.prototype._calculateForces = function() {
//edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2; //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
edgeLength = edge.length; edgeLength = edge.length;
// this implies that the edges between big clusters are longer
edgeLength += (edge.to.cluster_size + edge.from.cluster_size - 2) * this.constants.clustering.edgeGrowth;
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
angle = Math.atan2(dy, dx); angle = Math.atan2(dy, dx);
@ -1938,20 +1976,20 @@ Graph.prototype.start = function() {
graph.timer = undefined; graph.timer = undefined;
// benchmark the calculation // benchmark the calculation
var start = window.performance.now();
// var start = window.performance.now();
graph.start(); graph.start();
// Optionally call this twice for faster convergence // Optionally call this twice for faster convergence
// graph.start(); // graph.start();
var end = window.performance.now();
var time = end - start;
// console.log('Simulation time: ' + time);
// var end = window.performance.now();
// var time = end - start;
// console.log('Simulation time: ' + time);
start = window.performance.now();
// start = window.performance.now();
graph._redraw(); graph._redraw();
end = window.performance.now();
time = end - start;
// console.log('Drawing time: ' + time);
// end = window.performance.now();
// time = end - start;
// console.log('Drawing time: ' + time);
}, this.refreshRate); }, this.refreshRate);
} }

+ 5
- 4
src/graph/Node.js View File

@ -51,11 +51,12 @@ function Node(properties, imagelist, grouplist, constants) {
this.imagelist = imagelist; this.imagelist = imagelist;
this.grouplist = grouplist; this.grouplist = grouplist;
this.setProperties(properties, constants);
// creating the variables for clustering // creating the variables for clustering
this.resetCluster(); this.resetCluster();
this.remaining_edges = 0; this.remaining_edges = 0;
this.setProperties(properties, constants);
this.remaining_edges_unapplied = 0;
// mass, force, velocity // mass, force, velocity
this.mass = 50; // kg (mass is adjusted for the number of connected edges) this.mass = 50; // kg (mass is adjusted for the number of connected edges)
@ -87,7 +88,7 @@ Node.prototype.attachEdge = function(edge) {
this.edges.push(edge); this.edges.push(edge);
} }
this.remaining_edges = this.edges.length; this.remaining_edges = this.edges.length;
this.remaining_edges_tmp = this.edges.length;
this.remaining_edges_unapplied = this.edges.length;
this._updateMass(); this._updateMass();
}; };
@ -101,7 +102,7 @@ Node.prototype.detachEdge = function(edge) {
this.edges.splice(index, 1); this.edges.splice(index, 1);
} }
this.remaining_edges = this.edges.length; this.remaining_edges = this.edges.length;
this.remaining_edges_tmp = this.edges.length;
this.remaining_edges_unapplied = this.edges.length;
this._updateMass(); this._updateMass();
}; };

+ 76
- 37
vis.js View File

@ -4,8 +4,8 @@
* *
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 0.3.0-SNAPSHOT
* @date 2014-01-08
* @version @@version
* @date @@date
* *
* @license * @license
* Copyright (C) 2011-2013 Almende B.V, http://almende.com * Copyright (C) 2011-2013 Almende B.V, http://almende.com
@ -12498,11 +12498,12 @@ function Node(properties, imagelist, grouplist, constants) {
this.imagelist = imagelist; this.imagelist = imagelist;
this.grouplist = grouplist; this.grouplist = grouplist;
this.setProperties(properties, constants);
// creating the variables for clustering // creating the variables for clustering
this.resetCluster(); this.resetCluster();
this.remaining_edges = 0; this.remaining_edges = 0;
this.setProperties(properties, constants);
this.remaining_edges_unapplied = 0;
// mass, force, velocity // mass, force, velocity
this.mass = 50; // kg (mass is adjusted for the number of connected edges) this.mass = 50; // kg (mass is adjusted for the number of connected edges)
@ -12534,7 +12535,7 @@ Node.prototype.attachEdge = function(edge) {
this.edges.push(edge); this.edges.push(edge);
} }
this.remaining_edges = this.edges.length; this.remaining_edges = this.edges.length;
this.remaining_edges_tmp = this.edges.length;
this.remaining_edges_unapplied = this.edges.length;
this._updateMass(); this._updateMass();
}; };
@ -12548,7 +12549,7 @@ Node.prototype.detachEdge = function(edge) {
this.edges.splice(index, 1); this.edges.splice(index, 1);
} }
this.remaining_edges = this.edges.length; this.remaining_edges = this.edges.length;
this.remaining_edges_tmp = this.edges.length;
this.remaining_edges_unapplied = this.edges.length;
this._updateMass(); this._updateMass();
}; };
@ -14125,8 +14126,9 @@ function Graph (container, data, options) {
} }
}, },
clustering: { clustering: {
clusterLength: 30, // threshold length for clustering
zoomOffset: 0.1,
clusterLength: 50, // threshold length for clustering
fontSizeMultiplier: 2, // how much the fontsize grows
edgeGrowth: 10,
widthGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * widthGrowthFactor widthGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * widthGrowthFactor
heightGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * heightGrowthFactor heightGrowth: 15, // growth factor = ((parent_size + child_size) / parent_size) * heightGrowthFactor
massTransferCoefficient: 0.1 // parent.mass += massTransferCoefficient * child.mass massTransferCoefficient: 0.1 // parent.mass += massTransferCoefficient * child.mass
@ -14141,7 +14143,7 @@ function Graph (container, data, options) {
this.nodes = {}; // object with Node objects this.nodes = {}; // object with Node objects
this.edges = {}; // object with Edge objects this.edges = {}; // object with Edge objects
this.scale = 1; // defining the global scale variable in the constructor this.scale = 1; // defining the global scale variable in the constructor
this.previous_scale = 1; // use this to check if the zoom operation is in or out
this.previous_scale = this.scale; // this is used to check if the zoom operation is zooming in or out
// TODO: create a counter to keep track on the number of nodes having values // TODO: create a counter to keep track on the number of nodes having values
// TODO: create a counter to keep track on the number of nodes currently moving // TODO: create a counter to keep track on the number of nodes currently moving
// TODO: create a counter to keep track on the number of edges having values // TODO: create a counter to keep track on the number of edges having values
@ -14202,6 +14204,7 @@ function Graph (container, data, options) {
this.setData(data); this.setData(data);
}; };
/** /**
* This function checks if the zoom action is in or out. * This function checks if the zoom action is in or out.
* If out, check if we can form clusters, if in, check if we can open clusters. * If out, check if we can form clusters, if in, check if we can open clusters.
@ -14210,22 +14213,28 @@ function Graph (container, data, options) {
* @private * @private
*/ */
Graph.prototype._updateClusters = function() { Graph.prototype._updateClusters = function() {
var moving_before_clustering = this.moving;
if (this.previous_scale > this.scale) { // zoom out if (this.previous_scale > this.scale) { // zoom out
this._formClusters();
this._formClusters(false);
} }
else if (this.previous_scale < this.scale) { // zoom out else if (this.previous_scale < this.scale) { // zoom out
this._openClusters(); this._openClusters();
} }
this._updateNodeIndexList();
for (var nid in this.nodes) { for (var nid in this.nodes) {
var node = this.nodes[nid]; var node = this.nodes[nid];
node.label = String(node.remaining_edges);
node.label = String(node.remaining_edges).concat(":",String(node.cluster_size));
} }
this.previous_scale = this.scale; this.previous_scale = this.scale;
if (this.moving != moving_before_clustering) {
this.start();
}
}; };
/** /**
* This function loops over all nodes in the node_indices list. For each node it checks if it is a cluster and if it * This function loops over all nodes in the node_indices list. For each node it checks if it is a cluster and if it
* has to be opened based on the current zoom level. * has to be opened based on the current zoom level.
@ -14235,10 +14244,13 @@ Graph.prototype._updateClusters = function() {
Graph.prototype._openClusters = function() { Graph.prototype._openClusters = function() {
for (var i = 0; i < this.node_indices.length; i++) { for (var i = 0; i < this.node_indices.length; i++) {
var node = this.nodes[this.node_indices[i]]; var node = this.nodes[this.node_indices[i]];
this._expandClusterNode(node,true);
this._expandClusterNode(node,false);
} }
this._updateNodeIndexList();
}; };
/** /**
* This function checks if a node has to be opened. This is done by checking the zoom level. * This function checks if a node has to be opened. This is done by checking the zoom level.
* If the node contains child nodes, this function is recursively called on the child nodes as well. * If the node contains child nodes, this function is recursively called on the child nodes as well.
@ -14250,6 +14262,7 @@ Graph.prototype._openClusters = function() {
*/ */
Graph.prototype._expandClusterNode = function(node, recursive) { Graph.prototype._expandClusterNode = function(node, recursive) {
if (node.formation_scale != undefined) { if (node.formation_scale != undefined) {
console.log(this.scale,node.formation_scale)
if (this.scale > node.formation_scale) { if (this.scale > node.formation_scale) {
for (var contained_node_id in node.contained_nodes) { for (var contained_node_id in node.contained_nodes) {
if (node.contained_nodes.hasOwnProperty(contained_node_id)) { if (node.contained_nodes.hasOwnProperty(contained_node_id)) {
@ -14262,11 +14275,9 @@ Graph.prototype._expandClusterNode = function(node, recursive) {
node.mass -= this.constants.clustering.massTransferCoefficient * this.nodes[contained_node_id].mass; node.mass -= this.constants.clustering.massTransferCoefficient * this.nodes[contained_node_id].mass;
// decrease the size again // decrease the size again
node.cluster_size -= child_node.cluster_size;
var grow_coefficient = this.constants.clustering.zoomOffset + (node.cluster_size + child_node.cluster_size) / node.cluster_size;
node.width -= grow_coefficient * this.constants.clustering.widthGrowth;
node.height -= grow_coefficient * this.constants.clustering.heightGrowth;
node.fontSize -= 1 * child_node.cluster_size;
node.width -= child_node.cluster_size * this.constants.clustering.widthGrowth;
node.height -= child_node.cluster_size * this.constants.clustering.heightGrowth;
node.fontSize -= this.constants.clustering.fontSizeMultiplier * child_node.cluster_size;
// check if a further expansion step is possible if recursivity is enabled // check if a further expansion step is possible if recursivity is enabled
if (recursive == true) { if (recursive == true) {
@ -14285,17 +14296,23 @@ Graph.prototype._expandClusterNode = function(node, recursive) {
// reset the cluster settings of this node // reset the cluster settings of this node
node.resetCluster(); node.resetCluster();
// restart the simulation to reorganise all nodes
this.moving = true;
} }
} }
}; };
/** /**
* This function checks if any nodes at the end of their trees have edges below a threshold length * This function checks if any nodes at the end of their trees have edges below a threshold length
* This function is called only from _updateClusters() * This function is called only from _updateClusters()
* forceLevelCollapse ignores the length and
* *
* @private * @private
* @param force_level_collapse | Boolean
*/ */
Graph.prototype._formClusters = function() {
Graph.prototype._formClusters = function(force_level_collapse) {
var min_length = this.constants.clustering.clusterLength/this.scale; var min_length = this.constants.clustering.clusterLength/this.scale;
var dx,dy,length, var dx,dy,length,
@ -14319,8 +14336,8 @@ Graph.prototype._formClusters = function() {
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
if (length < min_length) {
// checking for clustering possiblities
if (length < min_length || force_level_collapse == true) {
// checking for clustering possibilities
// first check which node is larger // first check which node is larger
if (edge.to.mass > edge.from.mass) { if (edge.to.mass > edge.from.mass) {
@ -14337,18 +14354,24 @@ Graph.prototype._formClusters = function() {
// clusters. This will also have to be altered in the force calculation and rendering. // clusters. This will also have to be altered in the force calculation and rendering.
// This method is non-destructive and does not require a second set of data. // This method is non-destructive and does not require a second set of data.
if (child_node.remaining_edges == 1) { if (child_node.remaining_edges == 1) {
this._addToCluster(parent_node,child_node,edge);
this._addToCluster(parent_node,child_node,edge,force_level_collapse);
delete this.edges[edges_id_array[i]]; delete this.edges[edges_id_array[i]];
} }
else if (parent_node.remaining_edges == 1) { else if (parent_node.remaining_edges == 1) {
this._addToCluster(child_node,parent_node,edge);
this._addToCluster(child_node,parent_node,edge,force_level_collapse);
delete this.edges[edges_id_array[i]]; delete this.edges[edges_id_array[i]];
} }
} }
} }
} }
this._updateNodeIndexList();
if (force_level_collapse == true)
this._applyClusterLevel();
}; };
/** /**
* This function adds the childnode to the parentnode, creating a cluster if it is not already. * This function adds the childnode to the parentnode, creating a cluster if it is not already.
* This function is called only from _updateClusters() * This function is called only from _updateClusters()
@ -14358,7 +14381,7 @@ Graph.prototype._formClusters = function() {
* @param edge | Edge object: this edge will be deleted from the global this.edges and stored in the parent node * @param edge | Edge object: this edge will be deleted from the global this.edges and stored in the parent node
* @private * @private
*/ */
Graph.prototype._addToCluster = function(parent_node, child_node, edge) {
Graph.prototype._addToCluster = function(parent_node, child_node, edge, force_level_collapse) {
// join child node and edge in parent node // join child node and edge in parent node
parent_node.contained_nodes[child_node.id] = child_node; parent_node.contained_nodes[child_node.id] = child_node;
parent_node.contained_edges[child_node.id] = edge; parent_node.contained_edges[child_node.id] = edge;
@ -14368,14 +14391,28 @@ Graph.prototype._addToCluster = function(parent_node, child_node, edge) {
} }
var grow_coefficient = this.constants.clustering.zoomOffset + (parent_node.cluster_size + child_node.cluster_size) / parent_node.cluster_size;
//var grow_coefficient = (parent_node.cluster_size + child_node.cluster_size) / parent_node.cluster_size;
parent_node.mass += this.constants.clustering.massTransferCoefficient * child_node.mass; parent_node.mass += this.constants.clustering.massTransferCoefficient * child_node.mass;
parent_node.width += grow_coefficient * this.constants.clustering.widthGrowth;
parent_node.height += grow_coefficient * this.constants.clustering.heightGrowth;
parent_node.remaining_edges -= 1;
parent_node.width += child_node.cluster_size * this.constants.clustering.widthGrowth;
parent_node.height += child_node.cluster_size * this.constants.clustering.heightGrowth;
parent_node.formation_scale = this.scale; parent_node.formation_scale = this.scale;
parent_node.cluster_size += child_node.cluster_size; parent_node.cluster_size += child_node.cluster_size;
parent_node.fontSize += 1 * child_node.cluster_size;
parent_node.fontSize += this.constants.clustering.fontSizeMultiplier * child_node.cluster_size;
if (force_level_collapse == true)
parent_node.remaining_edges_unapplied -= 1;
else
parent_node.remaining_edges -= 1;
// restart the simulation to reorganise all nodes
this.moving = true;
};
Graph.prototype._applyClusterLevel = function() {
for (var i = 0; i < this.node_indices.length; i++) {
var node = this.nodes[this.node_indices[i]];
node.remaining_edges = node.remaining_edges_unapplied;
}
}; };
@ -15885,6 +15922,8 @@ Graph.prototype._calculateForces = function() {
//edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2; //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
edgeLength = edge.length; edgeLength = edge.length;
// this implies that the edges between big clusters are longer
edgeLength += (edge.to.cluster_size + edge.from.cluster_size - 2) * this.constants.clustering.edgeGrowth;
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
angle = Math.atan2(dy, dx); angle = Math.atan2(dy, dx);
@ -15991,20 +16030,20 @@ Graph.prototype.start = function() {
graph.timer = undefined; graph.timer = undefined;
// benchmark the calculation // benchmark the calculation
var start = window.performance.now();
// var start = window.performance.now();
graph.start(); graph.start();
// Optionally call this twice for faster convergence // Optionally call this twice for faster convergence
// graph.start(); // graph.start();
var end = window.performance.now();
var time = end - start;
// console.log('Simulation time: ' + time);
// var end = window.performance.now();
// var time = end - start;
// console.log('Simulation time: ' + time);
start = window.performance.now();
// start = window.performance.now();
graph._redraw(); graph._redraw();
end = window.performance.now();
time = end - start;
// console.log('Drawing time: ' + time);
// end = window.performance.now();
// time = end - start;
// console.log('Drawing time: ' + time);
}, this.refreshRate); }, this.refreshRate);
} }

Loading…
Cancel
Save