From ccbdd46cc4f33758dbbd100ca03c3c8d2c48fa66 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Tue, 12 May 2015 18:43:37 +0200 Subject: [PATCH] fixed bounding box bug, added function filter to configuration system --- dist/vis.js | 6901 +++++++++-------- docs/graph2d/graph2d.html | 20 +- docs/network/configure.html | 13 +- docs/network/physics.html | 2 +- lib/network/modules/CanvasRenderer.js | 3 + lib/network/modules/PhysicsEngine.js | 2 +- lib/network/modules/components/Node.js | 6 + .../modules/components/nodes/shapes/Box.js | 14 +- .../modules/components/nodes/shapes/Circle.js | 8 + .../components/nodes/shapes/CircularImage.js | 11 +- .../components/nodes/shapes/Database.js | 15 +- .../components/nodes/shapes/Ellipse.js | 21 +- .../modules/components/nodes/shapes/Empty.js | 4 +- .../modules/components/nodes/shapes/Icon.js | 11 +- .../modules/components/nodes/shapes/Image.js | 18 +- .../modules/components/nodes/shapes/Text.js | 7 + .../components/nodes/util/CircleImageBase.js | 5 + .../components/nodes/util/ShapeBase.js | 18 +- lib/network/options.js | 8 +- lib/shared/ConfigurationSystem.js | 128 +- 20 files changed, 3724 insertions(+), 3491 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index c0bdfd24..a6a0dae1 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -18563,6 +18563,7 @@ return /******/ (function(modules) { // webpackBootstrap this.parent = parentModule; this.changedOptions = []; this.container = defaultContainer; + this.allowCreation = false; this.options = {}; this.defaultOptions = { @@ -18608,6 +18609,9 @@ return /******/ (function(modules) { // webpackBootstrap } else if (typeof options === 'boolean') { this.options.filter = true; enabled = options; + } else if (typeof options === 'function') { + this.options.filter = options; + enabled = true; } this.options.enabled = enabled; } @@ -18622,7 +18626,7 @@ return /******/ (function(modules) { // webpackBootstrap if (this.options.container !== undefined) { this.container = this.options.container; } - this._create(this.options.filter); + this._create(); } } }, { @@ -18633,17 +18637,28 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Boolean | String} config * @private */ - value: function _create(config) { + value: function _create() { var _this = this; this._clean(); this.changedOptions = []; + var config = this.options.filter; var counter = 0; + var show = false; for (var option in this.configureOptions) { if (this.configureOptions.hasOwnProperty(option)) { - if (config === true || config.indexOf(option) !== -1) { - var optionObj = this.configureOptions[option]; + this.allowCreation = false; + show = false; + if (typeof config === 'function') { + show = config(option, []); + show = show || this._handleObject(this.configureOptions[option], [option]); + } else if (config === true || config.indexOf(option) !== -1) { + show = true; + } + + if (show !== false) { + this.allowCreation = true; // linebreak between categories if (counter > 0) { @@ -18653,8 +18668,7 @@ return /******/ (function(modules) { // webpackBootstrap this._makeHeader(option); // get the suboptions - var path = [option]; - this._handleObject(optionObj, path); + this._handleObject(this.configureOptions[option], [option]); } counter++; } @@ -18745,16 +18759,22 @@ return /******/ (function(modules) { // webpackBootstrap * @private */ value: function _makeItem(path) { + var _this2 = this; + for (var _len = arguments.length, domElements = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { domElements[_key - 1] = arguments[_key]; } - var item = document.createElement('div'); - item.className = 'vis-network-configuration item s' + path.length; - domElements.forEach(function (element) { - item.appendChild(element); - }); - this.domElements.push(item); + if (this.allowCreation === true) { + (function () { + var item = document.createElement('div'); + item.className = 'vis-network-configuration item s' + path.length; + domElements.forEach(function (element) { + item.appendChild(element); + }); + _this2.domElements.push(item); + })(); + } } }, { key: '_makeHeader', @@ -18954,7 +18974,7 @@ return /******/ (function(modules) { // webpackBootstrap * @private */ value: function _makeColorField(arr, value, path) { - var _this2 = this; + var _this3 = this; var defaultColor = arr[1]; var div = document.createElement('div'); @@ -18969,7 +18989,7 @@ return /******/ (function(modules) { // webpackBootstrap value = value === undefined ? defaultColor : value; div.onclick = function () { - _this2._showColorPicker(value, div, path); + _this3._showColorPicker(value, div, path); }; var label = this._makeLabel(path[path.length - 1], path); @@ -18987,7 +19007,7 @@ return /******/ (function(modules) { // webpackBootstrap * @private */ value: function _showColorPicker(value, div, path) { - var _this3 = this; + var _this4 = this; var rect = div.getBoundingClientRect(); var bodyRect = document.body.getBoundingClientRect(); @@ -18998,7 +19018,7 @@ return /******/ (function(modules) { // webpackBootstrap this.colorPicker.setCallback(function (color) { var colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')'; div.style.backgroundColor = colorString; - _this3._update(colorString, path); + _this4._update(colorString, path); }); } }, { @@ -19013,50 +19033,62 @@ return /******/ (function(modules) { // webpackBootstrap value: function _handleObject(obj) { var path = arguments[1] === undefined ? [] : arguments[1]; + var show = false; + var config = this.options.filter; + var visibleInSet = false; for (var subObj in obj) { if (obj.hasOwnProperty(subObj)) { - var item = obj[subObj]; - var newPath = util.copyAndExtendArray(path, subObj); - var value = this._getValue(newPath); - - if (item instanceof Array) { - this._handleArray(item, value, newPath); - } else if (typeof item === 'string') { - this._makeTextInput(item, value, newPath); - } else if (typeof item === 'boolean') { - this._makeCheckbox(item, value, newPath); - } else if (item instanceof Object) { - // collapse the physics options that are not enabled - var draw = true; - if (path.indexOf('physics') !== -1) { - if (this.moduleOptions.physics.solver !== subObj) { - draw = false; + show = false; + if (typeof config === 'function') { + show = config(subObj, path); + } + + if (show !== false) { + visibleInSet = true; + var item = obj[subObj]; + var newPath = util.copyAndExtendArray(path, subObj); + var value = this._getValue(newPath); + + if (item instanceof Array) { + this._handleArray(item, value, newPath); + } else if (typeof item === 'string') { + this._makeTextInput(item, value, newPath); + } else if (typeof item === 'boolean') { + this._makeCheckbox(item, value, newPath); + } else if (item instanceof Object) { + // collapse the physics options that are not enabled + var draw = true; + if (path.indexOf('physics') !== -1) { + if (this.moduleOptions.physics.solver !== subObj) { + draw = false; + } } - } - if (draw === true) { - // initially collapse options with an disabled enabled option. - if (item.enabled !== undefined) { - var enabledPath = util.copyAndExtendArray(newPath, 'enabled'); - var enabledValue = this._getValue(enabledPath); - if (enabledValue === true) { + if (draw === true) { + // initially collapse options with an disabled enabled option. + if (item.enabled !== undefined) { + var enabledPath = util.copyAndExtendArray(newPath, 'enabled'); + var enabledValue = this._getValue(enabledPath); + if (enabledValue === true) { + var label = this._makeLabel(subObj, newPath, true); + this._makeItem(newPath, label); + visibleInSet = this._handleObject(item, newPath) || visibleInSet; + } else { + this._makeCheckbox(item, enabledValue, newPath); + } + } else { var label = this._makeLabel(subObj, newPath, true); this._makeItem(newPath, label); - this._handleObject(item, newPath); - } else { - this._makeCheckbox(item, enabledValue, newPath); + visibleInSet = this._handleObject(item, newPath) || visibleInSet; } - } else { - var label = this._makeLabel(subObj, newPath, true); - this._makeItem(newPath, label); - this._handleObject(item, newPath); } + } else { + console.error('dont know how to handle', item, subObj, newPath); } - } else { - console.error('dont know how to handle', item, subObj, newPath); } } } + return visibleInSet; } }, { key: '_handleArray', @@ -21678,35 +21710,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__(106); + var _componentsPhysicsBarnesHutSolver = __webpack_require__(77); var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver); - var _componentsPhysicsRepulsionSolver = __webpack_require__(107); + var _componentsPhysicsRepulsionSolver = __webpack_require__(78); var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver); - var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(108); + var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(79); var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver); - var _componentsPhysicsSpringSolver = __webpack_require__(109); + var _componentsPhysicsSpringSolver = __webpack_require__(80); var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver); - var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(110); + var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(81); var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver); - var _componentsPhysicsCentralGravitySolver = __webpack_require__(111); + var _componentsPhysicsCentralGravitySolver = __webpack_require__(82); var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver); - var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(112); + var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(83); var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver); - var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(113); + var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(84); var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver); @@ -21743,7 +21775,7 @@ return /******/ (function(modules) { // webpackBootstrap }, forceAtlas2Based: { theta: 0.5, - gravitationalConstant: -800, + gravitationalConstant: -50, centralGravity: 0.01, springConstant: 0.08, springLength: 100, @@ -22284,7 +22316,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__(77); + var _componentsNodesCluster = __webpack_require__(85); var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster); @@ -23175,6 +23207,8 @@ return /******/ (function(modules) { // webpackBootstrap node.draw(ctx); } else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) { node.draw(ctx); + } else { + node.updateBoundingBox(ctx); } } } @@ -24044,11 +24078,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__(78); + var _componentsNavigationHandler = __webpack_require__(86); var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler); - var _componentsPopup = __webpack_require__(79); + var _componentsPopup = __webpack_require__(87); var _componentsPopup2 = _interopRequireDefault(_componentsPopup); @@ -27159,9 +27193,9 @@ return /******/ (function(modules) { // webpackBootstrap var allOptions = { configure: { enabled: { boolean: boolean }, - filter: { boolean: boolean, string: string, array: array }, + filter: { boolean: boolean, string: string, array: array, fn: fn }, container: { dom: dom }, - __type__: { object: object, boolean: boolean, string: string, array: array } + __type__: { object: object, boolean: boolean, string: string, array: array, fn: fn } }, edges: { arrows: { @@ -27571,7 +27605,7 @@ return /******/ (function(modules) { // webpackBootstrap }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], - gravitationalConstant: [-800, -3000, 0, 50], + gravitationalConstant: [-800, -3000, 0, 1], centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], springConstant: [0.08, 0, 5, 0.005], @@ -27593,7 +27627,7 @@ return /******/ (function(modules) { // webpackBootstrap }, maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], - solver: ['barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based'], + solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], timestep: [0.5, 0, 1, 0.05] }, global: { @@ -27894,7 +27928,7 @@ return /******/ (function(modules) { // webpackBootstrap 'use strict'; - var keycharm = __webpack_require__(80); + var keycharm = __webpack_require__(88); var Emitter = __webpack_require__(43); var Hammer = __webpack_require__(41); var util = __webpack_require__(1); @@ -31172,7 +31206,7 @@ return /******/ (function(modules) { // webpackBootstrap return _moment; })); - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(82)(module))) + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(90)(module))) /***/ }, /* 71 */ @@ -33858,7 +33892,7 @@ return /******/ (function(modules) { // webpackBootstrap prefixed: prefixed }); - if ("function" == TYPE_FUNCTION && __webpack_require__(83)) { + if ("function" == TYPE_FUNCTION && __webpack_require__(91)) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { return Hammer; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); @@ -34471,59 +34505,59 @@ return /******/ (function(modules) { // webpackBootstrap var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - var _nodesShapesBox = __webpack_require__(84); + var _nodesShapesBox = __webpack_require__(92); var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox); - var _nodesShapesCircle = __webpack_require__(85); + var _nodesShapesCircle = __webpack_require__(93); var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle); - var _nodesShapesCircularImage = __webpack_require__(86); + var _nodesShapesCircularImage = __webpack_require__(94); var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage); - var _nodesShapesDatabase = __webpack_require__(87); + var _nodesShapesDatabase = __webpack_require__(95); var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase); - var _nodesShapesDiamond = __webpack_require__(88); + var _nodesShapesDiamond = __webpack_require__(96); var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond); - var _nodesShapesDot = __webpack_require__(89); + var _nodesShapesDot = __webpack_require__(97); var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot); - var _nodesShapesEllipse = __webpack_require__(90); + var _nodesShapesEllipse = __webpack_require__(98); var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse); - var _nodesShapesIcon = __webpack_require__(91); + var _nodesShapesIcon = __webpack_require__(99); var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon); - var _nodesShapesImage = __webpack_require__(92); + var _nodesShapesImage = __webpack_require__(100); var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage); - var _nodesShapesSquare = __webpack_require__(93); + var _nodesShapesSquare = __webpack_require__(101); var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare); - var _nodesShapesStar = __webpack_require__(94); + var _nodesShapesStar = __webpack_require__(102); var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar); - var _nodesShapesText = __webpack_require__(95); + var _nodesShapesText = __webpack_require__(103); var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText); - var _nodesShapesTriangle = __webpack_require__(96); + var _nodesShapesTriangle = __webpack_require__(104); var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle); - var _nodesShapesTriangleDown = __webpack_require__(97); + var _nodesShapesTriangleDown = __webpack_require__(105); var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown); @@ -34864,6 +34898,15 @@ return /******/ (function(modules) { // webpackBootstrap value: function draw(ctx) { this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } + }, { + key: 'updateBoundingBox', + + /** + * Update the bounding box of the shape + */ + value: function updateBoundingBox() { + this.shape.updateBoundingBox(this.x, this.y); + } }, { key: 'resize', @@ -35281,15 +35324,15 @@ return /******/ (function(modules) { // webpackBootstrap var _sharedLabel2 = _interopRequireDefault(_sharedLabel); - var _edgesBezierEdgeDynamic = __webpack_require__(98); + var _edgesBezierEdgeDynamic = __webpack_require__(106); var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic); - var _edgesBezierEdgeStatic = __webpack_require__(99); + var _edgesBezierEdgeStatic = __webpack_require__(107); var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic); - var _edgesStraightEdge = __webpack_require__(100); + var _edgesStraightEdge = __webpack_require__(108); var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge); @@ -35815,1191 +35858,1106 @@ return /******/ (function(modules) { // webpackBootstrap /* 77 */ /***/ function(module, exports, __webpack_require__) { - 'use strict'; - - Object.defineProperty(exports, '__esModule', { - value: true - }); - - 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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _Node2 = __webpack_require__(74); - - var _Node3 = _interopRequireDefault(_Node2); - - /** - * - */ - - var Cluster = (function (_Node) { - function Cluster(options, body, imagelist, grouplist, globalOptions) { - _classCallCheck(this, Cluster); - - _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); - - this.isCluster = true; - this.containedNodes = {}; - this.containedEdges = {}; - } - - _inherits(Cluster, _Node); - - return Cluster; - })(_Node3['default']); - - exports['default'] = Cluster; - module.exports = exports['default']; - -/***/ }, -/* 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; }; })(); - - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - - var util = __webpack_require__(1); - var Hammer = __webpack_require__(41); - var hammerUtil = __webpack_require__(49); - var keycharm = __webpack_require__(80); + 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 NavigationHandler = (function () { - function NavigationHandler(body, canvas) { - var _this = this; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - _classCallCheck(this, NavigationHandler); + var BarnesHutSolver = (function () { + function BarnesHutSolver(body, physicsBody, options) { + _classCallCheck(this, BarnesHutSolver); this.body = body; - this.canvas = canvas; - - this.iconsCreated = false; - this.navigationHammers = []; - this.boundFunctions = {}; - this.touchTime = 0; - this.activated = false; - - this.body.emitter.on('release', function () { - _this._stopMovement(); - }); - this.body.emitter.on('activate', function () { - _this.activated = true;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('deactivate', function () { - _this.activated = false;_this.configureKeyboardBindings(); - }); - this.body.emitter.on('destroy', function () { - if (_this.keycharm !== undefined) { - _this.keycharm.destroy(); - } - }); - - this.options = {}; + this.physicsBody = physicsBody; + this.barnesHutTree; + this.setOptions(options); } - _createClass(NavigationHandler, [{ - key: 'setOptions', + _createClass(BarnesHutSolver, [{ + key: "setOptions", value: function setOptions(options) { - if (options !== undefined) { - this.options = options; - this.create(); - } + this.options = options; + this.thetaInversed = 1 / this.options.theta; } }, { - key: 'create', - value: function create() { - if (this.options.navigationButtons === true) { - if (this.iconsCreated === false) { - this.loadNavigationElements(); - } - } else if (this.iconsCreated === true) { - this.cleanNavigation(); - } + key: "solve", - this.configureKeyboardBindings(); - } - }, { - key: 'cleanNavigation', - value: function cleanNavigation() { - // clean hammer bindings - if (this.navigationHammers.length != 0) { - for (var i = 0; i < this.navigationHammers.length; i++) { - this.navigationHammers[i].destroy(); - } - this.navigationHammers = []; - } + /** + * 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 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; - this._navigationReleaseOverload = function () {}; + // create the tree + var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); - // clean up previous navigation items - if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { - this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); - } + // for debugging + this.barnesHutTree = barnesHutTree; - this.iconsCreated = false; + // 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: 'loadNavigationElements', + key: "_getForceContribution", /** - * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation - * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent - * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. - * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. + * 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 loadNavigationElements() { - this.cleanNavigation(); - - this.navigationDOM = {}; - var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; - var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; - - this.navigationDOM['wrapper'] = document.createElement('div'); - this.navigationDOM['wrapper'].className = 'vis-navigation'; - this.canvas.frame.appendChild(this.navigationDOM['wrapper']); + 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; - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDOM[navigationDivs[i]] = document.createElement('div'); - this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; - this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); + // 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); - var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); - if (navigationDivActions[i] === '_fit') { - hammerUtil.onTouch(hammer, this._fit.bind(this)); + // 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 { - hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); + // 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); + } + } } - - this.navigationHammers.push(hammer); } - - this.iconsCreated = true; } }, { - key: 'bindToRedraw', - value: function bindToRedraw(action) { - if (this.boundFunctions[action] === undefined) { - this.boundFunctions[action] = this[action].bind(this); - this.body.emitter.on('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_startRendering'); - } - } - }, { - key: 'unbindFromRedraw', - value: function unbindFromRedraw(action) { - if (this.boundFunctions[action] !== undefined) { - this.body.emitter.off('initRedraw', this.boundFunctions[action]); - this.body.emitter.emit('_stopRendering'); - delete this.boundFunctions[action]; - } - } - }, { - key: '_fit', + key: "_calculateForces", /** - * this stops all movement induced by the navigation buttons + * Calculate the forces based on the distance. * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch * @private */ - value: function _fit() { - if (new Date().valueOf() - this.touchTime > 700) { - // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) - this.body.emitter.emit('fit', { duration: 700 }); - this.touchTime = new Date().valueOf(); + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; } + // 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: '_stopMovement', + key: "_formBarnesHutTree", /** - * this stops all movement induced by the navigation buttons + * 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 _stopMovement() { - for (var boundAction in this.boundFunctions) { - if (this.boundFunctions.hasOwnProperty(boundAction)) { - this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); - this.body.emitter.emit('_stopRendering'); + 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; + } } } - this.boundFunctions = {}; - } - }, { - key: '_moveUp', - value: function _moveUp() { - this.body.view.translation.y += this.options.keyboard.speed.y; - } - }, { - key: '_moveDown', - value: function _moveDown() { - this.body.view.translation.y -= this.options.keyboard.speed.y; - } - }, { - key: '_moveLeft', - value: function _moveLeft() { - this.body.view.translation.x += this.options.keyboard.speed.x; - } - }, { - key: '_moveRight', - value: function _moveRight() { - this.body.view.translation.x -= this.options.keyboard.speed.x; - } - }, { - key: '_zoomIn', - value: function _zoomIn() { - this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; + // 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 = 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); + + // 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; } }, { - key: '_zoomOut', - value: function _zoomOut() { - this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; + key: "_updateBranchMass", + + /** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ + 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; } }, { - key: 'configureKeyboardBindings', + key: "_placeInTree", /** - * bind all keys using keycharm. + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private */ - value: function configureKeyboardBindings() { - if (this.keycharm !== undefined) { - this.keycharm.destroy(); + value: function _placeInTree(parentBranch, node, skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch, node); } - if (this.options.keyboard.enabled === true) { - - if (this.options.keyboard.bindToWindow === true) { - this.keycharm = keycharm({ container: window, preventDefault: true }); + 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 { - this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); + // in SW + this._placeInRegion(parentBranch, node, "SW"); } - - this.keycharm.reset(); - - if (this.activated === true) { - this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); - this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); - this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); - this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); - this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); - this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - - this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); - this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); - this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); - this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); - this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); - this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); - this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + } 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"); } } } - }]); - - return NavigationHandler; - })(); - - exports['default'] = NavigationHandler; - module.exports = exports['default']; - -/***/ }, -/* 79 */ -/***/ 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'); } } - - /** - * Popup is a class to create a popup window with some text - * @param {Element} container The container object. - * @param {Number} [x] - * @param {Number} [y] - * @param {String} [text] - * @param {Object} [style] An object containing borderColor, - * backgroundColor, etc. - */ - - var Popup = (function () { - function Popup(container) { - _classCallCheck(this, Popup); - - this.container = container; - - this.x = 0; - this.y = 0; - this.padding = 5; - this.hidden = false; - - // create the frame - this.frame = document.createElement('div'); - this.frame.className = 'vis-network-tooltip'; - this.container.appendChild(this.frame); - } - - _createClass(Popup, [{ - key: 'setPosition', + }, { + key: "_placeInRegion", /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private */ - value: function setPosition(x, y) { - this.x = parseInt(x); - this.y = parseInt(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: 'setText', + key: "_splitBranch", /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content + * 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 setText(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } else { - this.frame.innerHTML = content; // string containing text or HTML + 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); } } }, { - key: 'show', + key: "_insertRegion", /** - * Show the popup window - * @param {boolean} [doShow] Show or hide the window + * 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 show(doShow) { - if (doShow === undefined) { - doShow = true; + 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; } - if (doShow === true) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + 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: "_debug", - var top = this.y - height; - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + //--------------------------- DEBUGGING BELOW ---------------------------// - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; - } + /** + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private + */ + value: function _debug(ctx, color) { + if (this.barnesHutTree !== undefined) { - this.frame.style.left = left + 'px'; - this.frame.style.top = top + 'px'; - this.frame.style.visibility = 'visible'; - this.hidden = false; - } else { - this.hide(); + ctx.lineWidth = 1; + + this._drawBranch(this.barnesHutTree.root, ctx, color); } } }, { - key: 'hide', + key: "_drawBranch", /** - * Hide the popup window + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private */ - value: function hide() { - this.hidden = true; - this.frame.style.visibility = 'hidden'; - } - }]); - - return Popup; - })(); + value: function _drawBranch(branch, ctx, color) { + if (color === undefined) { + color = "#FF0000"; + } - exports['default'] = Popup; - module.exports = exports['default']; + 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(); -/***/ }, -/* 80 */ -/***/ function(module, exports, __webpack_require__) { + ctx.beginPath(); + ctx.moveTo(branch.range.maxX, branch.range.minY); + ctx.lineTo(branch.range.maxX, branch.range.maxY); + ctx.stroke(); - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; - /** - * Created by Alex on 11/6/2014. - */ + ctx.beginPath(); + ctx.moveTo(branch.range.maxX, branch.range.maxY); + ctx.lineTo(branch.range.minX, branch.range.maxY); + ctx.stroke(); - // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 - // if the module has no dependencies, the above pattern can be simplified to - (function (root, factory) { - if (true) { - // AMD. Register as an anonymous module. - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports === 'object') { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals (root is window) - root.keycharm = factory(); - } - }(this, function () { + ctx.beginPath(); + ctx.moveTo(branch.range.minX, branch.range.maxY); + ctx.lineTo(branch.range.minX, branch.range.minY); + ctx.stroke(); - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } + */ + } + }]); - var container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; + return BarnesHutSolver; + })(); - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} + exports["default"] = BarnesHutSolver; + module.exports = exports["default"]; - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { + "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; + 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; }; })(); - // handle the actualy bound key with the event - var handleEvent = function(event,type) { - if (_bound[type][event.keyCode] !== undefined) { - var bound = _bound[type][event.keyCode]; - for (var i = 0; i < bound.length; i++) { - if (bound[i].shift === undefined) { - bound[i].fn(event); - } - else if (bound[i].shift == true && event.shiftKey == true) { - bound[i].fn(event); - } - else if (bound[i].shift == false && event.shiftKey == false) { - bound[i].fn(event); - } - } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - if (preventDefault == true) { - event.preventDefault(); - } - } - }; + var RepulsionSolver = (function () { + function RepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, RepulsionSolver); - // bind a key to a callback - _exportFunctions.bind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (_bound[type][_keys[key].code] === undefined) { - _bound[type][_keys[key].code] = []; - } - _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); - }; + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } + + _createClass(RepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2; - // bind all keys to a call back (demo purposes) - _exportFunctions.bindAll = function(callback, type) { - if (type === undefined) { - type = 'keydown'; - } - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - _exportFunctions.bind(key,callback,type); - } - } - }; + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - // get the key label from an event - _exportFunctions.getKey = function(event) { - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { - return key; - } - else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { - return key; - } - else if (event.keyCode == _keys[key].code && key == 'shift') { - return key; - } - } - } - return "unknown key, currently not supported"; - }; + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; - // unbind either a specific callback from a key or all of them (by leaving callback undefined) - _exportFunctions.unbind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (callback !== undefined) { - var newBindings = []; - var bound = _bound[type][_keys[key].code]; - if (bound !== undefined) { - for (var i = 0; i < bound.length; i++) { - if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { - newBindings.push(_bound[type][_keys[key].code][i]); - } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; + // approximation constants + var a = -2 / 3 / nodeDistance; + var b = 4 / 3; - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; + // 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]]; - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); + // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } - // return the public functions. - return _exportFunctions; - } + 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; - return keycharm; - })); + 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"]; /***/ }, -/* 81 */ -/***/ function(module, exports, __webpack_require__) { - - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 81; - - -/***/ }, -/* 82 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } - - -/***/ }, -/* 83 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; - - /* WEBPACK VAR INJECTION */}.call(exports, {})) - -/***/ }, -/* 84 */ +/* 79 */ /***/ 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 _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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _utilNodeBase = __webpack_require__(101); - - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + 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; }; })(); - 'use strict'; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Box = (function (_NodeBase) { - function Box(options, body, labelModule) { - _classCallCheck(this, Box); + var HierarchicalRepulsionSolver = (function () { + function HierarchicalRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, HierarchicalRepulsionSolver); - _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); } - _inherits(Box, _NodeBase); - - _createClass(Box, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - } + _createClass(HierarchicalRepulsionSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + key: "solve", - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + value: function solve() { + var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; - var borderRadius = 6; - ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); + var nodes = this.body.nodes; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + // repulsing forces between nodes + var nodeDistance = this.options.nodeDistance; - // disable shadows for other elements. - this.disableShadow(ctx); + // 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]]; - ctx.stroke(); + // 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); - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + 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; - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + forces[node1.id].x -= fx; + forces[node1.id].y -= fy; + forces[node2.id].x += fx; + forces[node2.id].y += fy; + } + } + } } }]); - return Box; - })(_utilNodeBase2['default']); + return HierarchicalRepulsionSolver; + })(); - exports['default'] = Box; - module.exports = exports['default']; + exports["default"] = HierarchicalRepulsionSolver; + module.exports = exports["default"]; /***/ }, -/* 85 */ +/* 80 */ /***/ 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 _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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _utilCircleImageBase = __webpack_require__(102); - - var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + 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; }; })(); - 'use strict'; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Circle = (function (_CircleImageBase) { - function Circle(options, body, labelModule) { - _classCallCheck(this, Circle); + var SpringSolver = (function () { + function SpringSolver(body, physicsBody, options) { + _classCallCheck(this, SpringSolver); - _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); } - _inherits(Circle, _CircleImageBase); - - _createClass(Circle, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.size = diameter / 2; - - this.width = diameter; - this.height = diameter; - } + _createClass(SpringSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); + key: "solve", - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + 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; - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + // 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._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: "_calculateSpringForce", + + /** + * This is the code actually performing the calculation for the function above. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private + */ + value: function _calculateSpringForce(node1, node2, edgeLength) { + var dx = node1.x - node2.x; + var dy = node1.y - node2.y; + var 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. + 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 Circle; - })(_utilCircleImageBase2['default']); + return SpringSolver; + })(); - exports['default'] = Circle; - module.exports = exports['default']; + exports["default"] = SpringSolver; + module.exports = exports["default"]; /***/ }, -/* 86 */ +/* 81 */ /***/ 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; }; })(); - 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); } } }; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + var HierarchicalSpringSolver = (function () { + function HierarchicalSpringSolver(body, physicsBody, options) { + _classCallCheck(this, HierarchicalSpringSolver); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } - 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; } + _createClass(HierarchicalSpringSolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; + } + }, { + key: "solve", - var _utilCircleImageBase = __webpack_require__(102); + /** + * 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 _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + var edgeIndices = this.physicsBody.physicsEdgeIndices; + var nodeIndices = this.physicsBody.physicsNodeIndices; + var forces = this.physicsBody.forces; - 'use strict'; + // 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 CircularImage = (function (_CircleImageBase) { - function CircularImage(options, body, labelModule, imageObj) { - _classCallCheck(this, CircularImage); + // 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; - _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); - this.imageObj = imageObj; - this._swapToImageResizeWhenImageLoaded = true; - } + 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; - _inherits(CircularImage, _CircleImageBase); + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.options.springConstant * (edgeLength - distance) / distance; - _createClass(CircularImage, [{ - key: 'resize', - value: function resize() { - if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { - if (!this.width) { - var diameter = this.options.size * 2; - this.width = diameter; - this.height = diameter; - this._swapToImageResizeWhenImageLoaded = true; - } - } else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = undefined; - this.height = undefined; - this._swapToImageResizeWhenImageLoaded = false; + fx = dx * springForce; + fy = dy * springForce; + + 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; + } } - this._resizeImage(); } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(); - - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var size = Math.min(0.5 * this.height, 0.5 * this.width); - this._drawRawCircle(ctx, x, y, selected, hover, size); - - ctx.save(); - ctx.circle(x, y, size); - ctx.stroke(); - ctx.clip(); - - this._drawImageAtPosition(ctx); - ctx.restore(); + // 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)); - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + forces[nodeId].x += springFx; + forces[nodeId].y += springFy; + } - this._drawImageLabel(ctx, x, y, selected); + // 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; - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); + for (var i = 0; i < nodeIndices.length; i++) { + var nodeId = nodeIndices[i]; + forces[nodeId].x -= correctionFx; + forces[nodeId].y -= correctionFy; + } } }]); - return CircularImage; - })(_utilCircleImageBase2['default']); + return HierarchicalSpringSolver; + })(); - exports['default'] = CircularImage; - module.exports = exports['default']; + exports["default"] = HierarchicalSpringSolver; + module.exports = exports["default"]; /***/ }, -/* 87 */ +/* 82 */ /***/ 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; }; })(); - 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); } } }; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + var CentralGravitySolver = (function () { + function CentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, CentralGravitySolver); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + this.body = body; + this.physicsBody = physicsBody; + this.setOptions(options); + } - 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; } - - var _utilNodeBase = __webpack_require__(101); - - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - - 'use strict'; - - var Database = (function (_NodeBase) { - function Database(options, body, labelModule) { - _classCallCheck(this, Database); - - _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(Database, _NodeBase); - - _createClass(Database, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; - } + _createClass(CentralGravitySolver, [{ + key: "setOptions", + value: function setOptions(options) { + this.options = options; } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - - ctx.stroke(); + 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; - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + 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.labelModule.draw(ctx, x, y, selected); + this._calculateForces(distance, dx, dy, forces, node); + } } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * @private + */ + 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 Database; - })(_utilNodeBase2['default']); + return CentralGravitySolver; + })(); - exports['default'] = Database; - module.exports = exports['default']; + exports["default"] = CentralGravitySolver; + module.exports = exports["default"]; /***/ }, -/* 88 */ +/* 83 */ /***/ 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; }; })(); 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); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + 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); } } }; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - 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; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _utilShapeBase = __webpack_require__(103); + 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; } - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var _BarnesHutSolver2 = __webpack_require__(77); - 'use strict'; + var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); - var Diamond = (function (_ShapeBase) { - function Diamond(options, body, labelModule) { - _classCallCheck(this, Diamond); + var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { + function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); - _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); } - _inherits(Diamond, _ShapeBase); + _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); - _createClass(Diamond, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + _createClass(ForceAtlas2BasedRepulsionSolver, [{ + key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * + * @param distance + * @param dx + * @param dy + * @param node + * @param parentBranch + * @private + */ + value: function _calculateForces(distance, dx, dy, node, parentBranch) { + if (distance === 0) { + distance = 0.1 * Math.random(); + dx = distance; + } + 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 Diamond; - })(_utilShapeBase2['default']); + return ForceAtlas2BasedRepulsionSolver; + })(_BarnesHutSolver3["default"]); - exports['default'] = Diamond; - module.exports = exports['default']; + exports["default"] = ForceAtlas2BasedRepulsionSolver; + module.exports = exports["default"]; /***/ }, -/* 89 */ +/* 84 */ /***/ 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; }; })(); 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); } } }; - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + 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); } } }; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - 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; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var _utilShapeBase = __webpack_require__(103); + 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; } - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var _CentralGravitySolver2 = __webpack_require__(82); - 'use strict'; + var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); - var Dot = (function (_ShapeBase) { - function Dot(options, body, labelModule) { - _classCallCheck(this, Dot); + var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { + function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { + _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); - _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); } - _inherits(Dot, _ShapeBase); + _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); - _createClass(Dot, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'circle', 2, x, y, selected, hover); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this.options.size + this.options.borderWidth; + _createClass(ForceAtlas2BasedCentralGravitySolver, [{ + key: "_calculateForces", + + /** + * Calculate the forces based on the distance. + * @private + */ + 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 Dot; - })(_utilShapeBase2['default']); + return ForceAtlas2BasedCentralGravitySolver; + })(_CentralGravitySolver3["default"]); - exports['default'] = Dot; - module.exports = exports['default']; + exports["default"] = ForceAtlas2BasedCentralGravitySolver; + module.exports = exports["default"]; /***/ }, -/* 90 */ +/* 85 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37008,8 +36966,6 @@ return /******/ (function(modules) { // webpackBootstrap 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) { 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); } } }; @@ -37020,89 +36976,35 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilNodeBase = __webpack_require__(101); + var _Node2 = __webpack_require__(74); - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + var _Node3 = _interopRequireDefault(_Node2); - 'use strict'; - - var Ellipse = (function (_NodeBase) { - function Ellipse(options, body, labelModule) { - _classCallCheck(this, Ellipse); - - _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); - } - - _inherits(Ellipse, _NodeBase); - - _createClass(Ellipse, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var textSize = this.labelModule.getTextSize(ctx, selected); - - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; - } - } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; - - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.ellipse(this.left, this.top, this.width, this.height); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + /** + * + */ - // disable shadows for other elements. - this.disableShadow(ctx); + var Cluster = (function (_Node) { + function Cluster(options, body, imagelist, grouplist, globalOptions) { + _classCallCheck(this, Cluster); - ctx.stroke(); + _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions); - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; + this.isCluster = true; + this.containedNodes = {}; + this.containedEdges = {}; + } - this.labelModule.draw(ctx, x, y, selected); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); - } - }]); + _inherits(Cluster, _Node); - return Ellipse; - })(_utilNodeBase2['default']); + return Cluster; + })(_Node3['default']); - exports['default'] = Ellipse; + exports['default'] = Cluster; module.exports = exports['default']; /***/ }, -/* 91 */ +/* 86 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37113,186 +37015,266 @@ return /******/ (function(modules) { // webpackBootstrap 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) { 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); } } }; - - 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) subClass.__proto__ = superClass; } + var util = __webpack_require__(1); + var Hammer = __webpack_require__(41); + var hammerUtil = __webpack_require__(49); + var keycharm = __webpack_require__(88); - var _utilNodeBase = __webpack_require__(101); + var NavigationHandler = (function () { + function NavigationHandler(body, canvas) { + var _this = this; - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + _classCallCheck(this, NavigationHandler); - 'use strict'; + this.body = body; + this.canvas = canvas; - var Icon = (function (_NodeBase) { - function Icon(options, body, labelModule) { - _classCallCheck(this, Icon); + this.iconsCreated = false; + this.navigationHammers = []; + this.boundFunctions = {}; + this.touchTime = 0; + this.activated = false; - _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); - } + this.body.emitter.on('release', function () { + _this._stopMovement(); + }); + this.body.emitter.on('activate', function () { + _this.activated = true;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('deactivate', function () { + _this.activated = false;_this.configureKeyboardBindings(); + }); + this.body.emitter.on('destroy', function () { + if (_this.keycharm !== undefined) { + _this.keycharm.destroy(); + } + }); - _inherits(Icon, _NodeBase); + this.options = {}; + } - _createClass(Icon, [{ - key: 'resize', - value: function resize(ctx) { - if (this.width === undefined) { - var margin = 5; - var iconSize = { - width: Number(this.options.icon.size), - height: Number(this.options.icon.size) - }; - this.width = iconSize.width + 2 * margin; - this.height = iconSize.height + 2 * margin; + _createClass(NavigationHandler, [{ + key: 'setOptions', + value: function setOptions(options) { + if (options !== undefined) { + this.options = options; + this.create(); } } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx); - this.options.icon.size = this.options.icon.size || 50; - - this.left = x - this.width * 0.5; - this.top = y - this.height * 0.5; - this._icon(ctx, x, y, selected); - - this.boundingBox.top = y - this.options.icon.size * 0.5; - this.boundingBox.left = x - this.options.icon.size * 0.5; - this.boundingBox.right = x + this.options.icon.size * 0.5; - this.boundingBox.bottom = y + this.options.icon.size * 0.5; - - if (this.options.label !== undefined) { - var iconTextSpacing = 5; - this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + key: 'create', + value: function create() { + if (this.options.navigationButtons === true) { + if (this.iconsCreated === false) { + this.loadNavigationElements(); + } + } else if (this.iconsCreated === true) { + this.cleanNavigation(); } + + this.configureKeyboardBindings(); } }, { - key: '_icon', - value: function _icon(ctx, x, y, selected) { - var iconSize = Number(this.options.icon.size); - - if (this.options.icon.code !== undefined) { - ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; - - // draw icon - ctx.fillStyle = this.options.icon.color || 'black'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; + key: 'cleanNavigation', + value: function cleanNavigation() { + // clean hammer bindings + if (this.navigationHammers.length != 0) { + for (var i = 0; i < this.navigationHammers.length; i++) { + this.navigationHammers[i].destroy(); + } + this.navigationHammers = []; + } - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fillText(this.options.icon.code, x, y); + this._navigationReleaseOverload = function () {}; - // disable shadows for other elements. - this.disableShadow(ctx); - } else { - console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); + // clean up previous navigation items + if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { + this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); } + + this.iconsCreated = false; } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - this._distanceToBorder(angle); - } - }]); + key: 'loadNavigationElements', - return Icon; - })(_utilNodeBase2['default']); + /** + * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation + * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent + * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. + * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. + * + * @private + */ + value: function loadNavigationElements() { + this.cleanNavigation(); - exports['default'] = Icon; - module.exports = exports['default']; + this.navigationDOM = {}; + var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; + var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; -/***/ }, -/* 92 */ -/***/ function(module, exports, __webpack_require__) { + this.navigationDOM['wrapper'] = document.createElement('div'); + this.navigationDOM['wrapper'].className = 'vis-navigation'; + this.canvas.frame.appendChild(this.navigationDOM['wrapper']); - 'use strict'; + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDOM[navigationDivs[i]] = document.createElement('div'); + this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; + this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); - Object.defineProperty(exports, '__esModule', { - value: true - }); + var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); + if (navigationDivActions[i] === '_fit') { + hammerUtil.onTouch(hammer, this._fit.bind(this)); + } else { + hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); + } - 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.navigationHammers.push(hammer); + } - 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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _utilCircleImageBase = __webpack_require__(102); - - var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); + this.iconsCreated = true; + } + }, { + key: 'bindToRedraw', + value: function bindToRedraw(action) { + if (this.boundFunctions[action] === undefined) { + this.boundFunctions[action] = this[action].bind(this); + this.body.emitter.on('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_startRendering'); + } + } + }, { + key: 'unbindFromRedraw', + value: function unbindFromRedraw(action) { + if (this.boundFunctions[action] !== undefined) { + this.body.emitter.off('initRedraw', this.boundFunctions[action]); + this.body.emitter.emit('_stopRendering'); + delete this.boundFunctions[action]; + } + } + }, { + key: '_fit', - 'use strict'; + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + value: function _fit() { + if (new Date().valueOf() - this.touchTime > 700) { + // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) + this.body.emitter.emit('fit', { duration: 700 }); + this.touchTime = new Date().valueOf(); + } + } + }, { + key: '_stopMovement', - var Image = (function (_CircleImageBase) { - function Image(options, body, labelModule, imageObj) { - _classCallCheck(this, Image); + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + value: function _stopMovement() { + for (var boundAction in this.boundFunctions) { + if (this.boundFunctions.hasOwnProperty(boundAction)) { + this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]); + this.body.emitter.emit('_stopRendering'); + } + } + this.boundFunctions = {}; + } + }, { + key: '_moveUp', + value: function _moveUp() { + this.body.view.translation.y += this.options.keyboard.speed.y; + } + }, { + key: '_moveDown', + value: function _moveDown() { + this.body.view.translation.y -= this.options.keyboard.speed.y; + } + }, { + key: '_moveLeft', + value: function _moveLeft() { + this.body.view.translation.x += this.options.keyboard.speed.x; + } + }, { + key: '_moveRight', + value: function _moveRight() { + this.body.view.translation.x -= this.options.keyboard.speed.x; + } + }, { + key: '_zoomIn', + value: function _zoomIn() { + this.body.view.scale *= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: '_zoomOut', + value: function _zoomOut() { + this.body.view.scale /= 1 + this.options.keyboard.speed.zoom; + } + }, { + key: 'configureKeyboardBindings', - _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); - this.imageObj = imageObj; - } + /** + * bind all keys using keycharm. + */ + value: function configureKeyboardBindings() { + if (this.keycharm !== undefined) { + this.keycharm.destroy(); + } - _inherits(Image, _CircleImageBase); + if (this.options.keyboard.enabled === true) { - _createClass(Image, [{ - key: 'resize', - value: function resize() { - this._resizeImage(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + if (this.options.keyboard.bindToWindow === true) { + this.keycharm = keycharm({ container: window, preventDefault: true }); + } else { + this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); + } - this._drawImageAtPosition(ctx); + this.keycharm.reset(); - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + if (this.activated === true) { + this.keycharm.bind('up', this.bindToRedraw.bind(this, '_moveUp'), 'keydown'); + this.keycharm.bind('down', this.bindToRedraw.bind(this, '_moveDown'), 'keydown'); + this.keycharm.bind('left', this.bindToRedraw.bind(this, '_moveLeft'), 'keydown'); + this.keycharm.bind('right', this.bindToRedraw.bind(this, '_moveRight'), 'keydown'); + this.keycharm.bind('=', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num+', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('num-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('-', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind('[', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); + this.keycharm.bind(']', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pageup', this.bindToRedraw.bind(this, '_zoomIn'), 'keydown'); + this.keycharm.bind('pagedown', this.bindToRedraw.bind(this, '_zoomOut'), 'keydown'); - this._drawImageLabel(ctx, x, y, selected || hover); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; - var w = Math.sin(angle) * a; - var h = Math.cos(angle) * b; - return a * b / Math.sqrt(w * w + h * h); + this.keycharm.bind('up', this.unbindFromRedraw.bind(this, '_moveUp'), 'keyup'); + this.keycharm.bind('down', this.unbindFromRedraw.bind(this, '_moveDown'), 'keyup'); + this.keycharm.bind('left', this.unbindFromRedraw.bind(this, '_moveLeft'), 'keyup'); + this.keycharm.bind('right', this.unbindFromRedraw.bind(this, '_moveRight'), 'keyup'); + this.keycharm.bind('=', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num+', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('num-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('-', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind('[', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + this.keycharm.bind(']', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pageup', this.unbindFromRedraw.bind(this, '_zoomIn'), 'keyup'); + this.keycharm.bind('pagedown', this.unbindFromRedraw.bind(this, '_zoomOut'), 'keyup'); + } + } } }]); - return Image; - })(_utilCircleImageBase2['default']); + return NavigationHandler; + })(); - exports['default'] = Image; + exports['default'] = NavigationHandler; module.exports = exports['default']; /***/ }, -/* 93 */ +/* 87 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37303,197 +37285,358 @@ return /******/ (function(modules) { // webpackBootstrap 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) { 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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(103); + /** + * Popup is a class to create a popup window with some text + * @param {Element} container The container object. + * @param {Number} [x] + * @param {Number} [y] + * @param {String} [text] + * @param {Object} [style] An object containing borderColor, + * backgroundColor, etc. + */ - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var Popup = (function () { + function Popup(container) { + _classCallCheck(this, Popup); - 'use strict'; + this.container = container; - var Square = (function (_ShapeBase) { - function Square(options, body, labelModule) { - _classCallCheck(this, Square); + this.x = 0; + this.y = 0; + this.padding = 5; + this.hidden = false; - _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); + // create the frame + this.frame = document.createElement('div'); + this.frame.className = 'vis-network-tooltip'; + this.container.appendChild(this.frame); } - _inherits(Square, _ShapeBase); + _createClass(Popup, [{ + key: 'setPosition', - _createClass(Square, [{ - key: 'resize', - value: function resize() { - this._resizeShape(); + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ + value: function setPosition(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); } }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'square', 2, x, y, selected, hover); + key: 'setText', + + /** + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content + */ + value: function setText(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); + } else { + this.frame.innerHTML = content; // string containing text or HTML + } } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); - } - }]); - - return Square; - })(_utilShapeBase2['default']); + key: 'show', - exports['default'] = Square; - module.exports = exports['default']; - -/***/ }, -/* 94 */ -/***/ 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) { 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); } } }; - - 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) subClass.__proto__ = superClass; } - - var _utilShapeBase = __webpack_require__(103); - - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - - 'use strict'; + /** + * Show the popup window + * @param {boolean} [doShow] Show or hide the window + */ + value: function show(doShow) { + if (doShow === undefined) { + doShow = true; + } - var Star = (function (_ShapeBase) { - function Star(options, body, labelModule) { - _classCallCheck(this, Star); + if (doShow === true) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; - _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); - } + var top = this.y - height; + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } - _inherits(Star, _ShapeBase); + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; + } - _createClass(Star, [{ - key: 'resize', - value: function resize(ctx) { - this._resizeShape(); - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'star', 4, x, y, selected, hover); + this.frame.style.left = left + 'px'; + this.frame.style.top = top + 'px'; + this.frame.style.visibility = 'visible'; + this.hidden = false; + } else { + this.hide(); + } } }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + key: 'hide', + + /** + * Hide the popup window + */ + value: function hide() { + this.hidden = true; + this.frame.style.visibility = 'hidden'; } }]); - return Star; - })(_utilShapeBase2['default']); + return Popup; + })(); - exports['default'] = Star; + exports['default'] = Popup; module.exports = exports['default']; /***/ }, -/* 95 */ +/* 88 */ /***/ 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 __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + /** + * Created by Alex on 11/6/2014. + */ - 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); } } }; + // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60 + // if the module has no dependencies, the above pattern can be simplified to + (function (root, factory) { + if (true) { + // AMD. Register as an anonymous module. + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.keycharm = factory(); + } + }(this, function () { - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + var container = options && options.container || window; + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; - 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; } + // a - z + for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} + // A - Z + for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} + // 0 - 9 + for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} + // F1 - F12 + for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} + // num0 - num9 + for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - var _utilNodeBase = __webpack_require__(101); + // numpad misc + _keys['num*'] = {code:106, shift: false}; + _keys['num+'] = {code:107, shift: false}; + _keys['num-'] = {code:109, shift: false}; + _keys['num/'] = {code:111, shift: false}; + _keys['num.'] = {code:110, shift: false}; + // arrows + _keys['left'] = {code:37, shift: false}; + _keys['up'] = {code:38, shift: false}; + _keys['right'] = {code:39, shift: false}; + _keys['down'] = {code:40, shift: false}; + // extra keys + _keys['space'] = {code:32, shift: false}; + _keys['enter'] = {code:13, shift: false}; + _keys['shift'] = {code:16, shift: undefined}; + _keys['esc'] = {code:27, shift: false}; + _keys['backspace'] = {code:8, shift: false}; + _keys['tab'] = {code:9, shift: false}; + _keys['ctrl'] = {code:17, shift: false}; + _keys['alt'] = {code:18, shift: false}; + _keys['delete'] = {code:46, shift: false}; + _keys['pageup'] = {code:33, shift: false}; + _keys['pagedown'] = {code:34, shift: false}; + // symbols + _keys['='] = {code:187, shift: false}; + _keys['-'] = {code:189, shift: false}; + _keys[']'] = {code:221, shift: false}; + _keys['['] = {code:219, shift: false}; - var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - 'use strict'; - var Text = (function (_NodeBase) { - function Text(options, body, labelModule) { - _classCallCheck(this, Text); + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; - _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); - } + // handle the actualy bound key with the event + var handleEvent = function(event,type) { + if (_bound[type][event.keyCode] !== undefined) { + var bound = _bound[type][event.keyCode]; + for (var i = 0; i < bound.length; i++) { + if (bound[i].shift === undefined) { + bound[i].fn(event); + } + else if (bound[i].shift == true && event.shiftKey == true) { + bound[i].fn(event); + } + else if (bound[i].shift == false && event.shiftKey == false) { + bound[i].fn(event); + } + } - _inherits(Text, _NodeBase); + if (preventDefault == true) { + event.preventDefault(); + } + } + }; - _createClass(Text, [{ - key: 'resize', - value: function resize(ctx, selected) { - if (this.width === undefined) { - var margin = 5; - var textSize = this.labelModule.getTextSize(ctx, selected); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; } - } - }, { - key: 'draw', - value: function draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected || hover); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (_bound[type][_keys[key].code] === undefined) { + _bound[type][_keys[key].code] = []; + } + _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); + }; - // draw shadow if enabled - this.enableShadow(ctx); - this.labelModule.draw(ctx, x, y, selected || hover); - // disable shadows for other elements. - this.disableShadow(ctx); + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; + } + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); + } + } + }; + + // get the key label from an event + _exportFunctions.getKey = function(event) { + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { + return key; + } + else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { + return key; + } + else if (event.keyCode == _keys[key].code && key == 'shift') { + return key; + } + } + } + return "unknown key, currently not supported"; + }; + + // unbind either a specific callback from a key or all of them (by leaving callback undefined) + _exportFunctions.unbind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (callback !== undefined) { + var newBindings = []; + var bound = _bound[type][_keys[key].code]; + if (bound !== undefined) { + for (var i = 0; i < bound.length; i++) { + if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { + newBindings.push(_bound[type][_keys[key].code][i]); + } + } + } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; + } + }; + + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; + + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; + + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); + + // return the public functions. + return _exportFunctions; + } + + return keycharm; + })); - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - } - }, { - key: 'distanceToBorder', - value: function distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); - } - }]); - return Text; - })(_utilNodeBase2['default']); - exports['default'] = Text; - module.exports = exports['default']; /***/ }, -/* 96 */ +/* 89 */ +/***/ function(module, exports, __webpack_require__) { + + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 89; + + +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } + + +/***/ }, +/* 91 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__; + + /* WEBPACK VAR INJECTION */}.call(exports, {})) + +/***/ }, +/* 92 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37514,46 +37657,94 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilShapeBase = __webpack_require__(103); + var _utilNodeBase = __webpack_require__(109); - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); 'use strict'; - var Triangle = (function (_ShapeBase) { - function Triangle(options, body, labelModule) { - _classCallCheck(this, Triangle); + var Box = (function (_NodeBase) { + function Box(options, body, labelModule) { + _classCallCheck(this, Box); - _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(Triangle, _ShapeBase); + _inherits(Box, _NodeBase); - _createClass(Triangle, [{ + _createClass(Box, [{ key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth /= this.body.view.scale; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + + var borderRadius = 6; + ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return Triangle; - })(_utilShapeBase2['default']); + return Box; + })(_utilNodeBase2['default']); - exports['default'] = Triangle; + exports['default'] = Box; module.exports = exports['default']; /***/ }, -/* 97 */ +/* 93 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37574,46 +37765,79 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilShapeBase = __webpack_require__(103); + var _utilCircleImageBase = __webpack_require__(110); - var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); 'use strict'; - var TriangleDown = (function (_ShapeBase) { - function TriangleDown(options, body, labelModule) { - _classCallCheck(this, TriangleDown); + var Circle = (function (_CircleImageBase) { + function Circle(options, body, labelModule) { + _classCallCheck(this, Circle); - _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); + _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(TriangleDown, _ShapeBase); + _inherits(Circle, _CircleImageBase); - _createClass(TriangleDown, [{ + _createClass(Circle, [{ key: 'resize', - value: function resize(ctx) { - this._resizeShape(); + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.size = diameter / 2; + + this.width = diameter; + this.height = diameter; + } } }, { key: 'draw', value: function draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; + + this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); + + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; } }, { key: 'distanceToBorder', value: function distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return TriangleDown; - })(_utilShapeBase2['default']); + return Circle; + })(_utilCircleImageBase2['default']); - exports['default'] = TriangleDown; + exports['default'] = Circle; module.exports = exports['default']; /***/ }, -/* 98 */ +/* 94 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37634,144 +37858,93 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilBezierEdgeBase = __webpack_require__(104); - - var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); + var _utilCircleImageBase = __webpack_require__(110); - var BezierEdgeDynamic = (function (_BezierEdgeBase) { - function BezierEdgeDynamic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeDynamic); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); - //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. - _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below - } + 'use strict'; - _inherits(BezierEdgeDynamic, _BezierEdgeBase); + var CircularImage = (function (_CircleImageBase) { + function CircularImage(options, body, labelModule, imageObj) { + _classCallCheck(this, CircularImage); - _createClass(BezierEdgeDynamic, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - this.from = this.body.nodes[this.options.from]; - this.to = this.body.nodes[this.options.to]; - this.id = this.options.id; - this.setupSupportNode(); + _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule); + this.imageObj = imageObj; + this._swapToImageResizeWhenImageLoaded = true; + } - // fix weird behaviour - if (this.from.id === this.to.id) { - this.via.setOptions({ physics: false }); + _inherits(CircularImage, _CircleImageBase); + + _createClass(CircularImage, [{ + key: 'resize', + value: function resize() { + if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) { + if (!this.width) { + var diameter = this.options.size * 2; + this.width = diameter; + this.height = diameter; + this._swapToImageResizeWhenImageLoaded = true; + } } else { - this.via.setOptions({ physics: true }); - } - } - }, { - key: 'cleanup', - value: function cleanup() { - if (this.via !== undefined) { - delete this.body.nodes[this.via.id]; - this.via = undefined; - return true; + if (this._swapToImageResizeWhenImageLoaded) { + this.width = undefined; + this.height = undefined; + this._swapToImageResizeWhenImageLoaded = false; + } + this._resizeImage(); } - return false; } }, { - key: 'togglePhysics', - value: function togglePhysics(status) { - this.via.setOptions({ physics: status }); - } - }, { - key: 'setupSupportNode', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(); - /** - * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but - * are used for the force calculation. - * - * The changed data is not called, if needed, it is returned by the main edge constructor. - * @private - */ - value: function setupSupportNode() { - if (this.via === undefined) { - var nodeId = 'edgeId:' + this.id; - var node = this.body.functions.createNode({ - id: nodeId, - shape: 'circle', - physics: true, - hidden: true - }); - this.body.nodes[nodeId] = node; - this.via = node; - this.via.parentEdgeId = this.id; - this.positionBezierNode(); - } - } - }, { - key: 'positionBezierNode', - value: function positionBezierNode() { - if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { - this.via.x = 0.5 * (this.from.x + this.to.x); - this.via.y = 0.5 * (this.from.y + this.to.y); - } else if (this.via !== undefined) { - this.via.x = 0; - this.via.y = 0; - } - } - }, { - key: '_line', + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); - // draw shadow if enabled - this.enableShadow(ctx); + var size = Math.min(0.5 * this.height, 0.5 * this.width); + this._drawRawCircle(ctx, x, y, selected, hover, size); + + ctx.save(); + ctx.circle(x, y, size); ctx.stroke(); - this.disableShadow(ctx); - return this.via; - } - }, { - key: 'getPoint', + ctx.clip(); - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - var t = percentage; - var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; - var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; + this._drawImageAtPosition(ctx); - return { x: x, y: y }; + ctx.restore(); + + this._drawImageLabel(ctx, x, y, selected); + + this.updateBoundingBox(x, y); } }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - return this._findBorderPositionBezier(nearNode, ctx, this.via); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); } }]); - return BezierEdgeDynamic; - })(_utilBezierEdgeBase2['default']); + return CircularImage; + })(_utilCircleImageBase2['default']); - exports['default'] = BezierEdgeDynamic; + exports['default'] = CircularImage; module.exports = exports['default']; /***/ }, -/* 99 */ +/* 95 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -37782,9 +37955,9 @@ return /******/ (function(modules) { // webpackBootstrap 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(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, - property = _x5, - receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = 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); } } }; + 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); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -37792,252 +37965,154 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilBezierEdgeBase = __webpack_require__(104); + var _utilNodeBase = __webpack_require__(109); - var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var BezierEdgeStatic = (function (_BezierEdgeBase) { - function BezierEdgeStatic(options, body, labelModule) { - _classCallCheck(this, BezierEdgeStatic); + 'use strict'; - _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); + var Database = (function (_NodeBase) { + function Database(options, body, labelModule) { + _classCallCheck(this, Database); + + _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(BezierEdgeStatic, _BezierEdgeBase); + _inherits(Database, _NodeBase); - _createClass(BezierEdgeStatic, [{ - key: 'cleanup', - value: function cleanup() { - return false; + _createClass(Database, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + } } }, { - key: '_line', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - var via = this._getViaCoordinates(); - var returnValue = via; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth; + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); - // fallback to normal straight edges - if (via.x === undefined) { - ctx.lineTo(this.to.x, this.to.y); - returnValue = undefined; - } else { - ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); - } // draw shadow if enabled this.enableShadow(ctx); - ctx.stroke(); + ctx.fill(); + + // disable shadows for other elements. this.disableShadow(ctx); - return returnValue; + + ctx.stroke(); + + this.updateBoundingBox(x, y); + + this.labelModule.draw(ctx, x, y, selected); } }, { - key: '_getViaCoordinates', - value: function _getViaCoordinates() { - var xVia = undefined; - var yVia = undefined; - var factor = this.options.smooth.roundness; - var type = this.options.smooth.type; - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type === 'discrete' || type === 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } - } - if (type === 'discrete') { - xVia = dx < factor * dy ? this.from.x : xVia; - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } - } - if (type === 'discrete') { - yVia = dy < factor * dx ? this.from.y : yVia; - } - } - } else if (type === 'straightCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } else { - yVia = this.to.y + (1 - factor) * dy; - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } - } else if (type === 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } else if (type === 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } else { - yVia = this.to.y + (1 - factor) * dy; - } - } else if (type === 'curvedCW') { - dx = this.to.x - this.from.x; - dy = this.from.y - this.to.y; - var radius = Math.sqrt(dx * dx + dy * dy); - var pi = Math.PI; + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; - var originalAngle = Math.atan2(dy, dx); - var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); + } + }]); - xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); - yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); - } else if (type === 'curvedCCW') { - dx = this.to.x - this.from.x; - dy = this.from.y - this.to.y; - var radius = Math.sqrt(dx * dx + dy * dy); - var pi = Math.PI; + return Database; + })(_utilNodeBase2['default']); - var originalAngle = Math.atan2(dy, dx); - var myAngle = (originalAngle + (-factor * 0.5 + 0.5) * pi) % (2 * pi); + exports['default'] = Database; + module.exports = exports['default']; - xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); - yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); - } else { - // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - } else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - } - } - } - return { x: xVia, y: yVia }; - } - }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - var options = arguments[2] === undefined ? {} : arguments[2]; +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { - return this._findBorderPositionBezier(nearNode, ctx, options.via); + '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) { 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); } } }; + + 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) subClass.__proto__ = superClass; } + + var _utilShapeBase = __webpack_require__(111); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var Diamond = (function (_ShapeBase) { + function Diamond(options, body, labelModule) { + _classCallCheck(this, Diamond); + + _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(Diamond, _ShapeBase); + + _createClass(Diamond, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - var via = arguments[6] === undefined ? this._getViaCoordinates() : arguments[6]; - // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); } }, { - key: 'getPoint', - - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - var via = arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; - - var t = percentage; - var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * this.to.x; - var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * this.to.y; - - return { x: x, y: y }; + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); } }]); - return BezierEdgeStatic; - })(_utilBezierEdgeBase2['default']); + return Diamond; + })(_utilShapeBase2['default']); - exports['default'] = BezierEdgeStatic; + exports['default'] = Diamond; module.exports = exports['default']; /***/ }, -/* 100 */ +/* 97 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38058,164 +38133,46 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilEdgeBase = __webpack_require__(105); + var _utilShapeBase = __webpack_require__(111); - var _utilEdgeBase2 = _interopRequireDefault(_utilEdgeBase); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - var StraightEdge = (function (_EdgeBase) { - function StraightEdge(options, body, labelModule) { - _classCallCheck(this, StraightEdge); + 'use strict'; - _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); + var Dot = (function (_ShapeBase) { + function Dot(options, body, labelModule) { + _classCallCheck(this, Dot); + + _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(StraightEdge, _EdgeBase); + _inherits(Dot, _ShapeBase); - _createClass(StraightEdge, [{ - key: 'cleanup', - value: function cleanup() { - return false; - } - }, { - key: '_line', - - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function _line(ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); - // draw shadow if enabled - this.enableShadow(ctx); - ctx.stroke(); - this.disableShadow(ctx); - return undefined; - } - }, { - key: 'getPoint', - - /** - * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way - * @param percentage - * @param via - * @returns {{x: number, y: number}} - * @private - */ - value: function getPoint(percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - }; - } - }, { - key: '_findBorderPosition', - value: function _findBorderPosition(nearNode, ctx) { - var node1 = this.to; - var node2 = this.from; - if (nearNode.id === this.from.id) { - node1 = this.from; - node2 = this.to; - } - - var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); - var dx = node1.x - node2.x; - var dy = node1.y - node2.y; - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = nearNode.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - - var borderPos = {}; - borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x; - borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y; - - return borderPos; - } - }, { - key: '_getDistanceToEdge', - value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { - // x3,y3 is the point - return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); - } - }]); - - return StraightEdge; - })(_utilEdgeBase2['default']); - - exports['default'] = StraightEdge; - module.exports = exports['default']; - -/***/ }, -/* 101 */ -/***/ 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 NodeBase = (function () { - function NodeBase(options, body, labelModule) { - _classCallCheck(this, NodeBase); - - this.body = body; - this.labelModule = labelModule; - this.setOptions(options); - this.top = undefined; - this.left = undefined; - this.height = undefined; - this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; - } - - _createClass(NodeBase, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - } - }, { - key: '_distanceToBorder', - value: function _distanceToBorder(angle) { - var borderWidth = 1; - return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + _createClass(Dot, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: 'enableShadow', - value: function enableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0.5)'; - ctx.shadowBlur = this.options.shadow.size; - ctx.shadowOffsetX = this.options.shadow.x; - ctx.shadowOffsetY = this.options.shadow.y; - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'circle', 2, x, y, selected, hover); } }, { - key: 'disableShadow', - value: function disableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0)'; - ctx.shadowBlur = 0; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - } + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this.options.size + this.options.borderWidth; } }]); - return NodeBase; - })(); + return Dot; + })(_utilShapeBase2['default']); - exports['default'] = NodeBase; + exports['default'] = Dot; module.exports = exports['default']; /***/ }, -/* 102 */ +/* 98 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38236,57 +38193,52 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilNodeBase = __webpack_require__(101); + var _utilNodeBase = __webpack_require__(109); var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var CircleImageBase = (function (_NodeBase) { - function CircleImageBase(options, body, labelModule) { - _classCallCheck(this, CircleImageBase); + 'use strict'; - _get(Object.getPrototypeOf(CircleImageBase.prototype), 'constructor', this).call(this, options, body, labelModule); + var Ellipse = (function (_NodeBase) { + function Ellipse(options, body, labelModule) { + _classCallCheck(this, Ellipse); + + _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(CircleImageBase, _NodeBase); + _inherits(Ellipse, _NodeBase); - _createClass(CircleImageBase, [{ - key: '_resizeImage', - value: function _resizeImage() { - if (!this.width || !this.height) { - // undefined or 0 - var width, height, ratio; - if (this.imageObj.width && this.imageObj.height) { - // not undefined or 0 - width = 0; - height = 0; - } - if (this.imageObj.width > this.imageObj.height) { - ratio = this.imageObj.width / this.imageObj.height; - width = this.options.size * 2 * ratio || this.imageObj.width; - height = this.options.size * 2 || this.imageObj.height; - } else { - ratio = this.imageObj.height / this.imageObj.width; - width = this.options.size * 2 || this.imageObj.width; - height = this.options.size * 2 * ratio || this.imageObj.height; + _createClass(Ellipse, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var textSize = this.labelModule.getTextSize(ctx, selected); + + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; } - this.width = width; - this.height = height; } } }, { - key: '_drawRawCircle', - value: function _drawRawCircle(ctx, x, y, selected, hover, size) { + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected); + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + var borderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth /= this.body.view.scale; ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(x, y, size); + ctx.ellipse(this.left, this.top, this.width, this.height); // draw shadow if enabled this.enableShadow(ctx); @@ -38296,49 +38248,41 @@ return /******/ (function(modules) { // webpackBootstrap this.disableShadow(ctx); ctx.stroke(); + + this.updateBoundingBox(x, y); + this.labelModule.draw(ctx, x, y, selected); } }, { - key: '_drawImageAtPosition', - value: function _drawImageAtPosition(ctx) { - if (this.imageObj.width != 0) { - // draw the image - ctx.globalAlpha = 1; - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; - // disable shadows for other elements. - this.disableShadow(ctx); - } + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; } }, { - key: '_drawImageLabel', - value: function _drawImageLabel(ctx, x, y, selected) { - var yLabel; - var offset = 0; - - if (this.height !== undefined) { - offset = this.height * 0.5; - var labelDimensions = this.labelModule.getTextSize(ctx); - if (labelDimensions.lineCount >= 1) { - offset += labelDimensions.height / 2; - } - } - - yLabel = y + offset; - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width * 0.5; + var b = this.height * 0.5; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return CircleImageBase; + return Ellipse; })(_utilNodeBase2['default']); - exports['default'] = CircleImageBase; + exports['default'] = Ellipse; module.exports = exports['default']; /***/ }, -/* 103 */ +/* 99 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38359,78 +38303,105 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _utilNodeBase = __webpack_require__(101); + var _utilNodeBase = __webpack_require__(109); var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - var ShapeBase = (function (_NodeBase) { - function ShapeBase(options, body, labelModule) { - _classCallCheck(this, ShapeBase); + 'use strict'; - _get(Object.getPrototypeOf(ShapeBase.prototype), 'constructor', this).call(this, options, body, labelModule); + var Icon = (function (_NodeBase) { + function Icon(options, body, labelModule) { + _classCallCheck(this, Icon); + + _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule); } - _inherits(ShapeBase, _NodeBase); + _inherits(Icon, _NodeBase); - _createClass(ShapeBase, [{ - key: '_resizeShape', - value: function _resizeShape() { + _createClass(Icon, [{ + key: 'resize', + value: function resize(ctx) { if (this.width === undefined) { - var size = 2 * this.options.size; - this.width = size; - this.height = size; + var margin = 5; + var iconSize = { + width: Number(this.options.icon.size), + height: Number(this.options.icon.size) + }; + this.width = iconSize.width + 2 * margin; + this.height = iconSize.height + 2 * margin; } } }, { - key: '_drawShape', - value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { - this._resizeShape(); + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx); + this.options.icon.size = this.options.icon.size || 50; - this.left = x - this.width / 2; - this.top = y - this.height / 2; + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + this._icon(ctx, x, y, selected); - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); + } - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = selected ? selectionLineWidth : borderWidth; - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](x, y, this.options.size); + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.icon.size * 0.5; + this.boundingBox.left = x - this.options.icon.size * 0.5; + this.boundingBox.right = x + this.options.icon.size * 0.5; + this.boundingBox.bottom = y + this.options.icon.size * 0.5; - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); + } + } + }, { + key: '_icon', + value: function _icon(ctx, x, y, selected) { + var iconSize = Number(this.options.icon.size); - // disable shadows for other elements. - this.disableShadow(ctx); + if (this.options.icon.code !== undefined) { + ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face; - ctx.stroke(); + // draw icon + ctx.fillStyle = this.options.icon.color || 'black'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fillText(this.options.icon.code, x, y); - if (this.options.label !== undefined) { - var yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + // disable shadows for other elements. + this.disableShadow(ctx); + } else { + console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); } } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + this._distanceToBorder(angle); + } }]); - return ShapeBase; + return Icon; })(_utilNodeBase2['default']); - exports['default'] = ShapeBase; + exports['default'] = Icon; module.exports = exports['default']; /***/ }, -/* 104 */ +/* 100 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38441,9 +38412,9 @@ return /******/ (function(modules) { // webpackBootstrap 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(_x2, _x3, _x4) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x2, - property = _x3, - receiver = _x4; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = 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); } } }; + 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); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -38451,135 +38422,77 @@ return /******/ (function(modules) { // webpackBootstrap 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; } - var _EdgeBase2 = __webpack_require__(105); + var _utilCircleImageBase = __webpack_require__(110); - var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); + var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase); - var BezierEdgeBase = (function (_EdgeBase) { - function BezierEdgeBase(options, body, labelModule) { - _classCallCheck(this, BezierEdgeBase); + 'use strict'; - _get(Object.getPrototypeOf(BezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); + var Image = (function (_CircleImageBase) { + function Image(options, body, labelModule, imageObj) { + _classCallCheck(this, Image); + + _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule); + this.imageObj = imageObj; } - _inherits(BezierEdgeBase, _EdgeBase); + _inherits(Image, _CircleImageBase); - _createClass(BezierEdgeBase, [{ - key: '_findBorderPositionBezier', + _createClass(Image, [{ + key: 'resize', + value: function resize() { + this._resizeImage(); + } + }, { + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - * @param nearNode - * @param ctx - * @param viaNode - */ - value: function _findBorderPositionBezier(nearNode, ctx) { - var viaNode = arguments[2] === undefined ? this._getViaCoordinates() : arguments[2]; + this._drawImageAtPosition(ctx); - var maxIterations = 10; - var iteration = 0; - var low = 0; - var high = 1; - var pos, angle, distanceToBorder, distanceToPoint, difference; - var threshold = 0.2; - var node = this.to; - var from = false; - if (nearNode.id === this.from.id) { - node = this.from; - from = true; - } + this._drawImageLabel(ctx, x, y, selected || hover); - while (low <= high && iteration < maxIterations) { - var middle = (low + high) * 0.5; + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; - pos = this.getPoint(middle, viaNode); - angle = Math.atan2(node.y - pos.y, node.x - pos.x); - distanceToBorder = node.distanceToBorder(ctx, angle); - distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); - difference = distanceToBorder - distanceToPoint; - if (Math.abs(difference) < threshold) { - break; // found - } else if (difference < 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from === false) { - low = middle; - } else { - high = middle; - } - } else { - if (from === false) { - high = middle; - } else { - low = middle; - } - } + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - iteration++; + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } - pos.t = middle; - - return pos; } }, { - key: '_getDistanceToBezierEdge', - - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private - */ - value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { - // x3,y3 is the point - var xVia = undefined, - yVia = undefined; - xVia = via.x; - yVia = via.y; - var minDistance = 1000000000; - var distance = undefined; - var i = undefined, - t = undefined, - x = undefined, - y = undefined; - var lastX = x1; - var lastY = y1; - for (i = 1; i < 10; i++) { - t = 0.1 * i; - x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * xVia + Math.pow(t, 2) * x2; - y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * yVia + Math.pow(t, 2) * y2; - if (i > 0) { - distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); - minDistance = distance < minDistance ? distance : minDistance; - } - lastX = x; - lastY = y; - } - - return minDistance; + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + var a = this.width / 2; + var b = this.height / 2; + var w = Math.sin(angle) * a; + var h = Math.cos(angle) * b; + return a * b / Math.sqrt(w * w + h * h); } }]); - return BezierEdgeBase; - })(_EdgeBase3['default']); + return Image; + })(_utilCircleImageBase2['default']); - exports['default'] = BezierEdgeBase; + exports['default'] = Image; module.exports = exports['default']; /***/ }, -/* 105 */ +/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -38590,1682 +38503,1888 @@ return /******/ (function(modules) { // webpackBootstrap 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 _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); } } }; - var util = __webpack_require__(1); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - var EdgeBase = (function () { - function EdgeBase(options, body, labelModule) { - _classCallCheck(this, EdgeBase); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - this.body = body; - this.labelModule = labelModule; - this.setOptions(options); - this.colorDirty = true; - this.color = {}; - this.selectionWidth = 2; - this.hoverWidth = 1.5; + 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; } + + var _utilShapeBase = __webpack_require__(111); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var Square = (function (_ShapeBase) { + function Square(options, body, labelModule) { + _classCallCheck(this, Square); + + _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule); } - _createClass(EdgeBase, [{ - key: 'setOptions', - value: function setOptions(options) { - this.options = options; - this.from = this.body.nodes[this.options.from]; - this.to = this.body.nodes[this.options.to]; - this.id = this.options.id; - } - }, { - key: 'togglePhysics', + _inherits(Square, _ShapeBase); - /** - * overloadable if the shape has to toggle the via node to disabled - * @param status - */ - value: function togglePhysics(status) {} + _createClass(Square, [{ + key: 'resize', + value: function resize() { + this._resizeShape(); + } }, { - key: 'drawLine', - - /** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - value: function drawLine(ctx, selected, hover) { - // set style - ctx.strokeStyle = this.getColor(ctx); - ctx.lineWidth = this.getLineWidth(selected, hover); - var via = undefined; - if (this.options.dashes !== false) { - via = this._drawDashedLine(ctx); - } else { - via = this._drawLine(ctx); - } - return via; + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'square', 2, x, y, selected, hover); } }, { - key: '_drawLine', - value: function _drawLine(ctx) { - var via = undefined; - if (this.from != this.to) { - // draw line - via = this._line(ctx); - } else { - var _getCircleData = this._getCircleData(ctx); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); + } + }]); - var _getCircleData2 = _slicedToArray(_getCircleData, 3); + return Square; + })(_utilShapeBase2['default']); - var x = _getCircleData2[0]; - var y = _getCircleData2[1]; - var radius = _getCircleData2[2]; + exports['default'] = Square; + module.exports = exports['default']; - this._circle(ctx, x, y, radius); - } - return via; - } - }, { - key: '_drawDashedLine', - value: function _drawDashedLine(ctx) { - var via = undefined; - ctx.lineCap = 'round'; - var pattern = [5, 5]; - if (Array.isArray(this.options.dashes) === true) { - pattern = this.options.dashes; - } +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.setLineDash !== undefined) { - ctx.save(); + 'use strict'; - // set dash settings for chrome or firefox - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; + Object.defineProperty(exports, '__esModule', { + value: true + }); - // draw the line - if (this.from != this.to) { - // draw line - via = this._line(ctx); - } else { - var _getCircleData3 = this._getCircleData(ctx); + 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 _getCircleData32 = _slicedToArray(_getCircleData3, 3); + 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); } } }; - var x = _getCircleData32[0]; - var y = _getCircleData32[1]; - var radius = _getCircleData32[2]; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - this._circle(ctx, x, y, radius); - } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - // restore the dash settings. - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; - ctx.restore(); - } else { - // unsupporting smooth lines + 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; } - if (this.from != this.to) { - // draw line - ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern); - } else { - var _getCircleData4 = this._getCircleData(ctx); + var _utilShapeBase = __webpack_require__(111); - var _getCircleData42 = _slicedToArray(_getCircleData4, 3); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - var x = _getCircleData42[0]; - var y = _getCircleData42[1]; - var radius = _getCircleData42[2]; + 'use strict'; - this._circle(ctx, x, y, radius); - } - // draw shadow if enabled - this.enableShadow(ctx); + var Star = (function (_ShapeBase) { + function Star(options, body, labelModule) { + _classCallCheck(this, Star); - ctx.stroke(); + _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule); + } - // disable shadows for other elements. - this.disableShadow(ctx); - } - return via; + _inherits(Star, _ShapeBase); + + _createClass(Star, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: 'findBorderPosition', - value: function findBorderPosition(nearNode, ctx, options) { - if (this.from != this.to) { - return this._findBorderPosition(nearNode, ctx, options); - } else { - return this._findBorderPositionCircle(nearNode, ctx, options); - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'star', 4, x, y, selected, hover); } }, { - key: 'findBorderPositions', - value: function findBorderPositions(ctx) { - var from = {}; - var to = {}; - if (this.from != this.to) { - from = this._findBorderPosition(this.from, ctx); - to = this._findBorderPosition(this.to, ctx); - } else { - var _getCircleData5 = this._getCircleData(ctx); + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); + } + }]); - var _getCircleData52 = _slicedToArray(_getCircleData5, 3); + return Star; + })(_utilShapeBase2['default']); - var x = _getCircleData52[0]; - var y = _getCircleData52[1]; - var radius = _getCircleData52[2]; + exports['default'] = Star; + module.exports = exports['default']; - from = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); - to = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 }); - } - return { from: from, to: to }; - } - }, { - key: '_getCircleData', - value: function _getCircleData(ctx) { - var x = undefined, - y = undefined; - var node = this.from; - var radius = this.options.selfReferenceSize; +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { - if (ctx !== undefined) { - if (node.shape.width === undefined) { - node.shape.resize(ctx); - } - } + 'use strict'; - // get circle coordinates - if (node.shape.width > node.shape.height) { - x = node.x + node.shape.width * 0.5; - y = node.y - radius; - } else { - x = node.x + radius; - y = node.y - node.shape.height * 0.5; - } - return [x, y, radius]; - } - }, { - key: '_pointOnCircle', + Object.defineProperty(exports, '__esModule', { + value: true + }); - /** - * Get a point on a circle - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ - value: function _pointOnCircle(x, y, radius, percentage) { - var angle = percentage * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - }; - } - }, { - key: '_findBorderPositionCircle', - - /** - * This function uses binary search to look for the point where the circle crosses the border of the node. - * @param node - * @param ctx - * @param options - * @returns {*} - * @private - */ - value: function _findBorderPositionCircle(node, ctx, options) { - var x = options.x; - var y = options.y; - var low = options.low; - var high = options.high; - var direction = options.direction; + 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 maxIterations = 10; - var iteration = 0; - var radius = this.options.selfReferenceSize; - var pos = undefined, - angle = undefined, - distanceToBorder = undefined, - distanceToPoint = undefined, - difference = undefined; - var threshold = 0.05; - var middle = (low + high) * 0.5; + 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); } } }; - while (low <= high && iteration < maxIterations) { - middle = (low + high) * 0.5; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - pos = this._pointOnCircle(x, y, radius, middle); - angle = Math.atan2(node.y - pos.y, node.x - pos.x); - distanceToBorder = node.distanceToBorder(ctx, angle); - distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); - difference = distanceToBorder - distanceToPoint; - if (Math.abs(difference) < threshold) { - break; // found - } else if (difference > 0) { - // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (direction > 0) { - low = middle; - } else { - high = middle; - } - } else { - if (direction > 0) { - high = middle; - } else { - low = middle; - } - } - iteration++; - } - pos.t = middle; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - return pos; - } - }, { - key: 'getLineWidth', + 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 line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ - value: function getLineWidth(selected, hover) { - if (selected === true) { - return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); - } else { - if (hover === true) { - return Math.max(this.hoverWidth, 0.3 / this.body.view.scale); - } else { - return Math.max(this.options.width, 0.3 / this.body.view.scale); - } - } - } - }, { - key: 'getColor', - value: function getColor(ctx) { - var colorOptions = this.options.color; + var _utilNodeBase = __webpack_require__(109); - if (colorOptions.inherit !== false) { - // when this is a loop edge, just use the 'from' method - if (colorOptions.inherit === 'both' && this.from.id !== this.to.id) { - var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); - var fromColor = undefined, - toColor = undefined; - fromColor = this.from.options.color.highlight.border; - toColor = this.to.options.color.highlight.border; + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - if (this.from.selected === false && this.to.selected === false) { - fromColor = util.overrideOpacity(this.from.options.color.border, this.options.color.opacity); - toColor = util.overrideOpacity(this.to.options.color.border, this.options.color.opacity); - } else if (this.from.selected === true && this.to.selected === false) { - toColor = this.to.options.color.border; - } else if (this.from.selected === false && this.to.selected === true) { - fromColor = this.from.options.color.border; - } - grd.addColorStop(0, fromColor); - grd.addColorStop(1, toColor); + 'use strict'; - // -------------------- this returns -------------------- // - return grd; - } + var Text = (function (_NodeBase) { + function Text(options, body, labelModule) { + _classCallCheck(this, Text); - if (this.colorDirty === true) { - if (colorOptions.inherit === 'to') { - this.color.highlight = this.to.options.color.highlight.border; - this.color.hover = this.to.options.color.hover.border; - this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity); - } else { - // (this.options.color.inherit.source === "from") { - this.color.highlight = this.from.options.color.highlight.border; - this.color.hover = this.from.options.color.hover.border; - this.color.color = util.overrideOpacity(this.from.options.color.border, colorOptions.opacity); - } - } - } else if (this.colorDirty === true) { - this.color.highlight = colorOptions.highlight; - this.color.hover = colorOptions.hover; - this.color.color = util.overrideOpacity(colorOptions.color, colorOptions.opacity); - } + _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule); + } - // if color inherit is on and gradients are used, the function has already returned by now. - this.colorDirty = false; + _inherits(Text, _NodeBase); - if (this.selected === true) { - return this.color.highlight; - } else if (this.hover === true) { - return this.color.hover; - } else { - return this.color.color; + _createClass(Text, [{ + key: 'resize', + value: function resize(ctx, selected) { + if (this.width === undefined) { + var margin = 5; + var textSize = this.labelModule.getTextSize(ctx, selected); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; } } }, { - key: '_circle', + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this.resize(ctx, selected || hover); + this.left = x - this.width / 2; + this.top = y - this.height / 2; - /** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private - */ - value: function _circle(ctx, x, y, radius) { // draw shadow if enabled this.enableShadow(ctx); - - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + this.labelModule.draw(ctx, x, y, selected || hover); // disable shadows for other elements. this.disableShadow(ctx); + + this.updateBoundingBox(x, y); } }, { - key: 'getDistanceToEdge', - - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private - */ - value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) { - // x3,y3 is the point - var returnValue = 0; - if (this.from != this.to) { - returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via); - } else { - var _getCircleData6 = this._getCircleData(); - - var _getCircleData62 = _slicedToArray(_getCircleData6, 3); - - var x = _getCircleData62[0]; - var y = _getCircleData62[1]; - var radius = _getCircleData62[2]; - - var dx = x - x3; - var dy = y - y3; - returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radius); - } + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; - if (this.labelModule.size.left < x3 && this.labelModule.size.left + this.labelModule.size.width > x3 && this.labelModule.size.top < y3 && this.labelModule.size.top + this.labelModule.size.height > y3) { - return 0; - } else { - return returnValue; - } + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; } }, { - key: '_getDistanceToLine', - value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) { - var px = x2 - x1; - var py = y2 - y1; - var something = px * px + py * py; - var u = ((x3 - x1) * px + (y3 - y1) * py) / something; + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + this.resize(ctx); + return this._distanceToBorder(angle); + } + }]); - if (u > 1) { - u = 1; - } else if (u < 0) { - u = 0; - } + return Text; + })(_utilNodeBase2['default']); - var x = x1 + u * px; - var y = y1 + u * py; - var dx = x - x3; - var dy = y - y3; + exports['default'] = Text; + module.exports = exports['default']; - //# Note: If the actual distance does not matter, - //# if you only want to compare what this function - //# returns to other results of this function, you - //# can just return the squared distance instead - //# (i.e. remove the sqrt) to gain a little performance +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { - return Math.sqrt(dx * dx + dy * dy); - } - }, { - key: 'drawArrowHead', + 'use strict'; - /** - * - * @param ctx - * @param position - * @param viaNode - */ - value: function drawArrowHead(ctx, position, viaNode, selected, hover) { - // set style - ctx.strokeStyle = this.getColor(ctx); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this.getLineWidth(selected, hover); + Object.defineProperty(exports, '__esModule', { + value: true + }); - // set lets - var angle = undefined; - var length = undefined; - var arrowPos = undefined; - var node1 = undefined; - var node2 = undefined; - var guideOffset = undefined; - var scaleFactor = undefined; + 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; }; })(); - if (position === 'from') { - node1 = this.from; - node2 = this.to; - guideOffset = 0.1; - scaleFactor = this.options.arrows.from.scaleFactor; - } else if (position === 'to') { - node1 = this.to; - node2 = this.from; - guideOffset = -0.1; - scaleFactor = this.options.arrows.to.scaleFactor; - } else { - node1 = this.to; - node2 = this.from; - scaleFactor = this.options.arrows.middle.scaleFactor; - } + 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 not connected to itself - if (node1 != node2) { - if (position !== 'middle') { - // draw arrow head - if (this.options.smooth.enabled === true) { - arrowPos = this.findBorderPosition(node1, ctx, { via: viaNode }); - var guidePos = this.getPoint(Math.max(0, Math.min(1, arrowPos.t + guideOffset)), viaNode); - angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x); - } else { - angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); - arrowPos = this.findBorderPosition(node1, ctx); - } - } else { - angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); - arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow. - } - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * scaleFactor; - ctx.arrow(arrowPos.x, arrowPos.y, angle, length); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - // disable shadows for other elements. - this.disableShadow(ctx); - ctx.stroke(); - } else { - // draw circle - var _angle = undefined, - point = undefined; + 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; } - var _getCircleData7 = this._getCircleData(ctx); + var _utilShapeBase = __webpack_require__(111); - var _getCircleData72 = _slicedToArray(_getCircleData7, 3); + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); - var x = _getCircleData72[0]; - var y = _getCircleData72[1]; - var radius = _getCircleData72[2]; + 'use strict'; - if (position === 'from') { - point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); - _angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; - } else if (position === 'to') { - point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1, direction: 1 }); - _angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; - } else { - point = this._pointOnCircle(x, y, radius, 0.175); - _angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; - } + var Triangle = (function (_ShapeBase) { + function Triangle(options, body, labelModule) { + _classCallCheck(this, Triangle); - // draw the arrowhead - var _length = (10 + 5 * this.options.width) * scaleFactor; - ctx.arrow(point.x, point.y, _angle, _length); + _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule); + } - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); + _inherits(Triangle, _ShapeBase); - // disable shadows for other elements. - this.disableShadow(ctx); - ctx.stroke(); - } + _createClass(Triangle, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: 'enableShadow', - value: function enableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0.5)'; - ctx.shadowBlur = this.options.shadow.size; - ctx.shadowOffsetX = this.options.shadow.x; - ctx.shadowOffsetY = this.options.shadow.y; - } + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); } }, { - key: 'disableShadow', - value: function disableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0)'; - ctx.shadowBlur = 0; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - } + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); } }]); - return EdgeBase; - })(); + return Triangle; + })(_utilShapeBase2['default']); - exports['default'] = EdgeBase; + exports['default'] = Triangle; module.exports = exports['default']; /***/ }, -/* 106 */ +/* 105 */ /***/ 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + 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); } } }; - var BarnesHutSolver = (function () { - function BarnesHutSolver(body, physicsBody, options) { - _classCallCheck(this, BarnesHutSolver); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - this.body = body; - this.physicsBody = physicsBody; - this.barnesHutTree; - this.setOptions(options); + 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) subClass.__proto__ = superClass; } + + var _utilShapeBase = __webpack_require__(111); + + var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase); + + 'use strict'; + + var TriangleDown = (function (_ShapeBase) { + function TriangleDown(options, body, labelModule) { + _classCallCheck(this, TriangleDown); + + _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule); } - _createClass(BarnesHutSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - this.thetaInversed = 1 / this.options.theta; + _inherits(TriangleDown, _ShapeBase); + + _createClass(TriangleDown, [{ + key: 'resize', + value: function resize(ctx) { + this._resizeShape(); } }, { - key: "solve", + key: 'draw', + value: function draw(ctx, x, y, selected, hover) { + this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); + } + }, { + key: 'distanceToBorder', + value: function distanceToBorder(ctx, angle) { + return this._distanceToBorder(angle); + } + }]); - /** - * 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 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; + return TriangleDown; + })(_utilShapeBase2['default']); - // create the tree - var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); + exports['default'] = TriangleDown; + module.exports = exports['default']; - // for debugging - this.barnesHutTree = barnesHutTree; +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { - // 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: "_getForceContribution", + 'use strict'; - /** - * 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 _getForceContribution(parentBranch, node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx = undefined, - dy = undefined, - distance = undefined; + Object.defineProperty(exports, '__esModule', { + value: true + }); - // 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); + 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; }; })(); - // 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); - } - } - } + 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); } } }; + + 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) subClass.__proto__ = superClass; } + + var _utilBezierEdgeBase = __webpack_require__(112); + + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); + + var BezierEdgeDynamic = (function (_BezierEdgeBase) { + function BezierEdgeDynamic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeDynamic); + + //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. + _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), 'constructor', this).call(this, options, body, labelModule); // --> this calls the setOptions below + } + + _inherits(BezierEdgeDynamic, _BezierEdgeBase); + + _createClass(BezierEdgeDynamic, [{ + key: 'setOptions', + value: function setOptions(options) { + this.options = options; + this.from = this.body.nodes[this.options.from]; + this.to = this.body.nodes[this.options.to]; + this.id = this.options.id; + this.setupSupportNode(); + + // fix weird behaviour + if (this.from.id === this.to.id) { + this.via.setOptions({ physics: false }); + } else { + this.via.setOptions({ physics: true }); } } }, { - key: "_calculateForces", + key: 'cleanup', + value: function cleanup() { + if (this.via !== undefined) { + delete this.body.nodes[this.via.id]; + this.via = undefined; + return true; + } + return false; + } + }, { + key: 'togglePhysics', + value: function togglePhysics(status) { + this.via.setOptions({ physics: status }); + } + }, { + key: 'setupSupportNode', /** - * Calculate the forces based on the distance. + * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but + * are used for the force calculation. * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch + * The changed data is not called, if needed, it is returned by the main edge constructor. * @private */ - value: function _calculateForces(distance, dx, dy, node, parentBranch) { - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; + value: function setupSupportNode() { + if (this.via === undefined) { + var nodeId = 'edgeId:' + this.id; + var node = this.body.functions.createNode({ + id: nodeId, + shape: 'circle', + physics: true, + hidden: true + }); + this.body.nodes[nodeId] = node; + this.via = node; + this.via.parentEdgeId = this.id; + this.positionBezierNode(); } - // 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; + } + }, { + key: 'positionBezierNode', + value: function positionBezierNode() { + if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { + this.via.x = 0.5 * (this.from.x + this.to.x); + this.via.y = 0.5 * (this.from.y + this.to.y); + } else if (this.via !== undefined) { + this.via.x = 0; + this.via.y = 0; + } + } + }, { + key: '_line', - this.physicsBody.forces[node.id].x += fx; - this.physicsBody.forces[node.id].y += fy; + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); + // draw shadow if enabled + this.enableShadow(ctx); + ctx.stroke(); + this.disableShadow(ctx); + return this.via; } }, { - key: "_formBarnesHutTree", + key: 'getPoint', /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. - * - * @param nodes - * @param nodeIndices + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} * @private */ - value: function _formBarnesHutTree(nodes, nodeIndices) { - var node = undefined; - var nodeCount = nodeIndices.length; + value: function getPoint(percentage) { + var t = percentage; + var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x; + var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y; - var minX = nodes[nodeIndices[0]].x; - var minY = nodes[nodeIndices[0]].y; - var maxX = nodes[nodeIndices[0]].x; - var maxY = nodes[nodeIndices[0]].y; + return { x: x, y: y }; + } + }, { + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + return this._findBorderPositionBezier(nearNode, ctx, this.via); + } + }, { + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); + } + }]); - // 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 + return BezierEdgeDynamic; + })(_utilBezierEdgeBase2['default']); - 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); + exports['default'] = BezierEdgeDynamic; + module.exports = exports['default']; - // 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); +/***/ }, +/* 107 */ +/***/ function(module, exports, __webpack_require__) { - // 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); - } - } + 'use strict'; - // make global - return barnesHutTree; - } - }, { - key: "_updateBranchMass", + Object.defineProperty(exports, '__esModule', { + value: true + }); - /** - * this updates the mass of a branch. this is increased by adding a node. - * - * @param parentBranch - * @param node - * @private - */ - value: function _updateBranchMass(parentBranch, node) { - var totalMass = parentBranch.mass + node.options.mass; - var totalMassInv = 1 / totalMass; + 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; }; })(); - parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass; - parentBranch.centerOfMass.x *= totalMassInv; + var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x4, + property = _x5, + receiver = _x6; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = 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); } } }; - parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass; - parentBranch.centerOfMass.y *= totalMassInv; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - parentBranch.mass = totalMass; - var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); - parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; + 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) subClass.__proto__ = superClass; } + + var _utilBezierEdgeBase = __webpack_require__(112); + + var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase); + + var BezierEdgeStatic = (function (_BezierEdgeBase) { + function BezierEdgeStatic(options, body, labelModule) { + _classCallCheck(this, BezierEdgeStatic); + + _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(BezierEdgeStatic, _BezierEdgeBase); + + _createClass(BezierEdgeStatic, [{ + key: 'cleanup', + value: function cleanup() { + return false; } }, { - key: "_placeInTree", + key: '_line', /** - * determine in which branch the node will be placed. - * - * @param parentBranch - * @param node - * @param skipMassUpdate + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx * @private */ - value: function _placeInTree(parentBranch, node, skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch, node); - } + value: function _line(ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + var via = this._getViaCoordinates(); + var returnValue = via; - 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"); - } + // fallback to normal straight edges + if (via.x === undefined) { + ctx.lineTo(this.to.x, this.to.y); + returnValue = undefined; } 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"); - } + ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); } + // draw shadow if enabled + this.enableShadow(ctx); + ctx.stroke(); + this.disableShadow(ctx); + return returnValue; } }, { - key: "_placeInRegion", - - /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region - * @private - */ - 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(); + key: '_getViaCoordinates', + value: function _getViaCoordinates() { + var xVia = undefined; + var yVia = undefined; + var factor = this.options.smooth.roundness; + var type = this.options.smooth.type; + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type === 'discrete' || type === 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type === 'discrete') { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type === 'discrete') { + yVia = dy < factor * dx ? this.from.y : yVia; + } + } + } else if (type === 'straightCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; } else { - this._splitBranch(parentBranch.children[region]); - this._placeInTree(parentBranch.children[region], node); + yVia = this.to.y + (1 - factor) * dy; + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + } else if (type === 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } else if (type === 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } else { + yVia = this.to.y + (1 - factor) * dy; + } + } else if (type === 'curvedCW') { + dx = this.to.x - this.from.x; + dy = this.from.y - this.to.y; + var radius = Math.sqrt(dx * dx + dy * dy); + var pi = Math.PI; + + var originalAngle = Math.atan2(dy, dx); + var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); + + xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); + yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); + } else if (type === 'curvedCCW') { + dx = this.to.x - this.from.x; + dy = this.from.y - this.to.y; + var radius = Math.sqrt(dx * dx + dy * dy); + var pi = Math.PI; + + var originalAngle = Math.atan2(dy, dx); + var myAngle = (originalAngle + (-factor * 0.5 + 0.5) * pi) % (2 * pi); + + xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); + yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); + } else { + // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } } - break; - case 4: - // place in branch - this._placeInTree(parentBranch.children[region], node); - break; + } } + return { x: xVia, y: yVia }; } }, { - key: "_splitBranch", - - /** - * 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 _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"); + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + var options = arguments[2] === undefined ? {} : arguments[2]; - if (containedNode != null) { - this._placeInTree(parentBranch, containedNode); - } + return this._findBorderPositionBezier(nearNode, ctx, options.via); } }, { - key: "_insertRegion", + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + var via = arguments[6] === undefined ? this._getViaCoordinates() : arguments[6]; + // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); + } + }, { + key: 'getPoint', /** - * 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 + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} * @private */ - 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; - } + value: function getPoint(percentage) { + var via = arguments[1] === undefined ? this._getViaCoordinates() : arguments[1]; - 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 - }; + var t = percentage; + var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * this.to.x; + var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * this.to.y; + + return { x: x, y: y }; } - }, { - key: "_debug", + }]); - //--------------------------- DEBUGGING BELOW ---------------------------// + return BezierEdgeStatic; + })(_utilBezierEdgeBase2['default']); - /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private - */ - value: function _debug(ctx, color) { - if (this.barnesHutTree !== undefined) { + exports['default'] = BezierEdgeStatic; + module.exports = exports['default']; - ctx.lineWidth = 1; +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { - this._drawBranch(this.barnesHutTree.root, ctx, color); - } + '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) { 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); } } }; + + 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) subClass.__proto__ = superClass; } + + var _utilEdgeBase = __webpack_require__(113); + + var _utilEdgeBase2 = _interopRequireDefault(_utilEdgeBase); + + var StraightEdge = (function (_EdgeBase) { + function StraightEdge(options, body, labelModule) { + _classCallCheck(this, StraightEdge); + + _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(StraightEdge, _EdgeBase); + + _createClass(StraightEdge, [{ + key: 'cleanup', + value: function cleanup() { + return false; } }, { - key: "_drawBranch", + key: '_line', /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx * @private */ - 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(); - + value: function _line(ctx) { + // draw a straight line ctx.beginPath(); - ctx.moveTo(branch.range.maxX, branch.range.minY); - ctx.lineTo(branch.range.maxX, branch.range.maxY); + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); + // draw shadow if enabled + this.enableShadow(ctx); ctx.stroke(); + this.disableShadow(ctx); + return undefined; + } + }, { + key: 'getPoint', - ctx.beginPath(); - ctx.moveTo(branch.range.maxX, branch.range.maxY); - ctx.lineTo(branch.range.minX, branch.range.maxY); - ctx.stroke(); + /** + * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way + * @param percentage + * @param via + * @returns {{x: number, y: number}} + * @private + */ + value: function getPoint(percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y + }; + } + }, { + key: '_findBorderPosition', + value: function _findBorderPosition(nearNode, ctx) { + var node1 = this.to; + var node2 = this.from; + if (nearNode.id === this.from.id) { + node1 = this.from; + node2 = this.to; + } - ctx.beginPath(); - ctx.moveTo(branch.range.minX, branch.range.maxY); - ctx.lineTo(branch.range.minX, branch.range.minY); - ctx.stroke(); + var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); + var dx = node1.x - node2.x; + var dy = node1.y - node2.y; + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = nearNode.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - /* - if (branch.mass > 0) { - ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); - ctx.stroke(); - } - */ + var borderPos = {}; + borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x; + borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y; + + return borderPos; + } + }, { + key: '_getDistanceToEdge', + value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { + // x3,y3 is the point + return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); } }]); - return BarnesHutSolver; - })(); + return StraightEdge; + })(_utilEdgeBase2['default']); - exports["default"] = BarnesHutSolver; - module.exports = exports["default"]; + exports['default'] = StraightEdge; + module.exports = exports['default']; /***/ }, -/* 107 */ +/* 109 */ /***/ 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 _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 RepulsionSolver = (function () { - function RepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, RepulsionSolver); + var NodeBase = (function () { + function NodeBase(options, body, labelModule) { + _classCallCheck(this, NodeBase); this.body = body; - this.physicsBody = physicsBody; + this.labelModule = labelModule; this.setOptions(options); + this.top = undefined; + this.left = undefined; + this.height = undefined; + this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; } - _createClass(RepulsionSolver, [{ - key: "setOptions", + _createClass(NodeBase, [{ + key: 'setOptions', value: function setOptions(options) { this.options = options; } }, { - key: "solve", + key: '_distanceToBorder', + value: function _distanceToBorder(angle) { + var borderWidth = 1; + return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + } + }, { + key: 'enableShadow', + value: function enableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0.5)'; + ctx.shadowBlur = this.options.shadow.size; + ctx.shadowOffsetX = this.options.shadow.x; + ctx.shadowOffsetY = this.options.shadow.y; + } + } + }, { + key: 'disableShadow', + value: function disableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0)'; + ctx.shadowBlur = 0; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + } + } + }]); - /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2; + return NodeBase; + })(); - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; + exports['default'] = NodeBase; + module.exports = exports['default']; - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; +/***/ }, +/* 110 */ +/***/ function(module, exports, __webpack_require__) { - // approximation constants - var a = -2 / 3 / nodeDistance; - var b = 4 / 3; + 'use strict'; - // 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]]; + Object.defineProperty(exports, '__esModule', { + value: true + }); - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + 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; }; })(); - // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; - } + 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 (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; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - fx = dx * repulsingForce; - fy = dy * repulsingForce; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } + 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; } + + var _utilNodeBase = __webpack_require__(109); + + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); + + var CircleImageBase = (function (_NodeBase) { + function CircleImageBase(options, body, labelModule) { + _classCallCheck(this, CircleImageBase); + + _get(Object.getPrototypeOf(CircleImageBase.prototype), 'constructor', this).call(this, options, body, labelModule); + this.labelOffset = 0; + } + + _inherits(CircleImageBase, _NodeBase); + + _createClass(CircleImageBase, [{ + key: '_resizeImage', + value: function _resizeImage() { + if (!this.width || !this.height) { + // undefined or 0 + var width, height, ratio; + if (this.imageObj.width && this.imageObj.height) { + // not undefined or 0 + width = 0; + height = 0; + } + if (this.imageObj.width > this.imageObj.height) { + ratio = this.imageObj.width / this.imageObj.height; + width = this.options.size * 2 * ratio || this.imageObj.width; + height = this.options.size * 2 || this.imageObj.height; + } else { + ratio = this.imageObj.height / this.imageObj.width; + width = this.options.size * 2 || this.imageObj.width; + height = this.options.size * 2 * ratio || this.imageObj.height; + } + this.width = width; + this.height = height; + } + } + }, { + key: '_drawRawCircle', + value: function _drawRawCircle(ctx, x, y, selected, hover, size) { + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.circle(x, y, size); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + + // disable shadows for other elements. + this.disableShadow(ctx); + + ctx.stroke(); + } + }, { + key: '_drawImageAtPosition', + value: function _drawImageAtPosition(ctx) { + if (this.imageObj.width != 0) { + // draw the image + ctx.globalAlpha = 1; + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + + // disable shadows for other elements. + this.disableShadow(ctx); + } + } + }, { + key: '_drawImageLabel', + value: function _drawImageLabel(ctx, x, y, selected) { + var yLabel; + var offset = 0; + + if (this.height !== undefined) { + offset = this.height * 0.5; + var labelDimensions = this.labelModule.getTextSize(ctx); + if (labelDimensions.lineCount >= 1) { + offset += labelDimensions.height / 2; } } + + yLabel = y + offset; + + if (this.options.label) { + this.labelOffset = offset; + } + this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); } }]); - return RepulsionSolver; - })(); + return CircleImageBase; + })(_utilNodeBase2['default']); - exports["default"] = RepulsionSolver; - module.exports = exports["default"]; + exports['default'] = CircleImageBase; + module.exports = exports['default']; /***/ }, -/* 108 */ +/* 111 */ /***/ 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; }; })(); + + 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); } } }; + + 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) subClass.__proto__ = superClass; } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var _utilNodeBase = __webpack_require__(109); - var HierarchicalRepulsionSolver = (function () { - function HierarchicalRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, HierarchicalRepulsionSolver); + var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase); - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); + var ShapeBase = (function (_NodeBase) { + function ShapeBase(options, body, labelModule) { + _classCallCheck(this, ShapeBase); + + _get(Object.getPrototypeOf(ShapeBase.prototype), 'constructor', this).call(this, options, body, labelModule); } - _createClass(HierarchicalRepulsionSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; + _inherits(ShapeBase, _NodeBase); + + _createClass(ShapeBase, [{ + key: '_resizeShape', + value: function _resizeShape() { + if (this.width === undefined) { + var size = 2 * this.options.size; + this.width = size; + this.height = size; + } } }, { - key: "solve", + key: '_drawShape', + value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { + this._resizeShape(); - /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - value: function solve() { - var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; + this.left = x - this.width / 2; + this.top = y - this.height / 2; - var nodes = this.body.nodes; - var nodeIndices = this.physicsBody.physicsNodeIndices; - var forces = this.physicsBody.forces; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - // repulsing forces between nodes - var nodeDistance = this.options.nodeDistance; + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.lineWidth = selected ? selectionLineWidth : borderWidth; + ctx.lineWidth /= this.body.view.scale; + ctx.lineWidth = Math.min(this.width, ctx.lineWidth); + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx[shape](x, y, this.options.size); - // 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]]; + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); - // 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); + // disable shadows for other elements. + this.disableShadow(ctx); - 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; + ctx.stroke(); - forces[node1.id].x -= fx; - forces[node1.id].y -= fy; - forces[node2.id].x += fx; - forces[node2.id].y += fy; - } - } + if (this.options.label !== undefined) { + var yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. + this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + } + + this.updateBoundingBox(x, y); + } + }, { + key: 'updateBoundingBox', + value: function updateBoundingBox(x, y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + 3); } } }]); - return HierarchicalRepulsionSolver; - })(); + return ShapeBase; + })(_utilNodeBase2['default']); - exports["default"] = HierarchicalRepulsionSolver; - module.exports = exports["default"]; + exports['default'] = ShapeBase; + module.exports = exports['default']; /***/ }, -/* 109 */ +/* 112 */ /***/ 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { desc = parent = getter = undefined; _again = false; var object = _x2, + property = _x3, + receiver = _x4; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = 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); } } }; - var SpringSolver = (function () { - function SpringSolver(body, physicsBody, options) { - _classCallCheck(this, SpringSolver); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - _createClass(SpringSolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; - } - }, { - key: "solve", + 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; } - /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private - */ - 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; + var _EdgeBase2 = __webpack_require__(113); - // 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; + var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); - 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: "_calculateSpringForce", + var BezierEdgeBase = (function (_EdgeBase) { + function BezierEdgeBase(options, body, labelModule) { + _classCallCheck(this, BezierEdgeBase); + + _get(Object.getPrototypeOf(BezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule); + } + + _inherits(BezierEdgeBase, _EdgeBase); + + _createClass(BezierEdgeBase, [{ + key: '_findBorderPositionBezier', /** - * This is the code actually performing the calculation for the function above. + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. * - * @param node1 - * @param node2 - * @param edgeLength - * @private + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode + * @param nearNode + * @param ctx + * @param viaNode */ - value: function _calculateSpringForce(node1, node2, edgeLength) { - var dx = node1.x - node2.x; - var dy = node1.y - node2.y; - var distance = Math.sqrt(dx * dx + dy * dy); - distance = distance === 0 ? 0.01 : distance; + value: function _findBorderPositionBezier(nearNode, ctx) { + var viaNode = arguments[2] === undefined ? this._getViaCoordinates() : 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; + var maxIterations = 10; + var iteration = 0; + var low = 0; + var high = 1; + var pos, angle, distanceToBorder, distanceToPoint, difference; + var threshold = 0.2; + var node = this.to; + var from = false; + if (nearNode.id === this.from.id) { + node = this.from; + from = true; + } - var fx = dx * springForce; - var fy = dy * springForce; + while (low <= high && iteration < maxIterations) { + var middle = (low + high) * 0.5; + + pos = this.getPoint(middle, viaNode); + angle = Math.atan2(node.y - pos.y, node.x - pos.x); + distanceToBorder = node.distanceToBorder(ctx, angle); + distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); + difference = distanceToBorder - distanceToPoint; + if (Math.abs(difference) < threshold) { + break; // found + } else if (difference < 0) { + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from === false) { + low = middle; + } else { + high = middle; + } + } else { + if (from === false) { + high = middle; + } else { + low = middle; + } + } - // 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; + iteration++; } + pos.t = middle; - if (this.physicsBody.forces[node2.id] !== undefined) { - this.physicsBody.forces[node2.id].x -= fx; - this.physicsBody.forces[node2.id].y -= fy; + return pos; + } + }, { + key: '_getDistanceToBezierEdge', + + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @private + */ + value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { + // x3,y3 is the point + var xVia = undefined, + yVia = undefined; + xVia = via.x; + yVia = via.y; + var minDistance = 1000000000; + var distance = undefined; + var i = undefined, + t = undefined, + x = undefined, + y = undefined; + var lastX = x1; + var lastY = y1; + for (i = 1; i < 10; i++) { + t = 0.1 * i; + x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * xVia + Math.pow(t, 2) * x2; + y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * yVia + Math.pow(t, 2) * y2; + if (i > 0) { + distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); + minDistance = distance < minDistance ? distance : minDistance; + } + lastX = x; + lastY = y; } + + return minDistance; } }]); - return SpringSolver; - })(); + return BezierEdgeBase; + })(_EdgeBase3['default']); - exports["default"] = SpringSolver; - module.exports = exports["default"]; + exports['default'] = BezierEdgeBase; + module.exports = exports['default']; /***/ }, -/* 110 */ +/* 113 */ /***/ 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + 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'); } } - var HierarchicalSpringSolver = (function () { - function HierarchicalSpringSolver(body, physicsBody, options) { - _classCallCheck(this, HierarchicalSpringSolver); + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + + var util = __webpack_require__(1); + + var EdgeBase = (function () { + function EdgeBase(options, body, labelModule) { + _classCallCheck(this, EdgeBase); this.body = body; - this.physicsBody = physicsBody; + this.labelModule = labelModule; this.setOptions(options); + this.colorDirty = true; + this.color = {}; + this.selectionWidth = 2; + this.hoverWidth = 1.5; } - _createClass(HierarchicalSpringSolver, [{ - key: "setOptions", + _createClass(EdgeBase, [{ + key: 'setOptions', value: function setOptions(options) { this.options = options; + this.from = this.body.nodes[this.options.from]; + this.to = this.body.nodes[this.options.to]; + this.id = this.options.id; } }, { - key: "solve", + key: 'togglePhysics', /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private + * overloadable if the shape has to toggle the via node to disabled + * @param status */ - 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; + value: function togglePhysics(status) {} + }, { + key: 'drawLine', - // 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; + /** + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + value: function drawLine(ctx, selected, hover) { + // set style + ctx.strokeStyle = this.getColor(ctx); + ctx.lineWidth = this.getLineWidth(selected, hover); + var via = undefined; + if (this.options.dashes !== false) { + via = this._drawDashedLine(ctx); + } else { + via = this._drawLine(ctx); } + return via; + } + }, { + key: '_drawLine', + value: function _drawLine(ctx) { + var via = undefined; + if (this.from != this.to) { + // draw line + via = this._line(ctx); + } else { + var _getCircleData = this._getCircleData(ctx); - // 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 _getCircleData2 = _slicedToArray(_getCircleData, 3); - 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 x = _getCircleData2[0]; + var y = _getCircleData2[1]; + var radius = _getCircleData2[2]; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.options.springConstant * (edgeLength - distance) / distance; + this._circle(ctx, x, y, radius); + } + return via; + } + }, { + key: '_drawDashedLine', + value: function _drawDashedLine(ctx) { + var via = undefined; + ctx.lineCap = 'round'; + var pattern = [5, 5]; + if (Array.isArray(this.options.dashes) === true) { + pattern = this.options.dashes; + } - fx = dx * springForce; - fy = dy * springForce; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.setLineDash !== undefined) { + ctx.save(); - 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; - } - } - } + // set dash settings for chrome or firefox + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; - // 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)); + // draw the line + if (this.from != this.to) { + // draw line + via = this._line(ctx); + } else { + var _getCircleData3 = this._getCircleData(ctx); - forces[nodeId].x += springFx; - forces[nodeId].y += springFy; - } + var _getCircleData32 = _slicedToArray(_getCircleData3, 3); - // 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; + var x = _getCircleData32[0]; + var y = _getCircleData32[1]; + var radius = _getCircleData32[2]; - for (var i = 0; i < nodeIndices.length; i++) { - var nodeId = nodeIndices[i]; - forces[nodeId].x -= correctionFx; - forces[nodeId].y -= correctionFy; - } - } - }]); + this._circle(ctx, x, y, radius); + } - return HierarchicalSpringSolver; - })(); + // restore the dash settings. + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + ctx.restore(); + } else { + // unsupporting smooth lines - exports["default"] = HierarchicalSpringSolver; - module.exports = exports["default"]; + if (this.from != this.to) { + // draw line + ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern); + } else { + var _getCircleData4 = this._getCircleData(ctx); -/***/ }, -/* 111 */ -/***/ function(module, exports, __webpack_require__) { + var _getCircleData42 = _slicedToArray(_getCircleData4, 3); - "use strict"; + var x = _getCircleData42[0]; + var y = _getCircleData42[1]; + var radius = _getCircleData42[2]; - Object.defineProperty(exports, "__esModule", { - value: true - }); + this._circle(ctx, x, y, radius); + } + // draw shadow if enabled + this.enableShadow(ctx); - 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; }; })(); + ctx.stroke(); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // disable shadows for other elements. + this.disableShadow(ctx); + } + return via; + } + }, { + key: 'findBorderPosition', + value: function findBorderPosition(nearNode, ctx, options) { + if (this.from != this.to) { + return this._findBorderPosition(nearNode, ctx, options); + } else { + return this._findBorderPositionCircle(nearNode, ctx, options); + } + } + }, { + key: 'findBorderPositions', + value: function findBorderPositions(ctx) { + var from = {}; + var to = {}; + if (this.from != this.to) { + from = this._findBorderPosition(this.from, ctx); + to = this._findBorderPosition(this.to, ctx); + } else { + var _getCircleData5 = this._getCircleData(ctx); - var CentralGravitySolver = (function () { - function CentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, CentralGravitySolver); + var _getCircleData52 = _slicedToArray(_getCircleData5, 3); - this.body = body; - this.physicsBody = physicsBody; - this.setOptions(options); - } + var x = _getCircleData52[0]; + var y = _getCircleData52[1]; + var radius = _getCircleData52[2]; - _createClass(CentralGravitySolver, [{ - key: "setOptions", - value: function setOptions(options) { - this.options = options; + from = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); + to = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 }); + } + return { from: from, to: to }; } }, { - 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; + key: '_getCircleData', + value: function _getCircleData(ctx) { + var x = undefined, + y = undefined; + var node = this.from; + var radius = this.options.selfReferenceSize; - 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 (ctx !== undefined) { + if (node.shape.width === undefined) { + node.shape.resize(ctx); + } + } - this._calculateForces(distance, dx, dy, forces, node); + // get circle coordinates + if (node.shape.width > node.shape.height) { + x = node.x + node.shape.width * 0.5; + y = node.y - radius; + } else { + x = node.x + radius; + y = node.y - node.shape.height * 0.5; } + return [x, y, radius]; } }, { - key: "_calculateForces", + key: '_pointOnCircle', /** - * Calculate the forces based on the distance. + * Get a point on a circle + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point * @private */ - 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; + value: function _pointOnCircle(x, y, radius, percentage) { + var angle = percentage * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) + }; } - }]); - - return CentralGravitySolver; - })(); + }, { + key: '_findBorderPositionCircle', - exports["default"] = CentralGravitySolver; - module.exports = exports["default"]; + /** + * This function uses binary search to look for the point where the circle crosses the border of the node. + * @param node + * @param ctx + * @param options + * @returns {*} + * @private + */ + value: function _findBorderPositionCircle(node, ctx, options) { + var x = options.x; + var y = options.y; + var low = options.low; + var high = options.high; + var direction = options.direction; -/***/ }, -/* 112 */ -/***/ function(module, exports, __webpack_require__) { + var maxIterations = 10; + var iteration = 0; + var radius = this.options.selfReferenceSize; + var pos = undefined, + angle = undefined, + distanceToBorder = undefined, + distanceToPoint = undefined, + difference = undefined; + var threshold = 0.05; + var middle = (low + high) * 0.5; - "use strict"; + while (low <= high && iteration < maxIterations) { + middle = (low + high) * 0.5; - Object.defineProperty(exports, "__esModule", { - value: true - }); + pos = this._pointOnCircle(x, y, radius, middle); + angle = Math.atan2(node.y - pos.y, node.x - pos.x); + distanceToBorder = node.distanceToBorder(ctx, angle); + distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); + difference = distanceToBorder - distanceToPoint; + if (Math.abs(difference) < threshold) { + break; // found + } else if (difference > 0) { + // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (direction > 0) { + low = middle; + } else { + high = middle; + } + } else { + if (direction > 0) { + high = middle; + } else { + low = middle; + } + } + iteration++; + } + pos.t = middle; - 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; }; })(); + return pos; + } + }, { + key: 'getLineWidth', - 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); } } }; + /** + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width + * @private + */ + value: function getLineWidth(selected, hover) { + if (selected === true) { + return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); + } else { + if (hover === true) { + return Math.max(this.hoverWidth, 0.3 / this.body.view.scale); + } else { + return Math.max(this.options.width, 0.3 / this.body.view.scale); + } + } + } + }, { + key: 'getColor', + value: function getColor(ctx) { + var colorOptions = this.options.color; - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + if (colorOptions.inherit !== false) { + // when this is a loop edge, just use the 'from' method + if (colorOptions.inherit === 'both' && this.from.id !== this.to.id) { + var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); + var fromColor = undefined, + toColor = undefined; + fromColor = this.from.options.color.highlight.border; + toColor = this.to.options.color.highlight.border; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + if (this.from.selected === false && this.to.selected === false) { + fromColor = util.overrideOpacity(this.from.options.color.border, this.options.color.opacity); + toColor = util.overrideOpacity(this.to.options.color.border, this.options.color.opacity); + } else if (this.from.selected === true && this.to.selected === false) { + toColor = this.to.options.color.border; + } else if (this.from.selected === false && this.to.selected === true) { + fromColor = this.from.options.color.border; + } + grd.addColorStop(0, fromColor); + grd.addColorStop(1, toColor); - 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; } + // -------------------- this returns -------------------- // + return grd; + } - var _BarnesHutSolver2 = __webpack_require__(106); + if (this.colorDirty === true) { + if (colorOptions.inherit === 'to') { + this.color.highlight = this.to.options.color.highlight.border; + this.color.hover = this.to.options.color.hover.border; + this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity); + } else { + // (this.options.color.inherit.source === "from") { + this.color.highlight = this.from.options.color.highlight.border; + this.color.hover = this.from.options.color.hover.border; + this.color.color = util.overrideOpacity(this.from.options.color.border, colorOptions.opacity); + } + } + } else if (this.colorDirty === true) { + this.color.highlight = colorOptions.highlight; + this.color.hover = colorOptions.hover; + this.color.color = util.overrideOpacity(colorOptions.color, colorOptions.opacity); + } - var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); + // if color inherit is on and gradients are used, the function has already returned by now. + this.colorDirty = false; - var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) { - function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); + if (this.selected === true) { + return this.color.highlight; + } else if (this.hover === true) { + return this.color.hover; + } else { + return this.color.color; + } + } + }, { + key: '_circle', - _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } + /** + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @private + */ + value: function _circle(ctx, x, y, radius) { + // draw shadow if enabled + this.enableShadow(ctx); - _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); - _createClass(ForceAtlas2BasedRepulsionSolver, [{ - key: "_calculateForces", + // disable shadows for other elements. + this.disableShadow(ctx); + } + }, { + key: 'getDistanceToEdge', /** - * Calculate the forces based on the distance. - * - * @param distance - * @param dx - * @param dy - * @param node - * @param parentBranch + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 * @private */ - value: function _calculateForces(distance, dx, dy, node, parentBranch) { - if (distance === 0) { - distance = 0.1 * Math.random(); - dx = distance; + value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) { + // x3,y3 is the point + var returnValue = 0; + if (this.from != this.to) { + returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via); + } else { + var _getCircleData6 = this._getCircleData(); + + var _getCircleData62 = _slicedToArray(_getCircleData6, 3); + + var x = _getCircleData62[0]; + var y = _getCircleData62[1]; + var radius = _getCircleData62[2]; + + var dx = x - x3; + var dy = y - y3; + returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - 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; + if (this.labelModule.size.left < x3 && this.labelModule.size.left + this.labelModule.size.width > x3 && this.labelModule.size.top < y3 && this.labelModule.size.top + this.labelModule.size.height > y3) { + return 0; + } else { + return returnValue; + } } - }]); + }, { + key: '_getDistanceToLine', + value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) { + var px = x2 - x1; + var py = y2 - y1; + var something = px * px + py * py; + var u = ((x3 - x1) * px + (y3 - y1) * py) / something; - return ForceAtlas2BasedRepulsionSolver; - })(_BarnesHutSolver3["default"]); + if (u > 1) { + u = 1; + } else if (u < 0) { + u = 0; + } - exports["default"] = ForceAtlas2BasedRepulsionSolver; - module.exports = exports["default"]; + var x = x1 + u * px; + var y = y1 + u * py; + var dx = x - x3; + var dy = y - y3; -/***/ }, -/* 113 */ -/***/ function(module, exports, __webpack_require__) { + //# Note: If the actual distance does not matter, + //# if you only want to compare what this function + //# returns to other results of this function, you + //# can just return the squared distance instead + //# (i.e. remove the sqrt) to gain a little performance - "use strict"; + return Math.sqrt(dx * dx + dy * dy); + } + }, { + key: 'drawArrowHead', - Object.defineProperty(exports, "__esModule", { - value: true - }); + /** + * + * @param ctx + * @param position + * @param viaNode + */ + value: function drawArrowHead(ctx, position, viaNode, selected, hover) { + // set style + ctx.strokeStyle = this.getColor(ctx); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this.getLineWidth(selected, hover); - 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; }; })(); + // set lets + var angle = undefined; + var length = undefined; + var arrowPos = undefined; + var node1 = undefined; + var node2 = undefined; + var guideOffset = undefined; + var scaleFactor = 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 (position === 'from') { + node1 = this.from; + node2 = this.to; + guideOffset = 0.1; + scaleFactor = this.options.arrows.from.scaleFactor; + } else if (position === 'to') { + node1 = this.to; + node2 = this.from; + guideOffset = -0.1; + scaleFactor = this.options.arrows.to.scaleFactor; + } else { + node1 = this.to; + node2 = this.from; + scaleFactor = this.options.arrows.middle.scaleFactor; + } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + // if not connected to itself + if (node1 != node2) { + if (position !== 'middle') { + // draw arrow head + if (this.options.smooth.enabled === true) { + arrowPos = this.findBorderPosition(node1, ctx, { via: viaNode }); + var guidePos = this.getPoint(Math.max(0, Math.min(1, arrowPos.t + guideOffset)), viaNode); + angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x); + } else { + angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); + arrowPos = this.findBorderPosition(node1, ctx); + } + } else { + angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); + arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow. + } + // draw arrow at the end of the line + length = (10 + 5 * this.options.width) * scaleFactor; + ctx.arrow(arrowPos.x, arrowPos.y, angle, length); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); - 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; } + // disable shadows for other elements. + this.disableShadow(ctx); + ctx.stroke(); + } else { + // draw circle + var _angle = undefined, + point = undefined; - var _CentralGravitySolver2 = __webpack_require__(111); + var _getCircleData7 = this._getCircleData(ctx); - var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); + var _getCircleData72 = _slicedToArray(_getCircleData7, 3); - var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) { - function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { - _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); + var x = _getCircleData72[0]; + var y = _getCircleData72[1]; + var radius = _getCircleData72[2]; - _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options); - } + if (position === 'from') { + point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); + _angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; + } else if (position === 'to') { + point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1, direction: 1 }); + _angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; + } else { + point = this._pointOnCircle(x, y, radius, 0.175); + _angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; + } - _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); + // draw the arrowhead + var _length = (10 + 5 * this.options.width) * scaleFactor; + ctx.arrow(point.x, point.y, _angle, _length); - _createClass(ForceAtlas2BasedCentralGravitySolver, [{ - key: "_calculateForces", + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); - /** - * Calculate the forces based on the distance. - * @private - */ - 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; + // disable shadows for other elements. + this.disableShadow(ctx); + ctx.stroke(); + } + } + }, { + key: 'enableShadow', + value: function enableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0.5)'; + ctx.shadowBlur = this.options.shadow.size; + ctx.shadowOffsetX = this.options.shadow.x; + ctx.shadowOffsetY = this.options.shadow.y; + } + } + }, { + key: 'disableShadow', + value: function disableShadow(ctx) { + if (this.options.shadow.enabled === true) { + ctx.shadowColor = 'rgba(0,0,0,0)'; + ctx.shadowBlur = 0; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; } } }]); - return ForceAtlas2BasedCentralGravitySolver; - })(_CentralGravitySolver3["default"]); + return EdgeBase; + })(); - exports["default"] = ForceAtlas2BasedCentralGravitySolver; - module.exports = exports["default"]; + exports['default'] = EdgeBase; + module.exports = exports['default']; /***/ } /******/ ]) diff --git a/docs/graph2d/graph2d.html b/docs/graph2d/graph2d.html index 7fa8bcd1..37e834e2 100644 --- a/docs/graph2d/graph2d.html +++ b/docs/graph2d/graph2d.html @@ -10,8 +10,8 @@ vis.js - A dynamic, browser based visualization library. - - + + - - - + + + - + @@ -72,7 +72,7 @@ } - + @@ -1362,7 +1362,7 @@ Graph2d.off('rangechanged', onChange); - - + + - \ No newline at end of file + \ No newline at end of file diff --git a/docs/network/configure.html b/docs/network/configure.html index ca376b09..960a88a5 100644 --- a/docs/network/configure.html +++ b/docs/network/configure.html @@ -116,7 +116,7 @@ var options = { network.setOptions(options); -

As shown above, alternative to supplying an object, you can supply a String, Array or +

As shown above, alternative to supplying an object, you can supply a String, Array, Function or Boolean. These will do the same as the filter option described below.

@@ -134,12 +134,19 @@ network.setOptions(options); - + diff --git a/docs/network/physics.html b/docs/network/physics.html index 65fb8020..14552d66 100644 --- a/docs/network/physics.html +++ b/docs/network/physics.html @@ -154,7 +154,7 @@ network.setOptions(options); by them and makes use of the barnesHut implementation in vis. The main differences are the central gravity model, which is here distance independent, and the repulsion being linear instead of quadratic. Finally, all node masses have a multiplier based on the amount of connected edges plus one. - + diff --git a/lib/network/modules/CanvasRenderer.js b/lib/network/modules/CanvasRenderer.js index dc5e5ee7..6cd438b7 100644 --- a/lib/network/modules/CanvasRenderer.js +++ b/lib/network/modules/CanvasRenderer.js @@ -229,6 +229,9 @@ class CanvasRenderer { else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) { node.draw(ctx); } + else { + node.updateBoundingBox(ctx); + } } } diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index a5adf664..20c92170 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -39,7 +39,7 @@ class PhysicsEngine { }, forceAtlas2Based: { theta: 0.5, - gravitationalConstant: -800, + gravitationalConstant: -50, centralGravity: 0.01, springConstant: 0.08, springLength: 100, diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 336ec215..6f5b8472 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -368,6 +368,12 @@ class Node { this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); } + /** + * Update the bounding box of the shape + */ + updateBoundingBox() { + this.shape.updateBoundingBox(this.x,this.y); + } /** * Recalculate the size of this node in the given canvas diff --git a/lib/network/modules/components/nodes/shapes/Box.js b/lib/network/modules/components/nodes/shapes/Box.js index d872ff10..59d9e5eb 100644 --- a/lib/network/modules/components/nodes/shapes/Box.js +++ b/lib/network/modules/components/nodes/shapes/Box.js @@ -43,12 +43,18 @@ class Box extends NodeBase { ctx.stroke(); - this.boundingBox.top = this.top; + this.updateBoundingBox(x,y); + this.labelModule.draw(ctx, x, y, selected); + } + + updateBoundingBox(x,y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; + this.boundingBox.top = this.top; this.boundingBox.bottom = this.top + this.height; - - this.labelModule.draw(ctx, x, y, selected); + this.boundingBox.right = this.left + this.width; } distanceToBorder(ctx, angle) { diff --git a/lib/network/modules/components/nodes/shapes/Circle.js b/lib/network/modules/components/nodes/shapes/Circle.js index c96860a6..3c093a02 100644 --- a/lib/network/modules/components/nodes/shapes/Circle.js +++ b/lib/network/modules/components/nodes/shapes/Circle.js @@ -31,9 +31,17 @@ class Circle extends CircleImageBase { this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; + this.updateBoundingBox(x,y); this.labelModule.draw(ctx, x, y, selected); } + updateBoundingBox(x,y) { + this.boundingBox.top = y - this.options.size; + this.boundingBox.left = x - this.options.size; + this.boundingBox.right = x + this.options.size; + this.boundingBox.bottom = y + this.options.size; + } + distanceToBorder(ctx, angle) { this.resize(ctx); var a = this.width / 2; diff --git a/lib/network/modules/components/nodes/shapes/CircularImage.js b/lib/network/modules/components/nodes/shapes/CircularImage.js index c6403430..3f009ed9 100644 --- a/lib/network/modules/components/nodes/shapes/CircularImage.js +++ b/lib/network/modules/components/nodes/shapes/CircularImage.js @@ -47,16 +47,19 @@ class CircularImage extends CircleImageBase { ctx.restore(); + this._drawImageLabel(ctx, x, y, selected); + + this.updateBoundingBox(x,y); + } + + updateBoundingBox(x,y) { this.boundingBox.top = y - this.options.size; this.boundingBox.left = x - this.options.size; this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; - - this._drawImageLabel(ctx, x, y, selected); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } diff --git a/lib/network/modules/components/nodes/shapes/Database.js b/lib/network/modules/components/nodes/shapes/Database.js index c6a3efc3..2cc8e9c6 100644 --- a/lib/network/modules/components/nodes/shapes/Database.js +++ b/lib/network/modules/components/nodes/shapes/Database.js @@ -42,14 +42,21 @@ class Database extends NodeBase { ctx.stroke(); - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + this.updateBoundingBox(x,y); this.labelModule.draw(ctx, x, y, selected); } + updateBoundingBox(x,y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + + this.boundingBox.left = this.left; + this.boundingBox.top = this.top; + this.boundingBox.bottom = this.top + this.height; + this.boundingBox.right = this.left + this.width; + } + distanceToBorder(ctx, angle) { this.resize(ctx); var a = this.width / 2; diff --git a/lib/network/modules/components/nodes/shapes/Ellipse.js b/lib/network/modules/components/nodes/shapes/Ellipse.js index 6a619bff..4fb9c644 100644 --- a/lib/network/modules/components/nodes/shapes/Ellipse.js +++ b/lib/network/modules/components/nodes/shapes/Ellipse.js @@ -21,8 +21,8 @@ class Ellipse extends NodeBase { draw(ctx, x, y, selected, hover) { this.resize(ctx, selected); - this.left = x - this.width / 2; - this.top = y - this.height / 2; + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; var borderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; @@ -45,21 +45,24 @@ class Ellipse extends NodeBase { ctx.stroke(); + this.updateBoundingBox(x,y); + this.labelModule.draw(ctx, x, y, selected); + } + + updateBoundingBox(x,y) { + this.left = x - this.width * 0.5; + this.top = y - this.height * 0.5; + this.boundingBox.left = this.left; this.boundingBox.top = this.top; this.boundingBox.bottom = this.top + this.height; this.boundingBox.right = this.left + this.width; - - - - this.labelModule.draw(ctx, x, y, selected); - } distanceToBorder(ctx, angle) { this.resize(ctx); - var a = this.width / 2; - var b = this.height / 2; + var a = this.width * 0.5; + var b = this.height * 0.5; var w = (Math.sin(angle) * a); var h = (Math.cos(angle) * b); return a * b / Math.sqrt(w * w + h * h); diff --git a/lib/network/modules/components/nodes/shapes/Empty.js b/lib/network/modules/components/nodes/shapes/Empty.js index 85e975ac..5b27e5c4 100644 --- a/lib/network/modules/components/nodes/shapes/Empty.js +++ b/lib/network/modules/components/nodes/shapes/Empty.js @@ -14,6 +14,8 @@ class Empty extends NodeBase { draw() {} distanceToBorder() {} -} + + updateBoundingBox() {}} + export default Empty; \ No newline at end of file diff --git a/lib/network/modules/components/nodes/shapes/Icon.js b/lib/network/modules/components/nodes/shapes/Icon.js index d0e2e271..200176ca 100644 --- a/lib/network/modules/components/nodes/shapes/Icon.js +++ b/lib/network/modules/components/nodes/shapes/Icon.js @@ -27,7 +27,15 @@ class Icon extends NodeBase { this.top = y - this.height * 0.5; this._icon(ctx, x, y, selected); + if (this.options.label !== undefined) { + var iconTextSpacing = 5; + this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); + } + this.updateBoundingBox(x,y) + } + + updateBoundingBox(x,y) { this.boundingBox.top = y - this.options.icon.size * 0.5; this.boundingBox.left = x - this.options.icon.size * 0.5; this.boundingBox.right = x + this.options.icon.size * 0.5; @@ -35,10 +43,9 @@ class Icon extends NodeBase { if (this.options.label !== undefined) { var iconTextSpacing = 5; - this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); } } diff --git a/lib/network/modules/components/nodes/shapes/Image.js b/lib/network/modules/components/nodes/shapes/Image.js index 85bd1c24..f62d3746 100644 --- a/lib/network/modules/components/nodes/shapes/Image.js +++ b/lib/network/modules/components/nodes/shapes/Image.js @@ -19,15 +19,25 @@ class Image extends CircleImageBase { this._drawImageAtPosition(ctx); + this._drawImageLabel(ctx, x, y, selected || hover); + + this.updateBoundingBox(x,y); + } + + updateBoundingBox(x,y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; + this.boundingBox.top = this.top; this.boundingBox.left = this.left; this.boundingBox.right = this.left + this.width; this.boundingBox.bottom = this.top + this.height; - this._drawImageLabel(ctx, x, y, selected || hover); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + if (this.options.label !== undefined) { + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); + } } distanceToBorder(ctx, angle) { diff --git a/lib/network/modules/components/nodes/shapes/Text.js b/lib/network/modules/components/nodes/shapes/Text.js index 7aa73aea..7b6a3be2 100644 --- a/lib/network/modules/components/nodes/shapes/Text.js +++ b/lib/network/modules/components/nodes/shapes/Text.js @@ -28,6 +28,13 @@ class Text extends NodeBase { // disable shadows for other elements. this.disableShadow(ctx); + this.updateBoundingBox(x,y); + } + + updateBoundingBox(x,y) { + this.left = x - this.width / 2; + this.top = y - this.height / 2; + this.boundingBox.top = this.top; this.boundingBox.left = this.left; this.boundingBox.right = this.left + this.width; diff --git a/lib/network/modules/components/nodes/util/CircleImageBase.js b/lib/network/modules/components/nodes/util/CircleImageBase.js index d7333e9a..b075b8ed 100644 --- a/lib/network/modules/components/nodes/util/CircleImageBase.js +++ b/lib/network/modules/components/nodes/util/CircleImageBase.js @@ -3,6 +3,7 @@ import NodeBase from '../util/NodeBase' class CircleImageBase extends NodeBase { constructor(options, body, labelModule) { super(options, body, labelModule); + this.labelOffset = 0; } _resizeImage() { @@ -77,6 +78,10 @@ class CircleImageBase extends NodeBase { } yLabel = y + offset; + + if (this.options.label) { + this.labelOffset = offset; + } this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); } } diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index cb1e6655..82cd86ba 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -38,20 +38,30 @@ class ShapeBase extends NodeBase { ctx.stroke(); + + if (this.options.label !== undefined) { + let yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. + this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + } + + this.updateBoundingBox(x,y); + } + + updateBoundingBox(x,y) { this.boundingBox.top = y - this.options.size; this.boundingBox.left = x - this.options.size; this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; - if (this.options.label!== undefined) { - let yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + if (this.options.label !== undefined) { this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + 3); } } + + } export default ShapeBase; \ No newline at end of file diff --git a/lib/network/options.js b/lib/network/options.js index 7dc4f89a..297aba6f 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -19,9 +19,9 @@ let any = 'any' let allOptions = { configure: { enabled: {boolean}, - filter: {boolean,string,array}, + filter: {boolean,string,array,fn}, container: {dom}, - __type__: {object,boolean,string,array} + __type__: {object,boolean,string,array,fn} }, edges: { arrows: { @@ -432,7 +432,7 @@ let configureOptions = { }, forceAtlas2Based: { //theta: [0.5, 0.1, 1, 0.05], - gravitationalConstant: [-800, -3000, 0, 50], + gravitationalConstant: [-800, -3000, 0, 1], centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], springConstant: [0.08, 0, 5, 0.005], @@ -454,7 +454,7 @@ let configureOptions = { }, maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], - solver: ['barnesHut', 'repulsion', 'hierarchicalRepulsion','forceAtlas2Based'], + solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], timestep: [0.5, 0, 1, 0.05] }, global: { diff --git a/lib/shared/ConfigurationSystem.js b/lib/shared/ConfigurationSystem.js index c50e87c9..c91e33c0 100644 --- a/lib/shared/ConfigurationSystem.js +++ b/lib/shared/ConfigurationSystem.js @@ -21,6 +21,7 @@ class ConfigurationSystem { this.parent = parentModule; this.changedOptions = []; this.container = defaultContainer; + this.allowCreation = false; this.options = {}; this.defaultOptions = { @@ -68,6 +69,10 @@ class ConfigurationSystem { this.options.filter = true; enabled = options; } + else if (typeof options === 'function') { + this.options.filter = options; + enabled = true; + } this.options.enabled = enabled; } this._clean(); @@ -81,7 +86,7 @@ class ConfigurationSystem { if (this.options.container !== undefined) { this.container = this.options.container; } - this._create(this.options.filter); + this._create(); } } @@ -90,15 +95,27 @@ class ConfigurationSystem { * @param {Boolean | String} config * @private */ - _create(config) { + _create() { this._clean(); this.changedOptions = []; + let config = this.options.filter; let counter = 0; + let show = false; for (let option in this.configureOptions) { if (this.configureOptions.hasOwnProperty(option)) { - if (config === true || config.indexOf(option) !== -1) { - let optionObj = this.configureOptions[option]; + this.allowCreation = false; + show = false; + if (typeof config === 'function') { + show = config(option,[]); + show = show || this._handleObject(this.configureOptions[option], [option]); + } + else if (config === true || config.indexOf(option) !== -1) { + show = true; + } + + if (show !== false) { + this.allowCreation = true; // linebreak between categories if (counter > 0) { @@ -108,8 +125,7 @@ class ConfigurationSystem { this._makeHeader(option); // get the suboptions - let path = [option]; - this._handleObject(optionObj, path); + this._handleObject(this.configureOptions[option], [option]); } counter++; } @@ -191,12 +207,14 @@ class ConfigurationSystem { * @private */ _makeItem(path, ...domElements) { - let item = document.createElement('div'); - item.className = 'vis-network-configuration item s' + path.length; - domElements.forEach((element) => { - item.appendChild(element); - }); - this.domElements.push(item); + if (this.allowCreation === true) { + let item = document.createElement('div'); + item.className = 'vis-network-configuration item s' + path.length; + domElements.forEach((element) => { + item.appendChild(element); + }); + this.domElements.push(item); + } } @@ -431,56 +449,68 @@ class ConfigurationSystem { * @private */ _handleObject(obj, path = []) { + let show = false; + let config = this.options.filter; + let visibleInSet = false; for (let subObj in obj) { if (obj.hasOwnProperty(subObj)) { - let item = obj[subObj]; - let newPath = util.copyAndExtendArray(path, subObj); - let value = this._getValue(newPath); - - if (item instanceof Array) { - this._handleArray(item, value, newPath); - } - else if (typeof item === 'string') { - this._makeTextInput(item, value, newPath); - } - else if (typeof item === 'boolean') { - this._makeCheckbox(item, value, newPath); + show = false; + if (typeof config === 'function') { + show = config(subObj,path); } - else if (item instanceof Object) { - // collapse the physics options that are not enabled - let draw = true; - if (path.indexOf('physics') !== -1) { - if (this.moduleOptions.physics.solver !== subObj) { - draw = false; - } + + if (show !== false) { + visibleInSet = true; + let item = obj[subObj]; + let newPath = util.copyAndExtendArray(path, subObj); + let value = this._getValue(newPath); + + if (item instanceof Array) { + this._handleArray(item, value, newPath); } + else if (typeof item === 'string') { + this._makeTextInput(item, value, newPath); + } + else if (typeof item === 'boolean') { + this._makeCheckbox(item, value, newPath); + } + else if (item instanceof Object) { + // collapse the physics options that are not enabled + let draw = true; + if (path.indexOf('physics') !== -1) { + if (this.moduleOptions.physics.solver !== subObj) { + draw = false; + } + } - if (draw === true) { - // initially collapse options with an disabled enabled option. - if (item.enabled !== undefined) { - let enabledPath = util.copyAndExtendArray(newPath, 'enabled'); - let enabledValue = this._getValue(enabledPath); - if (enabledValue === true) { - let label = this._makeLabel(subObj, newPath, true); - this._makeItem(newPath, label); - this._handleObject(item, newPath); + if (draw === true) { + // initially collapse options with an disabled enabled option. + if (item.enabled !== undefined) { + let enabledPath = util.copyAndExtendArray(newPath, 'enabled'); + let enabledValue = this._getValue(enabledPath); + if (enabledValue === true) { + let label = this._makeLabel(subObj, newPath, true); + this._makeItem(newPath, label); + visibleInSet = this._handleObject(item, newPath) || visibleInSet; + } + else { + this._makeCheckbox(item, enabledValue, newPath); + } } else { - this._makeCheckbox(item, enabledValue, newPath); + let label = this._makeLabel(subObj, newPath, true); + this._makeItem(newPath, label); + visibleInSet = this._handleObject(item, newPath) || visibleInSet; } } - else { - let label = this._makeLabel(subObj, newPath, true); - this._makeItem(newPath, label); - this._handleObject(item, newPath); - } } - } - else { - console.error('dont know how to handle', item, subObj, newPath); + else { + console.error('dont know how to handle', item, subObj, newPath); + } } } } + return visibleInSet; }
filterString or Array or BooleanString, Array, Boolean, Function true When a boolean, true gives you all options, false will not show any. If a string is supplied, any combination of the following is allowed: nodes, edges, layout, interaction, manipulation, physics, selection, renderer. Feel free to come up with a fun seperating character. Finally, when supplied an - array of strings, any of the previously mentioned fields are accepted. + array of strings, any of the previously mentioned fields are accepted.

+ When supplying a function, you receive two arguments. The option and the path of the option within the options object. If it returns true, the options will be shown in the configurator. Example: +
+function (option, path) {
+  return path.indexOf('physics') !== -1;
+}
+                
+ This only shows the physics options.