return function applyEdgeAttraction(edge) { var na = edge.source; var nb = edge.target; vec2.sub(v, na.position, nb.position); var d = vec2.len(v); var w; if (this.edgeWeightInfluence === 0) { w = 1; } else if (this.edgeWeightInfluence == 1) { w = edge.weight; } else { w = Math.pow(edge.weight, this.edgeWeightInfluence); } var factor; if (this.preventOverlap) { d = d - na.size - nb.size; if (d <= 0) { // No attraction return; } } var factor = -w * d / this._k; vec2.scaleAndAdd(na.force, na.force, v, factor); vec2.scaleAndAdd(nb.force, nb.force, v, -factor); };
return function(node) { // PENDING Move to centerOfMass or [0, 0] ? // vec2.sub(v, this._rootRegion.centerOfMass, node.position); // vec2.negate(v, node.position); vec2.sub(v, this.center, node.position); var d = vec2.len(v); vec2.scaleAndAdd(node.force, node.force, v, this.gravity * node.mass / (d + 1)); }
return function(node) { // PENDING Move to centerOfMass or [0, 0] ? // vec2.sub(v, this._rootRegion.centerOfMass, node.position); // vec2.negate(v, node.position); vec2.sub(v, this.center, node.position); if (this.width > this.height) { // Stronger gravity on y axis v[1] *= this.width / this.height; } else { // Stronger gravity on x axis v[0] *= this.height / this.width; } var d = vec2.len(v) / 100; if (this.strongGravity) { vec2.scaleAndAdd(node.force, node.force, v, d * this.gravity * node.mass); } else { vec2.scaleAndAdd(node.force, node.force, v, this.gravity * node.mass / (d + 1)); } };
return function (pointSet, p0, p1, out) { // Limit the max turning angle var maxTurningAngleCos = Math.cos(this.maxTurningAngle); var maxTurningAngleTan = Math.tan(this.maxTurningAngle); vec2.sub(v10, p0, p1); vec2.normalize(v10, v10); // Simply copy the centroid point if no need to turn the angle vec2.copy(out, p0); var maxMovement = 0; for (var i = 0; i < pointSet.length; i++) { var p = pointSet[i]; vec2.sub(vTmp, p, p0); var len = vec2.len(vTmp); vec2.scale(vTmp, vTmp, 1 / len); var turningAngleCos = vec2.dot(vTmp, v10); // Turning angle is to large if (turningAngleCos < maxTurningAngleCos) { // Calculat p's project point on vector p1-p0 // and distance to the vector vec2.scaleAndAdd( project, p0, v10, len * turningAngleCos ); var distance = v2Dist(project, p); // Use the max turning angle to calculate the new meet point var d = distance / maxTurningAngleTan; vec2.scaleAndAdd(tmpOut, project, v10, -d); var movement = v2DistSquare(tmpOut, p0); if (movement > maxMovement) { maxMovement = movement; vec2.copy(out, tmpOut); } } } };
ForceLayout.prototype.update = function() { var nNodes = this.nodes.length; this.updateBBox(); this._k = 0.4 * this.scaling * Math.sqrt(this.width * this.height / nNodes); if (this.barnesHutOptimize) { this._rootRegion.setBBox( this.bbox[0], this.bbox[1], this.bbox[2], this.bbox[3] ); this._rootRegion.beforeUpdate(); for (var i = 0; i < nNodes; i++) { this._rootRegion.addNode(this.nodes[i]); } this._rootRegion.afterUpdate(); } else { // Update center of mass of whole graph var mass = 0; var centerOfMass = this._rootRegion.centerOfMass; vec2.set(centerOfMass, 0, 0); for (var i = 0; i < nNodes; i++) { var node = this.nodes[i]; mass += node.mass; vec2.scaleAndAdd(centerOfMass, centerOfMass, node.position, node.mass); } vec2.scale(centerOfMass, centerOfMass, 1 / mass); } // Reset forces for (var i = 0; i < nNodes; i++) { var node = this.nodes[i]; vec2.copy(node.forcePrev, node.force); vec2.copy(node.speedPrev, node.speed); vec2.set(node.force, 0, 0); } // Compute forces // Repulsion for (var i = 0; i < nNodes; i++) { var na = this.nodes[i]; if (this.barnesHutOptimize) { this.applyRegionToNodeRepulsion(this._rootRegion, na); } else { for (var j = i + 1; j < nNodes; j++) { var nb = this.nodes[j]; this.applyNodeToNodeRepulsion(na, nb, false); } } // Gravity if (this.gravity > 0) { this.applyNodeGravity(na); } } // Attraction for (var i = 0; i < this.edges.length; i++) { this.applyEdgeAttraction(this.edges[i]); } // Apply forces // var speed = vec2.create(); var v = vec2.create(); for (var i = 0; i < nNodes; i++) { var node = this.nodes[i]; var speed = node.speed; // var swing = vec2.dist(node.force, node.forcePrev); // // var swing = 30; // vec2.scale(node.force, node.force, 1 / (1 + Math.sqrt(swing))); vec2.scale(node.force, node.force, 1 / 30); // contraint force var df = vec2.len(node.force) + 0.1; var scale = Math.min(df, 500.0) / df; vec2.scale(node.force, node.force, scale); vec2.add(speed, speed, node.force); vec2.scale(speed, speed, this.temperature); // Prevent swinging // Limited the increase of speed up to 100% each step // TODO adjust by nodes number vec2.sub(v, speed, node.speedPrev); var swing = vec2.len(v); if (swing > 0) { vec2.scale(v, v, 1 / swing); var base = vec2.len(node.speedPrev); if (base > 0) { swing = Math.min(swing / base, this.maxSpeedIncrease) * base; vec2.scaleAndAdd(speed, node.speedPrev, v, swing); } } // constraint speed var ds = vec2.len(speed); var scale = Math.min(ds, 100.0) / (ds + 0.1); vec2.scale(speed, speed, scale); vec2.add(node.position, node.position, speed); } };
return function(node) { // vec2.negate(v, node.position); vec2.sub(v, this.center, node.position); var d = vec2.len(v) / 100; vec2.scaleAndAdd(node.force, node.force, v, d * this.gravity * node.mass); }