@ -4,8 +4,8 @@
*
*
* A dynamic , browser - based visualization library .
* A dynamic , browser - based visualization library .
*
*
* @ version 0.4 .0 - SNAPSHOT
* @ date 2014 - 01 - 17
* @ version @ @ version
* @ date @ @ date
*
*
* @ license
* @ license
* Copyright ( C ) 2011 - 2014 Almende B . V , http : //almende.com
* Copyright ( C ) 2011 - 2014 Almende B . V , http : //almende.com
@ -8794,6 +8794,7 @@ function Node(properties, imagelist, grouplist, constants) {
this . grouplist = grouplist ;
this . grouplist = grouplist ;
this . nodeProperties = properties ;
this . setProperties ( properties , constants ) ;
this . setProperties ( properties , constants ) ;
// creating the variables for clustering
// creating the variables for clustering
@ -9604,7 +9605,12 @@ Node.prototype.getTextSize = function(ctx) {
}
}
} ;
} ;
/ * *
* this is used to determine if a node is visible at all . this is used to determine when it needs to be drawn .
* there is a safety margin of 0.3 * width ;
*
* @ returns { boolean }
* /
Node . prototype . inArea = function ( ) {
Node . prototype . inArea = function ( ) {
if ( this . width !== undefined ) {
if ( this . width !== undefined ) {
return ( this . x + this . width * 0.8 >= this . canvasTopLeft . x &&
return ( this . x + this . width * 0.8 >= this . canvasTopLeft . x &&
@ -9617,6 +9623,10 @@ Node.prototype.inArea = function() {
}
}
}
}
/ * *
* checks if the core of the node is in the display area , this is used for opening clusters around zoom
* @ returns { boolean }
* /
Node . prototype . inView = function ( ) {
Node . prototype . inView = function ( ) {
return ( this . x >= this . canvasTopLeft . x &&
return ( this . x >= this . canvasTopLeft . x &&
this . x < this . canvasBottomRight . x &&
this . x < this . canvasBottomRight . x &&
@ -10518,6 +10528,156 @@ Images.prototype.load = function(url) {
return img ;
return img ;
} ;
} ;
function Universe ( ) {
this . universe = { } ;
this . activeUniverse = [ "default" ] ;
this . universe [ "activePockets" ] = { } ;
this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] = { "nodes" : { } , "edges" : { } , "nodeIndices" : [ ] } ;
this . universe [ "frozenPockets" ] = { } ;
this . universe [ "draw" ] = { } ;
this . nodeIndices = this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] [ "nodeIndices" ] ; // the node indices list is used to speed up the computation of the repulsion fields
}
Universe . prototype . _putDataInUniverse = function ( ) {
this . universe [ "activePockets" ] [ this . _universe ( ) ] . nodes = this . nodes ;
this . universe [ "activePockets" ] [ this . _universe ( ) ] . edges = this . edges ;
this . universe [ "activePockets" ] [ this . _universe ( ) ] . nodeIndices = this . nodeIndices ;
} ;
Universe . prototype . _switchToUniverse = function ( universeID ) {
this . nodeIndices = this . universe [ "activePockets" ] [ universeID ] [ "nodeIndices" ] ;
this . nodes = this . universe [ "activePockets" ] [ universeID ] [ "nodes" ] ;
this . edges = this . universe [ "activePockets" ] [ universeID ] [ "edges" ] ;
} ;
Universe . prototype . _loadActiveUniverse = function ( ) {
this . _switchToUniverse ( this . _universe ( ) ) ;
} ;
Universe . prototype . _universe = function ( ) {
return this . activeUniverse [ this . activeUniverse . length - 1 ] ;
} ;
Universe . prototype . _previousUniverse = function ( ) {
if ( this . activeUniverse . length > 1 ) {
return this . activeUniverse [ this . activeUniverse . length - 2 ] ;
}
else {
throw new TypeError ( 'there are not enough universes in the this.activeUniverse array.' ) ;
return "" ;
}
} ;
Universe . prototype . _setActiveUniverse = function ( newID ) {
this . activeUniverse . push ( newID ) ;
} ;
Universe . prototype . _forgetLastUniverse = function ( ) {
this . activeUniverse . pop ( ) ;
} ;
Universe . prototype . _createNewUniverse = function ( newID ) {
this . universe [ "activePockets" ] [ newID ] = { "nodes" : { } , "edges" : { } , "nodeIndices" : [ ] }
} ;
Universe . prototype . _deleteActiveUniverse = function ( universeID ) {
delete this . universe [ "activePockets" ] [ universeID ] ;
} ;
Universe . prototype . _deleteFrozenUniverse = function ( universeID ) {
delete this . universe [ "frozenPockets" ] [ universeID ] ;
} ;
Universe . prototype . _freezeUniverse = function ( universeID ) {
this . universe [ "frozenPockets" ] [ universeID ] = this . universe [ "activePockets" ] [ universeID ] ;
this . _deleteActiveUniverse ( universeID ) ;
} ;
Universe . prototype . _activateUniverse = function ( universeID ) {
this . universe [ "activePockets" ] [ universeID ] = this . universe [ "frozenPockets" ] [ universeID ] ;
this . _deleteFrozenUniverse ( universeID ) ;
} ;
Universe . prototype . _mergeThisWithFrozen = function ( universeID ) {
for ( var nodeID in this . nodes ) {
if ( this . nodes . hasOwnProperty ( nodeID ) ) {
this . universe [ "frozenPockets" ] [ universeID ] [ "nodes" ] [ nodeID ] = this . nodes [ nodeID ] ;
}
}
for ( var edgeID in this . edges ) {
if ( this . edges . hasOwnProperty ( edgeID ) ) {
this . universe [ "frozenPockets" ] [ universeID ] [ "edges" ] [ edgeID ] = this . edges [ edgeID ] ;
}
}
for ( var i = 0 ; i < this . nodeIndices . length ; i ++ ) {
this . universe [ "frozenPockets" ] [ universeID ] [ "edges" ] [ nodeIndices ] . push ( this . nodeIndices [ i ] ) ;
}
} ;
Universe . prototype . _collapseThisToSingleCluster = function ( ) {
this . clusterToFit ( 1 , false ) ;
} ;
Universe . prototype . _addUniverse = function ( node ) {
var universe = this . _universe ( ) ;
if ( this . universe [ 'activePockets' ] [ universe ] [ "nodes" ] . hasOwnProperty ( node . id ) ) {
console . log ( "the node is part of the active universe" ) ;
}
else {
console . log ( "I dont konw what the fuck happened!!" ) ;
}
delete this . nodes [ node . id ] ;
this . _freezeUniverse ( universe ) ;
this . _createNewUniverse ( node . id ) ;
this . _setActiveUniverse ( node . id ) ;
this . _switchToUniverse ( this . _universe ( ) ) ;
this . nodes [ node . id ] = node ;
//this.universe["draw"][node.id] = new Node(node.nodeProperties,node.imagelist,node.grouplist,this.constants);
} ;
Universe . prototype . _collapseUniverse = function ( ) {
var universe = this . _universe ( ) ;
if ( universe != "default" ) {
var isMovingBeforeClustering = this . moving ;
var previousUniverse = this . _previousUniverse ( ) ;
this . _collapseThisToSingleCluster ( ) ;
this . _mergeThisWithFrozen ( previousUniverse ) ;
this . _deleteActiveUniverse ( universe ) ;
this . _activateUniverse ( previousUniverse ) ;
this . _switchToUniverse ( previousUniverse ) ;
this . _forgetLastUniverse ( ) ;
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 ( ) ;
}
}
} ;
Universe . prototype . _doInAllActiveUniverses = function ( runFunction , arguments ) {
}
/ * *
/ * *
* @ constructor Cluster
* @ constructor Cluster
* Contains the cluster properties for the graph object
* Contains the cluster properties for the graph object
@ -10525,8 +10685,8 @@ Images.prototype.load = function(url) {
function Cluster ( ) {
function Cluster ( ) {
this . clusterSession = 0 ;
this . clusterSession = 0 ;
this . hubThreshold = 5 ;
this . hubThreshold = 5 ;
}
}
/ * *
/ * *
* This function can be called to open up a specific cluster .
* This function can be called to open up a specific cluster .
@ -10535,6 +10695,9 @@ function Cluster() {
* @ param node | Node object : cluster to open .
* @ param node | Node object : cluster to open .
* /
* /
Cluster . prototype . openCluster = function ( node ) {
Cluster . prototype . openCluster = function ( node ) {
if ( node . clusterSize > 15 ) {
this . _addUniverse ( node ) ;
}
var isMovingBeforeClustering = this . moving ;
var isMovingBeforeClustering = this . moving ;
this . _expandClusterNode ( node , false , true ) ;
this . _expandClusterNode ( node , false , true ) ;
@ -10594,6 +10757,11 @@ Cluster.prototype.updateClusters = function(zoomDirection,recursive,force) {
var isMovingBeforeClustering = this . moving ;
var isMovingBeforeClustering = this . moving ;
var amountOfNodes = this . nodeIndices . length ;
var amountOfNodes = this . nodeIndices . length ;
// on zoom out collapse the universe back to default
// if (this.previousScale > this.scale && zoomDirection == 0) {
// this._collapseUniverse();
// }
// check if we zoom in or out
// check if we zoom in or out
if ( this . previousScale > this . scale || zoomDirection == - 1 ) { // zoom out
if ( this . previousScale > this . scale || zoomDirection == - 1 ) { // zoom out
this . _formClusters ( force ) ;
this . _formClusters ( force ) ;
@ -11505,8 +11673,8 @@ function Graph (container, data, options) {
enableClustering : true ,
enableClustering : true ,
maxNumberOfNodes : 100 , // for automatic (initial) clustering
maxNumberOfNodes : 100 , // for automatic (initial) clustering
snakeThreshold : 0.5 , // maximum percentage of allowed snakes (long strings of connected nodes)
snakeThreshold : 0.5 , // maximum percentage of allowed snakes (long strings of connected nodes)
clusterLength : 30 , // threshold edge length for clusteringl
relativeOpenFactor : 0.5 , // if the width or height of a cluster takes up this much of the screen, open the cluster
clusterLength : 25 , // threshold edge length for clusteringl
relativeOpenFactor : 0.2 , // if the width or height of a cluster takes up this much of the screen, open the cluster
fontSizeMultiplier : 4 , // how much the cluster font size grows per node (in px)
fontSizeMultiplier : 4 , // how much the cluster font size grows per node (in px)
forceAmplification : 0.7 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
forceAmplification : 0.7 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
distanceAmplification : 0.3 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
distanceAmplification : 0.3 , // amount of clusterSize between two nodes multiply this value (+1) with the repulsion force
@ -11525,13 +11693,19 @@ function Graph (container, data, options) {
// call the constructor of the cluster object
// call the constructor of the cluster object
Cluster . call ( this ) ;
Cluster . call ( this ) ;
// call the universe constructor
Universe . call ( this ) ;
var graph = this ;
var graph = this ;
this . freezeSimulation = false ;
this . nodeIndices = [ ] ; // the node indices list is used to speed up the computation of the repulsion fields
this . tapTimer = 0 ;
this . pocketUniverse = { } ;
this . nodes = { } ; // object with Node objects
this . edges = { } ; // object with Edge objects
this . freezeSimulation = false ; // freeze the simulation
this . tapTimer = 0 ; // timer to detect doubleclick or double tap
this . nodeIndices = [ ] ; // array with all the indices of the nodes. Used to speed up forces calculation
this . nodes = { } ; // object with Node objects
this . edges = { } ; // object with Edge objects
this . canvasTopLeft = { "x" : 0 , "y" : 0 } ; // coordinates of the top left of the canvas. they will be set during calcForces.
this . canvasBottomRight = { "x" : 0 , "y" : 0 } ; // coordinates of the bottom right of the canvas. they will be set during calcForces
this . zoomCenter = { } ; // object with x and y elements used for determining the center of the zoom action
this . zoomCenter = { } ; // object with x and y elements used for determining the center of the zoom action
this . scale = 1 ; // defining the global scale variable in the constructor
this . scale = 1 ; // defining the global scale variable in the constructor
this . previousScale = this . scale ; // this is used to check if the zoom operation is zooming in or out
this . previousScale = this . scale ; // this is used to check if the zoom operation is zooming in or out
@ -11591,10 +11765,8 @@ function Graph (container, data, options) {
// apply options
// apply options
this . setOptions ( options ) ;
this . setOptions ( options ) ;
var disableStart = this . constants . clustering . enableClustering ;
// load data
this . setData ( data , disableStart ) ; //
// load data (the disable start variable will be the same as the enable clustering)
this . setData ( data , this . constants . clustering . enableClustering ) ; //
// zoom so all data will fit on the screen
// zoom so all data will fit on the screen
this . zoomToFit ( ) ;
this . zoomToFit ( ) ;
@ -11608,8 +11780,6 @@ function Graph (container, data, options) {
// this is called here because if clusterin is disabled, the start and stabilize are called in
// this is called here because if clusterin is disabled, the start and stabilize are called in
// the setData function.
// the setData function.
// find a stable position or start animating to a stable position
if ( this . stabilize ) {
if ( this . stabilize ) {
this . _doStabilize ( ) ;
this . _doStabilize ( ) ;
}
}
@ -11617,6 +11787,10 @@ function Graph (container, data, options) {
}
}
}
}
// add the universe functionality to this
Graph . prototype = Object . create ( Universe . prototype ) ;
/ * *
/ * *
* We add the functionality of the cluster object to the graph object
* We add the functionality of the cluster object to the graph object
* @ type { Cluster . prototype }
* @ type { Cluster . prototype }
@ -11632,11 +11806,11 @@ Graph.prototype = Object.create(Cluster.prototype);
Graph . prototype . clusterToFit = function ( maxNumberOfNodes , reposition ) {
Graph . prototype . clusterToFit = function ( maxNumberOfNodes , reposition ) {
var numberOfNodes = this . nodeIndices . length ;
var numberOfNodes = this . nodeIndices . length ;
var maxLevels = 10 ;
var maxLevels = 15 ;
var level = 0 ;
var level = 0 ;
// we first cluster the hubs, then we pull in the outliers, repeat
// we first cluster the hubs, then we pull in the outliers, repeat
while ( numberOfNodes >= maxNumberOfNodes && level < maxLevels ) {
while ( numberOfNodes > maxNumberOfNodes && level < maxLevels ) {
if ( level % 5 == 0 ) {
if ( level % 5 == 0 ) {
console . log ( "Aggregating Hubs @ level: " , level , ". Threshold:" , this . hubThreshold , "clusterSession" , this . clusterSession ) ;
console . log ( "Aggregating Hubs @ level: " , level , ". Threshold:" , this . hubThreshold , "clusterSession" , this . clusterSession ) ;
this . forceAggregateHubs ( ) ;
this . forceAggregateHubs ( ) ;
@ -11649,7 +11823,7 @@ Graph.prototype.clusterToFit = function(maxNumberOfNodes, reposition) {
level += 1 ;
level += 1 ;
}
}
// after the clustering we reposition the nodes to avoid initial chaos
// after the clustering we reposition the nodes to reduce the initial chaos
if ( level > 1 && reposition == true ) {
if ( level > 1 && reposition == true ) {
this . repositionNodes ( ) ;
this . repositionNodes ( ) ;
}
}
@ -11677,8 +11851,9 @@ Graph.prototype.zoomToFit = function() {
* @ private
* @ private
* /
* /
Graph . prototype . _updateNodeIndexList = function ( ) {
Graph . prototype . _updateNodeIndexList = function ( ) {
this . nodeIndices = [ ] ;
var universe = this . activeUniverse [ this . activeUniverse . length - 1 ] ;
this . universe [ "activePockets" ] [ universe ] [ "nodeIndices" ] = [ ] ;
this . nodeIndices = this . universe [ "activePockets" ] [ universe ] [ "nodeIndices" ] ;
for ( var idx in this . nodes ) {
for ( var idx in this . nodes ) {
if ( this . nodes . hasOwnProperty ( idx ) ) {
if ( this . nodes . hasOwnProperty ( idx ) ) {
this . nodeIndices . push ( idx ) ;
this . nodeIndices . push ( idx ) ;
@ -11724,6 +11899,8 @@ Graph.prototype.setData = function(data, disableStart) {
this . _setEdges ( data && data . edges ) ;
this . _setEdges ( data && data . edges ) ;
}
}
this . _putDataInUniverse ( ) ;
if ( ! disableStart ) {
if ( ! disableStart ) {
// find a stable position or start animating to a stable position
// find a stable position or start animating to a stable position
if ( this . stabilize ) {
if ( this . stabilize ) {
@ -11868,14 +12045,15 @@ Graph.prototype._create = function () {
this . hammer . on ( 'mousewheel' , me . _onMouseWheel . bind ( me ) ) ;
this . hammer . on ( 'mousewheel' , me . _onMouseWheel . bind ( me ) ) ;
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 ) ) ;
/ *
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 . updateClustersDefault . bind ( me ) ) ;
this . mouseTrap . bind ( "h" , this . updateClustersDefault . bind ( me ) ) ;
this . mouseTrap . bind ( "c" , this . _collapseUniverse . bind ( me ) ) ;
this . mouseTrap . bind ( "f" , this . toggleFreeze . bind ( me ) ) ;
this . mouseTrap . bind ( "f" , this . toggleFreeze . 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 ) ;
} ;
} ;
@ -12148,7 +12326,7 @@ Graph.prototype._zoom = function(scale, pointer) {
this . updateClustersDefault ( ) ;
this . updateClustersDefault ( ) ;
this . _redraw ( ) ;
this . _redraw ( ) ;
console . log ( "current zoomscale:" , this . scale ) ;
// console.log("current zoomscale:",this.scale);
return scale ;
return scale ;
} ;
} ;
@ -12433,16 +12611,36 @@ Graph.prototype._selectNodes = function(selection, append) {
* @ private
* @ private
* /
* /
Graph . prototype . _getNodesOverlappingWith = function ( obj ) {
Graph . prototype . _getNodesOverlappingWith = function ( obj ) {
var nodes = this . nodes ,
overlappingNodes = [ ] ;
var overlappingNodes = [ ] ;
var nodes ;
// search in all universes for nodes
for ( var universe in this . universe [ "activePockets" ] ) {
if ( this . universe [ "activePockets" ] . hasOwnProperty ( universe ) ) {
nodes = this . universe [ "activePockets" ] [ universe ] [ "nodes" ] ;
for ( var id in nodes ) {
if ( nodes . hasOwnProperty ( id ) ) {
if ( nodes [ id ] . isOverlappingWith ( obj ) ) {
overlappingNodes . push ( id ) ;
}
}
}
}
}
for ( var id in nodes ) {
if ( nodes . hasOwnProperty ( id ) ) {
if ( nodes [ id ] . isOverlappingWith ( obj ) ) {
overlappingNodes . push ( id ) ;
for ( var universe in this . universe [ "frozenPockets" ] ) {
if ( this . universe [ "frozenPockets" ] . hasOwnProperty ( universe ) ) {
nodes = this . universe [ "frozenPockets" ] [ universe ] [ "nodes" ] ;
for ( var id in nodes ) {
if ( nodes . hasOwnProperty ( id ) ) {
if ( nodes [ id ] . isOverlappingWith ( obj ) ) {
overlappingNodes . push ( id ) ;
}
}
}
}
}
}
}
}
this . nodes = this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] [ "nodes" ] ;
return overlappingNodes ;
return overlappingNodes ;
} ;
} ;
@ -12936,8 +13134,41 @@ Graph.prototype._redraw = function() {
ctx . translate ( this . translation . x , this . translation . y ) ;
ctx . translate ( this . translation . x , this . translation . y ) ;
ctx . scale ( this . scale , this . scale ) ;
ctx . scale ( this . scale , this . scale ) ;
this . _drawEdges ( ctx ) ;
this . _drawNodes ( ctx ) ;
// this._drawEdges(ctx);
// this._drawNodes(ctx);
for ( var universe in this . universe [ "activePockets" ] ) {
if ( this . universe [ "activePockets" ] . hasOwnProperty ( universe ) ) {
this . edges = this . universe [ "activePockets" ] [ universe ] [ "edges" ] ;
this . _drawEdges ( ctx ) ;
}
}
for ( var universe in this . universe [ "frozenPockets" ] ) {
if ( this . universe [ "frozenPockets" ] . hasOwnProperty ( universe ) ) {
this . edges = this . universe [ "frozenPockets" ] [ universe ] [ "edges" ] ;
this . _drawEdges ( ctx ) ;
}
}
for ( var universe in this . universe [ "activePockets" ] ) {
if ( this . universe [ "activePockets" ] . hasOwnProperty ( universe ) ) {
this . nodes = this . universe [ "activePockets" ] [ universe ] [ "nodes" ] ;
this . _drawNodes ( ctx ) ;
}
}
for ( var universe in this . universe [ "frozenPockets" ] ) {
if ( this . universe [ "frozenPockets" ] . hasOwnProperty ( universe ) ) {
this . nodes = this . universe [ "frozenPockets" ] [ universe ] [ "nodes" ] ;
this . _drawNodes ( ctx ) ;
}
}
this . nodeIndices = this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] [ "nodeIndices" ] ;
this . nodes = this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] [ "nodes" ] ;
this . edges = this . universe [ "activePockets" ] [ this . activeUniverse [ this . activeUniverse . length - 1 ] ] [ "edges" ] ;
// restore original scaling and translation
// restore original scaling and translation
ctx . restore ( ) ;
ctx . restore ( ) ;
@ -13046,15 +13277,9 @@ Graph.prototype._drawNodes = function(ctx) {
var nodes = this . nodes ;
var nodes = this . nodes ;
var selected = [ ] ;
var selected = [ ] ;
var canvasTopLeft = { "x" : ( 0 - this . translation . x ) / this . scale ,
"y" : ( 0 - this . translation . y ) / this . scale } ;
var canvasBottomRight = { "x" : ( this . frame . canvas . clientWidth - this . translation . x ) / this . scale ,
"y" : ( this . frame . canvas . clientHeight - this . translation . y ) / this . scale } ;
for ( var id in nodes ) {
for ( var id in nodes ) {
if ( nodes . hasOwnProperty ( id ) ) {
if ( nodes . hasOwnProperty ( id ) ) {
nodes [ id ] . setScaleAndPos ( this . scale , canvasTopLeft , canvasBottomRight ) ;
nodes [ id ] . setScaleAndPos ( this . scale , this . canvasTopLeft , this . canvasBottomRight ) ;
if ( nodes [ id ] . isSelected ( ) ) {
if ( nodes [ id ] . isSelected ( ) ) {
selected . push ( id ) ;
selected . push ( id ) ;
}
}
@ -13121,7 +13346,7 @@ Graph.prototype._doStabilize = function() {
* Forces are caused by : edges , repulsing forces between nodes , gravity
* Forces are caused by : edges , repulsing forces between nodes , gravity
* @ private
* @ private
* /
* /
Graph . prototype . _calculateForces = function ( ) {
Graph . prototype . _calculateForces = function ( nodes , edges ) {
// stop calculation if there is only one node
// stop calculation if there is only one node
if ( this . nodeIndices . length == 1 ) {
if ( this . nodeIndices . length == 1 ) {
this . nodes [ this . nodeIndices [ 0 ] ] . _setForce ( 0 , 0 ) ;
this . nodes [ this . nodeIndices [ 0 ] ] . _setForce ( 0 , 0 ) ;
@ -13132,6 +13357,13 @@ Graph.prototype._calculateForces = function() {
this . _calculateForces ( ) ;
this . _calculateForces ( ) ;
}
}
else {
else {
this . canvasTopLeft = { "x" : ( 0 - this . translation . x ) / this . scale ,
"y" : ( 0 - this . translation . y ) / this . scale } ;
this . canvasBottomRight = { "x" : ( this . frame . canvas . clientWidth - this . translation . x ) / this . scale ,
"y" : ( this . frame . canvas . clientHeight - this . translation . y ) / this . scale } ;
var centerPos = { "x" : 0.5 * ( this . canvasTopLeft . x + this . canvasBottomRight . x ) ,
"y" : 0.5 * ( this . canvasTopLeft . y + this . canvasBottomRight . y ) }
// create a local edge to the nodes and edges, that is faster
// create a local edge to the nodes and edges, that is faster
var dx , dy , angle , distance , fx , fy ,
var dx , dy , angle , distance , fx , fy ,
repulsingForce , springForce , length , edgeLength ,
repulsingForce , springForce , length , edgeLength ,
@ -13147,12 +13379,18 @@ Graph.prototype._calculateForces = function() {
var gravity = 0.08 ;
var gravity = 0.08 ;
for ( i = 0 ; i < this . nodeIndices . length ; i ++ ) {
for ( i = 0 ; i < this . nodeIndices . length ; i ++ ) {
node = nodes [ this . nodeIndices [ i ] ] ;
node = nodes [ this . nodeIndices [ i ] ] ;
dx = - node . x - this . translation . x + this . frame . canvas . clientWidth * 0.5 ;
dy = - node . y - this . translation . y + this . frame . canvas . clientHeight * 0.5 ;
if ( this . activeUniverse [ this . activeUniverse . length - 1 ] == "default" ) {
dx = - node . x + centerPos . x ;
dy = - node . y + centerPos . y ;
angle = Math . atan2 ( dy , dx ) ;
fx = Math . cos ( angle ) * gravity ;
fy = Math . sin ( angle ) * gravity ;
angle = Math . atan2 ( dy , dx ) ;
fx = Math . cos ( angle ) * gravity ;
fy = Math . sin ( angle ) * gravity ;
}
else {
fx = 0 ;
fy = 0 ;
}
node . _setForce ( fx , fy ) ;
node . _setForce ( fx , fy ) ;
node . updateDamping ( this . nodeIndices . length ) ;
node . updateDamping ( this . nodeIndices . length ) ;
@ -13249,25 +13487,28 @@ Graph.prototype._calculateForces = function() {
if ( edges . hasOwnProperty ( edgeID ) ) {
if ( edges . hasOwnProperty ( edgeID ) ) {
edge = edges [ edgeID ] ;
edge = edges [ edgeID ] ;
if ( edge . connected ) {
if ( edge . connected ) {
clusterSize = ( edge . to . clusterSize + edge . from . clusterSize - 2 ) ;
dx = ( edge . to . x - edge . from . x ) ;
dy = ( edge . to . y - edge . from . y ) ;
//edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
//edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
edgeLength = edge . length ;
// this implies that the edges between big clusters are longer
edgeLength += clusterSize * this . constants . clustering . edgeGrowth ;
length = Math . sqrt ( dx * dx + dy * dy ) ;
angle = Math . atan2 ( dy , dx ) ;
springForce = edge . stiffness * ( edgeLength - length ) ;
fx = Math . cos ( angle ) * springForce ;
fy = Math . sin ( angle ) * springForce ;
edge . from . _addForce ( - fx , - fy ) ;
edge . to . _addForce ( fx , fy ) ;
// only calculate forces if nodes are in the same universe
if ( this . nodes . hasOwnProperty ( edge . toId ) && this . nodes . hasOwnProperty ( edge . fromId ) ) {
clusterSize = ( edge . to . clusterSize + edge . from . clusterSize - 2 ) ;
dx = ( edge . to . x - edge . from . x ) ;
dy = ( edge . to . y - edge . from . y ) ;
//edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
//edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
//edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
edgeLength = edge . length ;
// this implies that the edges between big clusters are longer
edgeLength += clusterSize * this . constants . clustering . edgeGrowth ;
length = Math . sqrt ( dx * dx + dy * dy ) ;
angle = Math . atan2 ( dy , dx ) ;
springForce = edge . stiffness * ( edgeLength - length ) ;
fx = Math . cos ( angle ) * springForce ;
fy = Math . sin ( angle ) * springForce ;
edge . from . _addForce ( - fx , - fy ) ;
edge . to . _addForce ( fx , fy ) ;
}
}
}
}
}
}
}
@ -13348,15 +13589,28 @@ Graph.prototype._discreteStepNodes = function() {
/ * *
/ * *
* Start animating nodes and edges
* Start animating nodes and edges
* /
* /
Graph . prototype . start = function ( ) {
Graph . prototype . start = function ( ) {
if ( ! this . freezeSimulation ) {
if ( ! this . freezeSimulation ) {
if ( this . moving ) {
if ( this . moving ) {
var vmin = this . constants . minVelocity ;
/ *
this . _calculateForces ( ) ;
this . _calculateForces ( ) ;
this . _discreteStepNodes ( ) ;
this . _discreteStepNodes ( ) ;
var vmin = this . constants . minVelocity ;
this . moving = this . _isMoving ( vmin ) ;
this . moving = this . _isMoving ( vmin ) ;
* /
//console.log("no",this.nodes)
for ( var universe in this . universe [ "activePockets" ] ) {
if ( this . universe [ "activePockets" ] . hasOwnProperty ( universe ) ) {
this . _switchToUniverse ( universe ) ;
this . _calculateForces ( ) ;
this . _discreteStepNodes ( ) ;
this . moving = this . _isMoving ( vmin ) ;
}
}
this . _loadActiveUniverse ( ) ;
}
}
if ( this . moving ) {
if ( this . moving ) {
@ -13364,17 +13618,16 @@ Graph.prototype.start = function() {
if ( ! this . timer ) {
if ( ! this . timer ) {
var graph = this ;
var graph = this ;
this . timer = window . setTimeout ( function ( ) {
this . timer = window . setTimeout ( function ( ) {
var start , end , time ;
graph . timer = undefined ;
graph . timer = undefined ;
graph . start ( ) ;
graph . start ( ) ;
graph . start ( ) ;
graph . start ( ) ;
graph . _redraw ( ) ;
graph . _redraw ( ) ;
// start = window.performance.now();
// var start = window.performance.now();
// graph._redraw();
// graph._redraw();
// end = window.performance.now();
// time = end - start;
// var end = window.performance.now();
// var time = end - start;
// console.log('Drawing time: ' + time);
// console.log('Drawing time: ' + time);
} , this . refreshRate ) ;
} , this . refreshRate ) ;
}
}