
function Vector(x,y){this.x=x;this.y=y;}
Vector.random=function(){return new Vector((Math.random()*5)-10,(Math.random()*5)-10);}
Vector.angle=function(v1,v2){var artTan2Test=Math.atan2(v1.x-v2.x,v1.y-v2.y)
var v1N=v2.angleFromNorth();var v2N=v1.angleFromNorth();var diff=v2N>v1N?v2N-v1N:(2.0*Math.PI)+(v2N-v1N);return diff;}
Vector.prototype.angleFromNorth=function(){if(this.x>0)
return Math.acos(this.y);else
return((2*Math.PI)-Math.acos(this.y));};Vector.prototype.getX=function(){return this.x;};Vector.prototype.getY=function(){return this.y;};Vector.prototype.add=function(v2){return new Vector(this.x+v2.x,this.y+v2.y);};Vector.prototype.subtract=function(v2){return new Vector(this.x-v2.x,this.y-v2.y);};Vector.prototype.multiply=function(n){return new Vector(this.x*n,this.y*n);};Vector.prototype.divide=function(n){return new Vector(this.x/n,this.y/n);};Vector.prototype.magnitude=function(){return Math.sqrt(this.x*this.x+this.y*this.y);};Vector.prototype.normalise=function(){return this.divide(this.magnitude());};Vector.prototype.distance=function(v2){return Math.sqrt(Math.pow((v2.x-this.x),2.0)+Math.pow((v2.y-this.y),2.0));};function Node(position,width,height,mass,userData){this.position=position;this.width=width;this.height=height;this.mass=mass;this.userData=userData;this.inEdges=[];this.outEdges=[];this.velocity=new Vector(0.0,0.0);this.force=new Vector(0.0,0.0);this.uiElement=null;this.inManualMove=false;}
Node.prototype.getCenter=function(){return new Vector(this.position.getX()+(this.width>>1),this.position.getY()+(this.height>>1));};Node.prototype.degree=function(){return(this.inEdges.length+this.outEdges.length);};Node.prototype.getMass=function(){return(this.mass+this.degree());};Node.prototype.applyForce=function(f){if(!this.inManualMove)
this.force=this.force.add(f.divide(this.mass));}
function Edge(sourceNode,targetNode,length,stiffness){this.sourceNode=sourceNode;this.targetNode=targetNode;this.length=length;this.stiffness=stiffness;this.uiElement=null;this.uiArrowElement=null;sourceNode.outEdges.push(this);targetNode.inEdges.push(this);}
Edge.prototype.getTargetNode=function(){return this.targetNode;};Edge.prototype.getSourceNode=function(){return this.sourceNode;};function Graph(){}
Graph.prototype.nodes=[];Graph.prototype.addNode=function(n){this.nodes.push(n);return n;};Graph.prototype.edges=[];Graph.prototype.addEdge=function(n){this.edges.push(n);return n;};Graph.prototype.getBaryCenter=function(){var xTotal=0;var yTotal=0;var massTotal=0;for(var i=0;i<this.nodes.length;i++){var nodeCenter=this.nodes[i].getCenter();xTotal+=nodeCenter.getX()*this.nodes[i].getMass();yTotal+=nodeCenter.getY()*this.nodes[i].getMass();massTotal+=this.nodes[i].getMass();}
return new Vector((xTotal/massTotal),(yTotal/massTotal));};Graph.prototype.getCenter=function(){var xTotal=0;var yTotal=0;var massTotal=0;for(var i=0;i<this.nodes.length;i++){var nodeCenter=this.nodes[i].getCenter();xTotal+=nodeCenter.getX();yTotal+=nodeCenter.getY();massTotal+=1;}
return new Vector((xTotal/massTotal),(yTotal/massTotal));};var MAXITERATION=100000;function ForceDirectedLayout(g){this.iteration=0;this.graph=g;this.stable=false;}
ForceDirectedLayout.prototype.startLayout=function(){var oThis=this;var intervalId=setInterval(function(){oThis.mainIteration();},15);}
ForceDirectedLayout.prototype.mainIteration=function(){if(!this.stable){this.iteration=this.iteration+1;this.applyCoulombsLaw();this.applyHookesLaw();this.applyGravitionalForce();this.applyRandomForce();this.updateVelocity(0.055);this.updatePosition(0.065);}};ForceDirectedLayout.prototype.applyCoulombsLaw=function(){for(var i=0;i<this.graph.nodes.length;i++){var node1=this.graph.nodes[i];for(var j=i+1;j<this.graph.nodes.length;j++){var node2=this.graph.nodes[j];this.applyCoulombsForce(node1,node2);}}};ForceDirectedLayout.prototype.applyCoulombsForce=function(node1,node2){var d=node1.position.subtract(node2.position);var distance=d.magnitude()+1.0;var direction=d.normalise();var ke=(node1.getMass()*node2.getMass())<<3;var halfDistSquared=distance*distance;node1.applyForce(direction.multiply(ke).divide(halfDistSquared));node2.applyForce(direction.multiply(ke).divide(-halfDistSquared));};ForceDirectedLayout.prototype.applyHookesLaw=function()
{for(var i=0;i<this.graph.edges.length;i++)
{this.applyHookesForce(this.graph.edges[i]);}};ForceDirectedLayout.prototype.applyHookesForce=function(edge){edge.stiffness=Math.min(edge.stiffness+5,250.0);var sourceNode=edge.getSourceNode();var targetNode=edge.getTargetNode();var d=targetNode.position.subtract(sourceNode.position);var displacement=edge.length-d.magnitude();var direction=d.normalise();var pull=edge.stiffness*displacement*0.5;sourceNode.applyForce(direction.multiply(-pull));targetNode.applyForce(direction.multiply(pull));};ForceDirectedLayout.prototype.updateVelocity=function(timestep){var damping=0.65;for(var i=0;i<this.graph.nodes.length;i++){var node=this.graph.nodes[i];node.velocity=node.velocity.add(node.force.multiply(timestep)).multiply(damping);node.force=new Vector(0,0);}};ForceDirectedLayout.prototype.updatePosition=function(timestep)
{for(var i=0;i<this.graph.nodes.length;i++){var node=this.graph.nodes[i];node.position=node.position.add(node.velocity.multiply(timestep));}};ForceDirectedLayout.prototype.applyRandomForce=function(){return;var maxRandom=Math.max(100.0-this.iteration,0);for(var i=0;i<this.graph.nodes.length;i++){var node=this.graph.nodes[i];node.applyForce(new Vector((Math.random()*maxRandom*2)-maxRandom,(Math.random()*maxRandom*2)-maxRandom));}};var GRAV_CONSTANT_C_8=4.0;ForceDirectedLayout.prototype.applyGravitionalForce=function(){var targetNode=new Vector(0,0);for(var i=0;i<this.graph.nodes.length;i++){var sourceNode=this.graph.nodes[i];var d=targetNode.subtract(sourceNode.getCenter());var displacement=1.0-d.magnitude();var direction=d.normalise();sourceNode.applyForce(direction.multiply((sourceNode.getMass()*GRAV_CONSTANT_C_8)*displacement*-0.5));}};