@ -3816,6 +3816,7 @@ else {
// Try to load dependencies from the global window object.
// Try to load dependencies from the global window object.
// If not available there, load via require.
// If not available there, load via require.
var moment = ( typeof window !== 'undefined' ) && window [ 'moment' ] || require ( 'moment' ) ;
var moment = ( typeof window !== 'undefined' ) && window [ 'moment' ] || require ( 'moment' ) ;
var Hammer ;
var Hammer ;
@ -3830,6 +3831,8 @@ else {
}
}
// Internet Explorer 8 and older does not support Array.indexOf, so we define
// Internet Explorer 8 and older does not support Array.indexOf, so we define
// it here in that case.
// it here in that case.
// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
@ -12502,8 +12505,12 @@ function Node(properties, imagelist, grouplist, constants) {
// creating the variables for clustering
// creating the variables for clustering
this . resetCluster ( ) ;
this . resetCluster ( ) ;
this . remaining_edges = 0 ;
this . remaining_edges_unapplied = 0 ;
this . remainingEdges = 0 ;
this . remainingEdges_unapplied = 0 ;
this . clusterSizeWidthFactor = constants . clustering . clusterSizeWidthFactor ;
this . clusterSizeHeightFactor = constants . clustering . clusterSizeHeightFactor ;
this . clusterSizeRadiusFactor = constants . clustering . clusterSizeRadiusFactor ;
// mass, force, velocity
// mass, force, velocity
this . mass = 50 ; // kg (mass is adjusted for the number of connected edges)
this . mass = 50 ; // kg (mass is adjusted for the number of connected edges)
@ -12520,10 +12527,10 @@ function Node(properties, imagelist, grouplist, constants) {
* /
* /
Node . prototype . resetCluster = function ( ) {
Node . prototype . resetCluster = function ( ) {
// clustering variables
// clustering variables
this . formation_s cale = undefined ; // this is used to determine when to open the cluster
this . cluster_s ize = 1 ; // this signifies the total amount of nodes in this cluster
this . contained_n odes = { } ;
this . contained_e dges = { } ;
this . formationS cale = undefined ; // this is used to determine when to open the cluster
this . clusterS ize = 1 ; // this signifies the total amount of nodes in this cluster
this . containedN odes = { } ;
this . containedE dges = { } ;
} ;
} ;
/ * *
/ * *
@ -12534,8 +12541,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 . remaining_e dges = this . edges . length ;
this . remaining_e dges_unapplied = this . edges . length ;
this . remainingE dges = this . edges . length ;
this . remainingE dges_unapplied = this . edges . length ;
this . _updateMass ( ) ;
this . _updateMass ( ) ;
} ;
} ;
@ -12548,8 +12555,8 @@ Node.prototype.detachEdge = function(edge) {
if ( index != - 1 ) {
if ( index != - 1 ) {
this . edges . splice ( index , 1 ) ;
this . edges . splice ( index , 1 ) ;
}
}
this . remaining_e dges = this . edges . length ;
this . remaining_e dges_unapplied = this . edges . length ;
this . remainingE dges = this . edges . length ;
this . remainingE dges_unapplied = this . edges . length ;
this . _updateMass ( ) ;
this . _updateMass ( ) ;
} ;
} ;
@ -12723,8 +12730,7 @@ Node.parseColor = function(color) {
* /
* /
Node . prototype . select = function ( ) {
Node . prototype . select = function ( ) {
this . selected = true ;
this . selected = true ;
// why do this?
// this._reset();
this . _reset ( ) ;
} ;
} ;
/ * *
/ * *
@ -12732,8 +12738,15 @@ Node.prototype.select = function() {
* /
* /
Node . prototype . unselect = function ( ) {
Node . prototype . unselect = function ( ) {
this . selected = false ;
this . selected = false ;
// why do this?
// this._reset();
this . _reset ( ) ;
} ;
/ * *
* Reset the calculated size of the node , forces it to recalculate its size
* /
Node . prototype . clearSizeCache = function ( ) {
this . _reset ( ) ;
} ;
} ;
/ * *
/ * *
@ -12957,6 +12970,10 @@ Node.prototype._resizeImage = function (ctx) {
}
}
this . width = width ;
this . width = width ;
this . height = height ;
this . height = height ;
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -12986,6 +13003,10 @@ Node.prototype._resizeBox = function (ctx) {
var textSize = this . getTextSize ( ctx ) ;
var textSize = this . getTextSize ( ctx ) ;
this . width = textSize . width + 2 * margin ;
this . width = textSize . width + 2 * margin ;
this . height = textSize . height + 2 * margin ;
this . height = textSize . height + 2 * margin ;
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -12995,7 +13016,7 @@ Node.prototype._drawBox = function (ctx) {
this . left = this . x - this . width / 2 ;
this . left = this . x - this . width / 2 ;
this . top = this . y - this . height / 2 ;
this . top = this . y - this . height / 2 ;
if ( this . cluster_s ize > 1 ) {
if ( this . clusterS ize > 1 ) {
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
}
}
@ -13003,7 +13024,7 @@ Node.prototype._drawBox = function (ctx) {
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
}
}
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . cluster_s ize > 1 ) ? 2.0 : 0.0 ;
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . clusterS ize > 1 ) ? 2.0 : 0.0 ;
ctx . roundRect ( this . left , this . top , this . width , this . height , this . radius ) ;
ctx . roundRect ( this . left , this . top , this . width , this . height , this . radius ) ;
ctx . fill ( ) ;
ctx . fill ( ) ;
ctx . stroke ( ) ;
ctx . stroke ( ) ;
@ -13019,6 +13040,11 @@ Node.prototype._resizeDatabase = function (ctx) {
var size = textSize . width + 2 * margin ;
var size = textSize . width + 2 * margin ;
this . width = size ;
this . width = size ;
this . height = size ;
this . height = size ;
// scaling used for clustering
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -13027,7 +13053,7 @@ Node.prototype._drawDatabase = function (ctx) {
this . left = this . x - this . width / 2 ;
this . left = this . x - this . width / 2 ;
this . top = this . y - this . height / 2 ;
this . top = this . y - this . height / 2 ;
if ( this . cluster_s ize > 1 ) {
if ( this . clusterS ize > 1 ) {
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
}
}
@ -13035,7 +13061,7 @@ Node.prototype._drawDatabase = function (ctx) {
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
}
}
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . cluster_s ize > 1 ) ? 2.0 : 0.0 ;
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . clusterS ize > 1 ) ? 2.0 : 0.0 ;
ctx . database ( this . x - this . width / 2 , this . y - this . height * 0.5 , this . width , this . height ) ;
ctx . database ( this . x - this . width / 2 , this . y - this . height * 0.5 , this . width , this . height ) ;
ctx . fill ( ) ;
ctx . fill ( ) ;
ctx . stroke ( ) ;
ctx . stroke ( ) ;
@ -13053,6 +13079,11 @@ Node.prototype._resizeCircle = function (ctx) {
this . width = diameter ;
this . width = diameter ;
this . height = diameter ;
this . height = diameter ;
// scaling used for clustering
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -13061,7 +13092,7 @@ Node.prototype._drawCircle = function (ctx) {
this . left = this . x - this . width / 2 ;
this . left = this . x - this . width / 2 ;
this . top = this . y - this . height / 2 ;
this . top = this . y - this . height / 2 ;
if ( this . cluster_s ize > 1 ) {
if ( this . clusterS ize > 1 ) {
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
}
}
@ -13069,7 +13100,7 @@ Node.prototype._drawCircle = function (ctx) {
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
}
}
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . cluster_s ize > 1 ) ? 2.0 : 0.0 ;
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . clusterS ize > 1 ) ? 2.0 : 0.0 ;
ctx . circle ( this . x , this . y , this . radius ) ;
ctx . circle ( this . x , this . y , this . radius ) ;
ctx . fill ( ) ;
ctx . fill ( ) ;
ctx . stroke ( ) ;
ctx . stroke ( ) ;
@ -13086,6 +13117,11 @@ Node.prototype._resizeEllipse = function (ctx) {
if ( this . width < this . height ) {
if ( this . width < this . height ) {
this . width = this . height ;
this . width = this . height ;
}
}
// scaling used for clustering
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -13094,7 +13130,7 @@ Node.prototype._drawEllipse = function (ctx) {
this . left = this . x - this . width / 2 ;
this . left = this . x - this . width / 2 ;
this . top = this . y - this . height / 2 ;
this . top = this . y - this . height / 2 ;
if ( this . cluster_s ize > 1 ) {
if ( this . clusterS ize > 1 ) {
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
}
}
@ -13102,11 +13138,10 @@ Node.prototype._drawEllipse = function (ctx) {
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
}
}
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . cluster_s ize > 1 ) ? 2.0 : 0.0 ;
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . clusterS ize > 1 ) ? 2.0 : 0.0 ;
ctx . ellipse ( this . left , this . top , this . width , this . height ) ;
ctx . ellipse ( this . left , this . top , this . width , this . height ) ;
ctx . fill ( ) ;
ctx . fill ( ) ;
ctx . stroke ( ) ;
ctx . stroke ( ) ;
this . _label ( ctx , this . label , this . x , this . y ) ;
this . _label ( ctx , this . label , this . x , this . y ) ;
} ;
} ;
@ -13135,6 +13170,11 @@ Node.prototype._resizeShape = function (ctx) {
var size = 2 * this . radius ;
var size = 2 * this . radius ;
this . width = size ;
this . width = size ;
this . height = size ;
this . height = size ;
// scaling used for clustering
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -13143,8 +13183,7 @@ Node.prototype._drawShape = function (ctx, shape) {
this . left = this . x - this . width / 2 ;
this . left = this . x - this . width / 2 ;
this . top = this . y - this . height / 2 ;
this . top = this . y - this . height / 2 ;
if ( this . cluster_size > 1 ) {
if ( this . clusterSize > 1 ) {
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . strokeStyle = this . selected ? this . color . cluster . highlight . border : this . color . cluster . border ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
ctx . fillStyle = this . selected ? this . color . cluster . highlight . background : this . color . cluster . background ;
}
}
@ -13152,7 +13191,7 @@ Node.prototype._drawShape = function (ctx, shape) {
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . strokeStyle = this . selected ? this . color . highlight . border : this . color . border ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
ctx . fillStyle = this . selected ? this . color . highlight . background : this . color . background ;
}
}
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . cluster_s ize > 1 ) ? 2.0 : 0.0 ;
ctx . lineWidth = ( this . selected ? 2.0 : 1.0 ) + ( this . clusterS ize > 1 ) ? 2.0 : 0.0 ;
ctx [ shape ] ( this . x , this . y , this . radius ) ;
ctx [ shape ] ( this . x , this . y , this . radius ) ;
ctx . fill ( ) ;
ctx . fill ( ) ;
@ -13169,6 +13208,11 @@ Node.prototype._resizeText = function (ctx) {
var textSize = this . getTextSize ( ctx ) ;
var textSize = this . getTextSize ( ctx ) ;
this . width = textSize . width + 2 * margin ;
this . width = textSize . width + 2 * margin ;
this . height = textSize . height + 2 * margin ;
this . height = textSize . height + 2 * margin ;
// scaling used for clustering
this . width += this . clusterSize * this . clusterSizeWidthFactor ;
this . height += this . clusterSize * this . clusterSizeHeightFactor ;
this . radius += this . clusterSize * this . clusterSizeRadiusFactor ;
}
}
} ;
} ;
@ -14127,13 +14171,14 @@ function Graph (container, data, options) {
}
}
} ,
} ,
clustering : {
clustering : {
clusterLength : 50 , // threshold length for clustering
clusterLength : 50 , // threshold edge length for clustering
fontSizeMultiplier : 2 , // how much the cluster font size grows per node (in px)
fontSizeMultiplier : 2 , // how much the cluster font size grows per node (in px)
forceAmplification : 0.6 , // amount of cluster_size between two nodes multiply this value (+1) with the repulsion force
distanceAmplification : 0.1 , // amount of cluster_size between two nodes multiply this value (+1) with the repulsion force
edgeGrowth : 10 , // amount of cluster_size connected to the edge is multiplied with this and added to edgeLength
widthGrowth : 10 , // growth factor = ((parent_size + child_size) / parent_size) * widthGrowthFactor
heightGrowth : 10 , // growth factor = ((parent_size + child_size) / parent_size) * heightGrowthFactor
forceAmplification : 0.6 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
distanceAmplification : 0.1 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
edgeGrowth : 10 , // amount of clusterSize connected to the edge is multiplied with this and added to edgeLength
clusterSizeWidthFactor : 10 ,
clusterSizeHeightFactor : 10 ,
clusterSizeRadiusFactor : 10 ,
massTransferCoefficient : 0.1 // parent.mass += massTransferCoefficient * child.mass
massTransferCoefficient : 0.1 // parent.mass += massTransferCoefficient * child.mass
} ,
} ,
minForce : 0.05 ,
minForce : 0.05 ,
@ -14142,11 +14187,11 @@ function Graph (container, data, options) {
} ;
} ;
var graph = this ;
var graph = this ;
this . node_i ndices = [ ] ; // the node indices list is used to speed up the computation of the repulsion fields
this . nodeI ndices = [ ] ; // 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
this . scale = 1 ; // defining the global scale variable in the constructor
this . scale = 1 ; // defining the global scale variable in the constructor
this . previous_s cale = this . scale ; // this is used to check if the zoom operation is zooming in or out
this . previousS cale = this . scale ; // this is used to check if the zoom operation is zooming in or out
// TODO: create a counter to keep track on the number of nodes having values
// TODO: create a counter to keep track on the number of nodes having values
// TODO: create a counter to keep track on the number of nodes currently moving
// TODO: create a counter to keep track on the number of nodes currently moving
// TODO: create a counter to keep track on the number of edges having values
// TODO: create a counter to keep track on the number of edges having values
@ -14205,15 +14250,37 @@ function Graph (container, data, options) {
// draw data
// draw data
this . setData ( data ) ;
this . setData ( data ) ;
// zoom so all data will fit on the screen
this . zoomToFit ( ) ;
// cluster if the dataset is big
this . clusterToFit ( ) ;
}
}
Graph . prototype . clusterToFit = function ( ) {
var numberOfNodes = this . nodeIndices . length ;
if ( numberOfNodes >= 100 ) {
this . increaseClusterLevel ( ) ;
}
} ;
Graph . prototype . zoomToFit = function ( ) {
var numberOfNodes = this . nodeIndices . length ;
var zoomLevel = 105 / ( numberOfNodes + 80 ) ; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
if ( zoomLevel > 1.0 ) {
zoomLevel = 1.0 ;
}
this . _setScale ( zoomLevel ) ;
} ;
/ * *
/ * *
* 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 .
* This can be called externally ( by a keybind for instance ) to reduce the complexity of big datasets .
* This can be called externally ( by a keybind for instance ) to reduce the complexity of big datasets .
* /
* /
Graph . prototype . collapseClusterLevel = function ( ) {
Graph . prototype . increa seClusterLevel = function ( ) {
this . _formClusters ( true ) ;
this . _formClusters ( true ) ;
} ;
} ;
@ -14222,10 +14289,10 @@ Graph.prototype.collapseClusterLevel = function() {
* be unpacked if they are a cluster . This can be repeated as many times as needed .
* be unpacked if they are a cluster . This can be repeated as many times as needed .
* This can be called externally ( by a key - bind for instance ) to look into clusters without zooming .
* This can be called externally ( by a key - bind for instance ) to look into clusters without zooming .
* /
* /
Graph . prototype . expand ClusterLevel = function ( ) {
for ( var i = 0 ; i < this . node_i ndices . length ; i ++ ) {
var node = this . nodes [ this . node_i ndices [ i ] ] ;
if ( node . cluster_s ize > 1 && node . remaining_e dges == 1 ) {
Graph . prototype . decrease ClusterLevel = function ( ) {
for ( var i = 0 ; i < this . nodeI ndices . length ; i ++ ) {
var node = this . nodes [ this . nodeI ndices [ i ] ] ;
if ( node . clusterS ize > 1 && node . remainingE dges == 1 ) {
this . _expandClusterNode ( node , false , true ) ;
this . _expandClusterNode ( node , false , true ) ;
}
}
}
}
@ -14265,24 +14332,26 @@ Graph.prototype.openCluster = function(node) {
* @ private
* @ private
* /
* /
Graph . prototype . _updateClusters = function ( ) {
Graph . prototype . _updateClusters = function ( ) {
var moving_before_c lustering = this . moving ;
var isMovingBeforeC lustering = this . moving ;
if ( this . previous_s cale > this . scale ) { // zoom out
if ( this . previousS cale > this . scale ) { // zoom out
this . _formClusters ( false ) ;
this . _formClusters ( false ) ;
}
}
else if ( this . previous_s cale < this . scale ) { // zoom out
else if ( this . previousS cale < this . scale ) { // zoom out
this . _openClusters ( ) ;
this . _openClusters ( ) ;
}
}
this . _updateClusterLabels ( ) ;
this . _updateClusterLabels ( ) ;
this . _updateNodeLabels ( ) ;
this . _updateNodeLabels ( ) ;
this . previous_s cale = this . scale ;
this . _updateLabels ( ) ;
this . previousS cale = this . scale ;
// 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 != moving_before_c lustering) {
if ( this . moving != isMovingBeforeC lustering) {
this . start ( ) ;
this . start ( ) ;
}
}
// console.log(this.scale);
} ;
} ;
/ * *
/ * *
@ -14291,10 +14360,10 @@ Graph.prototype._updateClusters = function() {
* /
* /
Graph . prototype . _updateLabels = function ( ) {
Graph . prototype . _updateLabels = function ( ) {
// update node labels
// update node labels
for ( var node_id in this . nodes ) {
if ( this . nodes . hasOwnProperty ( node_id ) ) {
var node = this . nodes [ node_id ] ;
node . label = String ( node . remaining_e dges ) . concat ( ":" , String ( node . cluster_s ize ) ) ;
for ( var nodeID in this . nodes ) {
if ( this . nodes . hasOwnProperty ( nodeID ) ) {
var node = this . nodes [ nodeID ] ;
node . label = String ( node . remainingE dges ) . concat ( ":" , String ( node . clusterS ize ) ) ;
}
}
}
}
} ;
} ;
@ -14305,11 +14374,11 @@ Graph.prototype._updateLabels = function() {
* /
* /
Graph . prototype . _updateClusterLabels = function ( ) {
Graph . prototype . _updateClusterLabels = function ( ) {
// update node labels
// update node labels
for ( var node_id in this . nodes ) {
if ( this . nodes . hasOwnProperty ( node_id ) ) {
var node = this . nodes [ node_id ] ;
if ( node . cluster_s ize > 1 ) {
node . label = "[" . concat ( String ( node . cluster_s ize ) , "]" ) ;
for ( var nodeID in this . nodes ) {
if ( this . nodes . hasOwnProperty ( nodeID ) ) {
var node = this . nodes [ nodeID ] ;
if ( node . clusterS ize > 1 ) {
node . label = "[" . concat ( String ( node . clusterS ize ) , "]" ) ;
}
}
}
}
}
}
@ -14321,9 +14390,9 @@ Graph.prototype._updateClusterLabels = function() {
* /
* /
Graph . prototype . _updateNodeLabels = function ( ) {
Graph . prototype . _updateNodeLabels = function ( ) {
// update node labels
// update node labels
for ( var node_id in this . nodes ) {
var node = this . nodes [ node_id ] ;
if ( node . cluster_s ize == 1 ) {
for ( var nodeID in this . nodes ) {
var node = this . nodes [ nodeID ] ;
if ( node . clusterS ize == 1 ) {
node . label = String ( node . id ) ;
node . label = String ( node . id ) ;
}
}
}
}
@ -14331,14 +14400,14 @@ Graph.prototype._updateNodeLabels = function() {
/ * *
/ * *
* This function loops over all nodes in the node_i ndices list . For each node it checks if it is a cluster and if it
* This function loops over all nodes in the nodeI ndices list . For each node it checks if it is a cluster and if it
* has to be opened based on the current zoom level .
* has to be opened based on the current zoom level .
*
*
* @ private
* @ private
* /
* /
Graph . prototype . _openClusters = function ( ) {
Graph . prototype . _openClusters = function ( ) {
for ( var i = 0 ; i < this . node_i ndices . length ; i ++ ) {
var node = this . nodes [ this . node_i ndices [ i ] ] ;
for ( var i = 0 ; i < this . nodeI ndices . length ; i ++ ) {
var node = this . nodes [ this . nodeI ndices [ i ] ] ;
this . _expandClusterNode ( node , true , false ) ;
this . _expandClusterNode ( node , true , false ) ;
}
}
@ -14351,44 +14420,43 @@ Graph.prototype._openClusters = function() {
* If the node contains child nodes , this function is recursively called on the child nodes as well .
* If the node contains child nodes , this function is recursively called on the child nodes as well .
* This recursive behaviour is optional and can be set by the recursive argument .
* This recursive behaviour is optional and can be set by the recursive argument .
*
*
* @ param node | Node object : to check for cluster and expand
* @ param pare ntN ode | Node object : to check for cluster and expand
* @ param recursive | Boolean : enable or disable recursive calling
* @ param recursive | Boolean : enable or disable recursive calling
* @ param force_expand | Boolean : enable or disable forcing the last node to join the cluster to be expelled
* @ param forceExpand | Boolean : enable or disable forcing the last node to join the cluster to be expelled
* @ private
* @ private
* /
* /
Graph . prototype . _expandClusterNode = function ( node , recursive , force_e xpand ) {
Graph . prototype . _expandClusterNode = function ( pare ntN ode, recursive , forceE xpand ) {
// first check if node is a cluster
// first check if node is a cluster
if ( node . cluster_s ize > 1 ) {
if ( pare ntN ode. clusterS ize > 1 ) {
// if the last child has been added on a smaller scale than current scale (@optimization)
// if the last child has been added on a smaller scale than current scale (@optimization)
if ( node . formation_s cale < this . scale || force_e xpand == true ) {
if ( pare ntN ode. formationS cale < this . scale || forceE xpand == true ) {
// we will check if any of the contained child nodes should be removed from the cluster
// we will check if any of the contained child nodes should be removed from the cluster
var largest_cluster = 1 ;
for ( var contained_node_id in node . contained_nodes ) {
if ( node . contained_nodes . hasOwnProperty ( contained_node_id ) ) {
var child_node = node . contained_nodes [ contained_node_id ] ;
var largestClusterSize = 1 ;
for ( var containedNodeID in parentNode . containedNodes ) {
if ( parentNode . containedNodes . hasOwnProperty ( containedNodeID ) ) {
var child_node = parentNode . containedNodes [ containedNodeID ] ;
// force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that
// force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that
// the largest cluster is the one that comes from outside
// the largest cluster is the one that comes from outside
// TODO: introduce a level system for keeping track of which node was added when.
// TODO: introduce a level system for keeping track of which node was added when.
if ( force_e xpand == true ) {
if ( largest_cluster < child_node . cluster_s ize ) {
largest_cluster = child_node . cluster_s ize ;
if ( forceE xpand == true ) {
if ( largestClusterSize < child_node . clusterS ize ) {
largestClusterSize = child_node . clusterS ize ;
}
}
}
}
else {
else {
this . _expelChildFromParent ( node , contained_node_id , recursive , force_e xpand ) ;
this . _expelChildFromParent ( pare ntN ode, containedNodeID , recursive , forceE xpand ) ;
}
}
}
}
}
}
// we have determined the largest cluster size, we will now expel these
// we have determined the largest cluster size, we will now expel these
if ( force_e xpand == true ) {
for ( var contained_node_id in node . contained_n odes ) {
if ( node . contained_n odes . hasOwnProperty ( contained_node_id ) ) {
var child_node = node . contained_nodes [ contained_node_id ] ;
if ( child_node . cluster_size == largest_cluster ) {
this . _expelChildFromParent ( node , contained_node_id , recursive , force_e xpand ) ;
if ( forceE xpand == true ) {
for ( var containedNodeID in pare ntN ode. containedN odes ) {
if ( pare ntN ode. containedN odes . hasOwnProperty ( containedNodeID ) ) {
var child_node = pare ntN ode. containedNodes [ containedNodeID ] ;
if ( child_node . clusterSize == largestClusterSize ) {
this . _expelChildFromParent ( pare ntN ode, containedNodeID , recursive , forceE xpand ) ;
}
}
}
}
}
}
@ -14403,45 +14471,46 @@ Graph.prototype._expandClusterNode = function(node, recursive, force_expand) {
* the child node from the parent contained_node object and put it back into the global nodes object .
* the child node from the parent contained_node object and put it back into the global nodes object .
* The same holds for the edge that was connected to the child node . It is moved back into the global edges object .
* The same holds for the edge that was connected to the child node . It is moved back into the global edges object .
*
*
* @ param node | Node object : the parent node
* @ param contained_node_id | String : child_node id as it is contained in the contained_n odes object of the parent node
* @ param recursive | Boolean : This will also check if the child needs to be expanded .
* With force and recursive both true , the entire cluster is unpacked
* @ param force_expand | Boolean : This will disregard the zoom level and will expel this child from the parent
* @ param pare ntN ode | Node object : the parent node
* @ param containedNodeID | String : child_node id as it is contained in the containedN odes object of the parent node
* @ param recursive | Boolean : This will also check if the child needs to be expanded .
* With force and recursive both true , the entire cluster is unpacked
* @ param forceExpand | Boolean : This will disregard the zoom level and will expel this child from the parent
* @ private
* @ private
* /
* /
Graph . prototype . _expelChildFromParent = function ( node , contained_node_id , recursive , force_e xpand ) {
var child_n ode = node . contained_nodes [ contained_node_id ] ;
Graph . prototype . _expelChildFromParent = function ( pare ntN ode, containedNodeID , recursive , forceE xpand ) {
var childN ode = pare ntN ode. containedNodes [ 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 ( child_node . formation_s cale < this . scale || force_e xpand == true ) {
if ( childNode . formationS cale < this . scale || forceE xpand == 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 and the corresponding edge in the global edges object
this . nodes [ contained_node_id ] = child_n ode ;
this . edges [ node . contained_edges [ contained_node_id ] [ "edge_id" ] ] = node . contained_edges [ contained_node_id ] [ "edge_object" ] ;
this . nodes [ containedNodeID ] = childN ode ;
this . edges [ pare ntN ode. containedEdges [ containedNodeID ] . id ] = parentNode . containedEdges [ containedNodeID ] ;
// undo the changes from the clustering operation on the parent node
// undo the changes from the clustering operation on the parent node
node . mass -= this . constants . clustering . massTransferCoefficient * child_node . mass ;
node . width -= child_node . cluster_size * this . constants . clustering . widthGrowth ;
node . height -= child_node . cluster_size * this . constants . clustering . heightGrowth ;
node . fontSize -= this . constants . clustering . fontSizeMultiplier * child_node . cluster_size ;
node . cluster_size -= child_node . cluster_size ;
node . remaining_edges += 1 ;
parentNode . mass -= this . constants . clustering . massTransferCoefficient * childNode . mass ;
parentNode . fontSize -= this . constants . clustering . fontSizeMultiplier * childNode . clusterSize ;
parentNode . clusterSize -= childNode . clusterSize ;
parentNode . remainingEdges += 1 ;
// 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
child_n ode . x = node . x + Math . floor ( Math . random ( ) * node . width ) ;
child_n ode . y = node . y + Math . floor ( Math . random ( ) * node . height ) ;
childN ode . x = parentNode . x ;
childN ode . y = parentNode . y ;
// remove node from the list
// remove node from the list
delete node . contained_nodes [ contained_node_id ] ;
delete node . contained_edges [ contained_node_id ] ;
delete pare ntN ode. containedNodes [ containedNodeID ] ;
delete pare ntN ode. containedEdges [ containedNodeID ] ;
// restart the simulation to reorganise all nodes
// restart the simulation to reorganise all nodes
this . moving = true ;
this . moving = true ;
// recalculate the size of the node on the next time the node is rendered
parentNode . clearSizeCache ( ) ;
}
}
// check if a further expansion step is possible if recursivity is enabled
// check if a further expansion step is possible if recursivity is enabled
if ( recursive == true ) {
if ( recursive == true ) {
this . _expandClusterNode ( child_n ode , recursive , force_e xpand ) ;
this . _expandClusterNode ( childN ode , recursive , forceE xpand ) ;
}
}
} ;
} ;
@ -14456,62 +14525,61 @@ Graph.prototype._expelChildFromParent = function(node, contained_node_id, recurs
* @ private
* @ private
* @ param force_level_collapse | Boolean
* @ param force_level_collapse | Boolean
* /
* /
Graph . prototype . _formClusters = function ( force_level_c ollapse ) {
Graph . prototype . _formClusters = function ( forceLevelC ollapse ) {
var min_length = this . constants . clustering . clusterLength / this . scale ;
var min_length = this . constants . clustering . clusterLength / this . scale ;
var dx , dy , length ,
var dx , dy , length ,
edges = this . edges ;
edges = this . edges ;
// create an array of edge ids
// create an array of edge ids
var edges_id_ array = [ ]
var edgesID array = [ ]
for ( var id in edges ) {
for ( var id in edges ) {
if ( edges . hasOwnProperty ( id ) ) {
if ( edges . hasOwnProperty ( id ) ) {
edges_id_ array . push ( id ) ;
edgesID array . push ( id ) ;
}
}
}
}
// check if any edges are shorter than min_length and start the clustering
// check if any edges are shorter than min_length 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 < edges_id_array . length ; i ++ ) {
var edge_id = edges_id_array [ i ] ;
var edge = edges [ edge_id ] ;
for ( var i = 0 ; i < edgesIDarray . length ; i ++ ) {
var edgeID = edgesIDarray [ i ] ;
var edge = edges [ edgeID ] ;
edge . id = 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 < min_length || force_level_c ollapse == true ) {
if ( length < min_length || forceLevelC ollapse == true ) {
// checking for clustering possibilities
// checking for clustering possibilities
// first check which node is larger
// first check which node is larger
var parentNode = edge . from
var childNode = edge . to
if ( edge . to . mass > edge . from . mass ) {
if ( edge . to . mass > edge . from . mass ) {
var parent_node = edge . to
var child_node = edge . from
}
else {
var parent_node = edge . from
var child_node = edge . to
parentNode = edge . to
childNode = edge . from
}
}
// we allow clustering from outside in, ideally the child node in on the outside
// 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
// 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.
// clusters. This will also have to be altered in the force calculation and rendering.
// This method is non-destructive and does not require a second set of data.
// This method is non-destructive and does not require a second set of data.
if ( child_node . remaining_e dges == 1 ) {
this . _addToCluster ( parent_node , child_n ode , edge , edge_id , force_level_c ollapse) ;
delete this . edges [ edges_id_ array [ i ] ] ;
if ( childNode . remainingE dges == 1 ) {
this . _addToCluster ( parentNode , childN ode , edge , forceLevelC ollapse) ;
delete this . edges [ edgesID array [ i ] ] ;
}
}
else if ( parent_node . remaining_e dges == 1 ) {
this . _addToCluster ( child_node , parent_n ode , edge , edge_id , force_level_c ollapse) ;
delete this . edges [ edges_id_ array [ i ] ] ;
else if ( parentNode . remainingE dges == 1 ) {
this . _addToCluster ( childNode , parentN ode , edge , forceLevelC ollapse) ;
delete this . edges [ edgesID array [ i ] ] ;
}
}
}
}
}
}
}
}
this . _updateNodeIndexList ( ) ;
this . _updateNodeIndexList ( ) ;
if ( force_level_c ollapse == true )
if ( forceLevelC ollapse == true )
this . _applyClusterLevel ( ) ;
this . _applyClusterLevel ( ) ;
} ;
} ;
@ -14523,31 +14591,31 @@ Graph.prototype._formClusters = function(force_level_collapse) {
* @ 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 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 remaining_e dges at the very end of the clustering , ensuring single level collapse
* @ param force_level_collapse | Boolean : true will only update the remainingE dges at the very end of the clustering , ensuring single level collapse
* @ private
* @ private
* /
* /
Graph . prototype . _addToCluster = function ( parent_node , child_n ode , edge , edge_id , force_level_c ollapse) {
Graph . prototype . _addToCluster = function ( parentNode , childN ode , edge , forceLevelC ollapse) {
// join child node and edge in parent node
// join child node and edge in parent node
parent_node . contained_nodes [ child_n ode . id ] = child_n ode ;
parent_node . contained_edges [ child_n ode . id ] = { "edge_id" : edge_id , "edge_object" : edge } ; // the edge gets the node ID so we can easily recover it when expanding the cluster
parentNode . containedNodes [ childN ode . id ] = childN ode ;
parentNode . containedEdges [ childN ode . id ] = edge ; // the edge gets the node ID so we can easily recover it when expanding the cluster
if ( this . nodes . hasOwnProperty ( child_n ode . id ) ) {
delete this . nodes [ child_n ode . id ] ;
if ( this . nodes . hasOwnProperty ( childN ode . id ) ) {
delete this . nodes [ childN ode . id ] ;
}
}
//var grow_coefficient = (parent_node.cluster_size + child_node.cluster_size) / parent_node.cluster_size;
parent_node . mass += this . constants . clustering . massTransferCoefficient * child_node . mass ;
parent_node . width += child_node . cluster_size * this . constants . clustering . widthGrowth ;
parent_node . height += child_node . cluster_size * this . constants . clustering . heightGrowth ;
parent_node . cluster_size += child_node . cluster_size ;
parent_node . fontSize += this . constants . clustering . fontSizeMultiplier * child_node . cluster_size ;
parent_node . formation_scale = this . scale ; // The latest child has been added on this scale
parentNode . mass += this . constants . clustering . massTransferCoefficient * childNode . mass ;
parentNode . clusterSize += childNode . clusterSize ;
parentNode . fontSize += this . constants . clustering . fontSizeMultiplier * childNode . clusterSize ;
parentNode . formationScale = this . scale ; // The latest child has been added on this scale
// recalculate the size of the node on the next time the node is rendered
parentNode . clearSizeCache ( ) ;
parent_node . contained_nodes [ child_n ode . id ] . formation_s cale = this . scale ; // this child has been added at this scale.
if ( force_level_c ollapse == true )
parent_node . remaining_e dges_unapplied -= 1 ;
parentNode . containedNodes [ childN ode . id ] . formationS cale = this . scale ; // this child has been added at this scale.
if ( forceLevelC ollapse == true )
parentNode . remainingE dges_unapplied -= 1 ;
else
else
parent_node . remaining_e dges -= 1 ;
parentNode . remainingE dges -= 1 ;
// restart the simulation to reorganise all nodes
// restart the simulation to reorganise all nodes
this . moving = true ;
this . moving = true ;
@ -14555,29 +14623,29 @@ Graph.prototype._addToCluster = function(parent_node, child_node, edge, edge_id,
/ * *
/ * *
* This function will apply the changes made to the remaining_e dges during the formation of the clusters .
* This function will apply the changes made to the remainingE dges during the formation of the clusters .
* This is a seperate function to allow for level - wise collapsing of the node tree .
* This is a seperate function to allow for level - wise collapsing of the node tree .
* 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
* /
* /
Graph . prototype . _applyClusterLevel = function ( ) {
Graph . prototype . _applyClusterLevel = function ( ) {
for ( var i = 0 ; i < this . node_i ndices . length ; i ++ ) {
var node = this . nodes [ this . node_i ndices [ i ] ] ;
node . remaining_e dges = node . remaining_e dges_unapplied ;
for ( var i = 0 ; i < this . nodeI ndices . length ; i ++ ) {
var node = this . nodes [ this . nodeI ndices [ i ] ] ;
node . remainingE dges = node . remainingE dges_unapplied ;
}
}
} ;
} ;
/ * *
/ * *
* Update the this . node_i ndices with the most recent node index list
* Update the this . nodeI ndices with the most recent node index list
* @ private
* @ private
* /
* /
Graph . prototype . _updateNodeIndexList = function ( ) {
Graph . prototype . _updateNodeIndexList = function ( ) {
this . node_i ndices = [ ] ;
this . nodeI ndices = [ ] ;
for ( var idx in this . nodes ) {
for ( var idx in this . nodes ) {
if ( this . nodes . hasOwnProperty ( idx ) ) {
if ( this . nodes . hasOwnProperty ( idx ) ) {
this . node_i ndices . push ( idx ) ;
this . nodeI ndices . push ( idx ) ;
}
}
}
}
} ;
} ;
@ -14761,6 +14829,7 @@ Graph.prototype._create = function () {
this . hammer . on ( 'DOMMouseScroll' , me . _onMouseWheel . bind ( me ) ) ; // for FF
this . hammer . on ( 'DOMMouseScroll' , me . _onMouseWheel . bind ( me ) ) ; // for FF
this . hammer . on ( 'mousemove' , me . _onMouseMoveTitle . bind ( me ) ) ;
this . hammer . on ( 'mousemove' , me . _onMouseMoveTitle . 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 ) ;
} ;
} ;
@ -16008,17 +16077,17 @@ Graph.prototype._calculateForces = function() {
// we loop from i over all but the last entree in the array
// we loop from i over all but the last entree in the array
// j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
// j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
for ( var i = 0 ; i < this . node_i ndices . length - 1 ; i ++ ) {
var node1 = nodes [ this . node_i ndices [ i ] ] ;
for ( var j = i + 1 ; j < this . node_i ndices . length ; j ++ ) {
var node2 = nodes [ this . node_i ndices [ j ] ] ;
var cluster_s ize = ( node1 . cluster_s ize + node2 . cluster_s ize - 2 ) ;
for ( var i = 0 ; i < this . nodeI ndices . length - 1 ; i ++ ) {
var node1 = nodes [ this . nodeI ndices [ i ] ] ;
for ( var j = i + 1 ; j < this . nodeI ndices . length ; j ++ ) {
var node2 = nodes [ this . nodeI ndices [ j ] ] ;
var clusterS ize = ( node1 . clusterS ize + node2 . clusterS ize - 2 ) ;
dx = node2 . x - node1 . x ;
dx = node2 . x - node1 . x ;
dy = node2 . y - node1 . y ;
dy = node2 . y - node1 . y ;
distance = Math . sqrt ( dx * dx + dy * dy ) ;
distance = Math . sqrt ( dx * dx + dy * dy ) ;
minimumDistance = ( cluster_s ize == 0 ) ? this . constants . nodes . distance : ( this . constants . nodes . distance * ( 1 + cluster_s ize * this . constants . clustering . distanceAmplification ) ) ;
minimumDistance = ( clusterS ize == 0 ) ? this . constants . nodes . distance : ( this . constants . nodes . distance * ( 1 + clusterS ize * this . constants . clustering . distanceAmplification ) ) ;
if ( distance < 2 * minimumDistance ) { // at 2.0 * the minimum distance, the force is 0.000045
if ( distance < 2 * minimumDistance ) { // at 2.0 * the minimum distance, the force is 0.000045
angle = Math . atan2 ( dy , dx ) ;
angle = Math . atan2 ( dy , dx ) ;
@ -16037,7 +16106,7 @@ Graph.prototype._calculateForces = function() {
}
}
// amplify the repulsion for clusters.
// amplify the repulsion for clusters.
repulsingForce *= ( cluster_s ize == 0 ) ? 1 : 1 + cluster_s ize * this . constants . clustering . forceAmplification ;
repulsingForce *= ( clusterS ize == 0 ) ? 1 : 1 + clusterS ize * this . constants . clustering . forceAmplification ;
fx = Math . cos ( angle ) * repulsingForce ;
fx = Math . cos ( angle ) * repulsingForce ;
fy = Math . sin ( angle ) * repulsingForce ;
fy = Math . sin ( angle ) * repulsingForce ;
@ -16086,7 +16155,7 @@ Graph.prototype._calculateForces = function() {
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
edgeLength = edge . length ;
edgeLength = edge . length ;
// this implies that the edges between big clusters are longer
// this implies that the edges between big clusters are longer
edgeLength += ( edge . to . cluster_s ize + edge . from . cluster_s ize - 2 ) * this . constants . clustering . edgeGrowth ;
edgeLength += ( edge . to . clusterS ize + edge . from . clusterS ize - 2 ) * this . constants . clustering . edgeGrowth ;
length = Math . sqrt ( dx * dx + dy * dy ) ;
length = Math . sqrt ( dx * dx + dy * dy ) ;
angle = Math . atan2 ( dy , dx ) ;
angle = Math . atan2 ( dy , dx ) ;
@ -16100,7 +16169,7 @@ Graph.prototype._calculateForces = function() {
}
}
}
}
}
}
/ *
// TODO: re-implement repulsion of edges
// TODO: re-implement repulsion of edges
// repulsing forces between edges
// repulsing forces between edges
@ -16137,7 +16206,7 @@ Graph.prototype._calculateForces = function() {
edges [ l2 ] . to . _addForce ( fx , fy ) ;
edges [ l2 ] . to . _addForce ( fx , fy ) ;
}
}
}
}
* /
} ;
} ;