vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1484 lines
55 KiB

/**
* vis.js
* https://github.com/almende/vis
*
* A dynamic, browser-based visualization library.
*
* @version 4.9.1-SNAPSHOT
* @date 2015-10-05
*
* @license
* Copyright (C) 2011-2015 Almende B.V, http://almende.com
*
* Vis.js is dual licensed under both
*
* * The Apache 2.0 License
* http://www.apache.org/licenses/LICENSE-2.0
*
* and
*
* * The MIT License
* http://opensource.org/licenses/MIT
*
* Vis.js may be distributed under either license.
*/
"use strict";
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _PhysicsWorkerJs = __webpack_require__(1);
var _PhysicsWorkerJs2 = _interopRequireDefault(_PhysicsWorkerJs);
var physicsWorker = new _PhysicsWorkerJs2['default'](function (data) {
return postMessage(data);
});
self.addEventListener('message', function (event) {
return physicsWorker.handleMessage(event);
}, false);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _componentsPhysicsBarnesHutSolver = __webpack_require__(2);
var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver);
var _componentsPhysicsRepulsionSolver = __webpack_require__(3);
var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver);
var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(4);
var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver);
var _componentsPhysicsSpringSolver = __webpack_require__(5);
var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver);
var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(6);
var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver);
var _componentsPhysicsCentralGravitySolver = __webpack_require__(7);
var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver);
var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(8);
var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver);
var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(9);
var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver);
var PhysicsWorker = (function () {
function PhysicsWorker(postMessage) {
_classCallCheck(this, PhysicsWorker);
this.body = {};
this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} };
this.postMessage = postMessage;
this.options = {};
this.stabilized = false;
this.previousStates = {};
this.positions = {};
this.timestep = 0.5;
}
_createClass(PhysicsWorker, [{
key: 'handleMessage',
value: function handleMessage(event) {
var msg = event.data;
switch (msg.type) {
case 'calculateForces':
this.calculateForces();
this.moveNodes();
this.postMessage({
type: 'positions',
data: {
positions: this.positions,
stabilized: this.stabilized
}
});
break;
case 'update':
var node = this.body.nodes[msg.data.id];
node.x = msg.data.x;
node.y = msg.data.y;
break;
case 'options':
this.options = msg.data;
this.timestep = this.options.timestep;
this.init();
break;
case 'physicsObjects':
this.body.nodes = msg.data.nodes;
this.body.edges = msg.data.edges;
this.updatePhysicsData();
break;
default:
console.warn('unknown message from PhysicsEngine', msg);
}
}
/**
* configure the engine.
*/
}, {
key: 'init',
value: function init() {
var options;
if (this.options.solver === 'forceAtlas2Based') {
options = this.options.forceAtlas2Based;
this.nodesSolver = new _componentsPhysicsFA2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options);
this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
this.gravitySolver = new _componentsPhysicsFA2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options);
} else if (this.options.solver === 'repulsion') {
options = this.options.repulsion;
this.nodesSolver = new _componentsPhysicsRepulsionSolver2['default'](this.body, this.physicsBody, options);
this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
} else if (this.options.solver === 'hierarchicalRepulsion') {
options = this.options.hierarchicalRepulsion;
this.nodesSolver = new _componentsPhysicsHierarchicalRepulsionSolver2['default'](this.body, this.physicsBody, options);
this.edgesSolver = new _componentsPhysicsHierarchicalSpringSolver2['default'](this.body, this.physicsBody, options);
this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
} else {
// barnesHut
options = this.options.barnesHut;
this.nodesSolver = new _componentsPhysicsBarnesHutSolver2['default'](this.body, this.physicsBody, options);
this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
}
this.modelOptions = options;
}
/**
* Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time.
*
* @private
*/
}, {
key: 'updatePhysicsData',
value: function updatePhysicsData() {
this.physicsBody.forces = {};
this.physicsBody.physicsNodeIndices = [];
this.physicsBody.physicsEdgeIndices = [];
var nodes = this.body.nodes;
var edges = this.body.edges;
// get node indices for physics
for (var nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
this.physicsBody.physicsNodeIndices.push(nodeId);
this.positions[nodeId] = {
x: nodes[nodeId].x,
y: nodes[nodeId].y
};
}
}
// get edge indices for physics
for (var edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
this.physicsBody.physicsEdgeIndices.push(edgeId);
}
}
// get the velocity and the forces vector
for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) {
var nodeId = this.physicsBody.physicsNodeIndices[i];
this.physicsBody.forces[nodeId] = { x: 0, y: 0 };
// forces can be reset because they are recalculated. Velocities have to persist.
if (this.physicsBody.velocities[nodeId] === undefined) {
this.physicsBody.velocities[nodeId] = { x: 0, y: 0 };
}
}
// clean deleted nodes from the velocity vector
for (var nodeId in this.physicsBody.velocities) {
if (nodes[nodeId] === undefined) {
delete this.physicsBody.velocities[nodeId];
}
}
// console.log(this.physicsBody);
}
/**
* move the nodes one timestap and check if they are stabilized
* @returns {boolean}
*/
}, {
key: 'moveNodes',
value: function moveNodes() {
var nodeIndices = this.physicsBody.physicsNodeIndices;
var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9;
var maxNodeVelocity = 0;
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
var nodeVelocity = this._performStep(nodeId, maxVelocity);
// stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized
maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity);
}
// evaluating the stabilized and adaptiveTimestepEnabled conditions
this.stabilized = maxNodeVelocity < this.options.minVelocity;
}
/**
* Perform the actual step
*
* @param nodeId
* @param maxVelocity
* @returns {number}
* @private
*/
}, {
key: '_performStep',
value: function _performStep(nodeId, maxVelocity) {
var node = this.body.nodes[nodeId];
var timestep = this.timestep;
var forces = this.physicsBody.forces;
var velocities = this.physicsBody.velocities;
// store the state so we can revert
this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocities[nodeId].x, vy: velocities[nodeId].y };
if (node.options.fixed.x === false) {
var dx = this.modelOptions.damping * velocities[nodeId].x; // damping force
var ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration
velocities[nodeId].x += ax * timestep; // velocity
velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x;
node.x += velocities[nodeId].x * timestep; // position
this.positions[nodeId].x = node.x;
} else {
forces[nodeId].x = 0;
velocities[nodeId].x = 0;
}
if (node.options.fixed.y === false) {
var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force
var ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration
velocities[nodeId].y += ay * timestep; // velocity
velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y;
node.y += velocities[nodeId].y * timestep; // position
this.positions[nodeId].y = node.y;
} else {
forces[nodeId].y = 0;
velocities[nodeId].y = 0;
}
var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2));
return totalVelocity;
}
/**
* calculate the forces for one physics iteration.
*/
}, {
key: 'calculateForces',
value: function calculateForces() {
this.gravitySolver.solve();
this.nodesSolver.solve();
this.edgesSolver.solve();
}
}]);
return PhysicsWorker;
})();
exports['default'] = PhysicsWorker;
module.exports = exports['default'];
/***/ },
/* 2 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var BarnesHutSolver = (function () {
function BarnesHutSolver(body, physicsBody, options) {
_classCallCheck(this, BarnesHutSolver);
this.body = body;
this.physicsBody = physicsBody;
this.barnesHutTree;
this.setOptions(options);
this.randomSeed = 5;
}
_createClass(BarnesHutSolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
this.thetaInversed = 1 / this.options.theta;
this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius
}
}, {
key: "seededRandom",
value: function seededRandom() {
var x = Math.sin(this.randomSeed++) * 10000;
return x - Math.floor(x);
}
/**
* This function calculates the forces the nodes apply on eachother based on a gravitational model.
* The Barnes Hut method is used to speed up this N-body simulation.
*
* @private
*/
}, {
key: "solve",
value: function solve() {
if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) {
var node = undefined;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var nodeCount = nodeIndices.length;
// create the tree
var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices);
// for debugging
this.barnesHutTree = barnesHutTree;
// place the nodes one by one recursively
for (var i = 0; i < nodeCount; i++) {
node = nodes[nodeIndices[i]];
if (node.options.mass > 0) {
// starting with root is irrelevant, it never passes the BarnesHutSolver condition
this._getForceContribution(barnesHutTree.root.children.NW, node);
this._getForceContribution(barnesHutTree.root.children.NE, node);
this._getForceContribution(barnesHutTree.root.children.SW, node);
this._getForceContribution(barnesHutTree.root.children.SE, node);
}
}
}
}
/**
* This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass.
* If a region contains a single node, we check if it is not itself, then we apply the force.
*
* @param parentBranch
* @param node
* @private
*/
}, {
key: "_getForceContribution",
value: function _getForceContribution(parentBranch, node) {
// we get no force contribution from an empty region
if (parentBranch.childrenCount > 0) {
var dx = undefined,
dy = undefined,
distance = undefined;
// get the distance from the center of mass to the node.
dx = parentBranch.centerOfMass.x - node.x;
dy = parentBranch.centerOfMass.y - node.y;
distance = Math.sqrt(dx * dx + dy * dy);
// BarnesHutSolver condition
// original condition : s/d < theta = passed === d/s > 1/theta = passed
// calcSize = 1/s --> d * 1/s > 1/theta = passed
if (distance * parentBranch.calcSize > this.thetaInversed) {
this._calculateForces(distance, dx, dy, node, parentBranch);
} else {
// Did not pass the condition, go into children if available
if (parentBranch.childrenCount === 4) {
this._getForceContribution(parentBranch.children.NW, node);
this._getForceContribution(parentBranch.children.NE, node);
this._getForceContribution(parentBranch.children.SW, node);
this._getForceContribution(parentBranch.children.SE, node);
} else {
// parentBranch must have only one node, if it was empty we wouldnt be here
if (parentBranch.children.data.id != node.id) {
// if it is not self
this._calculateForces(distance, dx, dy, node, parentBranch);
}
}
}
}
}
/**
* Calculate the forces based on the distance.
*
* @param distance
* @param dx
* @param dy
* @param node
* @param parentBranch
* @private
*/
}, {
key: "_calculateForces",
value: function _calculateForces(distance, dx, dy, node, parentBranch) {
if (distance === 0) {
distance = 0.1;
dx = distance;
}
if (this.overlapAvoidanceFactor < 1) {
distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius);
}
// the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
// it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3);
var fx = dx * gravityForce;
var fy = dy * gravityForce;
this.physicsBody.forces[node.id].x += fx;
this.physicsBody.forces[node.id].y += fy;
}
/**
* This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes.
*
* @param nodes
* @param nodeIndices
* @private
*/
}, {
key: "_formBarnesHutTree",
value: function _formBarnesHutTree(nodes, nodeIndices) {
var node = undefined;
var nodeCount = nodeIndices.length;
var minX = nodes[nodeIndices[0]].x;
var minY = nodes[nodeIndices[0]].y;
var maxX = nodes[nodeIndices[0]].x;
var maxY = nodes[nodeIndices[0]].y;
// get the range of the nodes
for (var i = 1; i < nodeCount; i++) {
var x = nodes[nodeIndices[i]].x;
var y = nodes[nodeIndices[i]].y;
if (nodes[nodeIndices[i]].options.mass > 0) {
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
}
if (y > maxY) {
maxY = y;
}
}
}
// make the range a square
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
else {
minX += 0.5 * sizeDiff;
maxX -= 0.5 * sizeDiff;
} // xSize < ySize
var minimumTreeSize = 1e-5;
var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX));
var halfRootSize = 0.5 * rootSize;
var centerX = 0.5 * (minX + maxX),
centerY = 0.5 * (minY + maxY);
// construct the barnesHutTree
var barnesHutTree = {
root: {
centerOfMass: { x: 0, y: 0 },
mass: 0,
range: {
minX: centerX - halfRootSize, maxX: centerX + halfRootSize,
minY: centerY - halfRootSize, maxY: centerY + halfRootSize
},
size: rootSize,
calcSize: 1 / rootSize,
children: { data: null },
maxWidth: 0,
level: 0,
childrenCount: 4
}
};
this._splitBranch(barnesHutTree.root);
// place the nodes one by one recursively
for (var i = 0; i < nodeCount; i++) {
node = nodes[nodeIndices[i]];
if (node.options.mass > 0) {
this._placeInTree(barnesHutTree.root, node);
}
}
// make global
return barnesHutTree;
}
/**
* this updates the mass of a branch. this is increased by adding a node.
*
* @param parentBranch
* @param node
* @private
*/
}, {
key: "_updateBranchMass",
value: function _updateBranchMass(parentBranch, node) {
var totalMass = parentBranch.mass + node.options.mass;
var totalMassInv = 1 / totalMass;
parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass;
parentBranch.centerOfMass.x *= totalMassInv;
parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass;
parentBranch.centerOfMass.y *= totalMassInv;
parentBranch.mass = totalMass;
var biggestSize = Math.max(Math.max(node.height, node.radius), node.width);
parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth;
}
/**
* determine in which branch the node will be placed.
*
* @param parentBranch
* @param node
* @param skipMassUpdate
* @private
*/
}, {
key: "_placeInTree",
value: function _placeInTree(parentBranch, node, skipMassUpdate) {
if (skipMassUpdate != true || skipMassUpdate === undefined) {
// update the mass of the branch.
this._updateBranchMass(parentBranch, node);
}
if (parentBranch.children.NW.range.maxX > node.x) {
// in NW or SW
if (parentBranch.children.NW.range.maxY > node.y) {
// in NW
this._placeInRegion(parentBranch, node, "NW");
} else {
// in SW
this._placeInRegion(parentBranch, node, "SW");
}
} else {
// in NE or SE
if (parentBranch.children.NW.range.maxY > node.y) {
// in NE
this._placeInRegion(parentBranch, node, "NE");
} else {
// in SE
this._placeInRegion(parentBranch, node, "SE");
}
}
}
/**
* actually place the node in a region (or branch)
*
* @param parentBranch
* @param node
* @param region
* @private
*/
}, {
key: "_placeInRegion",
value: function _placeInRegion(parentBranch, node, region) {
switch (parentBranch.children[region].childrenCount) {
case 0:
// place node here
parentBranch.children[region].children.data = node;
parentBranch.children[region].childrenCount = 1;
this._updateBranchMass(parentBranch.children[region], node);
break;
case 1:
// convert into children
// if there are two nodes exactly overlapping (on init, on opening of cluster etc.)
// we move one node a pixel and we do not put it in the tree.
if (parentBranch.children[region].children.data.x === node.x && parentBranch.children[region].children.data.y === node.y) {
node.x += this.seededRandom();
node.y += this.seededRandom();
} else {
this._splitBranch(parentBranch.children[region]);
this._placeInTree(parentBranch.children[region], node);
}
break;
case 4:
// place in branch
this._placeInTree(parentBranch.children[region], node);
break;
}
}
/**
* this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch
* after the split is complete.
*
* @param parentBranch
* @private
*/
}, {
key: "_splitBranch",
value: function _splitBranch(parentBranch) {
// if the branch is shaded with a node, replace the node in the new subset.
var containedNode = null;
if (parentBranch.childrenCount === 1) {
containedNode = parentBranch.children.data;
parentBranch.mass = 0;
parentBranch.centerOfMass.x = 0;
parentBranch.centerOfMass.y = 0;
}
parentBranch.childrenCount = 4;
parentBranch.children.data = null;
this._insertRegion(parentBranch, "NW");
this._insertRegion(parentBranch, "NE");
this._insertRegion(parentBranch, "SW");
this._insertRegion(parentBranch, "SE");
if (containedNode != null) {
this._placeInTree(parentBranch, containedNode);
}
}
/**
* This function subdivides the region into four new segments.
* Specifically, this inserts a single new segment.
* It fills the children section of the parentBranch
*
* @param parentBranch
* @param region
* @param parentRange
* @private
*/
}, {
key: "_insertRegion",
value: function _insertRegion(parentBranch, region) {
var minX = undefined,
maxX = undefined,
minY = undefined,
maxY = undefined;
var childSize = 0.5 * parentBranch.size;
switch (region) {
case "NW":
minX = parentBranch.range.minX;
maxX = parentBranch.range.minX + childSize;
minY = parentBranch.range.minY;
maxY = parentBranch.range.minY + childSize;
break;
case "NE":
minX = parentBranch.range.minX + childSize;
maxX = parentBranch.range.maxX;
minY = parentBranch.range.minY;
maxY = parentBranch.range.minY + childSize;
break;
case "SW":
minX = parentBranch.range.minX;
maxX = parentBranch.range.minX + childSize;
minY = parentBranch.range.minY + childSize;
maxY = parentBranch.range.maxY;
break;
case "SE":
minX = parentBranch.range.minX + childSize;
maxX = parentBranch.range.maxX;
minY = parentBranch.range.minY + childSize;
maxY = parentBranch.range.maxY;
break;
}
parentBranch.children[region] = {
centerOfMass: { x: 0, y: 0 },
mass: 0,
range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY },
size: 0.5 * parentBranch.size,
calcSize: 2 * parentBranch.calcSize,
children: { data: null },
maxWidth: 0,
level: parentBranch.level + 1,
childrenCount: 0
};
}
//--------------------------- DEBUGGING BELOW ---------------------------//
/**
* This function is for debugging purposed, it draws the tree.
*
* @param ctx
* @param color
* @private
*/
}, {
key: "_debug",
value: function _debug(ctx, color) {
if (this.barnesHutTree !== undefined) {
ctx.lineWidth = 1;
this._drawBranch(this.barnesHutTree.root, ctx, color);
}
}
/**
* This function is for debugging purposes. It draws the branches recursively.
*
* @param branch
* @param ctx
* @param color
* @private
*/
}, {
key: "_drawBranch",
value: function _drawBranch(branch, ctx, color) {
if (color === undefined) {
color = "#FF0000";
}
if (branch.childrenCount === 4) {
this._drawBranch(branch.children.NW, ctx);
this._drawBranch(branch.children.NE, ctx);
this._drawBranch(branch.children.SE, ctx);
this._drawBranch(branch.children.SW, ctx);
}
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(branch.range.minX, branch.range.minY);
ctx.lineTo(branch.range.maxX, branch.range.minY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(branch.range.maxX, branch.range.minY);
ctx.lineTo(branch.range.maxX, branch.range.maxY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(branch.range.maxX, branch.range.maxY);
ctx.lineTo(branch.range.minX, branch.range.maxY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(branch.range.minX, branch.range.maxY);
ctx.lineTo(branch.range.minX, branch.range.minY);
ctx.stroke();
/*
if (branch.mass > 0) {
ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass);
ctx.stroke();
}
*/
}
}]);
return BarnesHutSolver;
})();
exports["default"] = BarnesHutSolver;
module.exports = exports["default"];
/***/ },
/* 3 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var RepulsionSolver = (function () {
function RepulsionSolver(body, physicsBody, options) {
_classCallCheck(this, RepulsionSolver);
this.body = body;
this.physicsBody = physicsBody;
this.setOptions(options);
}
_createClass(RepulsionSolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
}
/**
* Calculate the forces the nodes apply on each other based on a repulsion field.
* This field is linearly approximated.
*
* @private
*/
}, {
key: "solve",
value: function solve() {
var dx, dy, distance, fx, fy, repulsingForce, node1, node2;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// repulsing forces between nodes
var nodeDistance = this.options.nodeDistance;
// approximation constants
var a = -2 / 3 / nodeDistance;
var b = 4 / 3;
// 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
for (var i = 0; i < nodeIndices.length - 1; i++) {
node1 = nodes[nodeIndices[i]];
for (var j = i + 1; j < nodeIndices.length; j++) {
node2 = nodes[nodeIndices[j]];
dx = node2.x - node1.x;
dy = node2.y - node1.y;
distance = Math.sqrt(dx * dx + dy * dy);
// same condition as BarnesHutSolver, making sure nodes are never 100% overlapping.
if (distance === 0) {
distance = 0.1 * Math.random();
dx = distance;
}
if (distance < 2 * nodeDistance) {
if (distance < 0.5 * nodeDistance) {
repulsingForce = 1.0;
} else {
repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness))
}
repulsingForce = repulsingForce / distance;
fx = dx * repulsingForce;
fy = dy * repulsingForce;
forces[node1.id].x -= fx;
forces[node1.id].y -= fy;
forces[node2.id].x += fx;
forces[node2.id].y += fy;
}
}
}
}
}]);
return RepulsionSolver;
})();
exports["default"] = RepulsionSolver;
module.exports = exports["default"];
/***/ },
/* 4 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var HierarchicalRepulsionSolver = (function () {
function HierarchicalRepulsionSolver(body, physicsBody, options) {
_classCallCheck(this, HierarchicalRepulsionSolver);
this.body = body;
this.physicsBody = physicsBody;
this.setOptions(options);
}
_createClass(HierarchicalRepulsionSolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
}
/**
* Calculate the forces the nodes apply on each other based on a repulsion field.
* This field is linearly approximated.
*
* @private
*/
}, {
key: "solve",
value: function solve() {
var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// repulsing forces between nodes
var nodeDistance = this.options.nodeDistance;
// 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
for (i = 0; i < nodeIndices.length - 1; i++) {
node1 = nodes[nodeIndices[i]];
for (j = i + 1; j < nodeIndices.length; j++) {
node2 = nodes[nodeIndices[j]];
// nodes only affect nodes on their level
if (node1.level === node2.level) {
dx = node2.x - node1.x;
dy = node2.y - node1.y;
distance = Math.sqrt(dx * dx + dy * dy);
var steepness = 0.05;
if (distance < nodeDistance) {
repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2);
} else {
repulsingForce = 0;
}
// normalize force with
if (distance === 0) {
distance = 0.01;
} else {
repulsingForce = repulsingForce / distance;
}
fx = dx * repulsingForce;
fy = dy * repulsingForce;
forces[node1.id].x -= fx;
forces[node1.id].y -= fy;
forces[node2.id].x += fx;
forces[node2.id].y += fy;
}
}
}
}
}]);
return HierarchicalRepulsionSolver;
})();
exports["default"] = HierarchicalRepulsionSolver;
module.exports = exports["default"];
/***/ },
/* 5 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var SpringSolver = (function () {
function SpringSolver(body, physicsBody, options) {
_classCallCheck(this, SpringSolver);
this.body = body;
this.physicsBody = physicsBody;
this.setOptions(options);
}
_createClass(SpringSolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
}
/**
* This function calculates the springforces on the nodes, accounting for the support nodes.
*
* @private
*/
}, {
key: "solve",
value: function solve() {
var edgeLength = undefined,
edge = undefined;
var edgeIndices = this.physicsBody.physicsEdgeIndices;
var edges = this.body.edges;
var nodes = this.body.nodes;
var node1 = undefined,
node2 = undefined,
node3 = undefined;
// forces caused by the edges, modelled as springs
for (var i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
if (edge.connected === true && edge.toId !== edge.fromId) {
// only calculate forces if nodes are in the same sector
if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
if (edge.edgeType.via !== undefined) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
node1 = nodes[edge.to.id];
node2 = nodes[edge.edgeType.via.id];
node3 = nodes[edge.from.id];
this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
} else {
// the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use
// the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger.
edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length;
this._calculateSpringForce(nodes[edge.from.id], nodes[edge.to.id], edgeLength);
}
}
}
}
}
/**
* This is the code actually performing the calculation for the function above.
*
* @param node1
* @param node2
* @param edgeLength
* @private
*/
}, {
key: "_calculateSpringForce",
value: function _calculateSpringForce(node1, node2, edgeLength) {
var dx = node1.x - node2.x;
var dy = node1.y - node2.y;
var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01);
// the 1/distance is so the fx and fy can be calculated without sine or cosine.
var springForce = this.options.springConstant * (edgeLength - distance) / distance;
var fx = dx * springForce;
var fy = dy * springForce;
// handle the case where one node is not part of the physcis
if (this.physicsBody.forces[node1.id] !== undefined) {
this.physicsBody.forces[node1.id].x += fx;
this.physicsBody.forces[node1.id].y += fy;
}
if (this.physicsBody.forces[node2.id] !== undefined) {
this.physicsBody.forces[node2.id].x -= fx;
this.physicsBody.forces[node2.id].y -= fy;
}
}
}]);
return SpringSolver;
})();
exports["default"] = SpringSolver;
module.exports = exports["default"];
/***/ },
/* 6 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var HierarchicalSpringSolver = (function () {
function HierarchicalSpringSolver(body, physicsBody, options) {
_classCallCheck(this, HierarchicalSpringSolver);
this.body = body;
this.physicsBody = physicsBody;
this.setOptions(options);
}
_createClass(HierarchicalSpringSolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
}
/**
* This function calculates the springforces on the nodes, accounting for the support nodes.
*
* @private
*/
}, {
key: "solve",
value: function solve() {
var edgeLength, edge;
var dx, dy, fx, fy, springForce, distance;
var edges = this.body.edges;
var factor = 0.5;
var edgeIndices = this.physicsBody.physicsEdgeIndices;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// initialize the spring force counters
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
forces[nodeId].springFx = 0;
forces[nodeId].springFy = 0;
}
// forces caused by the edges, modelled as springs
for (var i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
if (edge.connected === true) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
dx = edge.from.x - edge.to.x;
dy = edge.from.y - edge.to.y;
distance = Math.sqrt(dx * dx + dy * dy);
distance = distance === 0 ? 0.01 : distance;
// the 1/distance is so the fx and fy can be calculated without sine or cosine.
springForce = this.options.springConstant * (edgeLength - distance) / distance;
fx = dx * springForce;
fy = dy * springForce;
if (edge.to.level != edge.from.level) {
if (forces[edge.toId] !== undefined) {
forces[edge.toId].springFx -= fx;
forces[edge.toId].springFy -= fy;
}
if (forces[edge.fromId] !== undefined) {
forces[edge.fromId].springFx += fx;
forces[edge.fromId].springFy += fy;
}
} else {
if (forces[edge.toId] !== undefined) {
forces[edge.toId].x -= factor * fx;
forces[edge.toId].y -= factor * fy;
}
if (forces[edge.fromId] !== undefined) {
forces[edge.fromId].x += factor * fx;
forces[edge.fromId].y += factor * fy;
}
}
}
}
// normalize spring forces
var springForce = 1;
var springFx, springFy;
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
springFx = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFx));
springFy = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFy));
forces[nodeId].x += springFx;
forces[nodeId].y += springFy;
}
// retain energy balance
var totalFx = 0;
var totalFy = 0;
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
totalFx += forces[nodeId].x;
totalFy += forces[nodeId].y;
}
var correctionFx = totalFx / nodeIndices.length;
var correctionFy = totalFy / nodeIndices.length;
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
forces[nodeId].x -= correctionFx;
forces[nodeId].y -= correctionFy;
}
}
}]);
return HierarchicalSpringSolver;
})();
exports["default"] = HierarchicalSpringSolver;
module.exports = exports["default"];
/***/ },
/* 7 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var CentralGravitySolver = (function () {
function CentralGravitySolver(body, physicsBody, options) {
_classCallCheck(this, CentralGravitySolver);
this.body = body;
this.physicsBody = physicsBody;
this.setOptions(options);
}
_createClass(CentralGravitySolver, [{
key: "setOptions",
value: function setOptions(options) {
this.options = options;
}
}, {
key: "solve",
value: function solve() {
var dx = undefined,
dy = undefined,
distance = undefined,
node = undefined;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i];
node = nodes[nodeId];
dx = -node.x;
dy = -node.y;
distance = Math.sqrt(dx * dx + dy * dy);
this._calculateForces(distance, dx, dy, forces, node);
}
}
/**
* Calculate the forces based on the distance.
* @private
*/
}, {
key: "_calculateForces",
value: function _calculateForces(distance, dx, dy, forces, node) {
var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance;
forces[node.id].x = dx * gravityForce;
forces[node.id].y = dy * gravityForce;
}
}]);
return CentralGravitySolver;
})();
exports["default"] = CentralGravitySolver;
module.exports = exports["default"];
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _BarnesHutSolver2 = __webpack_require__(2);
var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2);
var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) {
_inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver);
function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) {
_classCallCheck(this, ForceAtlas2BasedRepulsionSolver);
_get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options);
}
/**
* Calculate the forces based on the distance.
*
* @param distance
* @param dx
* @param dy
* @param node
* @param parentBranch
* @private
*/
_createClass(ForceAtlas2BasedRepulsionSolver, [{
key: "_calculateForces",
value: function _calculateForces(distance, dx, dy, node, parentBranch) {
if (distance === 0) {
distance = 0.1 * Math.random();
dx = distance;
}
if (this.overlapAvoidanceFactor < 1) {
distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius);
}
var degree = node.edges.length + 1;
// the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
// it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2);
var fx = dx * gravityForce;
var fy = dy * gravityForce;
this.physicsBody.forces[node.id].x += fx;
this.physicsBody.forces[node.id].y += fy;
}
}]);
return ForceAtlas2BasedRepulsionSolver;
})(_BarnesHutSolver3["default"]);
exports["default"] = ForceAtlas2BasedRepulsionSolver;
module.exports = exports["default"];
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _CentralGravitySolver2 = __webpack_require__(7);
var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2);
var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) {
_inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver);
function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) {
_classCallCheck(this, ForceAtlas2BasedCentralGravitySolver);
_get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options);
}
/**
* Calculate the forces based on the distance.
* @private
*/
_createClass(ForceAtlas2BasedCentralGravitySolver, [{
key: "_calculateForces",
value: function _calculateForces(distance, dx, dy, forces, node) {
if (distance > 0) {
var degree = node.edges.length + 1;
var gravityForce = this.options.centralGravity * degree * node.options.mass;
forces[node.id].x = dx * gravityForce;
forces[node.id].y = dy * gravityForce;
}
}
}]);
return ForceAtlas2BasedCentralGravitySolver;
})(_CentralGravitySolver3["default"]);
exports["default"] = ForceAtlas2BasedCentralGravitySolver;
module.exports = exports["default"];
/***/ }
/******/ ]);