diff --git a/dist/vis.js b/dist/vis.js index 79c3fd29..37d64779 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -1848,8 +1848,10 @@ return /******/ (function(modules) { // webpackBootstrap }); }; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.subscribe = DataSet.prototype.on; + // TODO: remove this deprecated function some day (replaced with `on` since version 0.5, deprecated since v4.0) + DataSet.prototype.subscribe = function () { + throw new Error('DataSet.subscribe is deprecated. Use DataSet.on instead.'); + }; /** * Unsubscribe from an event, remove an event listener @@ -1865,8 +1867,10 @@ return /******/ (function(modules) { // webpackBootstrap } }; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.unsubscribe = DataSet.prototype.off; + // TODO: remove this deprecated function some day (replaced with `on` since version 0.5, deprecated since v4.0) + DataSet.prototype.unsubscribe = function () { + throw new Error('DataSet.unsubscribe is deprecated. Use DataSet.off instead.'); + }; /** * Trigger an event @@ -6325,7 +6329,7 @@ return /******/ (function(modules) { // webpackBootstrap var CustomTime = __webpack_require__(27); var ItemSet = __webpack_require__(32); - var ConfigurationSystem = __webpack_require__(45); + var Configurator = __webpack_require__(45); var Validator = __webpack_require__(46)['default']; var printStyle = __webpack_require__(46).printStyle; var allOptions = __webpack_require__(47).allOptions; @@ -6433,7 +6437,7 @@ return /******/ (function(modules) { // webpackBootstrap }; // setup configuration system - this.configurationSystem = new ConfigurationSystem(this, container, configureOptions); + this.configurator = new Configurator(this, container, configureOptions); // apply options if (options) { @@ -6482,8 +6486,10 @@ return /******/ (function(modules) { // webpackBootstrap // force recreation of all items var itemsData = this.itemsData; if (itemsData) { + var selection = this.getSelection(); this.setItems(null); // remove all this.setItems(itemsData); // add all + this.setSelection(selection); // restore selection } } } @@ -6764,7 +6770,7 @@ return /******/ (function(modules) { // webpackBootstrap var CustomTime = __webpack_require__(27); var LineGraph = __webpack_require__(34); - var ConfigurationSystem = __webpack_require__(45); + var Configurator = __webpack_require__(45); var Validator = __webpack_require__(46)['default']; var printStyle = __webpack_require__(46).printStyle; var allOptions = __webpack_require__(48).allOptions; @@ -6859,13 +6865,11 @@ return /******/ (function(modules) { // webpackBootstrap me.emit('contextmenu', me.getEventProperties(event)); }; + // setup configuration system + this.configurator = new Configurator(this, container, configureOptions); + // apply options if (options) { - var errorFound = Validator.validate(options, allOptions); - if (errorFound === true) { - console.log('%cErrors have been found in the supplied options object.', printStyle); - } - this.setOptions(options); } @@ -6885,6 +6889,16 @@ return /******/ (function(modules) { // webpackBootstrap // Extend the functionality from Core Graph2d.prototype = new Core(); + Graph2d.prototype.setOptions = function (options) { + // validate options + var errorFound = Validator.validate(options, allOptions); + if (errorFound === true) { + console.log('%cErrors have been found in the supplied options object.', printStyle); + } + + Core.prototype.setOptions.call(this, options); + }; + /** * Set items * @param {vis.DataSet | Array | null} items @@ -15659,9 +15673,9 @@ return /******/ (function(modules) { // webpackBootstrap var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem); - var _sharedConfigurationSystem = __webpack_require__(45); + var _sharedConfigurator = __webpack_require__(45); - var _sharedConfigurationSystem2 = _interopRequireDefault(_sharedConfigurationSystem); + var _sharedConfigurator2 = _interopRequireDefault(_sharedConfigurator); var _sharedValidator = __webpack_require__(46); @@ -15777,7 +15791,7 @@ return /******/ (function(modules) { // webpackBootstrap this.canvas._create(); // setup configuration system - this.configurationSystem = new _sharedConfigurationSystem2['default'](this, this.body.container, _optionsJs.configureOptions, this.canvas.pixelRatio); + this.configurator = new _sharedConfigurator2['default'](this, this.body.container, _optionsJs.configureOptions, this.canvas.pixelRatio); // apply options this.setOptions(options); @@ -15829,10 +15843,10 @@ return /******/ (function(modules) { // webpackBootstrap //this.view.setOptions(options.view); //this.clustering.setOptions(options.clustering); - this.configurationSystem.setOptions(options.configure); + this.configurator.setOptions(options.configure); // if the configuration system is enabled, copy all options and put them into the config system - if (this.configurationSystem.options.enabled === true) { + if (this.configurator.options.enabled === true) { var networkOptions = { nodes: {}, edges: {}, layout: {}, interaction: {}, manipulation: {}, physics: {}, global: {} }; util.deepExtend(networkOptions.nodes, this.nodesHandler.options); util.deepExtend(networkOptions.edges, this.edgesHandler.options); @@ -15849,7 +15863,7 @@ return /******/ (function(modules) { // webpackBootstrap util.deepExtend(networkOptions.global, this.canvas.options); util.deepExtend(networkOptions.global, this.options); - this.configurationSystem.setModuleOptions(networkOptions); + this.configurator.setModuleOptions(networkOptions); } // handle network global options @@ -16004,7 +16018,7 @@ return /******/ (function(modules) { // webpackBootstrap delete this.manipulation; delete this.nodesHandler; delete this.edgesHandler; - delete this.configurationSystem; + delete this.configurator; delete this.images; // delete emitter bindings @@ -17864,16 +17878,15 @@ return /******/ (function(modules) { // webpackBootstrap }); // enable/disable configure - if (this.configurationSystem) { - this.configurationSystem.setOptions(options.configure); + if (this.configurator) { + this.configurator.setOptions(options.configure); // collect the settings of all components, and pass them to the configuration system var appliedOptions = util.deepExtend({}, this.options); this.components.forEach(function (component) { util.deepExtend(appliedOptions, component.options); }); - this.configurationSystem.setModuleOptions({ global: appliedOptions }); - console.log(appliedOptions); + this.configurator.setModuleOptions({ global: appliedOptions }); } // redraw everything @@ -18562,9 +18575,9 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _networkModulesComponentsColorPicker = __webpack_require__(73); + var _ColorPicker = __webpack_require__(73); - var _networkModulesComponentsColorPicker2 = _interopRequireDefault(_networkModulesComponentsColorPicker); + var _ColorPicker2 = _interopRequireDefault(_ColorPicker); var util = __webpack_require__(1); @@ -18583,11 +18596,11 @@ return /******/ (function(modules) { // webpackBootstrap * @param pixelRatio | canvas pixel ratio */ - var ConfigurationSystem = (function () { - function ConfigurationSystem(parentModule, defaultContainer, configureOptions) { + var Configurator = (function () { + function Configurator(parentModule, defaultContainer, configureOptions) { var pixelRatio = arguments[3] === undefined ? 1 : arguments[3]; - _classCallCheck(this, ConfigurationSystem); + _classCallCheck(this, Configurator); this.parent = parentModule; this.changedOptions = []; @@ -18605,11 +18618,11 @@ return /******/ (function(modules) { // webpackBootstrap this.configureOptions = configureOptions; this.moduleOptions = {}; this.domElements = []; - this.colorPicker = new _networkModulesComponentsColorPicker2['default'](pixelRatio); - this.wrapper; + this.colorPicker = new _ColorPicker2['default'](pixelRatio); + this.wrapper = undefined; } - _createClass(ConfigurationSystem, [{ + _createClass(Configurator, [{ key: 'setOptions', /** @@ -18663,7 +18676,6 @@ return /******/ (function(modules) { // webpackBootstrap /** * Create all DOM elements - * @param {Boolean | String} config * @private */ value: function _create() { @@ -18672,17 +18684,17 @@ return /******/ (function(modules) { // webpackBootstrap this._clean(); this.changedOptions = []; - var config = this.options.filter; + var filter = this.options.filter; var counter = 0; var show = false; for (var option in this.configureOptions) { if (this.configureOptions.hasOwnProperty(option)) { this.allowCreation = false; show = false; - if (typeof config === 'function') { - show = config(option, []); + if (typeof filter === 'function') { + show = filter(option, []); show = show || this._handleObject(this.configureOptions[option], [option]); - } else if (config === true || config.indexOf(option) !== -1) { + } else if (filter === true || filter.indexOf(option) !== -1) { show = true; } @@ -19063,13 +19075,15 @@ return /******/ (function(modules) { // webpackBootstrap var path = arguments[1] === undefined ? [] : arguments[1]; var show = false; - var config = this.options.filter; + var filter = this.options.filter; var visibleInSet = false; for (var subObj in obj) { if (obj.hasOwnProperty(subObj)) { show = false; - if (typeof config === 'function') { - show = config(subObj, path); + if (typeof filter === 'function') { + show = filter(subObj, path); + } else if (filter === true) { + show = true; } if (show !== false) { @@ -19197,10 +19211,10 @@ return /******/ (function(modules) { // webpackBootstrap } }]); - return ConfigurationSystem; + return Configurator; })(); - exports['default'] = ConfigurationSystem; + exports['default'] = Configurator; module.exports = exports['default']; /***/ }, @@ -19548,9 +19562,9 @@ return /******/ (function(modules) { // webpackBootstrap var allOptions = { configure: { enabled: { boolean: boolean }, - filter: { boolean: boolean, string: string, array: array }, + filter: { boolean: boolean, fn: fn }, container: { dom: dom }, - __type__: { object: object, boolean: boolean, string: string, array: array } + __type__: { object: object, boolean: boolean, fn: fn } }, //globals : @@ -19765,9 +19779,9 @@ return /******/ (function(modules) { // webpackBootstrap var allOptions = { configure: { enabled: { boolean: boolean }, - filter: { boolean: boolean, string: string, array: array }, + filter: { boolean: boolean, fn: fn }, container: { dom: dom }, - __type__: { object: object, boolean: boolean, string: string, array: array } + __type__: { object: object, boolean: boolean, fn: fn } }, //globals : @@ -19923,7 +19937,7 @@ return /******/ (function(modules) { // webpackBootstrap drawPoints: { enabled: true, size: [6, 2, 30, 1], - style: 'square' // square, circle + style: ['square', 'circle'] // square, circle }, dataAxis: { showMinorLabels: true, @@ -19988,7 +20002,7 @@ return /******/ (function(modules) { // webpackBootstrap maxHeight: '', min: '', minHeight: '', - movable: true, + moveable: true, orientation: ['both', 'bottom', 'top'], showCurrentTime: false, showMajorLabels: true, @@ -20846,11 +20860,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNode = __webpack_require__(74); + var _componentsNode = __webpack_require__(85); var _componentsNode2 = _interopRequireDefault(_componentsNode); - var _componentsSharedLabel = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(91); var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); @@ -20935,7 +20949,7 @@ return /******/ (function(modules) { // webpackBootstrap min: 14, max: 30, maxVisible: 30, - drawThreshold: 3 + drawThreshold: 6 }, customScalingFunction: function customScalingFunction(min, max, total, value) { if (max === min) { @@ -21319,11 +21333,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsEdge = __webpack_require__(76); + var _componentsEdge = __webpack_require__(86); var _componentsEdge2 = _interopRequireDefault(_componentsEdge); - var _componentsSharedLabel = __webpack_require__(75); + var _componentsSharedLabel = __webpack_require__(91); var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel); @@ -21393,7 +21407,7 @@ return /******/ (function(modules) { // webpackBootstrap min: 14, max: 30, maxVisible: 30, - drawThreshold: 3 + drawThreshold: 6 }, customScalingFunction: function customScalingFunction(min, max, total, value) { if (max === min) { @@ -21738,35 +21752,35 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsPhysicsBarnesHutSolver = __webpack_require__(77); + var _componentsPhysicsBarnesHutSolver = __webpack_require__(74); var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); - var _componentsPhysicsRepulsionSolver = __webpack_require__(78); + var _componentsPhysicsRepulsionSolver = __webpack_require__(75); var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); - var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(79); + var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(76); var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); - var _componentsPhysicsSpringSolver = __webpack_require__(80); + var _componentsPhysicsSpringSolver = __webpack_require__(77); var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); - var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(81); + var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(78); var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); - var _componentsPhysicsCentralGravitySolver = __webpack_require__(82); + var _componentsPhysicsCentralGravitySolver = __webpack_require__(79); var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); - var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(83); + var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(80); var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); - var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(84); + var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(81); var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); @@ -22354,7 +22368,7 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _componentsNodesCluster = __webpack_require__(85); + var _componentsNodesCluster = __webpack_require__(82); var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); @@ -24154,11 +24168,11 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - var _componentsNavigationHandler = __webpack_require__(86); + var _componentsNavigationHandler = __webpack_require__(83); var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _componentsPopup = __webpack_require__(87); + var _componentsPopup = __webpack_require__(84); var _componentsPopup2 = _interopRequireDefault(_componentsPopup); @@ -24858,8 +24872,8 @@ return /******/ (function(modules) { // webpackBootstrap function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Node = __webpack_require__(74); - var Edge = __webpack_require__(76); + var Node = __webpack_require__(85); + var Edge = __webpack_require__(86); var util = __webpack_require__(1); var SelectionHandler = (function () { @@ -28015,7 +28029,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var keycharm = __webpack_require__(88); + var keycharm = __webpack_require__(87); var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); @@ -31293,7 +31307,7 @@ return /******/ (function(modules) { // webpackBootstrap return _moment; })); - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(90)(module))) + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(89)(module))) /***/ }, /* 71 */ @@ -33979,7 +33993,7 @@ return /******/ (function(modules) { // webpackBootstrap prefixed: prefixed }); - if ("function" == TYPE_FUNCTION && __webpack_require__(91)) { + if ("function" == TYPE_FUNCTION && __webpack_require__(90)) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { return Hammer; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); @@ -34576,2492 +34590,2071 @@ return /******/ (function(modules) { // webpackBootstrap /* 74 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; + "use strict"; - Object.defineProperty(exports, '__esModule', { + 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 _sharedLabel = __webpack_require__(75); - - var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - - var _nodesShapesBox = __webpack_require__(97); - - var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox); - - var _nodesShapesCircle = __webpack_require__(98); - - var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle); - - var _nodesShapesCircularImage = __webpack_require__(99); - - var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage); - - var _nodesShapesDatabase = __webpack_require__(100); - - var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase); - - var _nodesShapesDiamond = __webpack_require__(101); - - var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond); - - var _nodesShapesDot = __webpack_require__(102); - - var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot); - - var _nodesShapesEllipse = __webpack_require__(103); - - var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse); - - var _nodesShapesIcon = __webpack_require__(104); - - var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon); - - var _nodesShapesImage = __webpack_require__(105); - - var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage); - - var _nodesShapesSquare = __webpack_require__(106); - - var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare); - - var _nodesShapesStar = __webpack_require__(107); - - var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar); - - var _nodesShapesText = __webpack_require__(108); - - var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText); - - var _nodesShapesTriangle = __webpack_require__(109); - - var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle); - - var _nodesShapesTriangleDown = __webpack_require__(110); - - var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown); - - var _sharedValidator = __webpack_require__(46); - - var _sharedValidator2 = _interopRequireDefault(_sharedValidator); - - var util = __webpack_require__(1); + 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; }; })(); - /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} options An object containing options for the node. All - * options are optional, except for the id. - * {number} id Id of the node. Required - * {string} label Text label for the node - * {number} x Horizontal position of the node - * {number} y Vertical position of the node - * {string} shape Node shape, available: - * "database", "circle", "ellipse", - * "box", "image", "text", "dot", - * "star", "triangle", "triangleDown", - * "square", "icon" - * {string} image An image url - * {string} title An title text, can be HTML - * {anytype} group A group name or number - * @param {Network.Images} imagelist A list with images. Only needed - * when the node has an image - * @param {Network.Groups} grouplist A list with groups. Needed for - * retrieving group options - * @param {Object} constants An object with default values for - * example for the color - * - */ + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Node = (function () { - function Node(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Node); + var BarnesHutSolver = (function () { + function BarnesHutSolver(body, physicsBody, options) { + _classCallCheck(this, BarnesHutSolver); - this.options = util.bridgeObject(globalOptions); this.body = body; - - this.edges = []; // all edges connected to this node - - // set defaults for the options - this.id = undefined; - this.imagelist = imagelist; - this.grouplist = grouplist; - - // state options - this.x = undefined; - this.y = undefined; - this.baseSize = this.options.size; - this.baseFontSize = this.options.font.size; - this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate - this.selected = false; - this.hover = false; - - this.labelModule = new _sharedLabel2['default'](this.body, this.options); + this.physicsBody = physicsBody; + this.barnesHutTree; this.setOptions(options); } - _createClass(Node, [{ - key: 'attachEdge', + _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: "solve", /** - * Attach a edge to the node - * @param {Edge} edge + * 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 */ - value: function attachEdge(edge) { - if (this.edges.indexOf(edge) === -1) { - this.edges.push(edge); + 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); + } + } } } }, { - key: 'detachEdge', + key: "_getForceContribution", /** - * Detach a edge from the node - * @param {Edge} edge + * 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 */ - value: function detachEdge(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); + 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); + } + } + } } } }, { - key: 'togglePhysics', + key: "_calculateForces", /** - * Enable or disable the physics. - * @param status + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private */ - value: function togglePhysics(status) { - this.options.physics = status; + 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); + } + + // 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; } }, { - key: 'setOptions', + key: "_formBarnesHutTree", /** - * Set or overwrite options for the node - * @param {Object} options an object with options - * @param {Object} constants and object with default, global options + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private */ - value: function setOptions(options) { - if (!options) { - return; - } - // basic options - if (options.id !== undefined) { - this.id = options.id; - } + value: function _formBarnesHutTree(nodes, nodeIndices) { + var node = undefined; + var nodeCount = nodeIndices.length; - if (this.id === undefined) { - throw 'Node must have an id'; - } + var minX = nodes[nodeIndices[0]].x; + var minY = nodes[nodeIndices[0]].y; + var maxX = nodes[nodeIndices[0]].x; + var maxY = nodes[nodeIndices[0]].y; - if (options.x !== undefined) { - this.x = parseInt(options.x);this.predefinedPosition = true; - } - if (options.y !== undefined) { - this.y = parseInt(options.y);this.predefinedPosition = true; - } - if (options.size !== undefined) { - this.baseSize = options.size; - } - if (options.value !== undefined) { - options.value = parseInt(options.value); + // 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 - // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true); + var minimumTreeSize = 0.00001; + 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); - // copy group options - if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') { - var groupObj = this.grouplist.get(options.group); - util.deepExtend(this.options, groupObj); - // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. - this.options.color = util.parseColor(this.options.color); - } + // 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); - // load the images - if (this.options.image !== undefined && this.options.image != '') { - if (this.imagelist) { - this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); - } else { - throw 'No imagelist provided'; + // 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); } } - this.updateShape(); - this.updateLabelModule(); - - // reset the size of the node, this can be changed - this._reset(); - } - }, { - key: 'updateLabelModule', - value: function updateLabelModule() { - if (this.options.label === undefined || this.options.label === null) { - this.options.label = ''; - } - this.labelModule.setOptions(this.options, true); - if (this.labelModule.baseSize !== undefined) { - this.baseFontSize = this.labelModule.baseSize; - } - } - }, { - key: 'updateShape', - value: function updateShape() { - // choose draw method depending on the shape - switch (this.options.shape) { - case 'box': - this.shape = new _nodesShapesBox2['default'](this.options, this.body, this.labelModule); - break; - case 'circle': - this.shape = new _nodesShapesCircle2['default'](this.options, this.body, this.labelModule); - break; - case 'circularImage': - this.shape = new _nodesShapesCircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'database': - this.shape = new _nodesShapesDatabase2['default'](this.options, this.body, this.labelModule); - break; - case 'diamond': - this.shape = new _nodesShapesDiamond2['default'](this.options, this.body, this.labelModule); - break; - case 'dot': - this.shape = new _nodesShapesDot2['default'](this.options, this.body, this.labelModule); - break; - case 'ellipse': - this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); - break; - case 'icon': - this.shape = new _nodesShapesIcon2['default'](this.options, this.body, this.labelModule); - break; - case 'image': - this.shape = new _nodesShapesImage2['default'](this.options, this.body, this.labelModule, this.imageObj); - break; - case 'square': - this.shape = new _nodesShapesSquare2['default'](this.options, this.body, this.labelModule); - break; - case 'star': - this.shape = new _nodesShapesStar2['default'](this.options, this.body, this.labelModule); - break; - case 'text': - this.shape = new _nodesShapesText2['default'](this.options, this.body, this.labelModule); - break; - case 'triangle': - this.shape = new _nodesShapesTriangle2['default'](this.options, this.body, this.labelModule); - break; - case 'triangleDown': - this.shape = new _nodesShapesTriangleDown2['default'](this.options, this.body, this.labelModule); - break; - default: - this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule); - break; - } - this._reset(); + // make global + return barnesHutTree; } }, { - key: 'select', + key: "_updateBranchMass", /** - * select this node + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private */ - value: function select() { - this.selected = true; - this._reset(); - } - }, { - key: 'unselect', + value: function _updateBranchMass(parentBranch, node) { + var totalMass = parentBranch.mass + node.options.mass; + var totalMassInv = 1 / totalMass; - /** - * unselect this node - */ - value: function unselect() { - this.selected = false; - this._reset(); + 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; } }, { - key: '_reset', + key: "_placeInTree", /** - * Reset the calculated size of the node, forces it to recalculate its size + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate * @private */ - value: function _reset() { - this.shape.width = undefined; - this.shape.height = undefined; + 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"); + } + } } }, { - key: 'getTitle', + key: "_placeInRegion", /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. - */ - value: function getTitle() { - return this.options.title; - } - }, { - key: 'distanceToBorder', - - /** - * Calculate the distance to the border of the Node - * @param {CanvasRenderingContext2D} ctx - * @param {Number} angle Angle in radians - * @returns {number} distance Distance to the border in pixels - */ - value: function distanceToBorder(ctx, angle) { - return this.shape.distanceToBorder(ctx, angle); - } - }, { - key: 'isFixed', - - /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private */ - value: function isFixed() { - return this.options.fixed.x && this.options.fixed.y; + 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 += Math.random(); + node.y += Math.random(); + } 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; + } } }, { - key: 'isSelected', + key: "_splitBranch", /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false + * 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 */ - value: function isSelected() { - return this.selected; - } - }, { - key: 'getValue', + 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"); - /** - * Retrieve the value of the node. Can be undefined - * @return {Number} value - */ - value: function getValue() { - return this.options.value; + if (containedNode != null) { + this._placeInTree(parentBranch, containedNode); + } } }, { - key: 'setValueRange', + key: "_insertRegion", /** - * Adjust the value range of the node. The node will adjust it's size - * based on its value. - * @param {Number} min - * @param {Number} max + * 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 */ - value: function setValueRange(min, max, total) { - if (this.options.value !== undefined) { - var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); - var sizeDiff = this.options.scaling.max - this.options.scaling.min; - if (this.options.scaling.label.enabled === true) { - var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; - this.options.font.size = this.options.scaling.label.min + scale * fontDiff; - } - this.options.size = this.options.scaling.min + scale * sizeDiff; - } else { - this.options.size = this.baseSize; - this.options.font.size = this.baseFontSize; + 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; } - } - }, { - key: 'draw', - /** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ - value: function draw(ctx) { - this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); + 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 + }; } }, { - key: 'updateBoundingBox', + key: "_debug", - /** - * Update the bounding box of the shape - */ - value: function updateBoundingBox() { - this.shape.updateBoundingBox(this.x, this.y); - } - }, { - key: 'resize', + //--------------------------- DEBUGGING BELOW ---------------------------// /** - * Recalculate the size of this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private */ - value: function resize(ctx) { - this.shape.resize(ctx); - } - }, { - key: 'isOverlappingWith', + value: function _debug(ctx, color) { + if (this.barnesHutTree !== undefined) { - /** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top, right, bottom - * @return {boolean} True if location is located on node - */ - value: function isOverlappingWith(obj) { - return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; - } - }, { - key: 'isBoundingBoxOverlappingWith', + ctx.lineWidth = 1; - /** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top, right, bottom - * @return {boolean} True if location is located on node - */ - value: function isBoundingBoxOverlappingWith(obj) { - return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; + this._drawBranch(this.barnesHutTree.root, ctx, color); + } } - }], [{ - key: 'parseOptions', + }, { + key: "_drawBranch", /** - * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. - * Static so it can also be used by the handler. - * @param parentOptions - * @param newOptions + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private */ - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; - - var fields = ['color', 'font', 'fixed', 'shadow']; - util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); - - // merge the shadow options into the parent. - util.mergeOptions(parentOptions, newOptions, 'shadow'); - - // individual shape newOptions - if (newOptions.color !== undefined && newOptions.color !== null) { - var parsedColor = util.parseColor(newOptions.color); - util.fillIfDefined(parentOptions.color, parsedColor); - } else if (allowDeletion === true && newOptions.color === null) { - parentOptions.color = undefined; - delete parentOptions.color; + value: function _drawBranch(branch, ctx, color) { + if (color === undefined) { + color = "#FF0000"; } - if (newOptions.fixed !== undefined && newOptions.fixed !== null) { - if (typeof newOptions.fixed === 'boolean') { - parentOptions.fixed.x = newOptions.fixed; - parentOptions.fixed.y = newOptions.fixed; - } else { - if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { - parentOptions.fixed.x = newOptions.fixed.x; - } - if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { - parentOptions.fixed.y = newOptions.fixed.y; - } - } + 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(); - if (newOptions.font !== undefined) { - _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions); - } + ctx.beginPath(); + ctx.moveTo(branch.range.maxX, branch.range.minY); + ctx.lineTo(branch.range.maxX, branch.range.maxY); + ctx.stroke(); - if (newOptions.scaling !== undefined) { - util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); - } + 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 Node; + return BarnesHutSolver; })(); - exports['default'] = Node; - module.exports = exports['default']; + exports["default"] = BarnesHutSolver; + module.exports = exports["default"]; /***/ }, /* 75 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; + "use strict"; - Object.defineProperty(exports, '__esModule', { + 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 _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + 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 util = __webpack_require__(1); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Label = (function () { - function Label(body, options) { - _classCallCheck(this, Label); + var RepulsionSolver = (function () { + function RepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, RepulsionSolver); this.body = body; - - this.baseSize = undefined; + this.physicsBody = physicsBody; this.setOptions(options); - this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached } - _createClass(Label, [{ - key: 'setOptions', + _createClass(RepulsionSolver, [{ + key: "setOptions", value: function setOptions(options) { - var allowDeletion = arguments[1] === undefined ? false : arguments[1]; - this.options = options; - - if (options.label !== undefined) { - this.labelDirty = true; - } - - if (options.font !== undefined) { - Label.parseOptions(this.options.font, options, allowDeletion); - if (typeof options.font === 'string') { - this.baseSize = this.options.font.size; - } else if (typeof options.font === 'object') { - if (options.font.size !== undefined) { - this.baseSize = options.font.size; - } - } - } } }, { - key: 'draw', + key: "solve", /** - * Main function. This is called from anything that wants to draw a label. - * @param ctx - * @param x - * @param y - * @param selected - * @param baseline + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private */ - value: function draw(ctx, x, y, selected) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2; - // if no label, return - if (this.options.label === undefined) return; + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - // check if we have to render the label - var viewFontSize = this.options.font.size * this.body.view.scale; - if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) return; + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; - // update the size cache if required - this.calculateLabelSize(ctx, selected, x, y, baseline); + // approximation constants + var a = -2 / 3 / nodeDistance; + var b = 4 / 3; - // create the fontfill background - this._drawBackground(ctx); - // draw text - this._drawText(ctx, selected, x, y, baseline); - } - }, { - key: '_drawBackground', + // 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]]; - /** - * Draws the label background - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _drawBackground(ctx) { - if (this.options.font.background !== undefined && this.options.font.background !== 'none') { - ctx.fillStyle = this.options.font.background; + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - var lineMargin = 2; + // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } - switch (this.options.font.align) { - case 'middle': - ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); - break; - case 'top': - ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height); - break; - case 'bottom': - ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); - break; - default: - ctx.fillRect(this.size.left, this.size.top - 0.5 * lineMargin, this.size.width, this.size.height); - break; - } - } - } - }, { - key: '_drawText', + if (distance < 2 * nodeDistance) { + if (distance < 0.5 * nodeDistance) { + repulsingForce = 1; + } else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) + } + repulsingForce = repulsingForce / distance; - /** - * - * @param ctx - * @param x - * @param baseline - * @private - */ - value: function _drawText(ctx, selected, x, y) { - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + fx = dx * repulsingForce; + fy = dy * repulsingForce; - var fontSize = this.options.font.size; - var viewFontSize = fontSize * this.body.view.scale; - // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) - if (viewFontSize >= this.options.scaling.label.maxVisible) { - fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; + forces[node1.id].x -= fx; + forces[node1.id].y -= fy; + forces[node2.id].x += fx; + forces[node2.id].y += fy; + } + } } + } + }]); - var yLine = this.size.yLine; + return RepulsionSolver; + })(); - var _getColor = this._getColor(viewFontSize); + exports["default"] = RepulsionSolver; + module.exports = exports["default"]; - var _getColor2 = _slicedToArray(_getColor, 2); +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { - var fontColor = _getColor2[0]; - var strokeColor = _getColor2[1]; + "use strict"; - var _setAlignment = this._setAlignment(ctx, x, yLine, baseline); + Object.defineProperty(exports, "__esModule", { + value: true + }); - var _setAlignment2 = _slicedToArray(_setAlignment, 2); + 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; }; })(); - x = _setAlignment2[0]; - yLine = _setAlignment2[1]; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - // configure context for drawing the text - ctx.font = (selected ? 'bold ' : '') + fontSize + 'px ' + this.options.font.face; - ctx.fillStyle = fontColor; - ctx.textAlign = 'center'; + var HierarchicalRepulsionSolver = (function () { + function HierarchicalRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, HierarchicalRepulsionSolver); - // set the strokeWidth - if (this.options.font.strokeWidth > 0) { - ctx.lineWidth = this.options.font.strokeWidth; - ctx.strokeStyle = strokeColor; - ctx.lineJoin = 'round'; - } + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } - // draw the text - for (var i = 0; i < this.lineCount; i++) { - if (this.options.font.strokeWidth > 0) { - ctx.strokeText(this.lines[i], x, yLine); - } - ctx.fillText(this.lines[i], x, yLine); - yLine += fontSize; - } + _createClass(HierarchicalRepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; } }, { - key: '_setAlignment', - value: function _setAlignment(ctx, x, yLine, baseline) { - // check for label alignment (for edges) - // TODO: make alignment for nodes - if (this.options.font.align !== 'horizontal') { - x = 0; - yLine = 0; - - var lineMargin = 2; - if (this.options.font.align === 'top') { - ctx.textBaseline = 'alphabetic'; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } else if (this.options.font.align === 'bottom') { - ctx.textBaseline = 'hanging'; - yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers - } else { - ctx.textBaseline = 'middle'; - } - } else { - ctx.textBaseline = baseline; - } - - return [x, yLine]; - } - }, { - key: '_getColor', + key: "solve", /** - * fade in when relative scale is between threshold and threshold - 1. - * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. * - * @param viewFontSize - * @returns {*[]} * @private */ - value: function _getColor(viewFontSize) { - var fontColor = this.options.font.color || '#000000'; - var strokeColor = this.options.font.strokeColor || '#ffffff'; - if (viewFontSize <= this.options.scaling.label.drawThreshold) { - var opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); - fontColor = util.overrideOpacity(fontColor, opacity); - strokeColor = util.overrideOpacity(strokeColor, opacity); + 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 [fontColor, strokeColor]; } - }, { - key: 'getTextSize', + }]); - /** - * - * @param ctx - * @param selected - * @returns {{width: number, height: number}} - */ - value: function getTextSize(ctx) { - var selected = arguments[1] === undefined ? false : arguments[1]; + return HierarchicalRepulsionSolver; + })(); - var size = { - width: this._processLabel(ctx, selected), - height: this.options.font.size * this.lineCount, - lineCount: this.lineCount - }; - return size; + exports["default"] = HierarchicalRepulsionSolver; + module.exports = exports["default"]; + +/***/ }, +/* 77 */ +/***/ 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 _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; } }, { - key: 'calculateLabelSize', + key: "solve", /** + * This function calculates the springforces on the nodes, accounting for the support nodes. * - * @param ctx - * @param selected - * @param x - * @param y - * @param baseline + * @private */ - value: function calculateLabelSize(ctx, selected) { - var x = arguments[2] === undefined ? 0 : arguments[2]; - var y = arguments[3] === undefined ? 0 : arguments[3]; - var baseline = arguments[4] === undefined ? 'middle' : arguments[4]; + value: function solve() { + var edgeLength = undefined, + edge = undefined; + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var edges = this.body.edges; + var node1 = undefined, + node2 = undefined, + node3 = undefined; - if (this.labelDirty === true) { - this.size.width = this._processLabel(ctx, selected); - } - this.size.height = this.options.font.size * this.lineCount; - this.size.left = x - this.size.width * 0.5; - this.size.top = y - this.size.height * 0.5; - this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; - if (baseline === 'hanging') { - this.size.top += 0.5 * this.options.font.size; - this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - this.size.yLine += 4; // distance from node - } + // 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 = edge.to; + node2 = edge.edgeType.via; + node3 = edge.from; - this.labelDirty = false; + 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(edge.from, edge.to, edgeLength); + } + } + } + } } }, { - key: '_processLabel', + key: "_calculateSpringForce", /** - * This calculates the width as well as explodes the label string and calculates the amount of lines. - * @param ctx - * @param selected - * @returns {number} + * This is the code actually performing the calculation for the function above. + * + * @param node1 + * @param node2 + * @param edgeLength * @private */ - value: function _processLabel(ctx, selected) { - var width = 0; - var lines = ['']; - var lineCount = 0; - if (this.options.label !== undefined) { - lines = String(this.options.label).split('\n'); - lineCount = lines.length; - ctx.font = (selected ? 'bold ' : '') + this.options.font.size + 'px ' + this.options.font.face; - width = ctx.measureText(lines[0]).width; - for (var i = 1; i < lineCount; i++) { - var lineWidth = ctx.measureText(lines[i]).width; - width = lineWidth > width ? lineWidth : width; - } - } - this.lines = lines; - this.lineCount = lineCount; + 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); - return width; - } - }], [{ - key: 'parseOptions', - value: function parseOptions(parentOptions, newOptions) { - var allowDeletion = arguments[2] === undefined ? false : arguments[2]; + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + var springForce = this.options.springConstant * (edgeLength - distance) / distance; - if (typeof newOptions.font === 'string') { - var newOptionsArray = newOptions.font.split(' '); - parentOptions.size = newOptionsArray[0].replace('px', ''); - parentOptions.face = newOptionsArray[1]; - parentOptions.color = newOptionsArray[2]; - } else if (typeof newOptions.font === 'object') { - util.fillIfDefined(parentOptions, newOptions.font, allowDeletion); + 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; } - parentOptions.size = Number(parentOptions.size); } }]); - return Label; + return SpringSolver; })(); - exports['default'] = Label; - module.exports = exports['default']; + exports["default"] = SpringSolver; + module.exports = exports["default"]; /***/ }, -/* 76 */ +/* 78 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; + "use strict"; - Object.defineProperty(exports, '__esModule', { + 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 _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"); } } - 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); - var _sharedLabel = __webpack_require__(75); + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } - var _sharedLabel2 = _interopRequireDefault(_sharedLabel); + _createClass(HierarchicalSpringSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", - var _edgesBezierEdgeDynamic = __webpack_require__(92); + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + value: function solve() { + var edgeLength, edge; + var dx, dy, fx, fy, springForce, distance; + var edges = this.body.edges; + var factor = 0.5; - var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic); + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - var _edgesBezierEdgeStatic = __webpack_require__(93); + // 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; + } - var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic); + // 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; - var _edgesStraightEdge = __webpack_require__(94); + 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; - var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.options.springConstant * (edgeLength - distance) / distance; - var util = __webpack_require__(1); + fx = dx * springForce; + fy = dy * springForce; - /** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with options. Must contain - * At least options from and to. - * Available options: from (number), - * to (number), label (string, color (string), - * width (number), style (string), - * length (number), title (string) - * @param {Network} network A Network object, used to find and edge to - * nodes. - * @param {Object} constants An object with default values for - * example for the color - */ + if (edge.to.level != edge.from.level) { + forces[edge.toId].springFx -= fx; + forces[edge.toId].springFy -= fy; + forces[edge.fromId].springFx += fx; + forces[edge.fromId].springFy += fy; + } else { + forces[edge.toId].x -= factor * fx; + forces[edge.toId].y -= factor * fy; + forces[edge.fromId].x += factor * fx; + forces[edge.fromId].y += factor * fy; + } + } + } - var Edge = (function () { - function Edge(options, body, globalOptions) { - _classCallCheck(this, Edge); + // 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)); - if (body === undefined) { - throw 'No body provided'; + 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; + } } - this.options = util.bridgeObject(globalOptions); - this.body = body; + }]); - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.selected = false; - this.hover = false; - this.labelDirty = true; - this.colorDirty = true; + return HierarchicalSpringSolver; + })(); - this.baseWidth = this.options.width; - this.baseFontSize = this.options.font.size; + exports["default"] = HierarchicalSpringSolver; + module.exports = exports["default"]; - this.from = undefined; // a node - this.to = undefined; // a node +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { - this.edgeType = undefined; + "use strict"; - this.connected = false; + Object.defineProperty(exports, "__esModule", { + value: true + }); - this.labelModule = new _sharedLabel2['default'](this.body, this.options); + 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(Edge, [{ - key: 'setOptions', - - /** - * Set or overwrite options for the edge - * @param {Object} options an object with options - * @param doNotEmit - */ + _createClass(CentralGravitySolver, [{ + key: "setOptions", value: function setOptions(options) { - if (!options) { - return; - } - this.colorDirty = true; + 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; - Edge.parseOptions(this.options, options, true); + 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); - if (options.id !== undefined) { - this.id = options.id; - } - if (options.from !== undefined) { - this.fromId = options.from; - } - if (options.to !== undefined) { - this.toId = options.to; - } - if (options.title !== undefined) { - this.title = options.title; - } - if (options.value !== undefined) { - options.value = parseInt(options.value); + this._calculateForces(distance, dx, dy, forces, node); } - - // A node is connected when it has a from and to node that both exist in the network.body.nodes. - this.connect(); - - // update label Module - this.updateLabelModule(); - - var dataChanged = this.updateEdgeType(); - - // if anything has been updates, reset the selection width and the hover width - this._setInteractionWidths(); - - return dataChanged; } }, { - key: 'updateLabelModule', + key: "_calculateForces", /** - * update the options in the label module + * Calculate the forces based on the distance. + * @private */ - value: function updateLabelModule() { - this.labelModule.setOptions(this.options, true); - if (this.labelModule.baseSize !== undefined) { - this.baseFontSize = this.labelModule.baseSize; - } + 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; } - }, { - key: 'updateEdgeType', + }]); - /** - * update the edge type, set the options - * @returns {boolean} - */ - value: function updateEdgeType() { - var dataChanged = false; - var changeInType = true; - if (this.edgeType !== undefined) { - if (this.edgeType instanceof _edgesBezierEdgeDynamic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type === 'dynamic') { - changeInType = false; - } - if (this.edgeType instanceof _edgesBezierEdgeStatic2['default'] && this.options.smooth.enabled === true && this.options.smooth.type !== 'dynamic') { - changeInType = false; - } - if (this.edgeType instanceof _edgesStraightEdge2['default'] && this.options.smooth.enabled === false) { - changeInType = false; - } + return CentralGravitySolver; + })(); - if (changeInType === true) { - dataChanged = this.edgeType.cleanup(); - } - } + exports["default"] = CentralGravitySolver; + module.exports = exports["default"]; - if (changeInType === true) { - if (this.options.smooth.enabled === true) { - if (this.options.smooth.type === 'dynamic') { - dataChanged = true; - this.edgeType = new _edgesBezierEdgeDynamic2['default'](this.options, this.body, this.labelModule); - } else { - this.edgeType = new _edgesBezierEdgeStatic2['default'](this.options, this.body, this.labelModule); - } - } else { - this.edgeType = new _edgesStraightEdge2['default'](this.options, this.body, this.labelModule); - } - } else { - // if nothing changes, we just set the options. - this.edgeType.setOptions(this.options); - } +/***/ }, +/* 80 */ +/***/ function(module, exports, __webpack_require__) { - return dataChanged; - } - }, { - key: 'togglePhysics', + "use strict"; - /** - * Enable or disable the physics. - * @param status - */ - value: function togglePhysics(status) { - this.options.physics = status; - this.edgeType.togglePhysics(status); - } - }, { - key: 'connect', + Object.defineProperty(exports, "__esModule", { + value: true + }); - /** - * Connect an edge to its nodes - */ - value: function connect() { - this.disconnect(); + 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; }; })(); - this.from = this.body.nodes[this.fromId] || undefined; - this.to = this.body.nodes[this.toId] || undefined; - this.connected = this.from !== undefined && this.to !== undefined; + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x, + property = _x2, + receiver = _x3; 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); } } }; - if (this.connected === true) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } else { - if (this.from) { - this.from.detachEdge(this); - } - if (this.to) { - this.to.detachEdge(this); - } - } - } - }, { - key: 'disconnect', + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /** - * Disconnect an edge from its nodes - */ - value: function disconnect() { - if (this.from) { - this.from.detachEdge(this); - this.from = undefined; - } - if (this.to) { - this.to.detachEdge(this); - this.to = undefined; - } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - this.connected = false; - } - }, { - key: 'getTitle', + 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) subClass.__proto__ = superClass; } - /** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. - */ - value: function getTitle() { - return this.title; - } - }, { - key: 'isSelected', + var _BarnesHutSolver2 = __webpack_require__(74); - /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false - */ - value: function isSelected() { - return this.selected; - } - }, { - key: 'getValue', + var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); - /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ - value: function getValue() { - return this.options.value; - } - }, { - key: 'setValueRange', + var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { + function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); - /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - * @param total - */ - value: function setValueRange(min, max, total) { - if (this.options.value !== undefined) { - var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); - var widthDiff = this.options.scaling.max - this.options.scaling.min; - if (this.options.scaling.label.enabled === true) { - var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; - this.options.font.size = this.options.scaling.label.min + scale * fontDiff; - } - this.options.width = this.options.scaling.min + scale * widthDiff; - } else { - this.options.width = this.baseWidth; - this.options.font.size = this.baseFontSize; - } + _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this,