Browse Source

Modified code structure, fixed barnes Hut bugs

css_transitions
Alex de Mulder 10 years ago
parent
commit
45692d8681
7 changed files with 87 additions and 160 deletions
  1. +43
    -79
      dist/vis.js
  2. +4
    -4
      src/graph/ClusterMixin.js
  3. +2
    -4
      src/graph/Edge.js
  4. +12
    -5
      src/graph/Graph.js
  5. +2
    -2
      src/graph/Node.js
  6. +22
    -65
      src/graph/physicsMixin.js
  7. +2
    -1
      src/util.js

+ 43
- 79
dist/vis.js View File

@ -1136,6 +1136,8 @@ util.hexToHSV = function hexToHSV(hex) {
var rgb = util.hexToRGB(hex); var rgb = util.hexToRGB(hex);
return util.RGBToHSV(rgb.r,rgb.g,rgb.b); return util.RGBToHSV(rgb.r,rgb.g,rgb.b);
} }
/** /**
* Event listener (singleton) * Event listener (singleton)
*/ */
@ -10075,9 +10077,9 @@ Node.prototype.clearVelocity = function() {
*/ */
Node.prototype.updateVelocity = function(massBeforeClustering) { Node.prototype.updateVelocity = function(massBeforeClustering) {
var energyBefore = this.vx * this.vx * massBeforeClustering; var energyBefore = this.vx * this.vx * massBeforeClustering;
this.vx = Math.sqrt(energyBefore/this.mass);
this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass);
energyBefore = this.vy * this.vy * massBeforeClustering; energyBefore = this.vy * this.vy * massBeforeClustering;
this.vy = Math.sqrt(energyBefore/this.mass);
this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass);
}; };
@ -10114,7 +10116,7 @@ function Edge (properties, graph, constants) {
this.title = undefined; this.title = undefined;
this.width = constants.edges.width; this.width = constants.edges.width;
this.value = undefined; this.value = undefined;
this.length = constants.edges.length;
this.length = constants.physics.springLength;
this.selected = false; this.selected = false;
this.from = null; // a node this.from = null; // a node
@ -10132,13 +10134,12 @@ function Edge (properties, graph, constants) {
// 2012-08-08 // 2012-08-08
this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
this.stiffness = undefined; // depends on the length of the edge
this.springConstant = constants.physics.springConstant;
this.color = constants.edges.color; this.color = constants.edges.color;
this.widthFixed = false; this.widthFixed = false;
this.lengthFixed = false; this.lengthFixed = false;
this.setProperties(properties, constants); this.setProperties(properties, constants);
} }
/** /**
@ -10186,7 +10187,6 @@ Edge.prototype.setProperties = function(properties, constants) {
this.widthFixed = this.widthFixed || (properties.width !== undefined); this.widthFixed = this.widthFixed || (properties.width !== undefined);
this.lengthFixed = this.lengthFixed || (properties.length !== undefined); this.lengthFixed = this.lengthFixed || (properties.length !== undefined);
this.stiffness = 1 / this.length;
// set draw method based on style // set draw method based on style
switch (this.style) { switch (this.style) {
@ -10966,7 +10966,7 @@ var physicsMixin = {
// we now start the force calculation // we now start the force calculation
this._calculateForcesBarnesHut(); this._calculateForcesBarnesHut();
// this._calculateForcesOriginal();
// this._calculateForcesRepulsion();
} }
}, },
@ -10976,23 +10976,23 @@ var physicsMixin = {
* Forces are caused by: edges, repulsing forces between nodes, gravity * Forces are caused by: edges, repulsing forces between nodes, gravity
* @private * @private
*/ */
_calculateForcesOriginal : function() {
_calculateForcesRepulsion : function() {
// Gravity is required to keep separated groups from floating off // Gravity is required to keep separated groups from floating off
// the forces are reset to zero in this loop by using _setForce instead // the forces are reset to zero in this loop by using _setForce instead
// of _addForce // of _addForce
// var startTimeAll = Date.now(); // var startTimeAll = Date.now();
this._calculateGravitationalForces(1);
this._applyCentralGravity();
// var startTimeRepulsion = Date.now(); // var startTimeRepulsion = Date.now();
// All nodes repel eachother. // All nodes repel eachother.
this._calculateRepulsionForces();
this._applyNodeRepulsion();
// var endTimeRepulsion = Date.now(); // var endTimeRepulsion = Date.now();
// the edges are strings // the edges are strings
this._calculateSpringForces(1);
this._applySpringForces();
// var endTimeAll = Date.now(); // var endTimeAll = Date.now();
@ -11012,7 +11012,7 @@ var physicsMixin = {
// var startTimeAll = Date.now(); // var startTimeAll = Date.now();
this._clearForces();
this._applyCentralGravity();
// var startTimeRepulsion = Date.now(); // var startTimeRepulsion = Date.now();
// All nodes repel eachother. // All nodes repel eachother.
@ -11021,7 +11021,7 @@ var physicsMixin = {
// var endTimeRepulsion = Date.now(); // var endTimeRepulsion = Date.now();
// the edges are strings // the edges are strings
this._calculateSpringForces(1);
this._applySpringForces();
// var endTimeAll = Date.now(); // var endTimeAll = Date.now();
@ -11041,10 +11041,10 @@ var physicsMixin = {
} }
}, },
_calculateGravitationalForces : function(boost) {
_applyCentralGravity : function() {
var dx, dy, angle, fx, fy, node, i; var dx, dy, angle, fx, fy, node, i;
var nodes = this.nodes; var nodes = this.nodes;
var gravity = 0.08 * boost;
var gravity = this.constants.physics.centralGravity;
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]];
@ -11066,13 +11066,13 @@ var physicsMixin = {
} }
}, },
_calculateRepulsionForces : function() {
_applyNodeRepulsion : function() {
var dx, dy, angle, distance, fx, fy, clusterSize, var dx, dy, angle, distance, fx, fy, clusterSize,
repulsingForce, node1, node2, i, j; repulsingForce, node1, node2, i, j;
var nodes = this.nodes; var nodes = this.nodes;
// approximation constants // approximation constants
var a_base = (-2/3);
var a_base = -2/3;
var b = 4/3; var b = 4/3;
// repulsing forces between nodes // repulsing forces between nodes
@ -11116,7 +11116,7 @@ var physicsMixin = {
} }
}, },
_calculateSpringForces : function(boost) {
_applySpringForces : function() {
var dx, dy, angle, fx, fy, springForce, length, edgeLength, edge, edgeId, clusterSize; var dx, dy, angle, fx, fy, springForce, length, edgeLength, edge, edgeId, clusterSize;
var edges = this.edges; var edges = this.edges;
@ -11138,7 +11138,7 @@ var physicsMixin = {
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
angle = Math.atan2(dy, dx); angle = Math.atan2(dy, dx);
springForce = 0.02 * (edgeLength - length) * boost;
springForce = edge.springConstant * (edgeLength - length);
fx = Math.cos(angle) * springForce; fx = Math.cos(angle) * springForce;
fy = Math.sin(angle) * springForce; fy = Math.sin(angle) * springForce;
@ -11161,10 +11161,6 @@ var physicsMixin = {
var barnesHutTree = this.barnesHutTree; var barnesHutTree = this.barnesHutTree;
this.theta = 0.2;
this.graviationalConstant = -10000;
// place the nodes one by one recursively // place the nodes one by one recursively
for (var i = 0; i < nodeCount; i++) { for (var i = 0; i < nodeCount; i++) {
node = nodes[nodeIndices[i]]; node = nodes[nodeIndices[i]];
@ -11189,8 +11185,9 @@ var physicsMixin = {
if (distance > 0) { // distance is 0 if it looks to apply a force on itself. if (distance > 0) { // distance is 0 if it looks to apply a force on itself.
// we invert it here because we need the inverted distance for the force calculation too. // we invert it here because we need the inverted distance for the force calculation too.
var distanceInv = 1/distance; var distanceInv = 1/distance;
// BarnesHut condition // BarnesHut condition
if (parentBranch.size * distanceInv > this.theta) {
if (parentBranch.size * distanceInv > this.constants.physics.barnesHutTheta) {
// Did not pass the condition, go into children if available // Did not pass the condition, go into children if available
if (parentBranch.childrenCount == 4) { if (parentBranch.childrenCount == 4) {
this._getForceContribution(parentBranch.children.NW,node); this._getForceContribution(parentBranch.children.NW,node);
@ -11199,7 +11196,9 @@ var physicsMixin = {
this._getForceContribution(parentBranch.children.SE,node); this._getForceContribution(parentBranch.children.SE,node);
} }
else { // parentBranch must have only one node, if it was empty we wouldnt be here else { // parentBranch must have only one node, if it was empty we wouldnt be here
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv);
if (parentBranch.children.data.id != node.id) { // if it is not self
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv);
}
} }
} }
else { else {
@ -11211,7 +11210,7 @@ var physicsMixin = {
_getForceOnNode : function(parentBranch, node, dx ,dy, distanceInv) { _getForceOnNode : function(parentBranch, node, dx ,dy, distanceInv) {
// even if the parentBranch only has one node, its Center of Mass is at the right place (the node in this case). // even if the parentBranch only has one node, its Center of Mass is at the right place (the node in this case).
var gravityForce = this.graviationalConstant * parentBranch.mass * node.mass * distanceInv * distanceInv;
var gravityForce = this.constants.physics.nodeGravityConstant * parentBranch.mass * node.mass * distanceInv * distanceInv;
var angle = Math.atan2(dy, dx); var angle = Math.atan2(dy, dx);
var fx = Math.cos(angle) * gravityForce; var fx = Math.cos(angle) * gravityForce;
var fy = Math.sin(angle) * gravityForce; var fy = Math.sin(angle) * gravityForce;
@ -11242,7 +11241,7 @@ var physicsMixin = {
// make the range a square // make the range a square
var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y
if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize
else {minX += 0.5 * sizeDiff; maxY -= 0.5 * sizeDiff;} // xSize < ySize
else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize
// construct the barnesHutTree // construct the barnesHutTree
@ -11388,11 +11387,10 @@ var physicsMixin = {
}; };
}, },
_drawTree : function(ctx,color) { _drawTree : function(ctx,color) {
if (this.barnesHutTree !== undefined) { if (this.barnesHutTree !== undefined) {
ctx.lineWidth = 2;
ctx.lineWidth = 1;
this._drawBranch(this.barnesHutTree.root,ctx,color); this._drawBranch(this.barnesHutTree.root,ctx,color);
} }
@ -11437,48 +11435,7 @@ var physicsMixin = {
} }
*/ */
} }
}; };
function echo() {
switch (arguments.length) {
case 1:
echoN1(arguments[0]); break;
case 2:
echoN2(arguments[0],arguments[1]); break;
case 3:
echoN3(arguments[0],arguments[1],arguments[2]); break;
}
}
function echoN1(message) {
console.log(message);
}
function echoN2(message1,message2) {
console.log(message1,message2);
}
function echoN3(message1,message2,message3) {
console.log(message1,message2,message3);
}
/** /**
* Created by Alex on 2/4/14. * Created by Alex on 2/4/14.
*/ */
@ -12851,8 +12808,8 @@ var ClusterMixin = {
parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; 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.3 * (0.5 - Math.random()) * parentNode.clusterSize;
childNode.y = parentNode.y + this.constants.edges.length * 0.3 * (0.5 - Math.random()) * parentNode.clusterSize;
childNode.x = parentNode.x + this.constants.edges.length * (0.1 + 0.3 * (0.5 - Math.random()) * parentNode.clusterSize);
childNode.y = parentNode.y + this.constants.edges.length * (0.1 + 0.3 * (0.5 - Math.random()) * parentNode.clusterSize);
// remove node from the list // remove node from the list
delete parentNode.containedNodes[containedNodeId]; delete parentNode.containedNodes[containedNodeId];
@ -13110,7 +13067,7 @@ var ClusterMixin = {
// update the properties of the child and parent // update the properties of the child and parent
var massBefore = parentNode.mass; var massBefore = parentNode.mass;
childNode.clusterSession = this.clusterSession; childNode.clusterSession = this.clusterSession;
parentNode.mass += this.constants.clustering.massTransferCoefficient * childNode.mass;
parentNode.mass += childNode.mass;
parentNode.clusterSize += childNode.clusterSize; parentNode.clusterSize += childNode.clusterSize;
parentNode.fontSize += this.constants.clustering.fontSizeMultiplier * childNode.clusterSize; parentNode.fontSize += this.constants.clustering.fontSizeMultiplier * childNode.clusterSize;
@ -13423,7 +13380,7 @@ var ClusterMixin = {
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]];
if (!node.isFixed()) { if (!node.isFixed()) {
var radius = this.constants.edges.length * (1 + 0.6*node.clusterSize);
var radius = this.constants.physics.springLength * (1 + 0.6*node.clusterSize);
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);
@ -14474,6 +14431,13 @@ function Graph (container, data, options) {
altLength: undefined altLength: undefined
} }
}, },
physics: {
springConstant:0.05,
springLength: 100,
centralGravity: 0.1,
nodeGravityConstant: -10000,
barnesHutTheta: 0.2
},
clustering: { // Per Node in Cluster = PNiC clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering. enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
@ -14490,8 +14454,7 @@ function Graph (container, data, options) {
nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster. nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster.
height: 10, // (px PNiC) | growth of the height per node in cluster. height: 10, // (px PNiC) | growth of the height per node in cluster.
radius: 10}, // (px PNiC) | growth of the radius per node in cluster. radius: 10}, // (px PNiC) | growth of the radius per node in cluster.
activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open.
massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass
activeAreaBoxSize: 100 // (px) | box area around the curser where clusters are popped open.
}, },
navigation: { navigation: {
enabled: false, enabled: false,
@ -14853,7 +14816,7 @@ Graph.prototype.setOptions = function (options) {
if (options.edges.length !== undefined && if (options.edges.length !== undefined &&
options.nodes && options.nodes.distance === undefined) { options.nodes && options.nodes.distance === undefined) {
this.constants.edges.length = options.edges.length;
this.constants.physics.springLength = options.edges.length;
this.constants.nodes.distance = options.edges.length * 1.25; this.constants.nodes.distance = options.edges.length * 1.25;
} }
@ -15905,7 +15868,7 @@ Graph.prototype._redraw = function() {
this._doInAllSectors("_drawEdges",ctx); this._doInAllSectors("_drawEdges",ctx);
this._doInAllSectors("_drawNodes",ctx,true); this._doInAllSectors("_drawNodes",ctx,true);
//this._drawTree(ctx,"#F00F0F");
this._drawTree(ctx,"#F00F0F");
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); ctx.restore();
@ -16113,7 +16076,7 @@ Graph.prototype._isMoving = function(vmin) {
* @private * @private
*/ */
Graph.prototype._discreteStepNodes = function() { Graph.prototype._discreteStepNodes = function() {
var interval = 1.0;
var interval = 0.5;
var nodes = this.nodes; var nodes = this.nodes;
this.constants.maxVelocity = 30; this.constants.maxVelocity = 30;
@ -16172,6 +16135,7 @@ Graph.prototype.start = function() {
graph._zoom(graph.scale*(1 + graph.zoomIncrement), center); graph._zoom(graph.scale*(1 + graph.zoomIncrement), center);
} }
graph.start();
graph.start(); graph.start();
graph._redraw(); graph._redraw();

+ 4
- 4
src/graph/ClusterMixin.js View File

@ -358,8 +358,8 @@ var ClusterMixin = {
parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; 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.3 * (0.5 - Math.random()) * parentNode.clusterSize;
childNode.y = parentNode.y + this.constants.edges.length * 0.3 * (0.5 - Math.random()) * parentNode.clusterSize;
childNode.x = parentNode.x + this.constants.edges.length * (0.1 + 0.3 * (0.5 - Math.random()) * parentNode.clusterSize);
childNode.y = parentNode.y + this.constants.edges.length * (0.1 + 0.3 * (0.5 - Math.random()) * parentNode.clusterSize);
// remove node from the list // remove node from the list
delete parentNode.containedNodes[containedNodeId]; delete parentNode.containedNodes[containedNodeId];
@ -617,7 +617,7 @@ var ClusterMixin = {
// update the properties of the child and parent // update the properties of the child and parent
var massBefore = parentNode.mass; var massBefore = parentNode.mass;
childNode.clusterSession = this.clusterSession; childNode.clusterSession = this.clusterSession;
parentNode.mass += this.constants.clustering.massTransferCoefficient * childNode.mass;
parentNode.mass += childNode.mass;
parentNode.clusterSize += childNode.clusterSize; parentNode.clusterSize += childNode.clusterSize;
parentNode.fontSize += this.constants.clustering.fontSizeMultiplier * childNode.clusterSize; parentNode.fontSize += this.constants.clustering.fontSizeMultiplier * childNode.clusterSize;
@ -930,7 +930,7 @@ var ClusterMixin = {
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]];
if (!node.isFixed()) { if (!node.isFixed()) {
var radius = this.constants.edges.length * (1 + 0.6*node.clusterSize);
var radius = this.constants.physics.springLength * (1 + 0.6*node.clusterSize);
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);

+ 2
- 4
src/graph/Edge.js View File

@ -31,7 +31,7 @@ function Edge (properties, graph, constants) {
this.title = undefined; this.title = undefined;
this.width = constants.edges.width; this.width = constants.edges.width;
this.value = undefined; this.value = undefined;
this.length = constants.edges.length;
this.length = constants.physics.springLength;
this.selected = false; this.selected = false;
this.from = null; // a node this.from = null; // a node
@ -49,13 +49,12 @@ function Edge (properties, graph, constants) {
// 2012-08-08 // 2012-08-08
this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
this.stiffness = undefined; // depends on the length of the edge
this.springConstant = constants.physics.springConstant;
this.color = constants.edges.color; this.color = constants.edges.color;
this.widthFixed = false; this.widthFixed = false;
this.lengthFixed = false; this.lengthFixed = false;
this.setProperties(properties, constants); this.setProperties(properties, constants);
} }
/** /**
@ -103,7 +102,6 @@ Edge.prototype.setProperties = function(properties, constants) {
this.widthFixed = this.widthFixed || (properties.width !== undefined); this.widthFixed = this.widthFixed || (properties.width !== undefined);
this.lengthFixed = this.lengthFixed || (properties.length !== undefined); this.lengthFixed = this.lengthFixed || (properties.length !== undefined);
this.stiffness = 1 / this.length;
// set draw method based on style // set draw method based on style
switch (this.style) { switch (this.style) {

+ 12
- 5
src/graph/Graph.js View File

@ -65,6 +65,13 @@ function Graph (container, data, options) {
altLength: undefined altLength: undefined
} }
}, },
physics: {
springConstant:0.05,
springLength: 100,
centralGravity: 0.1,
nodeGravityConstant: -10000,
barnesHutTheta: 0.2
},
clustering: { // Per Node in Cluster = PNiC clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering. enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
@ -81,8 +88,7 @@ function Graph (container, data, options) {
nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster. nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster.
height: 10, // (px PNiC) | growth of the height per node in cluster. height: 10, // (px PNiC) | growth of the height per node in cluster.
radius: 10}, // (px PNiC) | growth of the radius per node in cluster. radius: 10}, // (px PNiC) | growth of the radius per node in cluster.
activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open.
massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass
activeAreaBoxSize: 100 // (px) | box area around the curser where clusters are popped open.
}, },
navigation: { navigation: {
enabled: false, enabled: false,
@ -444,7 +450,7 @@ Graph.prototype.setOptions = function (options) {
if (options.edges.length !== undefined && if (options.edges.length !== undefined &&
options.nodes && options.nodes.distance === undefined) { options.nodes && options.nodes.distance === undefined) {
this.constants.edges.length = options.edges.length;
this.constants.physics.springLength = options.edges.length;
this.constants.nodes.distance = options.edges.length * 1.25; this.constants.nodes.distance = options.edges.length * 1.25;
} }
@ -1496,7 +1502,7 @@ Graph.prototype._redraw = function() {
this._doInAllSectors("_drawEdges",ctx); this._doInAllSectors("_drawEdges",ctx);
this._doInAllSectors("_drawNodes",ctx,true); this._doInAllSectors("_drawNodes",ctx,true);
//this._drawTree(ctx,"#F00F0F");
this._drawTree(ctx,"#F00F0F");
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); ctx.restore();
@ -1704,7 +1710,7 @@ Graph.prototype._isMoving = function(vmin) {
* @private * @private
*/ */
Graph.prototype._discreteStepNodes = function() { Graph.prototype._discreteStepNodes = function() {
var interval = 1.0;
var interval = 0.5;
var nodes = this.nodes; var nodes = this.nodes;
this.constants.maxVelocity = 30; this.constants.maxVelocity = 30;
@ -1763,6 +1769,7 @@ Graph.prototype.start = function() {
graph._zoom(graph.scale*(1 + graph.zoomIncrement), center); graph._zoom(graph.scale*(1 + graph.zoomIncrement), center);
} }
graph.start();
graph.start(); graph.start();
graph._redraw(); graph._redraw();

+ 2
- 2
src/graph/Node.js View File

@ -993,8 +993,8 @@ Node.prototype.clearVelocity = function() {
*/ */
Node.prototype.updateVelocity = function(massBeforeClustering) { Node.prototype.updateVelocity = function(massBeforeClustering) {
var energyBefore = this.vx * this.vx * massBeforeClustering; var energyBefore = this.vx * this.vx * massBeforeClustering;
this.vx = Math.sqrt(energyBefore/this.mass);
this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass);
energyBefore = this.vy * this.vy * massBeforeClustering; energyBefore = this.vy * this.vy * massBeforeClustering;
this.vy = Math.sqrt(energyBefore/this.mass);
this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass);
}; };

+ 22
- 65
src/graph/physicsMixin.js View File

@ -24,7 +24,7 @@ var physicsMixin = {
// we now start the force calculation // we now start the force calculation
this._calculateForcesBarnesHut(); this._calculateForcesBarnesHut();
// this._calculateForcesOriginal();
// this._calculateForcesRepulsion();
} }
}, },
@ -34,23 +34,23 @@ var physicsMixin = {
* Forces are caused by: edges, repulsing forces between nodes, gravity * Forces are caused by: edges, repulsing forces between nodes, gravity
* @private * @private
*/ */
_calculateForcesOriginal : function() {
_calculateForcesRepulsion : function() {
// Gravity is required to keep separated groups from floating off // Gravity is required to keep separated groups from floating off
// the forces are reset to zero in this loop by using _setForce instead // the forces are reset to zero in this loop by using _setForce instead
// of _addForce // of _addForce
// var startTimeAll = Date.now(); // var startTimeAll = Date.now();
this._calculateGravitationalForces(1);
this._applyCentralGravity();
// var startTimeRepulsion = Date.now(); // var startTimeRepulsion = Date.now();
// All nodes repel eachother. // All nodes repel eachother.
this._calculateRepulsionForces();
this._applyNodeRepulsion();
// var endTimeRepulsion = Date.now(); // var endTimeRepulsion = Date.now();
// the edges are strings // the edges are strings
this._calculateSpringForces(1);
this._applySpringForces();
// var endTimeAll = Date.now(); // var endTimeAll = Date.now();
@ -70,7 +70,7 @@ var physicsMixin = {
// var startTimeAll = Date.now(); // var startTimeAll = Date.now();
this._clearForces();
this._applyCentralGravity();
// var startTimeRepulsion = Date.now(); // var startTimeRepulsion = Date.now();
// All nodes repel eachother. // All nodes repel eachother.
@ -79,7 +79,7 @@ var physicsMixin = {
// var endTimeRepulsion = Date.now(); // var endTimeRepulsion = Date.now();
// the edges are strings // the edges are strings
this._calculateSpringForces(1);
this._applySpringForces();
// var endTimeAll = Date.now(); // var endTimeAll = Date.now();
@ -99,10 +99,10 @@ var physicsMixin = {
} }
}, },
_calculateGravitationalForces : function(boost) {
_applyCentralGravity : function() {
var dx, dy, angle, fx, fy, node, i; var dx, dy, angle, fx, fy, node, i;
var nodes = this.nodes; var nodes = this.nodes;
var gravity = 0.08 * boost;
var gravity = this.constants.physics.centralGravity;
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]];
@ -124,13 +124,13 @@ var physicsMixin = {
} }
}, },
_calculateRepulsionForces : function() {
_applyNodeRepulsion : function() {
var dx, dy, angle, distance, fx, fy, clusterSize, var dx, dy, angle, distance, fx, fy, clusterSize,
repulsingForce, node1, node2, i, j; repulsingForce, node1, node2, i, j;
var nodes = this.nodes; var nodes = this.nodes;
// approximation constants // approximation constants
var a_base = (-2/3);
var a_base = -2/3;
var b = 4/3; var b = 4/3;
// repulsing forces between nodes // repulsing forces between nodes
@ -174,7 +174,7 @@ var physicsMixin = {
} }
}, },
_calculateSpringForces : function(boost) {
_applySpringForces : function() {
var dx, dy, angle, fx, fy, springForce, length, edgeLength, edge, edgeId, clusterSize; var dx, dy, angle, fx, fy, springForce, length, edgeLength, edge, edgeId, clusterSize;
var edges = this.edges; var edges = this.edges;
@ -196,7 +196,7 @@ var physicsMixin = {
length = Math.sqrt(dx * dx + dy * dy); length = Math.sqrt(dx * dx + dy * dy);
angle = Math.atan2(dy, dx); angle = Math.atan2(dy, dx);
springForce = 0.02 * (edgeLength - length) * boost;
springForce = edge.springConstant * (edgeLength - length);
fx = Math.cos(angle) * springForce; fx = Math.cos(angle) * springForce;
fy = Math.sin(angle) * springForce; fy = Math.sin(angle) * springForce;
@ -219,10 +219,6 @@ var physicsMixin = {
var barnesHutTree = this.barnesHutTree; var barnesHutTree = this.barnesHutTree;
this.theta = 0.2;
this.graviationalConstant = -10000;
// place the nodes one by one recursively // place the nodes one by one recursively
for (var i = 0; i < nodeCount; i++) { for (var i = 0; i < nodeCount; i++) {
node = nodes[nodeIndices[i]]; node = nodes[nodeIndices[i]];
@ -247,8 +243,9 @@ var physicsMixin = {
if (distance > 0) { // distance is 0 if it looks to apply a force on itself. if (distance > 0) { // distance is 0 if it looks to apply a force on itself.
// we invert it here because we need the inverted distance for the force calculation too. // we invert it here because we need the inverted distance for the force calculation too.
var distanceInv = 1/distance; var distanceInv = 1/distance;
// BarnesHut condition // BarnesHut condition
if (parentBranch.size * distanceInv > this.theta) {
if (parentBranch.size * distanceInv > this.constants.physics.barnesHutTheta) {
// Did not pass the condition, go into children if available // Did not pass the condition, go into children if available
if (parentBranch.childrenCount == 4) { if (parentBranch.childrenCount == 4) {
this._getForceContribution(parentBranch.children.NW,node); this._getForceContribution(parentBranch.children.NW,node);
@ -257,7 +254,9 @@ var physicsMixin = {
this._getForceContribution(parentBranch.children.SE,node); this._getForceContribution(parentBranch.children.SE,node);
} }
else { // parentBranch must have only one node, if it was empty we wouldnt be here else { // parentBranch must have only one node, if it was empty we wouldnt be here
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv);
if (parentBranch.children.data.id != node.id) { // if it is not self
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv);
}
} }
} }
else { else {
@ -269,7 +268,7 @@ var physicsMixin = {
_getForceOnNode : function(parentBranch, node, dx ,dy, distanceInv) { _getForceOnNode : function(parentBranch, node, dx ,dy, distanceInv) {
// even if the parentBranch only has one node, its Center of Mass is at the right place (the node in this case). // even if the parentBranch only has one node, its Center of Mass is at the right place (the node in this case).
var gravityForce = this.graviationalConstant * parentBranch.mass * node.mass * distanceInv * distanceInv;
var gravityForce = this.constants.physics.nodeGravityConstant * parentBranch.mass * node.mass * distanceInv * distanceInv;
var angle = Math.atan2(dy, dx); var angle = Math.atan2(dy, dx);
var fx = Math.cos(angle) * gravityForce; var fx = Math.cos(angle) * gravityForce;
var fy = Math.sin(angle) * gravityForce; var fy = Math.sin(angle) * gravityForce;
@ -300,7 +299,7 @@ var physicsMixin = {
// make the range a square // make the range a square
var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y
if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize
else {minX += 0.5 * sizeDiff; maxY -= 0.5 * sizeDiff;} // xSize < ySize
else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize
// construct the barnesHutTree // construct the barnesHutTree
@ -446,11 +445,10 @@ var physicsMixin = {
}; };
}, },
_drawTree : function(ctx,color) { _drawTree : function(ctx,color) {
if (this.barnesHutTree !== undefined) { if (this.barnesHutTree !== undefined) {
ctx.lineWidth = 2;
ctx.lineWidth = 1;
this._drawBranch(this.barnesHutTree.root,ctx,color); this._drawBranch(this.barnesHutTree.root,ctx,color);
} }
@ -495,45 +493,4 @@ var physicsMixin = {
} }
*/ */
} }
};
function echo() {
switch (arguments.length) {
case 1:
echoN1(arguments[0]); break;
case 2:
echoN2(arguments[0],arguments[1]); break;
case 3:
echoN3(arguments[0],arguments[1],arguments[2]); break;
}
}
function echoN1(message) {
console.log(message);
}
function echoN2(message1,message2) {
console.log(message1,message2);
}
function echoN3(message1,message2,message3) {
console.log(message1,message2,message3);
}
};

+ 2
- 1
src/util.js View File

@ -824,4 +824,5 @@ util.HSVToHex = function HSVToHex(h,s,v) {
util.hexToHSV = function hexToHSV(hex) { util.hexToHSV = function hexToHSV(hex) {
var rgb = util.hexToRGB(hex); var rgb = util.hexToRGB(hex);
return util.RGBToHSV(rgb.r,rgb.g,rgb.b); return util.RGBToHSV(rgb.r,rgb.g,rgb.b);
}
}

Loading…
Cancel
Save