/** * 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"]; /***/ } /******/ ]);