|  | @ -5835,6 +5835,7 @@ function DataSet (options) { | 
														
													
														
															
																|  |  | this.data = {};                                 // map with data indexed by id |  |  | this.data = {};                                 // map with data indexed by id | 
														
													
														
															
																|  |  | this.fieldId = this.options.fieldId || 'id';    // name of the field containing id |  |  | this.fieldId = this.options.fieldId || 'id';    // name of the field containing id | 
														
													
														
															
																|  |  | this.convert = {};                              // field types by field name |  |  | this.convert = {};                              // field types by field name | 
														
													
														
															
																|  |  |  |  |  | this.showInternalIds = this.options.showInternalIds || false; // show internal ids with the get function | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | if (this.options.convert) { |  |  | if (this.options.convert) { | 
														
													
														
															
																|  |  | for (var field in this.options.convert) { |  |  | for (var field in this.options.convert) { | 
														
													
												
													
														
															
																|  | @ -6068,6 +6069,7 @@ DataSet.prototype.update = function (data, senderId) { | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | DataSet.prototype.get = function (args) { |  |  | DataSet.prototype.get = function (args) { | 
														
													
														
															
																|  |  | var me = this; |  |  | var me = this; | 
														
													
														
															
																|  |  |  |  |  | var globalShowInternalIds = this.showInternalIds; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // parse the arguments |  |  | // parse the arguments | 
														
													
														
															
																|  |  | var id, ids, options, data; |  |  | var id, ids, options, data; | 
														
													
												
													
														
															
																|  | @ -6111,6 +6113,13 @@ DataSet.prototype.get = function (args) { | 
														
													
														
															
																|  |  | type = 'Array'; |  |  | type = 'Array'; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // we allow the setting of this value for a single get request. | 
														
													
														
															
																|  |  |  |  |  | if (options != undefined) { | 
														
													
														
															
																|  |  |  |  |  | if (options.showInternalIds != undefined) { | 
														
													
														
															
																|  |  |  |  |  | this.showInternalIds = options.showInternalIds; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // build options |  |  | // build options | 
														
													
														
															
																|  |  | var convert = options && options.convert || this.options.convert; |  |  | var convert = options && options.convert || this.options.convert; | 
														
													
														
															
																|  |  | var filter = options && options.filter; |  |  | var filter = options && options.filter; | 
														
													
												
													
														
															
																|  | @ -6145,6 +6154,9 @@ DataSet.prototype.get = function (args) { | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // restore the global value of showInternalIds | 
														
													
														
															
																|  |  |  |  |  | this.showInternalIds = globalShowInternalIds; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // order the results |  |  | // order the results | 
														
													
														
															
																|  |  | if (options && options.order && id == undefined) { |  |  | if (options && options.order && id == undefined) { | 
														
													
														
															
																|  |  | this._sort(items, options.order); |  |  | this._sort(items, options.order); | 
														
													
												
													
														
															
																|  | @ -6624,7 +6636,7 @@ DataSet.prototype._getItem = function (id, convert) { | 
														
													
														
															
																|  |  | if (raw.hasOwnProperty(field)) { |  |  | if (raw.hasOwnProperty(field)) { | 
														
													
														
															
																|  |  | value = raw[field]; |  |  | value = raw[field]; | 
														
													
														
															
																|  |  | // output all fields, except internal ids |  |  | // output all fields, except internal ids | 
														
													
														
															
																|  |  | if ((field != fieldId) || !(value in internalIds)) { |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) { | 
														
													
														
															
																|  |  | converted[field] = util.convert(value, convert[field]); |  |  | converted[field] = util.convert(value, convert[field]); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
												
													
														
															
																|  | @ -6636,13 +6648,12 @@ DataSet.prototype._getItem = function (id, convert) { | 
														
													
														
															
																|  |  | if (raw.hasOwnProperty(field)) { |  |  | if (raw.hasOwnProperty(field)) { | 
														
													
														
															
																|  |  | value = raw[field]; |  |  | value = raw[field]; | 
														
													
														
															
																|  |  | // output all fields, except internal ids |  |  | // output all fields, except internal ids | 
														
													
														
															
																|  |  | if ((field != fieldId) || !(value in internalIds)) { |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) { | 
														
													
														
															
																|  |  | converted[field] = value; |  |  | converted[field] = value; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | return converted; |  |  | return converted; | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -6676,6 +6687,17 @@ DataSet.prototype._updateItem = function (item) { | 
														
													
														
															
																|  |  | return id; |  |  | return id; | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * check if an id is an internal or external id | 
														
													
														
															
																|  |  |  |  |  | * @param id | 
														
													
														
															
																|  |  |  |  |  | * @returns {boolean} | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | DataSet.prototype.isInternalId = function(id) { | 
														
													
														
															
																|  |  |  |  |  | return (id in this.internalIds); | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * Get an array with the column names of a Google DataTable |  |  | * Get an array with the column names of a Google DataTable | 
														
													
														
															
																|  |  | * @param {DataTable} dataTable |  |  | * @param {DataTable} dataTable | 
														
													
												
													
														
															
																|  | @ -13289,6 +13311,8 @@ function Node(properties, imagelist, grouplist, constants) { | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.edges = []; // all edges connected to this node |  |  | this.edges = []; // all edges connected to this node | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdges = []; | 
														
													
														
															
																|  |  |  |  |  | this.reroutedEdges = {}; | 
														
													
														
															
																|  |  | this.group = constants.nodes.group; |  |  | this.group = constants.nodes.group; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.fontSize = constants.nodes.fontSize; |  |  | this.fontSize = constants.nodes.fontSize; | 
														
													
												
													
														
															
																|  | @ -13317,8 +13341,7 @@ function Node(properties, imagelist, grouplist, constants) { | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // creating the variables for clustering |  |  | // creating the variables for clustering | 
														
													
														
															
																|  |  | this.resetCluster(); |  |  | this.resetCluster(); | 
														
													
														
															
																|  |  | this.remainingEdges = 0; |  |  |  | 
														
													
														
															
																|  |  | this.remainingEdges_unapplied = 0; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdgesLength = 0; | 
														
													
														
															
																|  |  | this.clusterSession = 0; |  |  | this.clusterSession = 0; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.clusterSizeWidthFactor  = constants.clustering.clusterSizeWidthFactor; |  |  | this.clusterSizeWidthFactor  = constants.clustering.clusterSizeWidthFactor; | 
														
													
												
													
														
															
																|  | @ -13354,8 +13377,8 @@ Node.prototype.attachEdge = function(edge) { | 
														
													
														
															
																|  |  | if (this.edges.indexOf(edge) == -1) { |  |  | if (this.edges.indexOf(edge) == -1) { | 
														
													
														
															
																|  |  | this.edges.push(edge); |  |  | this.edges.push(edge); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | this.remainingEdges = this.edges.length; |  |  |  | 
														
													
														
															
																|  |  | this.remainingEdges_unapplied = this.remainingEdges; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdges = this.edges; | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdgesLength = this.edges.length; | 
														
													
														
															
																|  |  | this._updateMass(); |  |  | this._updateMass(); | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -13368,8 +13391,8 @@ Node.prototype.detachEdge = function(edge) { | 
														
													
														
															
																|  |  | if (index != -1) { |  |  | if (index != -1) { | 
														
													
														
															
																|  |  | this.edges.splice(index, 1); |  |  | this.edges.splice(index, 1); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | this.remainingEdges = this.edges.length; |  |  |  | 
														
													
														
															
																|  |  | this.remainingEdges_unapplied = this.remainingEdges; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdges = this.edges; | 
														
													
														
															
																|  |  |  |  |  | this.dynamicEdgesLength = this.edges.length; | 
														
													
														
															
																|  |  | this._updateMass(); |  |  | this._updateMass(); | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -14114,6 +14137,12 @@ function Edge (properties, graph, constants) { | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.from = null;   // a node |  |  | this.from = null;   // a node | 
														
													
														
															
																|  |  | this.to = null;     // a node |  |  | this.to = null;     // a node | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster | 
														
													
														
															
																|  |  |  |  |  | // by storing the original information we can revert to the original connection when the cluser is opened. | 
														
													
														
															
																|  |  |  |  |  | this.originalFromID = []; | 
														
													
														
															
																|  |  |  |  |  | this.originalToID = []; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.connected = false; |  |  | this.connected = false; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // Added to support dashed lines |  |  | // Added to support dashed lines | 
														
													
												
													
														
															
																|  | @ -14918,8 +14947,6 @@ function Cluster() { | 
														
													
														
															
																|  |  | this.clusterSession = 0; |  |  | this.clusterSession = 0; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will |  |  | * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will | 
														
													
														
															
																|  |  | * be clustered with their connected node. This can be repeated as many times as needed. |  |  | * be clustered with their connected node. This can be repeated as many times as needed. | 
														
													
												
													
														
															
																|  | @ -14932,7 +14959,7 @@ Cluster.prototype.increaseClusterLevel = function() { | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded |  |  | // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded | 
														
													
														
															
																|  |  | if (this.moving != isMovingBeforeClustering) { |  |  | if (this.moving != isMovingBeforeClustering) { | 
														
													
														
															
																|  |  | //this.start(); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.start(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -14958,25 +14985,8 @@ Cluster.prototype.decreaseClusterLevel = function() { | 
														
													
														
															
																|  |  | if (this.moving != isMovingBeforeClustering) { |  |  | if (this.moving != isMovingBeforeClustering) { | 
														
													
														
															
																|  |  | this.start(); |  |  | this.start(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  |  | 
														
													
														
															
																|  |  | * This function can be called to open up a specific cluster. |  |  |  | 
														
													
														
															
																|  |  | * It will recursively unpack the entire cluster back to individual nodes. |  |  |  | 
														
													
														
															
																|  |  | * |  |  |  | 
														
													
														
															
																|  |  | * @param node    | Node object: cluster to open. |  |  |  | 
														
													
														
															
																|  |  | */ |  |  |  | 
														
													
														
															
																|  |  | Cluster.prototype.fullyOpenCluster = function(node) { |  |  |  | 
														
													
														
															
																|  |  | var isMovingBeforeClustering = this.moving; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this._expandClusterNode(node,true,true); |  |  |  | 
														
													
														
															
																|  |  | this._updateNodeIndexList(); |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded |  |  |  | 
														
													
														
															
																|  |  | if (this.moving != isMovingBeforeClustering) { |  |  |  | 
														
													
														
															
																|  |  | this.start(); |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this._updateLabels(); | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -15033,14 +15043,17 @@ Cluster.prototype._updateClusters = function() { | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | Cluster.prototype._updateLabels = function() { |  |  | Cluster.prototype._updateLabels = function() { | 
														
													
														
															
																|  |  | // update node labels |  |  | // update node labels | 
														
													
														
															
																|  |  |  |  |  | //this._updateClusterLabels(); | 
														
													
														
															
																|  |  |  |  |  | // this._updateNodeLabels(); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // Debug : | 
														
													
														
															
																|  |  | for (var nodeID in this.nodes) { |  |  | for (var nodeID in this.nodes) { | 
														
													
														
															
																|  |  | if (this.nodes.hasOwnProperty(nodeID)) { |  |  | if (this.nodes.hasOwnProperty(nodeID)) { | 
														
													
														
															
																|  |  | var node = this.nodes[nodeID]; |  |  | var node = this.nodes[nodeID]; | 
														
													
														
															
																|  |  | node.label = String(node.remainingEdges).concat(":",node.remainingEdges_unapplied,":",String(node.clusterSize)); |  |  |  | 
														
													
														
															
																|  |  | //      node.label = String(Math.round(this.zoomCenter.x)).concat(",",String(Math.round(this.zoomCenter.y)), |  |  |  | 
														
													
														
															
																|  |  | //                        "::",String(Math.round(node.x)),"x",String(Math.round(node.y))); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | node.label = String(node.dynamicEdges.length).concat(":",node.dynamicEdgesLength,":",String(node.clusterSize),":::",String(node.id)); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
												
													
														
															
																|  | @ -15136,10 +15149,8 @@ Cluster.prototype._expandClusterNode = function(parentNode, recursive, forceExpa | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | Cluster.prototype._parentNodeInActiveArea = function(node) { |  |  | Cluster.prototype._parentNodeInActiveArea = function(node) { | 
														
													
														
															
																|  |  | if (node.selected) |  |  |  | 
														
													
														
															
																|  |  | console.log(node.x,this.zoomCenter.x,node.y, this.zoomCenter.y) |  |  |  | 
														
													
														
															
																|  |  | if (Math.abs(node.x - this.zoomCenter.x) <= this.constants.clustering.activeAreaRadius && |  |  |  | 
														
													
														
															
																|  |  | Math.abs(node.y - this.zoomCenter.y) <= this.constants.clustering.activeAreaRadius) { |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (Math.abs(node.x - this.zoomCenter.x) <= this.constants.clustering.activeAreaRadius/this.scale && | 
														
													
														
															
																|  |  |  |  |  | Math.abs(node.y - this.zoomCenter.y) <= this.constants.clustering.activeAreaRadius/this.scale) { | 
														
													
														
															
																|  |  | return true; |  |  | return true; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | else { |  |  | else { | 
														
													
												
													
														
															
																|  | @ -15164,16 +15175,21 @@ Cluster.prototype._expelChildFromParent = function(parentNode, containedNodeID, | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // if child node has been added on smaller scale than current, kick out |  |  | // if child node has been added on smaller scale than current, kick out | 
														
													
														
															
																|  |  | if (childNode.formationScale < this.scale || forceExpand == true) { |  |  | if (childNode.formationScale < this.scale || forceExpand == true) { | 
														
													
														
															
																|  |  | // put the child node back in the global nodes object and the corresponding edge in the global edges object |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // put the child node back in the global nodes object | 
														
													
														
															
																|  |  | this.nodes[containedNodeID] = childNode; |  |  | this.nodes[containedNodeID] = childNode; | 
														
													
														
															
																|  |  | this.edges[parentNode.containedEdges[containedNodeID].id] = parentNode.containedEdges[containedNodeID]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // release the contained edges from this childNode back into the global edges | 
														
													
														
															
																|  |  |  |  |  | this._releaseContainedEdges(parentNode,childNode) | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // reconnect rerouted edges to the childNode | 
														
													
														
															
																|  |  |  |  |  | this._connectEdgeBackToChild(parentNode,childNode); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // undo the changes from the clustering operation on the parent node |  |  | // undo the changes from the clustering operation on the parent node | 
														
													
														
															
																|  |  | parentNode.mass -= this.constants.clustering.massTransferCoefficient * childNode.mass; |  |  | parentNode.mass -= this.constants.clustering.massTransferCoefficient * childNode.mass; | 
														
													
														
															
																|  |  | parentNode.fontSize -= this.constants.clustering.fontSizeMultiplier * childNode.clusterSize; |  |  | parentNode.fontSize -= this.constants.clustering.fontSizeMultiplier * childNode.clusterSize; | 
														
													
														
															
																|  |  | parentNode.clusterSize -= childNode.clusterSize; |  |  | parentNode.clusterSize -= childNode.clusterSize; | 
														
													
														
															
																|  |  | parentNode.remainingEdges += 1; |  |  |  | 
														
													
														
															
																|  |  | parentNode.remainingEdges_unapplied = parentNode.remainingEdges; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // place the child node near the parent, not at the exact same location to avoid chaos in the system |  |  | // place the child node near the parent, not at the exact same location to avoid chaos in the system | 
														
													
														
															
																|  |  | childNode.x = parentNode.x + this.constants.edges.length * 0.2 * (0.5 - Math.random()) * parentNode.clusterSize; |  |  | childNode.x = parentNode.x + this.constants.edges.length * 0.2 * (0.5 - Math.random()) * parentNode.clusterSize; | 
														
													
												
													
														
															
																|  | @ -15184,7 +15200,6 @@ Cluster.prototype._expelChildFromParent = function(parentNode, containedNodeID, | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // remove node from the list |  |  | // remove node from the list | 
														
													
														
															
																|  |  | delete parentNode.containedNodes[containedNodeID]; |  |  | delete parentNode.containedNodes[containedNodeID]; | 
														
													
														
															
																|  |  | delete parentNode.containedEdges[containedNodeID]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // restart the simulation to reorganise all nodes |  |  | // restart the simulation to reorganise all nodes | 
														
													
														
															
																|  |  | this.moving = true; |  |  | this.moving = true; | 
														
													
												
													
														
															
																|  | @ -15200,7 +15215,6 @@ Cluster.prototype._expelChildFromParent = function(parentNode, containedNodeID, | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * 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() | 
														
													
												
													
														
															
																|  | @ -15211,34 +15225,48 @@ Cluster.prototype._expelChildFromParent = function(parentNode, containedNodeID, | 
														
													
														
															
																|  |  | * @param force_level_collapse    | Boolean |  |  | * @param force_level_collapse    | Boolean | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | Cluster.prototype._formClusters = function(forceLevelCollapse) { |  |  | Cluster.prototype._formClusters = function(forceLevelCollapse) { | 
														
													
														
															
																|  |  | var minLength = this.constants.clustering.clusterLength/this.scale; |  |  |  | 
														
													
														
															
																|  |  | var amountOfNodes = this.nodeIndices.length; |  |  | var amountOfNodes = this.nodeIndices.length; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | var dx,dy,length, |  |  |  | 
														
													
														
															
																|  |  | edges = this.edges; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (forceLevelCollapse == false) { | 
														
													
														
															
																|  |  |  |  |  | this._formClustersByZoom(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else { | 
														
													
														
															
																|  |  |  |  |  | this._forceClustersByZoom(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (this.nodeIndices.length != amountOfNodes) { // this means a clustering operation has taken place | 
														
													
														
															
																|  |  |  |  |  | this.clusterSession += 1; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function handles the clustering by zooming out, this is based on minimum edge distance | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._formClustersByZoom = function() { | 
														
													
														
															
																|  |  |  |  |  | var dx,dy,length, | 
														
													
														
															
																|  |  |  |  |  | minLength = this.constants.clustering.clusterLength/this.scale; | 
														
													
														
															
																|  |  | // create an array of edge ids |  |  | // create an array of edge ids | 
														
													
														
															
																|  |  | var edgesIDarray = [] |  |  | var edgesIDarray = [] | 
														
													
														
															
																|  |  | for (var id in edges) { |  |  |  | 
														
													
														
															
																|  |  | if (edges.hasOwnProperty(id)) { |  |  |  | 
														
													
														
															
																|  |  |  |  |  | for (var id in this.edges) { | 
														
													
														
															
																|  |  |  |  |  | if (this.edges.hasOwnProperty(id)) { | 
														
													
														
															
																|  |  | edgesIDarray.push(id); |  |  | edgesIDarray.push(id); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // check if any edges are shorter than minLength and start the clustering |  |  | // check if any edges are shorter than minLength and start the clustering | 
														
													
														
															
																|  |  | // the clustering favours the node with the larger mass |  |  | // the clustering favours the node with the larger mass | 
														
													
														
															
																|  |  | for (var i = 0; i < edgesIDarray.length; i++) { |  |  | for (var i = 0; i < edgesIDarray.length; i++) { | 
														
													
														
															
																|  |  | var edgeID = edgesIDarray[i]; |  |  | var edgeID = edgesIDarray[i]; | 
														
													
														
															
																|  |  | var edge = edges[edgeID]; |  |  |  | 
														
													
														
															
																|  |  | edge.id = edgeID; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | var edge = this.edges[edgeID]; | 
														
													
														
															
																|  |  | if (edge.connected) { |  |  | if (edge.connected) { | 
														
													
														
															
																|  |  | dx = (edge.to.x - edge.from.x); |  |  | dx = (edge.to.x - edge.from.x); | 
														
													
														
															
																|  |  | dy = (edge.to.y - edge.from.y); |  |  | dy = (edge.to.y - edge.from.y); | 
														
													
														
															
																|  |  | length = Math.sqrt(dx * dx + dy * dy); |  |  | length = Math.sqrt(dx * dx + dy * dy); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | if (length < minLength || forceLevelCollapse == true) { |  |  |  | 
														
													
														
															
																|  |  | // checking for clustering possibilities |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (length < minLength) { | 
														
													
														
															
																|  |  | // first check which node is larger |  |  | // first check which node is larger | 
														
													
														
															
																|  |  | var parentNode = edge.from |  |  | var parentNode = edge.from | 
														
													
														
															
																|  |  | var childNode = edge.to |  |  | var childNode = edge.to | 
														
													
												
													
														
															
																|  | @ -15247,51 +15275,124 @@ Cluster.prototype._formClusters = function(forceLevelCollapse) { | 
														
													
														
															
																|  |  | childNode = edge.from |  |  | childNode = edge.from | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // we allow clustering from outside in, ideally the child node in on the outside |  |  |  | 
														
													
														
															
																|  |  | // if we do not cluster from outside in, we would have to reconnect edges or keep a second set of edges for the |  |  |  | 
														
													
														
															
																|  |  | // clusters. This will also have to be altered in the force calculation and rendering. |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // we allow clustering from outside in | 
														
													
														
															
																|  |  |  |  |  | // if we do not cluster from outside in, we would have to reconnect edges or keep | 
														
													
														
															
																|  |  |  |  |  | // a second set of edges for the 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 (childNode.remainingEdges == 1 && childNode.remainingEdges_unapplied != 0) { |  |  |  | 
														
													
														
															
																|  |  | this._addToCluster(parentNode,childNode,edge,forceLevelCollapse); |  |  |  | 
														
													
														
															
																|  |  | delete this.edges[edgesIDarray[i]]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (childNode.dynamicEdgesLength == 1) { | 
														
													
														
															
																|  |  |  |  |  | this._addToCluster(parentNode,childNode,edge,false); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | else if (parentNode.remainingEdges == 1 && parentNode.remainingEdges_unapplied != 0) { |  |  |  | 
														
													
														
															
																|  |  | this._addToCluster(childNode,parentNode,edge,forceLevelCollapse); |  |  |  | 
														
													
														
															
																|  |  | delete this.edges[edgesIDarray[i]]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | else if (parentNode.dynamicEdgesLength == 1) { | 
														
													
														
															
																|  |  |  |  |  | this._addToCluster(childNode,parentNode,edge,false); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | this._updateNodeIndexList(); |  |  | this._updateNodeIndexList(); | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | if (forceLevelCollapse == true) { |  |  |  | 
														
													
														
															
																|  |  | this._applyClusterLevel(); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function forces the graph to cluster all nodes with only one connecting edge to their | 
														
													
														
															
																|  |  |  |  |  | * connected node. | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._forceClustersByZoom = function() { | 
														
													
														
															
																|  |  |  |  |  | for (var nodeID = 0; nodeID < this.nodeIndices.length; nodeID++) { | 
														
													
														
															
																|  |  |  |  |  | var childNode = this.nodes[this.nodeIndices[nodeID]]; | 
														
													
														
															
																|  |  |  |  |  | if (childNode.dynamicEdgesLength == 1) { | 
														
													
														
															
																|  |  |  |  |  | var edge = childNode.dynamicEdges[0]; | 
														
													
														
															
																|  |  |  |  |  | var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; | 
														
													
														
															
																|  |  |  |  |  | this._addToCluster(parentNode,childNode,true); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  | this._updateNodeIndexList(); | 
														
													
														
															
																|  |  |  |  |  | this._updateDynamicEdges(); | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | if (this.nodeIndices.length != amountOfNodes) { // this means a clustering operation has taken place |  |  |  | 
														
													
														
															
																|  |  | this.clusterSession += 1; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype.aggregateHubs = function() { | 
														
													
														
															
																|  |  |  |  |  | var isMovingBeforeClustering = this.moving; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | var hubThreshold = 4; | 
														
													
														
															
																|  |  |  |  |  | this._forceClustersByHub(hubThreshold); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded | 
														
													
														
															
																|  |  |  |  |  | if (this.moving != isMovingBeforeClustering) { | 
														
													
														
															
																|  |  |  |  |  | this.start(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param hubThreshold | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._forceClustersByHub = function(hubThreshold) { | 
														
													
														
															
																|  |  |  |  |  | for (var nodeID = 0; nodeID < this.nodeIndices.length; nodeID++) { | 
														
													
														
															
																|  |  |  |  |  | if (this.nodes.hasOwnProperty(this.nodeIndices[nodeID])) { | 
														
													
														
															
																|  |  |  |  |  | var hubNode = this.nodes[this.nodeIndices[nodeID]]; | 
														
													
														
															
																|  |  |  |  |  | if (hubNode.dynamicEdges.length >= hubThreshold) { | 
														
													
														
															
																|  |  |  |  |  | var edgesIDarray = [] | 
														
													
														
															
																|  |  |  |  |  | var amountOfInitialEdges = hubNode.dynamicEdges.length; | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < amountOfInitialEdges; i++) { | 
														
													
														
															
																|  |  |  |  |  | edgesIDarray.push(hubNode.dynamicEdges[i].id); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < amountOfInitialEdges; i++) { | 
														
													
														
															
																|  |  |  |  |  | var edge = this.edges[edgesIDarray[i]]; | 
														
													
														
															
																|  |  |  |  |  | var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; | 
														
													
														
															
																|  |  |  |  |  | this._addToCluster(hubNode,childNode,true); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | break; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | this._updateNodeIndexList(); | 
														
													
														
															
																|  |  |  |  |  | this._updateDynamicEdges(); | 
														
													
														
															
																|  |  |  |  |  | this._updateLabels(); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (this.moving != isMovingBeforeClustering) { | 
														
													
														
															
																|  |  |  |  |  | this.start(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * This function adds the child node to the parent node, creating a cluster if it is not already. |  |  | * This function adds the child node to the parent node, creating a cluster if it is not already. | 
														
													
														
															
																|  |  | * This function is called only from _updateClusters() |  |  | * This function is called only from _updateClusters() | 
														
													
														
															
																|  |  | * |  |  | * | 
														
													
														
															
																|  |  | * @param parent_node           | Node object: this is the node that will house the child node |  |  | * @param parent_node           | Node object: this is the node that will house the child node | 
														
													
														
															
																|  |  | * @param child_node            | Node object: this node will be deleted from the global this.nodes and stored in the parent node |  |  | * @param child_node            | Node object: this node will be deleted from the global this.nodes 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 |  |  |  | 
														
													
														
															
																|  |  | * @param force_level_collapse  | Boolean: true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse |  |  | * @param force_level_collapse  | Boolean: true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse | 
														
													
														
															
																|  |  | * @private |  |  | * @private | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | Cluster.prototype._addToCluster = function(parentNode, childNode, edge, forceLevelCollapse) { |  |  |  | 
														
													
														
															
																|  |  | // join child node and edge in parent node |  |  |  | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._addToCluster = function(parentNode, childNode, forceLevelCollapse) { | 
														
													
														
															
																|  |  |  |  |  | // join child node in the parent node | 
														
													
														
															
																|  |  | parentNode.containedNodes[childNode.id] = childNode; |  |  | parentNode.containedNodes[childNode.id] = childNode; | 
														
													
														
															
																|  |  | parentNode.containedEdges[childNode.id] = edge;  // the edge gets the node ID so we can easily recover it when expanding the cluster |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | if (this.nodes.hasOwnProperty(childNode.id)) { |  |  |  | 
														
													
														
															
																|  |  | delete this.nodes[childNode.id]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (forceLevelCollapse == false) { | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdgesLength += childNode.dynamicEdges.length - 2; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < childNode.dynamicEdges.length; i++) { | 
														
													
														
															
																|  |  |  |  |  | var edge = childNode.dynamicEdges[i]; | 
														
													
														
															
																|  |  |  |  |  | if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode | 
														
													
														
															
																|  |  |  |  |  | this._addToContainedEdges(parentNode,childNode,edge); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else { | 
														
													
														
															
																|  |  |  |  |  | console.log('connecting edge to cluster'); | 
														
													
														
															
																|  |  |  |  |  | this._connectEdgeToCluster(parentNode,childNode,edge); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  | childNode.dynamicEdges = []; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // remove the childNode from the global nodes object | 
														
													
														
															
																|  |  |  |  |  | delete this.nodes[childNode.id]; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | childNode.clusterSession = this.clusterSession; |  |  | childNode.clusterSession = this.clusterSession; | 
														
													
														
															
																|  |  | parentNode.mass += this.constants.clustering.massTransferCoefficient * childNode.mass; |  |  | parentNode.mass += this.constants.clustering.massTransferCoefficient * childNode.mass; | 
														
													
												
													
														
															
																|  | @ -15304,18 +15405,13 @@ Cluster.prototype._addToCluster = function(parentNode, childNode, edge, forceLev | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | else { |  |  | else { | 
														
													
														
															
																|  |  | parentNode.formationScale = this.scale; // The latest child has been added on this scale |  |  | parentNode.formationScale = this.scale; // The latest child has been added on this scale | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // recalculate the size of the node on the next time the node is rendered |  |  | // recalculate the size of the node on the next time the node is rendered | 
														
													
														
															
																|  |  | parentNode.clearSizeCache(); |  |  | parentNode.clearSizeCache(); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | parentNode.containedNodes[childNode.id].formationScale = this.scale; // this child has been added at this scale. |  |  | parentNode.containedNodes[childNode.id].formationScale = this.scale; // this child has been added at this scale. | 
														
													
														
															
																|  |  | if (forceLevelCollapse == true) { |  |  |  | 
														
													
														
															
																|  |  | parentNode.remainingEdges_unapplied -= 1; |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | else { |  |  |  | 
														
													
														
															
																|  |  | parentNode.remainingEdges -= 1; |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // restart the simulation to reorganise all nodes |  |  | // restart the simulation to reorganise all nodes | 
														
													
														
															
																|  |  | this.moving = true; |  |  | this.moving = true; | 
														
													
												
													
														
															
																|  | @ -15328,10 +15424,10 @@ Cluster.prototype._addToCluster = function(parentNode, childNode, edge, forceLev | 
														
													
														
															
																|  |  | * It has to be called if a level is collapsed. It is called by _formClusters(). |  |  | * It has to be called if a level is collapsed. It is called by _formClusters(). | 
														
													
														
															
																|  |  | * @private |  |  | * @private | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | Cluster.prototype._applyClusterLevel = function() { |  |  |  | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._updateDynamicEdges = function() { | 
														
													
														
															
																|  |  | for (var i = 0; i < this.nodeIndices.length; i++) { |  |  | for (var i = 0; i < this.nodeIndices.length; i++) { | 
														
													
														
															
																|  |  | var node = this.nodes[this.nodeIndices[i]]; |  |  | var node = this.nodes[this.nodeIndices[i]]; | 
														
													
														
															
																|  |  | node.remainingEdges = node.remainingEdges_unapplied; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | node.dynamicEdgesLength = node.dynamicEdges.length; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -15342,16 +15438,150 @@ Cluster.prototype._repositionNodes = function() { | 
														
													
														
															
																|  |  | if (!node.isFixed()) { |  |  | if (!node.isFixed()) { | 
														
													
														
															
																|  |  | // TODO: position new nodes in a smarter way! |  |  | // TODO: position new nodes in a smarter way! | 
														
													
														
															
																|  |  | var radius = this.constants.edges.length * (1 + 0.5*node.clusterSize); |  |  | var radius = this.constants.edges.length * (1 + 0.5*node.clusterSize); | 
														
													
														
															
																|  |  | var count = this.nodeIndices.length; |  |  |  | 
														
													
														
															
																|  |  | var angle = 2 * Math.PI * Math.random(); |  |  | var angle = 2 * Math.PI * Math.random(); | 
														
													
														
															
																|  |  | node.x = radius * Math.cos(angle); |  |  | node.x = radius * Math.cos(angle); | 
														
													
														
															
																|  |  | node.y = radius * Math.sin(angle); |  |  | node.y = radius * Math.sin(angle); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This adds an edge from the childNode to the contained edges of the parent node | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param parentNode    | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param childNode     | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param edge          | Edge object | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._addToContainedEdges = function(parentNode, childNode, edge) { | 
														
													
														
															
																|  |  |  |  |  | // create an array object if it does not yet exist for this childNode | 
														
													
														
															
																|  |  |  |  |  | if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { | 
														
													
														
															
																|  |  |  |  |  | parentNode.containedEdges[childNode.id] = [] | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | // add this edge to the list | 
														
													
														
															
																|  |  |  |  |  | parentNode.containedEdges[childNode.id].push(edge); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // remove the edge from the global edges object | 
														
													
														
															
																|  |  |  |  |  | delete this.edges[edge.id]; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // remove the edge from the parent object | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < parentNode.dynamicEdges.length; i++) { | 
														
													
														
															
																|  |  |  |  |  | if (parentNode.dynamicEdges[i].id == edge.id) { | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdges.splice(i,1); | 
														
													
														
															
																|  |  |  |  |  | break; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function connects an edge that was connected to a child node to the parent node. | 
														
													
														
															
																|  |  |  |  |  | * It keeps track of which nodes it has been connected to with the originalID array. | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param parentNode    | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param childNode     | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param edge          | Edge object | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._connectEdgeToCluster = function(parentNode, childNode, edge) { | 
														
													
														
															
																|  |  |  |  |  | if (edge.toId == childNode.id) {    // edge connected to other node on the "to" side | 
														
													
														
															
																|  |  |  |  |  | edge.originalToID.push(childNode.id); | 
														
													
														
															
																|  |  |  |  |  | edge.to = parentNode; | 
														
													
														
															
																|  |  |  |  |  | edge.toId = parentNode.id; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else {                                  // edge connected to other node with the "from" side | 
														
													
														
															
																|  |  |  |  |  | edge.originalFromID.push(childNode.id); | 
														
													
														
															
																|  |  |  |  |  | edge.from = parentNode; | 
														
													
														
															
																|  |  |  |  |  | edge.fromId = parentNode.id; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this._addToReroutedEdges(parentNode,childNode,edge); | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This adds an edge from the childNode to the rerouted edges of the parent node | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param parentNode    | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param childNode     | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param edge          | Edge object | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._addToReroutedEdges = function(parentNode, childNode, edge) { | 
														
													
														
															
																|  |  |  |  |  | // create an array object if it does not yet exist for this childNode | 
														
													
														
															
																|  |  |  |  |  | // we store the edge in the rerouted edges so we can restore it when the cluster pops open | 
														
													
														
															
																|  |  |  |  |  | if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { | 
														
													
														
															
																|  |  |  |  |  | parentNode.reroutedEdges[childNode.id] = []; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | parentNode.reroutedEdges[childNode.id].push(edge); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // this edge becomes part of the dynamicEdges of the cluster node | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdges.push(edge); | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function connects an edge that was connected to a cluster node back to the child node. | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param parentNode    | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param childNode     | Node object | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._connectEdgeBackToChild = function(parentNode, childNode) { | 
														
													
														
															
																|  |  |  |  |  | if (parentNode.reroutedEdges[childNode.id] != undefined) { | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { | 
														
													
														
															
																|  |  |  |  |  | var edge = parentNode.reroutedEdges[childNode.id][i]; | 
														
													
														
															
																|  |  |  |  |  | if (edge.originalFromID[edge.originalFromID.length-1] == childNode.id) { | 
														
													
														
															
																|  |  |  |  |  | edge.originalFromID.pop(); | 
														
													
														
															
																|  |  |  |  |  | edge.fromId = childNode.id; | 
														
													
														
															
																|  |  |  |  |  | edge.from = childNode; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else { | 
														
													
														
															
																|  |  |  |  |  | edge.originalToID.pop(); | 
														
													
														
															
																|  |  |  |  |  | edge.toId = childNode.id; | 
														
													
														
															
																|  |  |  |  |  | edge.to = childNode; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // append this edge to the list of edges connecting to the childnode | 
														
													
														
															
																|  |  |  |  |  | childNode.dynamicEdges.push(edge); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // remove the edge from the parent object | 
														
													
														
															
																|  |  |  |  |  | for (var j = 0; j < parentNode.dynamicEdges.length; j++) { | 
														
													
														
															
																|  |  |  |  |  | if (parentNode.dynamicEdges[j].id == edge.id) { | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdges.splice(j,1); | 
														
													
														
															
																|  |  |  |  |  | break; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | // remove the entry from the rerouted edges | 
														
													
														
															
																|  |  |  |  |  | delete parentNode.reroutedEdges[childNode.id]; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function released the contained edges back into the global domain and puts them back into the | 
														
													
														
															
																|  |  |  |  |  | * dynamic edges of both parent and child. | 
														
													
														
															
																|  |  |  |  |  | * | 
														
													
														
															
																|  |  |  |  |  | * @param parentNode    | Node object | 
														
													
														
															
																|  |  |  |  |  | * @param childNode     | Node object | 
														
													
														
															
																|  |  |  |  |  | * @private | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  |  |  |  | Cluster.prototype._releaseContainedEdges = function(parentNode, childNode) { | 
														
													
														
															
																|  |  |  |  |  | for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { | 
														
													
														
															
																|  |  |  |  |  | var edge = parentNode.containedEdges[childNode.id][i]; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // put the edge back in the global edges object | 
														
													
														
															
																|  |  |  |  |  | this.edges[edge.id] = edge; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // put the edge back in the dynamic edges of the child and parent | 
														
													
														
															
																|  |  |  |  |  | childNode.dynamicEdges.push(edge); | 
														
													
														
															
																|  |  |  |  |  | parentNode.dynamicEdges.push(edge); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | // remove the entry from the contained edges | 
														
													
														
															
																|  |  |  |  |  | delete parentNode.containedEdges[childNode.id]; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * @constructor Graph |  |  | * @constructor Graph | 
														
													
														
															
																|  |  | * Create a graph visualization, displaying nodes and edges. |  |  | * Create a graph visualization, displaying nodes and edges. | 
														
													
												
													
														
															
																|  | @ -15446,7 +15676,6 @@ function Graph (container, data, options) { | 
														
													
														
															
																|  |  | Cluster.call(this); |  |  | Cluster.call(this); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | var graph = this; |  |  | var graph = this; | 
														
													
														
															
																|  |  | this.simulationStep = 100; |  |  |  | 
														
													
														
															
																|  |  | this.nodeIndices = [];      // the node indices list is used to speed up the computation of the repulsion fields |  |  | this.nodeIndices = [];      // the node indices list is used to speed up the computation of the repulsion fields | 
														
													
														
															
																|  |  | this.nodes = {};            // object with Node objects |  |  | this.nodes = {};            // object with Node objects | 
														
													
														
															
																|  |  | this.edges = {};            // object with Edge objects |  |  | this.edges = {};            // object with Edge objects | 
														
													
												
													
														
															
																|  | @ -15490,7 +15719,7 @@ function Graph (container, data, options) { | 
														
													
														
															
																|  |  | me.start(); |  |  | me.start(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | console.log("here") | 
														
													
														
															
																|  |  | this.groups = new Groups(); // object with groups |  |  | this.groups = new Groups(); // object with groups | 
														
													
														
															
																|  |  | this.images = new Images(); // object with images |  |  | this.images = new Images(); // object with images | 
														
													
														
															
																|  |  | this.images.setOnloadCallback(function () { |  |  | this.images.setOnloadCallback(function () { | 
														
													
												
													
														
															
																|  | @ -15518,6 +15747,15 @@ function Graph (container, data, options) { | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // cluster if the data set is big |  |  | // cluster if the data set is big | 
														
													
														
															
																|  |  | this.clusterToFit(); |  |  | this.clusterToFit(); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // updates the lables after clustering | 
														
													
														
															
																|  |  |  |  |  | this._updateLabels(); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | // find a stable position or start animating to a stable position | 
														
													
														
															
																|  |  |  |  |  | if (this.stabilize) { | 
														
													
														
															
																|  |  |  |  |  | this._doStabilize(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | this.start(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
												
													
														
															
																|  | @ -15526,22 +15764,25 @@ function Graph (container, data, options) { | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | Graph.prototype = Object.create(Cluster.prototype); |  |  | Graph.prototype = Object.create(Cluster.prototype); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * This function clusters untill the maxNumberOfNodes has been reached | 
														
													
														
															
																|  |  |  |  |  | */ | 
														
													
														
															
																|  |  | Graph.prototype.clusterToFit = function() { |  |  | Graph.prototype.clusterToFit = function() { | 
														
													
														
															
																|  |  | var numberOfNodes = this.nodeIndices.length; |  |  | var numberOfNodes = this.nodeIndices.length; | 
														
													
														
															
																|  |  | var maxNumberOfNodes = 100; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | var maxNumberOfNodes = 100; // TODO: set in constants | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | var maxLevels = 10; |  |  | var maxLevels = 10; | 
														
													
														
															
																|  |  | var level = 0; |  |  | var level = 0; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.simulationStep = 100; |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | while (numberOfNodes >= maxNumberOfNodes && level < maxLevels) { |  |  | while (numberOfNodes >= maxNumberOfNodes && level < maxLevels) { | 
														
													
														
															
																|  |  | console.log(level) |  |  |  | 
														
													
														
															
																|  |  | this.increaseClusterLevel(); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | if (level % 2 == 0) { | 
														
													
														
															
																|  |  |  |  |  | this.increaseClusterLevel(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else { | 
														
													
														
															
																|  |  |  |  |  | this.aggregateHubs(); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  | numberOfNodes = this.nodeIndices.length; |  |  | numberOfNodes = this.nodeIndices.length; | 
														
													
														
															
																|  |  | level += 1; |  |  | level += 1; | 
														
													
														
															
																|  |  | this.simulationStep -= 20; |  |  |  | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // after the clustering we reposition the nodes to avoid initial chaos |  |  | // after the clustering we reposition the nodes to avoid initial chaos | 
														
													
												
													
														
															
																|  | @ -15611,13 +15852,6 @@ Graph.prototype.setData = function(data) { | 
														
													
														
															
																|  |  | this._setEdges(data && data.edges); |  |  | this._setEdges(data && data.edges); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | // updating the list of node indices |  |  | // updating the list of node indices | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // find a stable position or start animating to a stable position |  |  |  | 
														
													
														
															
																|  |  | if (this.stabilize) { |  |  |  | 
														
													
														
															
																|  |  | this._doStabilize(); |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | this.start(); |  |  |  | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
												
													
														
															
																|  | @ -15758,10 +15992,10 @@ Graph.prototype._create = function () { | 
														
													
														
															
																|  |  | this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); |  |  | this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.mouseTrap = mouseTrap; |  |  | this.mouseTrap = mouseTrap; | 
														
													
														
															
																|  |  | this.mouseTrap.bind("=", this.decreaseClusterLevel.bind(me)); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.mouseTrap.bind("=",this.decreaseClusterLevel.bind(me)); | 
														
													
														
															
																|  |  | this.mouseTrap.bind("-",this.increaseClusterLevel.bind(me)); |  |  | this.mouseTrap.bind("-",this.increaseClusterLevel.bind(me)); | 
														
													
														
															
																|  |  | this.mouseTrap.bind("s",this.singleStep.bind(me)); |  |  | this.mouseTrap.bind("s",this.singleStep.bind(me)); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.mouseTrap.bind("h",this._forceClustersByHub.bind(me)); | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // add the frame to the container element |  |  | // add the frame to the container element | 
														
													
														
															
																|  |  | this.containerElement.appendChild(this.frame); |  |  | this.containerElement.appendChild(this.frame); | 
														
													
												
													
														
															
																|  | @ -16018,8 +16252,8 @@ Graph.prototype._zoom = function(scale, pointer) { | 
														
													
														
															
																|  |  | var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; |  |  | var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; | 
														
													
														
															
																|  |  | var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; |  |  | var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | this.zoomCenter = {"x" : pointer.x  - translation.x, |  |  |  | 
														
													
														
															
																|  |  | "y" : pointer.y  - translation.y}; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | this.zoomCenter = {"x" : this._canvasToX(pointer.x), | 
														
													
														
															
																|  |  |  |  |  | "y" : this._canvasToY(pointer.y)}; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | // this.zoomCenter = {"x" : pointer.x,"y" : pointer.y }; |  |  | // this.zoomCenter = {"x" : pointer.x,"y" : pointer.y }; | 
														
													
														
															
																|  |  | this._setScale(scale); |  |  | this._setScale(scale); | 
														
													
												
													
														
															
																|  | @ -16658,6 +16892,7 @@ Graph.prototype._setEdges = function(edges) { | 
														
													
														
															
																|  |  | Graph.prototype._addEdges = function (ids) { |  |  | Graph.prototype._addEdges = function (ids) { | 
														
													
														
															
																|  |  | var edges = this.edges, |  |  | var edges = this.edges, | 
														
													
														
															
																|  |  | edgesData = this.edgesData; |  |  | edgesData = this.edgesData; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | for (var i = 0, len = ids.length; i < len; i++) { |  |  | for (var i = 0, len = ids.length; i < len; i++) { | 
														
													
														
															
																|  |  | var id = ids[i]; |  |  | var id = ids[i]; | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -16666,7 +16901,7 @@ Graph.prototype._addEdges = function (ids) { | 
														
													
														
															
																|  |  | oldEdge.disconnect(); |  |  | oldEdge.disconnect(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
														
															
																|  |  | var data = edgesData.get(id); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | var data = edgesData.get(id, {"showInternalIds" : true}); | 
														
													
														
															
																|  |  | edges[id] = new Edge(data, this, this.constants); |  |  | edges[id] = new Edge(data, this, this.constants); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  |  |  |  |  | 
														
													
												
													
														
															
																|  | @ -16992,8 +17227,7 @@ Graph.prototype._calculateForces = function() { | 
														
													
														
															
																|  |  | // the graph |  |  | // the graph | 
														
													
														
															
																|  |  | // Also, the forces are reset to zero in this loop by using _setForce instead |  |  | // Also, the forces are reset to zero in this loop by using _setForce instead | 
														
													
														
															
																|  |  | // of _addForce |  |  | // of _addForce | 
														
													
														
															
																|  |  | var dynamicGravity = 100.0 - 1*this.simulationStep; |  |  |  | 
														
													
														
															
																|  |  | var gravity = (dynamicGravity < 0.05 ? 0.05 : dynamicGravity); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | var gravity = 0.05; | 
														
													
														
															
																|  |  | for (id in nodes) { |  |  | for (id in nodes) { | 
														
													
														
															
																|  |  | if (nodes.hasOwnProperty(id)) { |  |  | if (nodes.hasOwnProperty(id)) { | 
														
													
														
															
																|  |  | var node = nodes[id]; |  |  | var node = nodes[id]; | 
														
													
												
													
														
															
																|  |  |