From 014645d1546faf2363d18bc22e55b39e0ac466f5 Mon Sep 17 00:00:00 2001 From: jos Date: Thu, 4 Feb 2016 14:56:12 +0100 Subject: [PATCH 01/16] Fixed #1630: fixed docs of `getItemRange` and `getDataRange` in docs --- HISTORY.md | 11 +++++++++++ docs/graph2d/index.html | 13 ++++++------- docs/timeline/index.html | 6 ++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 412837d2..b85074e5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,17 @@ http://visjs.org +## not yet released, version 4.14.1 + +### Timeline + +- Fixed #1630: method `getItemRange` missing in docs. + +### Graph2d + +- Fixed #1630: method `getDataRange` was wrongly called `getItemRange` in docs. + + ## 2016-02-04, version 4.14.0 ### Timeline diff --git a/docs/graph2d/index.html b/docs/graph2d/index.html index abea895d..e6412f7b 100644 --- a/docs/graph2d/index.html +++ b/docs/graph2d/index.html @@ -1145,6 +1145,12 @@ function (option, path) { + + getDataRange() + Object + Get the range of all the items as an object containing min: Date and max: Date. + + getEventProperties(event) Object @@ -1176,13 +1182,6 @@ function (option, path) { Get the current visible window. Returns an object with properties start: Date and end: Date. - - getItemRange() - Object - Get the range of all the items as an object containing min: Date and max: Date. - - - isGroupVisible(groupId) Boolean diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 83f9afe5..9d41a32b 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -1150,6 +1150,12 @@ document.getElementById('myTimeline').onclick = function (event) { + + getItemRange() + Object + Get the range of all the items as an object containing min: Date and max: Date. + + getSelection() number[] From 8e180da2a253093a457570b2922a67b6367a329e Mon Sep 17 00:00:00 2001 From: jos Date: Tue, 9 Feb 2016 12:05:34 +0100 Subject: [PATCH 02/16] Fixed #1634: several minor HTML/CSS bugs --- docs/network/index.html | 16 ++++++++-------- lib/network/css/network-tooltip.css | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/network/index.html b/docs/network/index.html index 4fe58dfb..bd7d7c3c 100644 --- a/docs/network/index.html +++ b/docs/network/index.html @@ -339,49 +339,49 @@ network.setOptions(options); configure Object Object - All options in this object are explained in the configure module. + All options in this object are explained in the configure module. edges Object Object - All options in this object are explained in the edges module. + All options in this object are explained in the edges module. nodes Object Object - All options in this object are explained in the nodes module. + All options in this object are explained in the nodes module. groups Object Object - All options in this object are explained in the groups module. + All options in this object are explained in the groups module. layout Object Object - All options in this object are explained in the layout module. + All options in this object are explained in the layout module. interaction Object Object - All options in this object are explained in the interaction module. + All options in this object are explained in the interaction module. manipulation Object Object - All options in this object are explained in the manipulation module. + All options in this object are explained in the manipulation module. physics Object Object - All options in this object are explained in the physics module. + All options in this object are explained in the physics module. diff --git a/lib/network/css/network-tooltip.css b/lib/network/css/network-tooltip.css index 1c5a9a36..f8ababcf 100644 --- a/lib/network/css/network-tooltip.css +++ b/lib/network/css/network-tooltip.css @@ -6,7 +6,7 @@ div.vis-network-tooltip { font-family: verdana; font-size:14px; - font-color:#000000; + color:#000000; background-color: #f5f4ed; -moz-border-radius: 3px; From 1e1f59a7a4343f74e2ef4191ef57cda614c3742c Mon Sep 17 00:00:00 2001 From: jos Date: Tue, 9 Feb 2016 12:39:28 +0100 Subject: [PATCH 03/16] Fixed #1637: the built-in tooltip now shows the provided `xLabel`, `yLabel`, and `zLabel` instead of `'x'`, `'y'`, and `'z'` --- HISTORY.md | 5 +++++ lib/graph3d/Graph3d.js | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b85074e5..9751b692 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,11 @@ http://visjs.org - Fixed #1630: method `getDataRange` was wrongly called `getItemRange` in docs. +### Graph3d + +- The built-in tooltip now shows the provided `xLabel`, `yLabel`, and `zLabel` + instead of `'x'`, `'y'`, and `'z'`. Thanks @jacklightbody. + ## 2016-02-04, version 4.14.0 diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 68390079..9db4fbeb 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -2237,9 +2237,9 @@ Graph3d.prototype._showTooltip = function (dataPoint) { } else { content.innerHTML = '' + - '' + - '' + - '' + + '' + + '' + + '' + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
' + this.xLabel + ':' + dataPoint.point.x + '
' + this.yLabel + ':' + dataPoint.point.y + '
' + this.zLabel + ':' + dataPoint.point.z + '
'; } From 53d37f13447c7e61e69fb79535a3c74c1c831ae9 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Fri, 12 Feb 2016 12:16:11 +0100 Subject: [PATCH 04/16] Fixed #1635: edges are now referring to the correct points --- HISTORY.md | 3 + dist/vis.css | 2 +- dist/vis.js | 24 +- dist/vis.min.css | 2 +- lib/network/modules/ManipulationSystem.js | 10 +- lib/network/modules/components/Edge.js | 4 +- test/networkTest.html | 13174 +++++++++++++++++++- 7 files changed, 13163 insertions(+), 56 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 9751b692..d37c49b7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -17,6 +17,9 @@ http://visjs.org - The built-in tooltip now shows the provided `xLabel`, `yLabel`, and `zLabel` instead of `'x'`, `'y'`, and `'z'`. Thanks @jacklightbody. +### Network + +- Fixed #1635: edges are now referring to the correct points. ## 2016-02-04, version 4.14.0 diff --git a/dist/vis.css b/dist/vis.css index 7fcbb4ce..93889d03 100644 --- a/dist/vis.css +++ b/dist/vis.css @@ -1056,7 +1056,7 @@ div.vis-network-tooltip { font-family: verdana; font-size:14px; - font-color:#000000; + color:#000000; background-color: #f5f4ed; -moz-border-radius: 3px; diff --git a/dist/vis.js b/dist/vis.js index 661263a5..a9d8ac86 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.14.0 - * @date 2016-02-04 + * @date 2016-02-12 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -1582,7 +1582,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module) {//! moment.js - //! version : 2.11.2 + //! version : 2.11.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com @@ -3399,7 +3399,7 @@ return /******/ (function(modules) { // webpackBootstrap } // ASP.NET json date format regex - var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/; + var aspNetRegex = /(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere @@ -5154,7 +5154,7 @@ return /******/ (function(modules) { // webpackBootstrap // Side effect imports - utils_hooks__hooks.version = '2.11.2'; + utils_hooks__hooks.version = '2.11.1'; setHookCallback(local__createLocal); @@ -9343,7 +9343,7 @@ return /******/ (function(modules) { // webpackBootstrap if (typeof this.showTooltip === 'function') { content.innerHTML = this.showTooltip(dataPoint.point); } else { - content.innerHTML = '' + '' + '' + '' + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; + content.innerHTML = '' + '' + '' + '' + '
' + this.xLabel + ':' + dataPoint.point.x + '
' + this.yLabel + ':' + dataPoint.point.y + '
' + this.zLabel + ':' + dataPoint.point.z + '
'; } content.style.left = '0'; @@ -32129,8 +32129,8 @@ return /******/ (function(modules) { // webpackBootstrap var arrowData = {}; // restore edge targets to defaults - this.edgeType.fromPoint = this.from; - this.edgeType.toPoint = this.to; + this.edgeType.fromPoint = this.edgeType.from; + this.edgeType.toPoint = this.edgeType.to; // from and to arrows give a different end point for edges. we set them here if (this.options.arrows.from.enabled === true) { @@ -42395,6 +42395,11 @@ return /******/ (function(modules) { // webpackBootstrap edge.edgeType.to = to; } + // we use the selection to find the node that is being dragged. We explicitly select it here. + if (this.selectedControlNode !== undefined) { + this.selectionHandler.selectObject(this.selectedControlNode); + } + this.body.emitter.emit('_redraw'); } @@ -42409,7 +42414,6 @@ return /******/ (function(modules) { // webpackBootstrap this.body.emitter.emit('disablePhysics'); var pointer = this.body.functions.getPointer(event.center); var pos = this.canvas.DOMtoCanvas(pointer); - if (this.selectedControlNode !== undefined) { this.selectedControlNode.x = pos.x; this.selectedControlNode.y = pos.y; @@ -42433,12 +42437,13 @@ return /******/ (function(modules) { // webpackBootstrap var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); var edge = this.body.edges[this.edgeBeingEditedId]; - // if the node that was dragged is not a control node, return if (this.selectedControlNode === undefined) { return; } + // we use the selection to find the node that is being dragged. We explicitly DEselect the control node here. + this.selectionHandler.unselectAll(); var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); var node = undefined; for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { @@ -42447,7 +42452,6 @@ return /******/ (function(modules) { // webpackBootstrap break; } } - // perform the connection if (node !== undefined && this.selectedControlNode !== undefined) { if (node.isCluster === true) { diff --git a/dist/vis.min.css b/dist/vis.min.css index cd227e3f..d7e4286c 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;font-color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file +.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file diff --git a/lib/network/modules/ManipulationSystem.js b/lib/network/modules/ManipulationSystem.js index 158f6a9a..a0ab16de 100644 --- a/lib/network/modules/ManipulationSystem.js +++ b/lib/network/modules/ManipulationSystem.js @@ -848,6 +848,11 @@ class ManipulationSystem { edge.edgeType.to = to; } + // we use the selection to find the node that is being dragged. We explicitly select it here. + if (this.selectedControlNode !== undefined) { + this.selectionHandler.selectObject(this.selectedControlNode) + } + this.body.emitter.emit('_redraw'); } @@ -860,7 +865,6 @@ class ManipulationSystem { this.body.emitter.emit('disablePhysics'); let pointer = this.body.functions.getPointer(event.center); let pos = this.canvas.DOMtoCanvas(pointer); - if (this.selectedControlNode !== undefined) { this.selectedControlNode.x = pos.x; this.selectedControlNode.y = pos.y; @@ -884,12 +888,13 @@ class ManipulationSystem { let pointer = this.body.functions.getPointer(event.center); let pointerObj = this.selectionHandler._pointerToPositionObject(pointer); let edge = this.body.edges[this.edgeBeingEditedId]; - // if the node that was dragged is not a control node, return if (this.selectedControlNode === undefined) { return; } + // we use the selection to find the node that is being dragged. We explicitly DEselect the control node here. + this.selectionHandler.unselectAll(); let overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); let node = undefined; for (let i = overlappingNodeIds.length-1; i >= 0; i--) { @@ -898,7 +903,6 @@ class ManipulationSystem { break; } } - // perform the connection if (node !== undefined && this.selectedControlNode !== undefined) { if (node.isCluster === true) { diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index 01c84829..7afa94b1 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -379,8 +379,8 @@ class Edge { let arrowData = {}; // restore edge targets to defaults - this.edgeType.fromPoint = this.from; - this.edgeType.toPoint = this.to; + this.edgeType.fromPoint = this.edgeType.from; + this.edgeType.toPoint = this.edgeType.to; // from and to arrows give a different end point for edges. we set them here if (this.options.arrows.from.enabled === true) { diff --git a/test/networkTest.html b/test/networkTest.html index a7ec8161..d1422420 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -16,53 +16,13149 @@

Network Test

- + + + + \ No newline at end of file From 2bff294789bf693b755e8472c8a7580b55b5a6b1 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Sat, 13 Feb 2016 14:37:12 +0100 Subject: [PATCH 05/16] - Fixed #1644, #1631: overlapping nodes in hierarchical layout should no longer occur. - Added parentCentralization option for hierarchical layout. --- HISTORY.md | 2 + dist/vis.js | 158 +- docs/network/layout.html | 2 + .../hierarchicalLayoutWithoutPhysics.html | 4 + lib/network/modules/LayoutEngine.js | 145 +- lib/network/options.js | 2 + test/networkTest.html | 13208 +--------------- 7 files changed, 263 insertions(+), 13258 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index d37c49b7..506f4636 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -20,6 +20,8 @@ http://visjs.org ### Network - Fixed #1635: edges are now referring to the correct points. +- Fixed #1644, #1631: overlapping nodes in hierarchical layout should no longer occur. +- Added parentCentralization option for hierarchical layout. ## 2016-02-04, version 4.14.0 diff --git a/dist/vis.js b/dist/vis.js index a9d8ac86..b1220eec 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.14.0 - * @date 2016-02-12 + * @date 2016-02-13 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -1582,7 +1582,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module) {//! moment.js - //! version : 2.11.1 + //! version : 2.11.2 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com @@ -3399,7 +3399,7 @@ return /******/ (function(modules) { // webpackBootstrap } // ASP.NET json date format regex - var aspNetRegex = /(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/; + var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere @@ -5154,7 +5154,7 @@ return /******/ (function(modules) { // webpackBootstrap // Side effect imports - utils_hooks__hooks.version = '2.11.1'; + utils_hooks__hooks.version = '2.11.2'; setHookCallback(local__createLocal); @@ -40083,6 +40083,7 @@ return /******/ (function(modules) { // webpackBootstrap treeSpacing: 200, blockShifting: true, edgeMinimization: true, + parentCentralization: true, direction: 'UD', // UD, DU, LR, RL sortMethod: 'hubsize' // hubsize, directed } @@ -40364,8 +40365,8 @@ return /******/ (function(modules) { // webpackBootstrap var undefinedLevel = false; this.hierarchicalLevels = {}; this.lastNodeOnLevel = {}; - this.hierarchicalParents = {}; - this.hierarchicalChildren = {}; + this.hierarchicalChildrenReference = {}; + this.hierarchicalParentReference = {}; this.hierarchicalTrees = {}; this.treeIndex = -1; @@ -40447,9 +40448,7 @@ return /******/ (function(modules) { // webpackBootstrap var treeSizes = getTreeSizes(); for (var i = 0; i < treeSizes.length - 1; i++) { var diff = treeSizes[i].max - treeSizes[i + 1].min; - if (diff !== _this2.options.hierarchical.treeSpacing) { - shiftTree(i + 1, diff - _this2.options.hierarchical.treeSpacing); - } + shiftTree(i + 1, diff + _this2.options.hierarchical.treeSpacing); } }; @@ -40458,7 +40457,9 @@ return /******/ (function(modules) { // webpackBootstrap for (var nodeId in _this2.hierarchicalTrees) { if (_this2.hierarchicalTrees.hasOwnProperty(nodeId)) { if (_this2.hierarchicalTrees[nodeId] === index) { - _this2._setPositionForHierarchy(_this2.body.nodes[nodeId], offset, undefined, true); + var node = _this2.body.nodes[nodeId]; + var pos = _this2._getPositionForHierarchy(node); + _this2._setPositionForHierarchy(node, pos + offset, undefined, true); } } } @@ -40483,7 +40484,7 @@ return /******/ (function(modules) { // webpackBootstrap // get the width of all trees var getTreeSizes = function getTreeSizes() { var treeWidths = []; - for (var i = 0; i < _this2.treeIndex; i++) { + for (var i = 0; i <= _this2.treeIndex; i++) { treeWidths.push(getTreeSize(i)); } return treeWidths; @@ -40492,8 +40493,8 @@ return /******/ (function(modules) { // webpackBootstrap // get a map of all nodes in this branch var getBranchNodes = function getBranchNodes(source, map) { map[source.id] = true; - if (_this2.hierarchicalParents[source.id]) { - var children = _this2.hierarchicalParents[source.id].children; + if (_this2.hierarchicalChildrenReference[source.id]) { + var children = _this2.hierarchicalChildrenReference[source.id]; if (children.length > 0) { for (var i = 0; i < children.length; i++) { getBranchNodes(_this2.body.nodes[children[i]], map); @@ -40543,8 +40544,8 @@ return /******/ (function(modules) { // webpackBootstrap // get the maximum level of a branch. var getMaxLevel = function getMaxLevel(nodeId) { var level = _this2.hierarchicalLevels[nodeId]; - if (_this2.hierarchicalParents[nodeId]) { - var children = _this2.hierarchicalParents[nodeId].children; + if (_this2.hierarchicalChildrenReference[nodeId]) { + var children = _this2.hierarchicalChildrenReference[nodeId]; if (children.length > 0) { for (var i = 0; i < children.length; i++) { level = Math.max(level, getMaxLevel(children[i])); @@ -40563,13 +40564,12 @@ return /******/ (function(modules) { // webpackBootstrap // check if two nodes have the same parent(s) var hasSameParent = function hasSameParent(node1, node2) { - var parents1 = _this2.hierarchicalChildren[node1.id]; - var parents2 = _this2.hierarchicalChildren[node2.id]; + var parents1 = _this2.hierarchicalParentReference[node1.id]; + var parents2 = _this2.hierarchicalParentReference[node2.id]; if (parents1 === undefined || parents2 === undefined) { return false; } - parents1 = parents1.parents; - parents2 = parents2.parents; + for (var i = 0; i < parents1.length; i++) { for (var j = 0; j < parents2.length; j++) { if (parents1[i] == parents2[j]) { @@ -40782,7 +40782,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (newPosition !== nodePosition) { - //console.log("moving Node:",diff, minSpace, maxSpace) + //console.log("moving Node:",diff, minSpace, maxSpace); _this2._setPositionForHierarchy(node, newPosition, undefined, true); //this.body.emitter.emit("_redraw"); stillShifting = true; @@ -40816,7 +40816,7 @@ return /******/ (function(modules) { // webpackBootstrap } }; - //// method to remove whitespace between branches. Because we do bottom up, we can center the parents. + // method to remove whitespace between branches. Because we do bottom up, we can center the parents. var shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) { var levels = Object.keys(_this2.distributionOrdering); levels = levels.reverse(); @@ -40837,6 +40837,19 @@ return /******/ (function(modules) { // webpackBootstrap } }; + // center all parents + var centerAllParentsBottomUp = function centerAllParentsBottomUp() { + var levels = Object.keys(_this2.distributionOrdering); + levels = levels.reverse(); + for (var i = 0; i < levels.length; i++) { + var level = levels[i]; + var levelNodes = _this2.distributionOrdering[level]; + for (var j = 0; j < levelNodes.length; j++) { + _this2._centerParent(levelNodes[j]); + } + } + }; + // the actual work is done here. if (this.options.hierarchical.blockShifting === true) { shiftBranchesCloserBottomUp(5); @@ -40848,6 +40861,10 @@ return /******/ (function(modules) { // webpackBootstrap minimizeEdgeLengthBottomUp(20); } + if (this.options.hierarchical.parentCentralization === true) { + centerAllParentsBottomUp(); + } + shiftTrees(); } @@ -40902,16 +40919,16 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: '_centerParent', value: function _centerParent(node) { - if (this.hierarchicalChildren[node.id]) { - var parents = this.hierarchicalChildren[node.id].parents; + if (this.hierarchicalParentReference[node.id]) { + var parents = this.hierarchicalParentReference[node.id]; for (var i = 0; i < parents.length; i++) { var parentId = parents[i]; var parentNode = this.body.nodes[parentId]; - if (this.hierarchicalParents[parentId]) { + if (this.hierarchicalChildrenReference[parentId]) { // get the range of the children var minPos = 1e9; var maxPos = -1e9; - var children = this.hierarchicalParents[parentId].children; + var children = this.hierarchicalChildrenReference[parentId]; if (children.length > 0) { for (var _i = 0; _i < children.length; _i++) { var childNode = this.body.nodes[children[_i]]; @@ -40956,13 +40973,22 @@ return /******/ (function(modules) { // webpackBootstrap var nodeArray = Object.keys(distribution[level]); nodeArray = this._indexArrayToNodes(nodeArray); this._sortNodeArray(nodeArray); + var handledNodeCount = 0; for (var i = 0; i < nodeArray.length; i++) { var node = nodeArray[i]; if (this.positionedNodes[node.id] === undefined) { - this._setPositionForHierarchy(node, this.options.hierarchical.nodeSpacing * i, level); + var pos = this.options.hierarchical.nodeSpacing * handledNodeCount; + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (handledNodeCount > 0) { + pos = this._getPositionForHierarchy(nodeArray[i - 1]) + this.options.hierarchical.nodeSpacing; + } + this._setPositionForHierarchy(node, pos, level); + this.positionedNodes[node.id] = true; this._placeBranchNodes(node.id, level); + + handledNodeCount++; } } } @@ -41178,14 +41204,14 @@ return /******/ (function(modules) { // webpackBootstrap if (_this6.hierarchicalLevels[childNode.id] > _this6.hierarchicalLevels[parentNode.id]) { var parentNodeId = parentNode.id; var childNodeId = childNode.id; - if (_this6.hierarchicalParents[parentNodeId] === undefined) { - _this6.hierarchicalParents[parentNodeId] = { children: [], amount: 0 }; + if (_this6.hierarchicalChildrenReference[parentNodeId] === undefined) { + _this6.hierarchicalChildrenReference[parentNodeId] = []; } - _this6.hierarchicalParents[parentNodeId].children.push(childNodeId); - if (_this6.hierarchicalChildren[childNodeId] === undefined) { - _this6.hierarchicalChildren[childNodeId] = { parents: [], amount: 0 }; + _this6.hierarchicalChildrenReference[parentNodeId].push(childNodeId); + if (_this6.hierarchicalParentReference[childNodeId] === undefined) { + _this6.hierarchicalParentReference[childNodeId] = []; } - _this6.hierarchicalChildren[childNodeId].parents.push(parentNodeId); + _this6.hierarchicalParentReference[childNodeId].push(parentNodeId); } }; @@ -41201,11 +41227,21 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: '_crawlNetwork', value: function _crawlNetwork(callback, startingNodeId) { + var _this7 = this; + if (callback === undefined) callback = function () {}; var progress = {}; - var crawler = function crawler(node) { + var treeIndex = 0; + + var crawler = function crawler(node, tree) { if (progress[node.id] === undefined) { + + if (_this7.hierarchicalTrees[node.id] === undefined) { + _this7.hierarchicalTrees[node.id] = tree; + _this7.treeIndex = Math.max(tree, _this7.treeIndex); + } + progress[node.id] = true; var childNode = undefined; for (var i = 0; i < node.edges.length; i++) { @@ -41218,7 +41254,7 @@ return /******/ (function(modules) { // webpackBootstrap if (node.id !== childNode.id) { callback(node, childNode, node.edges[i]); - crawler(childNode); + crawler(childNode, tree); } } } @@ -41229,7 +41265,10 @@ return /******/ (function(modules) { // webpackBootstrap if (startingNodeId === undefined) { for (var i = 0; i < this.body.nodeIndices.length; i++) { var node = this.body.nodes[this.body.nodeIndices[i]]; - crawler(node); + if (progress[node.id] === undefined) { + crawler(node, treeIndex); + treeIndex += 1; + } } } else { var node = this.body.nodes[startingNodeId]; @@ -41253,14 +41292,14 @@ return /******/ (function(modules) { // webpackBootstrap key: '_placeBranchNodes', value: function _placeBranchNodes(parentId, parentLevel) { // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalParents[parentId] === undefined) { + if (this.hierarchicalChildrenReference[parentId] === undefined) { return; } // get a list of childNodes var childNodes = []; - for (var i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalParents[parentId].children[i]]); + for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); } // use the positions to order the nodes. @@ -41329,9 +41368,9 @@ return /******/ (function(modules) { // webpackBootstrap } else { this.body.nodes[parentId].y += diff; } - if (this.hierarchicalParents[parentId] !== undefined) { - for (var i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { - this._shiftBlock(this.hierarchicalParents[parentId].children[i], diff); + if (this.hierarchicalChildrenReference[parentId] !== undefined) { + for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + this._shiftBlock(this.hierarchicalChildrenReference[parentId][i], diff); } } } @@ -41346,22 +41385,22 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: '_findCommonParent', value: function _findCommonParent(childA, childB) { - var _this7 = this; + var _this8 = this; var parents = {}; var iterateParents = function iterateParents(parents, child) { - if (_this7.hierarchicalChildren[child] !== undefined) { - for (var i = 0; i < _this7.hierarchicalChildren[child].parents.length; i++) { - var _parent = _this7.hierarchicalChildren[child].parents[i]; + if (_this8.hierarchicalParentReference[child] !== undefined) { + for (var i = 0; i < _this8.hierarchicalParentReference[child].length; i++) { + var _parent = _this8.hierarchicalParentReference[child][i]; parents[_parent] = true; iterateParents(parents, _parent); } } }; var findParent = function findParent(parents, child) { - if (_this7.hierarchicalChildren[child] !== undefined) { - for (var i = 0; i < _this7.hierarchicalChildren[child].parents.length; i++) { - var _parent2 = _this7.hierarchicalChildren[child].parents[i]; + if (_this8.hierarchicalParentReference[child] !== undefined) { + for (var i = 0; i < _this8.hierarchicalParentReference[child].length; i++) { + var _parent2 = _this8.hierarchicalParentReference[child][i]; if (parents[_parent2] !== undefined) { return { foundParent: _parent2, withChild: child }; } @@ -41401,27 +41440,6 @@ return /******/ (function(modules) { // webpackBootstrap this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; } this.distributionOrderingPresence[level][node.id] = true; - - if (this.hierarchicalTrees[node.id] === undefined) { - if (this.hierarchicalChildren[node.id] !== undefined) { - var tree = 1; - // get the lowest tree denominator. - for (var i = 0; i < this.hierarchicalChildren[node.id].parents.length; i++) { - var parentId = this.hierarchicalChildren[node.id].parents[i]; - if (this.hierarchicalTrees[parentId] !== undefined) { - //tree = Math.min(tree,this.hierarchicalTrees[parentId]); - tree = this.hierarchicalTrees[parentId]; - } - } - //for (let i = 0; i < this.hierarchicalChildren.parents.length; i++) { - // let parentId = this.hierarchicalChildren.parents[i]; - // this.hierarchicalTrees[parentId] = tree; - //} - this.hierarchicalTrees[node.id] = tree; - } else { - this.hierarchicalTrees[node.id] = ++this.treeIndex; - } - } } if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { @@ -42829,6 +42847,7 @@ return /******/ (function(modules) { // webpackBootstrap treeSpacing: { number: number }, blockShifting: { boolean: boolean }, edgeMinimization: { boolean: boolean }, + parentCentralization: { boolean: boolean }, direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed __type__: { object: object, boolean: boolean } @@ -43132,6 +43151,7 @@ return /******/ (function(modules) { // webpackBootstrap treeSpacing: [200, 20, 500, 5], blockShifting: true, edgeMinimization: true, + parentCentralization: true, direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL sortMethod: ['hubsize', 'directed'] // hubsize, directed } diff --git a/docs/network/layout.html b/docs/network/layout.html index 08f1eca9..3b38048b 100644 --- a/docs/network/layout.html +++ b/docs/network/layout.html @@ -109,6 +109,7 @@ var options = { treeSpacing: 200, blockShifting: true, edgeMinimization: true, + parentCentralization: true, direction: 'UD', // UD, DU, LR, RL sortMethod: 'hubsize' // hubsize, directed } @@ -142,6 +143,7 @@ network.setOptions(options); it's branch along with it for as far as it can, respecting the nodeSpacing on any level. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though! hierarchical.edgeMinimizationBooleantrue Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process. Each node will try to move along its free axis to reduce the total length of it's edges. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though! + hierarchical.parentCentralizationBooleantrue When true, the parents nodes will be centered again after the the layout algorithm has been finished. hierarchical.directionString'UD' The direction of the hierarchical layout. The available options are: UD, DU, LR, RL. To simplify: up-down, down-up, left-right, right-left. hierarchical.sortMethodString'hubsize' The algorithm used to ascertain the levels of the nodes based on the data. The possible options are: hubsize, directed.

Hubsize takes the nodes with the most edges and puts them at the top. From that the rest of the hierarchy is evaluated.

diff --git a/examples/network/layout/hierarchicalLayoutWithoutPhysics.html b/examples/network/layout/hierarchicalLayoutWithoutPhysics.html index dfa32f3b..27854810 100644 --- a/examples/network/layout/hierarchicalLayoutWithoutPhysics.html +++ b/examples/network/layout/hierarchicalLayoutWithoutPhysics.html @@ -47,6 +47,10 @@ The hierarchical layout can now be controlled without the use of physics. This i Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process. Each node will try to move along its free axis to reduce the total length of it's edges. + + parentCentralization + When true, the parents nodes will be centered again after the the layout algorithm has been finished. +

Play with the settings below the network and see how the layout changes! diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index 8e4fc6b3..48a6aa98 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -23,6 +23,7 @@ class LayoutEngine { treeSpacing: 200, blockShifting: true, edgeMinimization: true, + parentCentralization: true, direction: 'UD', // UD, DU, LR, RL sortMethod: 'hubsize' // hubsize, directed } @@ -294,8 +295,8 @@ class LayoutEngine { let undefinedLevel = false; this.hierarchicalLevels = {}; this.lastNodeOnLevel = {}; - this.hierarchicalParents = {}; - this.hierarchicalChildren = {}; + this.hierarchicalChildrenReference = {}; + this.hierarchicalParentReference = {}; this.hierarchicalTrees = {}; this.treeIndex = -1; @@ -379,9 +380,7 @@ class LayoutEngine { let treeSizes = getTreeSizes(); for (let i = 0; i < treeSizes.length - 1; i++) { let diff = treeSizes[i].max - treeSizes[i+1].min; - if (diff !== this.options.hierarchical.treeSpacing) { - shiftTree(i + 1, diff - this.options.hierarchical.treeSpacing); - } + shiftTree(i + 1, diff + this.options.hierarchical.treeSpacing); } }; @@ -390,7 +389,9 @@ class LayoutEngine { for (let nodeId in this.hierarchicalTrees) { if (this.hierarchicalTrees.hasOwnProperty(nodeId)) { if (this.hierarchicalTrees[nodeId] === index) { - this._setPositionForHierarchy(this.body.nodes[nodeId], offset, undefined, true); + let node = this.body.nodes[nodeId]; + let pos = this._getPositionForHierarchy(node); + this._setPositionForHierarchy(node, pos + offset, undefined, true); } } } @@ -415,7 +416,7 @@ class LayoutEngine { // get the width of all trees let getTreeSizes = () => { let treeWidths = []; - for (let i = 0; i < this.treeIndex; i++) { + for (let i = 0; i <= this.treeIndex; i++) { treeWidths.push(getTreeSize(i)); } return treeWidths; @@ -425,8 +426,8 @@ class LayoutEngine { // get a map of all nodes in this branch let getBranchNodes = (source, map) => { map[source.id] = true; - if (this.hierarchicalParents[source.id]) { - let children = this.hierarchicalParents[source.id].children; + if (this.hierarchicalChildrenReference[source.id]) { + let children = this.hierarchicalChildrenReference[source.id]; if (children.length > 0) { for (let i = 0; i < children.length; i++) { getBranchNodes(this.body.nodes[children[i]], map); @@ -467,8 +468,8 @@ class LayoutEngine { // get the maximum level of a branch. let getMaxLevel = (nodeId) => { let level = this.hierarchicalLevels[nodeId]; - if (this.hierarchicalParents[nodeId]) { - let children = this.hierarchicalParents[nodeId].children; + if (this.hierarchicalChildrenReference[nodeId]) { + let children = this.hierarchicalChildrenReference[nodeId]; if (children.length > 0) { for (let i = 0; i < children.length; i++) { level = Math.max(level,getMaxLevel(children[i])); @@ -487,13 +488,12 @@ class LayoutEngine { // check if two nodes have the same parent(s) let hasSameParent = (node1, node2) => { - let parents1 = this.hierarchicalChildren[node1.id]; - let parents2 = this.hierarchicalChildren[node2.id]; + let parents1 = this.hierarchicalParentReference[node1.id]; + let parents2 = this.hierarchicalParentReference[node2.id]; if (parents1 === undefined || parents2 === undefined) { return false; } - parents1 = parents1.parents; - parents2 = parents2.parents; + for (let i = 0; i < parents1.length; i++) { for (let j = 0; j < parents2.length; j++) { if (parents1[i] == parents2[j]) { @@ -676,7 +676,7 @@ class LayoutEngine { } if (newPosition !== nodePosition) { - //console.log("moving Node:",diff, minSpace, maxSpace) + //console.log("moving Node:",diff, minSpace, maxSpace); this._setPositionForHierarchy(node, newPosition, undefined, true); //this.body.emitter.emit("_redraw"); stillShifting = true; @@ -710,7 +710,7 @@ class LayoutEngine { } }; - //// method to remove whitespace between branches. Because we do bottom up, we can center the parents. + // method to remove whitespace between branches. Because we do bottom up, we can center the parents. let shiftBranchesCloserBottomUp = (iterations) => { let levels = Object.keys(this.distributionOrdering); levels = levels.reverse(); @@ -732,6 +732,19 @@ class LayoutEngine { } }; + // center all parents + let centerAllParentsBottomUp = () => { + let levels = Object.keys(this.distributionOrdering); + levels = levels.reverse(); + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + let levelNodes = this.distributionOrdering[level]; + for (let j = 0; j < levelNodes.length; j++) { + this._centerParent(levelNodes[j]); + } + } + }; + // the actual work is done here. if (this.options.hierarchical.blockShifting === true) { shiftBranchesCloserBottomUp(5); @@ -743,6 +756,10 @@ class LayoutEngine { minimizeEdgeLengthBottomUp(20); } + if (this.options.hierarchical.parentCentralization === true) { + centerAllParentsBottomUp() + } + shiftTrees(); } @@ -794,16 +811,16 @@ class LayoutEngine { * @private */ _centerParent(node) { - if (this.hierarchicalChildren[node.id]) { - let parents = this.hierarchicalChildren[node.id].parents; + if (this.hierarchicalParentReference[node.id]) { + let parents = this.hierarchicalParentReference[node.id]; for (var i = 0; i < parents.length; i++) { let parentId = parents[i]; let parentNode = this.body.nodes[parentId]; - if (this.hierarchicalParents[parentId]) { + if (this.hierarchicalChildrenReference[parentId]) { // get the range of the children let minPos = 1e9; let maxPos = -1e9; - let children = this.hierarchicalParents[parentId].children; + let children = this.hierarchicalChildrenReference[parentId]; if (children.length > 0) { for (let i = 0; i < children.length; i++) { let childNode = this.body.nodes[children[i]]; @@ -841,13 +858,20 @@ class LayoutEngine { let nodeArray = Object.keys(distribution[level]); nodeArray = this._indexArrayToNodes(nodeArray); this._sortNodeArray(nodeArray); + let handledNodeCount = 0; for (let i = 0; i < nodeArray.length; i++) { let node = nodeArray[i]; if (this.positionedNodes[node.id] === undefined) { - this._setPositionForHierarchy(node, this.options.hierarchical.nodeSpacing * i, level); + let pos = this.options.hierarchical.nodeSpacing * handledNodeCount; + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (handledNodeCount > 0) {pos = this._getPositionForHierarchy(nodeArray[i-1]) + this.options.hierarchical.nodeSpacing;} + this._setPositionForHierarchy(node, pos, level); + this.positionedNodes[node.id] = true; this._placeBranchNodes(node.id, level); + + handledNodeCount++; } } } @@ -1047,14 +1071,14 @@ class LayoutEngine { if (this.hierarchicalLevels[childNode.id] > this.hierarchicalLevels[parentNode.id]) { let parentNodeId = parentNode.id; let childNodeId = childNode.id; - if (this.hierarchicalParents[parentNodeId] === undefined) { - this.hierarchicalParents[parentNodeId] = {children: [], amount: 0}; + if (this.hierarchicalChildrenReference[parentNodeId] === undefined) { + this.hierarchicalChildrenReference[parentNodeId] = []; } - this.hierarchicalParents[parentNodeId].children.push(childNodeId); - if (this.hierarchicalChildren[childNodeId] === undefined) { - this.hierarchicalChildren[childNodeId] = {parents: [], amount: 0}; + this.hierarchicalChildrenReference[parentNodeId].push(childNodeId); + if (this.hierarchicalParentReference[childNodeId] === undefined) { + this.hierarchicalParentReference[childNodeId] = []; } - this.hierarchicalChildren[childNodeId].parents.push(parentNodeId); + this.hierarchicalParentReference[childNodeId].push(parentNodeId); } }; @@ -1070,8 +1094,16 @@ class LayoutEngine { */ _crawlNetwork(callback = function() {}, startingNodeId) { let progress = {}; - let crawler = (node) => { + let treeIndex = 0; + + let crawler = (node, tree) => { if (progress[node.id] === undefined) { + + if (this.hierarchicalTrees[node.id] === undefined) { + this.hierarchicalTrees[node.id] = tree; + this.treeIndex = Math.max(tree, this.treeIndex); + } + progress[node.id] = true; let childNode; for (let i = 0; i < node.edges.length; i++) { @@ -1085,7 +1117,7 @@ class LayoutEngine { if (node.id !== childNode.id) { callback(node, childNode, node.edges[i]); - crawler(childNode); + crawler(childNode, tree); } } } @@ -1097,7 +1129,10 @@ class LayoutEngine { if (startingNodeId === undefined) { for (let i = 0; i < this.body.nodeIndices.length; i++) { let node = this.body.nodes[this.body.nodeIndices[i]]; - crawler(node); + if (progress[node.id] === undefined) { + crawler(node, treeIndex); + treeIndex += 1; + } } } else { @@ -1121,14 +1156,14 @@ class LayoutEngine { */ _placeBranchNodes(parentId, parentLevel) { // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalParents[parentId] === undefined) { + if (this.hierarchicalChildrenReference[parentId] === undefined) { return; } // get a list of childNodes let childNodes = []; - for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalParents[parentId].children[i]]); + for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); } // use the positions to order the nodes. @@ -1182,6 +1217,8 @@ class LayoutEngine { } + + /** * Shift a branch a certain distance * @param parentId @@ -1195,9 +1232,9 @@ class LayoutEngine { else { this.body.nodes[parentId].y += diff; } - if (this.hierarchicalParents[parentId] !== undefined) { - for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { - this._shiftBlock(this.hierarchicalParents[parentId].children[i], diff); + if (this.hierarchicalChildrenReference[parentId] !== undefined) { + for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + this._shiftBlock(this.hierarchicalChildrenReference[parentId][i], diff); } } } @@ -1213,18 +1250,18 @@ class LayoutEngine { _findCommonParent(childA,childB) { let parents = {}; let iterateParents = (parents,child) => { - if (this.hierarchicalChildren[child] !== undefined) { - for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { - let parent = this.hierarchicalChildren[child].parents[i]; + if (this.hierarchicalParentReference[child] !== undefined) { + for (let i = 0; i < this.hierarchicalParentReference[child].length; i++) { + let parent = this.hierarchicalParentReference[child][i]; parents[parent] = true; iterateParents(parents, parent) } } }; let findParent = (parents, child) => { - if (this.hierarchicalChildren[child] !== undefined) { - for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { - let parent = this.hierarchicalChildren[child].parents[i]; + if (this.hierarchicalParentReference[child] !== undefined) { + for (let i = 0; i < this.hierarchicalParentReference[child].length; i++) { + let parent = this.hierarchicalParentReference[child][i]; if (parents[parent] !== undefined) { return {foundParent:parent, withChild:child}; } @@ -1260,28 +1297,6 @@ class LayoutEngine { this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; } this.distributionOrderingPresence[level][node.id] = true; - - if (this.hierarchicalTrees[node.id] === undefined) { - if (this.hierarchicalChildren[node.id] !== undefined) { - let tree = 1; - // get the lowest tree denominator. - for (let i = 0; i < this.hierarchicalChildren[node.id].parents.length; i++) { - let parentId = this.hierarchicalChildren[node.id].parents[i]; - if (this.hierarchicalTrees[parentId] !== undefined) { - //tree = Math.min(tree,this.hierarchicalTrees[parentId]); - tree = this.hierarchicalTrees[parentId]; - } - } - //for (let i = 0; i < this.hierarchicalChildren.parents.length; i++) { - // let parentId = this.hierarchicalChildren.parents[i]; - // this.hierarchicalTrees[parentId] = tree; - //} - this.hierarchicalTrees[node.id] = tree; - } - else { - this.hierarchicalTrees[node.id] = ++this.treeIndex; - } - } } if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { diff --git a/lib/network/options.js b/lib/network/options.js index 59904163..91bb6b16 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -127,6 +127,7 @@ let allOptions = { treeSpacing: { number }, blockShifting: { boolean }, edgeMinimization: { boolean }, + parentCentralization: { boolean }, direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed __type__: { object, boolean } @@ -431,6 +432,7 @@ let configureOptions = { treeSpacing: [200, 20, 500, 5], blockShifting: true, edgeMinimization: true, + parentCentralization: true, direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL sortMethod: ['hubsize', 'directed'] // hubsize, directed } diff --git a/test/networkTest.html b/test/networkTest.html index d1422420..60fcb6e7 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -18,13140 +18,100 @@
From 1cb16f64eec495845edbc97cad4490b59d879226 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Sat, 13 Feb 2016 14:59:45 +0100 Subject: [PATCH 06/16] Fixed #1575: fixed selection events --- HISTORY.md | 1 + dist/vis.js | 38 +++++++++++------------ lib/network/modules/InteractionHandler.js | 34 ++++++++++---------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 506f4636..75c77209 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -22,6 +22,7 @@ http://visjs.org - Fixed #1635: edges are now referring to the correct points. - Fixed #1644, #1631: overlapping nodes in hierarchical layout should no longer occur. - Added parentCentralization option for hierarchical layout. +- Fixed #1575: fixed selection events ## 2016-02-04, version 4.14.0 diff --git a/dist/vis.js b/dist/vis.js index b1220eec..3dbe3015 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -38286,8 +38286,8 @@ return /******/ (function(modules) { // webpackBootstrap var _determineIfDifferent2 = this._determineIfDifferent(previousSelection, currentSelection); - var nodesChanges = _determineIfDifferent2.nodesChanges; - var edgesChanges = _determineIfDifferent2.edgesChanges; + var nodesChanged = _determineIfDifferent2.nodesChanged; + var edgesChanged = _determineIfDifferent2.edgesChanged; var nodeSelected = false; @@ -38296,15 +38296,15 @@ return /******/ (function(modules) { // webpackBootstrap this.selectionHandler._generateClickEvent('selectNode', event, pointer); selected = true; nodeSelected = true; - } else if (selectedNodesCount - previouslySelectedNodeCount < 0) { - // node was deselected - this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); - selected = true; - } else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) { + } else if (nodesChanged === true && selectedNodesCount > 0) { this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); this.selectionHandler._generateClickEvent('selectNode', event, pointer); nodeSelected = true; selected = true; + } else if (selectedNodesCount - previouslySelectedNodeCount < 0) { + // node was deselected + this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); + selected = true; } // handle the selected edges @@ -38312,13 +38312,13 @@ return /******/ (function(modules) { // webpackBootstrap // edge was selected this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; - } else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { - // edge was deselected + } else if (selectedEdgesCount > 0 && edgesChanged === true) { this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection); + this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; - } else if (selectedEdgesCount === previouslySelectedEdgeCount && edgesChanges === true) { + } else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { + // edge was deselected this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection); - this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; } @@ -38333,37 +38333,37 @@ return /******/ (function(modules) { // webpackBootstrap * This function checks if the nodes and edges previously selected have changed. * @param previousSelection * @param currentSelection - * @returns {{nodesChanges: boolean, edgesChanges: boolean}} + * @returns {{nodesChanged: boolean, edgesChanged: boolean}} * @private */ }, { key: '_determineIfDifferent', value: function _determineIfDifferent(previousSelection, currentSelection) { - var nodesChanges = false; - var edgesChanges = false; + var nodesChanged = false; + var edgesChanged = false; for (var i = 0; i < previousSelection.nodes.length; i++) { if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { - nodesChanges = true; + nodesChanged = true; } } for (var i = 0; i < currentSelection.nodes.length; i++) { if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { - nodesChanges = true; + nodesChanged = true; } } for (var i = 0; i < previousSelection.edges.length; i++) { if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) { - edgesChanges = true; + edgesChanged = true; } } for (var i = 0; i < currentSelection.edges.length; i++) { if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) { - edgesChanges = true; + edgesChanged = true; } } - return { nodesChanges: nodesChanges, edgesChanges: edgesChanges }; + return { nodesChanged: nodesChanged, edgesChanged: edgesChanged }; } /** diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index f1b7fecd..b4e6fec4 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -189,7 +189,7 @@ class InteractionHandler { let selectedNodesCount = this.selectionHandler._getSelectedNodeCount(); let currentSelection = this.selectionHandler.getSelection(); - let {nodesChanges, edgesChanges} = this._determineIfDifferent(previousSelection, currentSelection); + let {nodesChanged, edgesChanged} = this._determineIfDifferent(previousSelection, currentSelection); let nodeSelected = false; if (selectedNodesCount - previouslySelectedNodeCount > 0) { // node was selected @@ -197,32 +197,34 @@ class InteractionHandler { selected = true; nodeSelected = true; } - else if (selectedNodesCount - previouslySelectedNodeCount < 0) { // node was deselected + else if (nodesChanged === true && selectedNodesCount > 0) { this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); + this.selectionHandler._generateClickEvent('selectNode', event, pointer); + nodeSelected = true; selected = true; } - else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) { + else if (selectedNodesCount - previouslySelectedNodeCount < 0) { // node was deselected this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); - this.selectionHandler._generateClickEvent('selectNode', event, pointer); - nodeSelected = true; selected = true; } + // handle the selected edges if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) { // edge was selected this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; } - else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { // edge was deselected + else if (selectedEdgesCount > 0 && edgesChanged === true) { this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection); + this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; } - else if (selectedEdgesCount === previouslySelectedEdgeCount && edgesChanges === true) { + else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { // edge was deselected this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection); - this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; } + // fire the select event if anything has been selected or deselected if (selected === true) { // select or unselect this.selectionHandler._generateClickEvent('select', event, pointer); @@ -234,35 +236,35 @@ class InteractionHandler { * This function checks if the nodes and edges previously selected have changed. * @param previousSelection * @param currentSelection - * @returns {{nodesChanges: boolean, edgesChanges: boolean}} + * @returns {{nodesChanged: boolean, edgesChanged: boolean}} * @private */ _determineIfDifferent(previousSelection,currentSelection) { - let nodesChanges = false; - let edgesChanges = false; + let nodesChanged = false; + let edgesChanged = false; for (let i = 0; i < previousSelection.nodes.length; i++) { if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { - nodesChanges = true; + nodesChanged = true; } } for (let i = 0; i < currentSelection.nodes.length; i++) { if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { - nodesChanges = true; + nodesChanged = true; } } for (let i = 0; i < previousSelection.edges.length; i++) { if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) { - edgesChanges = true; + edgesChanged = true; } } for (let i = 0; i < currentSelection.edges.length; i++) { if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) { - edgesChanges = true; + edgesChanged = true; } } - return {nodesChanges, edgesChanges}; + return {nodesChanged, edgesChanged}; } From bc90ffecc8b483dcf53bf90d7442bc66ad808dbf Mon Sep 17 00:00:00 2001 From: Tom Joseph Date: Sat, 13 Feb 2016 21:51:15 -0500 Subject: [PATCH 07/16] Use parseFloat instead of Number.parseFloat Internet Explorer does not support the ES6 Number.parseFloat (see http://caniuse.com/#feat=es6-number). Instead, substitute the regular parseFloat method. --- lib/timeline/component/DataAxis.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js index 0f3ec598..61055666 100644 --- a/lib/timeline/component/DataAxis.js +++ b/lib/timeline/component/DataAxis.js @@ -29,12 +29,12 @@ function DataAxis (body, options, svg, linegraphOptions) { alignZeros: true, left:{ range: {min:undefined,max:undefined}, - format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));}, + format: function (value) {return ''+parseFloat(value.toPrecision(3));}, title: {text:undefined,style:undefined} }, right:{ range: {min:undefined,max:undefined}, - format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));}, + format: function (value) {return ''+parseFloat(value.toPrecision(3));}, title: {text:undefined,style:undefined} } }; From b11ab7756f33e784a78a62fe7e7fa4060bb20edc Mon Sep 17 00:00:00 2001 From: jos Date: Mon, 15 Feb 2016 10:11:51 +0100 Subject: [PATCH 08/16] Updated history --- HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 75c77209..0fa3b1e6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,8 @@ http://visjs.org ### Graph2d - Fixed #1630: method `getDataRange` was wrongly called `getItemRange` in docs. +- Fixed #1655: use parseFloat instead of Number.parseFloat, as the latter is + not supported in IE. ### Graph3d From d7382cd7eb86649e2129688f19e813ee1763b4ba Mon Sep 17 00:00:00 2001 From: jos Date: Mon, 15 Feb 2016 10:55:08 +0100 Subject: [PATCH 09/16] Updated history --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0fa3b1e6..992a29bc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,7 +12,7 @@ http://visjs.org - Fixed #1630: method `getDataRange` was wrongly called `getItemRange` in docs. - Fixed #1655: use parseFloat instead of Number.parseFloat, as the latter is - not supported in IE. + not supported in IE. Thanks @ttjoseph. ### Graph3d From 02de079d19a7f07300b1424a4634571a9e5b64e0 Mon Sep 17 00:00:00 2001 From: jos Date: Tue, 16 Feb 2016 16:02:13 +0100 Subject: [PATCH 10/16] Fixed #1661: main files for bower should not be minified --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 7da02ad8..2b709948 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "vis", - "main": ["dist/vis.min.js", "dist/vis.min.css"], + "main": ["dist/vis.js", "dist/vis.css"], "description": "A dynamic, browser-based visualization library.", "homepage": "http://visjs.org/", "license": ["Apache-2.0", "MIT"], From 386082e443c9a4db7e69a1ab7ffe1c7619cfc245 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Sun, 21 Feb 2016 14:11:11 +0100 Subject: [PATCH 11/16] Fixed #1677: updating groups through manipulation now works as it should. --- HISTORY.md | 1 + dist/vis.js | 4 +- examples/network/nodeStyles/customGroups.html | 8 +- lib/network/modules/ManipulationSystem.js | 2 +- test/networkTest.html | 292 +++++++++++++----- 5 files changed, 214 insertions(+), 93 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 75c77209..ec0cc1bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -23,6 +23,7 @@ http://visjs.org - Fixed #1644, #1631: overlapping nodes in hierarchical layout should no longer occur. - Added parentCentralization option for hierarchical layout. - Fixed #1575: fixed selection events +- Fixed #1677: updating groups through manipulation now works as it should. ## 2016-02-04, version 4.14.0 diff --git a/dist/vis.js b/dist/vis.js index 3dbe3015..4172875f 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.14.0 - * @date 2016-02-13 + * @date 2016-02-21 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -41792,7 +41792,7 @@ return /******/ (function(modules) { // webpackBootstrap this.inMode = 'editNode'; if (typeof this.options.editNode === 'function') { if (node.isCluster !== true) { - var data = util.deepExtend({}, node.options, true); + var data = util.deepExtend({}, node.options, false); data.x = node.x; data.y = node.y; diff --git a/examples/network/nodeStyles/customGroups.html b/examples/network/nodeStyles/customGroups.html index 04eb8e04..bf810bcc 100644 --- a/examples/network/nodeStyles/customGroups.html +++ b/examples/network/nodeStyles/customGroups.html @@ -26,12 +26,9 @@ - + We use an icon once in the DOM so the CSS for fontAwesome is loaded.
diff --git a/lib/network/modules/ManipulationSystem.js b/lib/network/modules/ManipulationSystem.js index a0ab16de..94b8b5f7 100644 --- a/lib/network/modules/ManipulationSystem.js +++ b/lib/network/modules/ManipulationSystem.js @@ -263,7 +263,7 @@ class ManipulationSystem { this.inMode = 'editNode'; if (typeof this.options.editNode === 'function') { if (node.isCluster !== true) { - let data = util.deepExtend({}, node.options, true); + let data = util.deepExtend({}, node.options, false); data.x = node.x; data.y = node.y; diff --git a/test/networkTest.html b/test/networkTest.html index 60fcb6e7..d4f3d0ef 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -18,106 +18,228 @@
+ showPath(); + showPath(); - - + function getNodeId(nodeLabels) { + var resultArray = []; + var unmatchedNodesLabel = []; + + for (var i = 0; i < nodeLabels.length; i++) { + for (var nodeId in allNodes) { + var matchflag = false; + + if (nodeLabels[i].toUpperCase() === allNodes[nodeId].label.toUpperCase()) { + resultArray.push(allNodes[nodeId].id); + matchflag = true; + break; + } + } + + if (!matchflag) { + console.log('An unmatched nodeLabel exists!' + nodeLabels[i]); + } + } + + return resultArray; + } + + function getLinkedEdge(fromNodeId, toNodeId) { + var result; + + if (fromNodeId) { + var allLinkedEdges = network.getConnectedEdges(fromNodeId); + + for (var i = 0; i < allLinkedEdges.length; i++) { + var linkedNodes = network.getConnectedNodes(allLinkedEdges[i]); + + for (var j = 0; j < linkedNodes.length; j++) { + if (toNodeId === linkedNodes[j]) { + result = allLinkedEdges[i]; + if (j == 0) { + if (allEdges[allLinkedEdges[i]].arrows === undefined) { + allEdges[allLinkedEdges[i]].arrows = 'from'; + } else { + allEdges[allLinkedEdges[i]].arrows = 'to;from'; + } + } else { + if (allEdges[allLinkedEdges[i]].arrows === undefined) { + allEdges[allLinkedEdges[i]].arrows = 'to'; + } else { + allEdges[allLinkedEdges[i]].arrows = 'to;from'; + } + } + break; + } + } + } + } + + return result; + } + + function updateNodesEdges() { + // update nodes and edges + var updateNodes = []; + var updateEdges = []; + + for (var nodeId in allNodes) { + if (allNodes.hasOwnProperty(nodeId)) { + updateNodes.push(allNodes[nodeId]); + } + } + + for (var edgeId in allEdges) { + if (allEdges.hasOwnProperty(edgeId)) { + updateEdges.push(allEdges[edgeId]); + } + } + + nodesDataset.update(updateNodes); + edgesDataset.update(updateEdges); + } + + function showPath() { + // Test values + var pathData = 'test1,test3,test4,test5'; + var nodesOrder = pathData.split(','); + + var orderedNodeId = getNodeId(nodesOrder); + + if (orderedNodeId.length > 0) { + pathActive = true; + + // mark all nodes as hard to read + for (var nodeId in allNodes) { + allNodes[nodeId].color = 'rgba(200,200,200,0.5)'; + } + + // mark all edges as hard to read + for (var edgeId in allEdges) { + allEdges[edgeId].color = 'rgba(200,200,200,0.5)'; + } + + // the selected nodes and edges get its own color and its label back. + for (var i = 0; i < orderedNodeId.length; i++) { + allNodes[orderedNodeId[i]].color = undefined; + + // linked edge + if (i < orderedNodeId.length) { + var linkedEdge = getLinkedEdge(orderedNodeId[i], orderedNodeId[i + 1]); + + if (linkedEdge) { + allEdges[linkedEdge].color = undefined; + } + } + } + + updateNodesEdges(); + } + + // reset all nodes + for (var nodeId in allNodes) { + allNodes[nodeId].color = undefined; + } + + // reset all edges <--- it does NOT work! although edge's color undfined! + for (var edgeId in allEdges) { + allEdges[edgeId].color = null; + allEdges[edgeId].arrows = null; + } + + updateNodesEdges(); + } From 43472862bcce9881bd4be25147b6ca41d5fe70fb Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Sun, 21 Feb 2016 15:09:40 +0100 Subject: [PATCH 12/16] - Fixed #1672: Implemented stepped scaling for nice interpolation of images. - Added interpolation option for interpolation of images, default true. --- HISTORY.md | 2 + dist/vis.js | 38 +++++++++++++++++-- docs/network/nodes.html | 8 ++++ lib/network/modules/NodesHandler.js | 1 + .../components/nodes/util/CircleImageBase.js | 33 +++++++++++++++- lib/network/options.js | 2 + 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7d77917e..786e299e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -26,6 +26,8 @@ http://visjs.org - Added parentCentralization option for hierarchical layout. - Fixed #1575: fixed selection events - Fixed #1677: updating groups through manipulation now works as it should. +- Fixed #1672: Implemented stepped scaling for nice interpolation of images. +- Added interpolation option for interpolation of images, default true. ## 2016-02-04, version 4.14.0 diff --git a/dist/vis.js b/dist/vis.js index 4172875f..7f1769af 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -25592,14 +25592,14 @@ return /******/ (function(modules) { // webpackBootstrap left: { range: { min: undefined, max: undefined }, format: function format(value) { - return '' + Number.parseFloat(value.toPrecision(3)); + return '' + parseFloat(value.toPrecision(3)); }, title: { text: undefined, style: undefined } }, right: { range: { min: undefined, max: undefined }, format: function format(value) { - return '' + Number.parseFloat(value.toPrecision(3)); + return '' + parseFloat(value.toPrecision(3)); }, title: { text: undefined, style: undefined } } @@ -28552,6 +28552,7 @@ return /******/ (function(modules) { // webpackBootstrap shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape + interpolation: true, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false // only for image shape }, @@ -30221,8 +30222,35 @@ return /******/ (function(modules) { // webpackBootstrap // draw shadow if enabled this.enableShadow(ctx); - // draw image - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + var factor = this.imageObj.width / this.width / this.body.view.scale; + if (factor > 2 && this.options.shapeProperties.interpolation === true) { + var w = this.imageObj.width; + var h = this.imageObj.height; + var can2 = document.createElement('canvas'); + can2.width = w; + can2.height = w; + var ctx2 = can2.getContext('2d'); + + factor *= 0.5; + w *= 0.5; + h *= 0.5; + ctx2.drawImage(this.imageObj, 0, 0, w, h); + + var distance = 0; + var iterations = 1; + while (factor > 2 && iterations < 4) { + ctx2.drawImage(can2, distance, 0, w, h, distance + w, 0, w / 2, h / 2); + distance += w; + factor *= 0.5; + w *= 0.5; + h *= 0.5; + iterations += 1; + } + ctx.drawImage(can2, distance, 0, w, h, this.left, this.top, this.width, this.height); + } else { + // draw image + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + } // disable shadows for other elements. this.disableShadow(ctx); @@ -42941,6 +42969,7 @@ return /******/ (function(modules) { // webpackBootstrap shapeProperties: { borderDashes: { boolean: boolean, array: array }, borderRadius: { number: number }, + interpolation: { boolean: boolean }, useImageSize: { boolean: boolean }, useBorderWithImage: { boolean: boolean }, __type__: { object: object } @@ -43081,6 +43110,7 @@ return /******/ (function(modules) { // webpackBootstrap shapeProperties: { borderDashes: false, borderRadius: [6, 0, 20, 1], + interpolation: true, useImageSize: false }, size: [25, 0, 200, 1] diff --git a/docs/network/nodes.html b/docs/network/nodes.html index 146a2233..c2ee7d39 100644 --- a/docs/network/nodes.html +++ b/docs/network/nodes.html @@ -180,6 +180,7 @@ var options = { shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape + interpolation: false, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false // only for image shape } @@ -645,6 +646,13 @@ mySize = minSize + diff * scale; This property is used only for the box shape. It allows you to determine the roundness of the corners of the shape. + + shapeProperties.interpolation + Boolean + true + This property only applies to the image and circularImage shapes. When true, the image is resampled when scaled down, resulting in a nicer image at the cost of computional time. + + shapeProperties.useImageSize Boolean diff --git a/lib/network/modules/NodesHandler.js b/lib/network/modules/NodesHandler.js index f7f6d650..475092fc 100644 --- a/lib/network/modules/NodesHandler.js +++ b/lib/network/modules/NodesHandler.js @@ -96,6 +96,7 @@ class NodesHandler { shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape + interpolation: true, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false // only for image shape }, diff --git a/lib/network/modules/components/nodes/util/CircleImageBase.js b/lib/network/modules/components/nodes/util/CircleImageBase.js index ab0cf97f..0bf68a82 100644 --- a/lib/network/modules/components/nodes/util/CircleImageBase.js +++ b/lib/network/modules/components/nodes/util/CircleImageBase.js @@ -103,8 +103,37 @@ class CircleImageBase extends NodeBase { // draw shadow if enabled this.enableShadow(ctx); - // draw image - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + let factor = (this.imageObj.width / this.width) / this.body.view.scale; + if (factor > 2 && this.options.shapeProperties.interpolation === true) { + let w = this.imageObj.width; + let h = this.imageObj.height; + var can2 = document.createElement('canvas'); + can2.width = w; + can2.height = w; + var ctx2 = can2.getContext('2d'); + + factor *= 0.5; + w *= 0.5; + h *= 0.5; + ctx2.drawImage(this.imageObj, 0, 0, w, h); + + let distance = 0; + let iterations = 1; + while (factor > 2 && iterations < 4) { + ctx2.drawImage(can2, distance, 0, w, h, distance+w, 0, w/2, h/2); + distance += w; + factor *= 0.5; + w *= 0.5; + h *= 0.5; + iterations += 1; + } + ctx.drawImage(can2, distance, 0, w, h, this.left, this.top, this.width, this.height); + } + else { + // draw image + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + } + // disable shadows for other elements. this.disableShadow(ctx); diff --git a/lib/network/options.js b/lib/network/options.js index 91bb6b16..59c84641 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -221,6 +221,7 @@ let allOptions = { shapeProperties: { borderDashes: { boolean, array }, borderRadius: { number }, + interpolation: { boolean }, useImageSize: { boolean }, useBorderWithImage: { boolean }, __type__: { object } @@ -362,6 +363,7 @@ let configureOptions = { shapeProperties: { borderDashes: false, borderRadius: [6, 0, 20, 1], + interpolation: true, useImageSize: false }, size: [25, 0, 200, 1] From 448734ceb99340fca5bc4a20d348610f7e06e5ca Mon Sep 17 00:00:00 2001 From: jos Date: Tue, 23 Feb 2016 11:10:34 +0100 Subject: [PATCH 13/16] Fixed #1683: implemented `currentTimeTick` event --- HISTORY.md | 1 + docs/graph2d/index.html | 6 ++++++ docs/timeline/index.html | 5 +++++ lib/timeline/component/CurrentTime.js | 1 + test/timeline.html | 4 ++++ 5 files changed, 17 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 786e299e..cffe8437 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,7 @@ http://visjs.org ### Timeline +- Implemented `currentTimeTick` event (see #1683). - Fixed #1630: method `getItemRange` missing in docs. ### Graph2d diff --git a/docs/graph2d/index.html b/docs/graph2d/index.html index e6412f7b..ed062202 100644 --- a/docs/graph2d/index.html +++ b/docs/graph2d/index.html @@ -1304,6 +1304,12 @@ Graph2d.off('rangechanged', onChange); Properties Description + + + currentTimeTick + Fired when the current time bar redraws. The rate depends on the zoom level. + + click diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 9d41a32b..f0b4d742 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -1348,6 +1348,11 @@ timeline.off('select', onSelect); Description + + currentTimeTick + Fired when the current time bar redraws. The rate depends on the zoom level. + + click diff --git a/lib/timeline/component/CurrentTime.js b/lib/timeline/component/CurrentTime.js index d23ba377..ee1c7148 100644 --- a/lib/timeline/component/CurrentTime.js +++ b/lib/timeline/component/CurrentTime.js @@ -129,6 +129,7 @@ CurrentTime.prototype.start = function() { if (interval > 1000) interval = 1000; me.redraw(); + me.body.emitter.emit('currentTimeTick'); // start a renderTimer to adjust for the new time me.currentTimeTimer = setTimeout(update, interval); diff --git a/test/timeline.html b/test/timeline.html index 9cc1f384..7887b340 100644 --- a/test/timeline.html +++ b/test/timeline.html @@ -216,6 +216,10 @@ items.on('update', console.log.bind(console)); items.on('remove', console.log.bind(console)); + timeline.on('currentTimeTick', function () { + console.log('currentTimeTick') + }); + // timeline.setOptions({timeAxis:{scale: 'minute', step: 5}}) From 822243e02a7b40dd95304986d7eb5d3e1b8414a5 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Tue, 23 Feb 2016 14:27:32 +0100 Subject: [PATCH 14/16] fixed another bug in hierarchical layout --- dist/vis.js | 168 +++++++------- lib/network/modules/LayoutEngine.js | 161 +++++++------ test/networkTest.html | 344 ++++++++++++---------------- 3 files changed, 317 insertions(+), 356 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index 7f1769af..0a083a9a 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.14.0 - * @date 2016-02-21 + * @date 2016-02-23 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -41012,9 +41012,7 @@ return /******/ (function(modules) { // webpackBootstrap pos = this._getPositionForHierarchy(nodeArray[i - 1]) + this.options.hierarchical.nodeSpacing; } this._setPositionForHierarchy(node, pos, level); - - this.positionedNodes[node.id] = true; - this._placeBranchNodes(node.id, level); + this._validataPositionAndContinue(node, level, pos); handledNodeCount++; } @@ -41023,6 +41021,93 @@ return /******/ (function(modules) { // webpackBootstrap } } + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param parentId + * @param parentLevel + * @private + */ + }, { + key: '_placeBranchNodes', + value: function _placeBranchNodes(parentId, parentLevel) { + // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. + if (this.hierarchicalChildrenReference[parentId] === undefined) { + return; + } + + // get a list of childNodes + var childNodes = []; + for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); + } + + // use the positions to order the nodes. + this._sortNodeArray(childNodes); + + // position the childNodes + for (var i = 0; i < childNodes.length; i++) { + var childNode = childNodes[i]; + var childNodeLevel = this.hierarchicalLevels[childNode.id]; + // check if the child node is below the parent node and if it has already been positioned. + if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { + // get the amount of space required for this node. If parent the width is based on the amount of children. + var pos = undefined; + + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (i === 0) { + pos = this._getPositionForHierarchy(this.body.nodes[parentId]); + } else { + pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing; + } + this._setPositionForHierarchy(childNode, pos, childNodeLevel); + this._validataPositionAndContinue(childNode, childNodeLevel, pos); + } else { + return; + } + } + + // center the parent nodes. + var minPos = 1e9; + var maxPos = -1e9; + for (var i = 0; i < childNodes.length; i++) { + var childNodeId = childNodes[i].id; + minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + } + this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); + } + + /** + * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. + * Finally it will call _placeBranchNodes to place the branch nodes. + * @param node + * @param level + * @param pos + * @private + */ + }, { + key: '_validataPositionAndContinue', + value: function _validataPositionAndContinue(node, level, pos) { + // if overlap has been detected, we shift the branch + if (this.lastNodeOnLevel[level] !== undefined) { + var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]); + if (pos - previousPos < this.options.hierarchical.nodeSpacing) { + var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; + var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); + this._shiftBlock(sharedParent.withChild, diff); + } + } + + // store change in position. + this.lastNodeOnLevel[level] = node.id; + + this.positionedNodes[node.id] = true; + + this._placeBranchNodes(node.id, level); + } + /** * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on * node properties. @@ -41308,80 +41393,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param parentId - * @param parentLevel - * @private - */ - }, { - key: '_placeBranchNodes', - value: function _placeBranchNodes(parentId, parentLevel) { - // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalChildrenReference[parentId] === undefined) { - return; - } - - // get a list of childNodes - var childNodes = []; - for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); - } - - // use the positions to order the nodes. - this._sortNodeArray(childNodes); - - // position the childNodes - for (var i = 0; i < childNodes.length; i++) { - var childNode = childNodes[i]; - var childNodeLevel = this.hierarchicalLevels[childNode.id]; - // check if the child node is below the parent node and if it has already been positioned. - if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { - // get the amount of space required for this node. If parent the width is based on the amount of children. - var pos = undefined; - - // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y - if (i === 0) { - pos = this._getPositionForHierarchy(this.body.nodes[parentId]); - } else { - pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing; - } - this._setPositionForHierarchy(childNode, pos, childNodeLevel); - - // if overlap has been detected, we shift the branch - if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { - var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]); - if (pos - previousPos < this.options.hierarchical.nodeSpacing) { - var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; - var sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id); - this._shiftBlock(sharedParent.withChild, diff); - } - } - - // store change in position. - this.lastNodeOnLevel[childNodeLevel] = childNode.id; - - this.positionedNodes[childNode.id] = true; - - this._placeBranchNodes(childNode.id, childNodeLevel); - } else { - return; - } - } - - // center the parent nodes. - var minPos = 1e9; - var maxPos = -1e9; - for (var i = 0; i < childNodes.length; i++) { - var childNodeId = childNodes[i].id; - minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - } - this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); - } - /** * Shift a branch a certain distance * @param parentId @@ -41457,6 +41468,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function _setPositionForHierarchy(node, position, level) { var doNotUpdate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; + //console.log('_setPositionForHierarchy',node.id, position) if (doNotUpdate !== true) { if (this.distributionOrdering[level] === undefined) { this.distributionOrdering[level] = []; diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index 48a6aa98..78ab7f79 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -867,9 +867,7 @@ class LayoutEngine { // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y if (handledNodeCount > 0) {pos = this._getPositionForHierarchy(nodeArray[i-1]) + this.options.hierarchical.nodeSpacing;} this._setPositionForHierarchy(node, pos, level); - - this.positionedNodes[node.id] = true; - this._placeBranchNodes(node.id, level); + this._validataPositionAndContinue(node, level, pos); handledNodeCount++; } @@ -878,6 +876,89 @@ class LayoutEngine { } } + + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param parentId + * @param parentLevel + * @private + */ + _placeBranchNodes(parentId, parentLevel) { + // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. + if (this.hierarchicalChildrenReference[parentId] === undefined) { + return; + } + + // get a list of childNodes + let childNodes = []; + for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); + } + + // use the positions to order the nodes. + this._sortNodeArray(childNodes); + + // position the childNodes + for (let i = 0; i < childNodes.length; i++) { + let childNode = childNodes[i]; + let childNodeLevel = this.hierarchicalLevels[childNode.id]; + // check if the child node is below the parent node and if it has already been positioned. + if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { + // get the amount of space required for this node. If parent the width is based on the amount of children. + let pos; + + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);} + else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;} + this._setPositionForHierarchy(childNode, pos, childNodeLevel); + this._validataPositionAndContinue(childNode, childNodeLevel, pos); + } + else { + return; + } + } + + // center the parent nodes. + let minPos = 1e9; + let maxPos = -1e9; + for (let i = 0; i < childNodes.length; i++) { + let childNodeId = childNodes[i].id; + minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + } + this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); + } + + + /** + * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. + * Finally it will call _placeBranchNodes to place the branch nodes. + * @param node + * @param level + * @param pos + * @private + */ + _validataPositionAndContinue(node, level, pos) { + // if overlap has been detected, we shift the branch + if (this.lastNodeOnLevel[level] !== undefined) { + let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]); + if (pos - previousPos < this.options.hierarchical.nodeSpacing) { + let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos; + let sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); + this._shiftBlock(sharedParent.withChild, diff); + } + } + + // store change in position. + this.lastNodeOnLevel[level] = node.id; + + this.positionedNodes[node.id] = true; + + this._placeBranchNodes(node.id, level); + } + /** * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on * node properties. @@ -1146,79 +1227,6 @@ class LayoutEngine { } - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param parentId - * @param parentLevel - * @private - */ - _placeBranchNodes(parentId, parentLevel) { - // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalChildrenReference[parentId] === undefined) { - return; - } - - // get a list of childNodes - let childNodes = []; - for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); - } - - // use the positions to order the nodes. - this._sortNodeArray(childNodes); - - // position the childNodes - for (let i = 0; i < childNodes.length; i++) { - let childNode = childNodes[i]; - let childNodeLevel = this.hierarchicalLevels[childNode.id]; - // check if the child node is below the parent node and if it has already been positioned. - if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { - // get the amount of space required for this node. If parent the width is based on the amount of children. - let pos; - - // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y - if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);} - else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;} - this._setPositionForHierarchy(childNode, pos, childNodeLevel); - - // if overlap has been detected, we shift the branch - if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { - let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]); - if (pos - previousPos < this.options.hierarchical.nodeSpacing) { - let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos; - let sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id); - this._shiftBlock(sharedParent.withChild, diff); - } - } - - // store change in position. - this.lastNodeOnLevel[childNodeLevel] = childNode.id; - - this.positionedNodes[childNode.id] = true; - - this._placeBranchNodes(childNode.id, childNodeLevel); - } - else { - return; - } - } - - // center the parent nodes. - let minPos = 1e9; - let maxPos = -1e9; - for (let i = 0; i < childNodes.length; i++) { - let childNodeId = childNodes[i].id; - minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - } - this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); - } - - - - /** * Shift a branch a certain distance * @param parentId @@ -1286,6 +1294,7 @@ class LayoutEngine { * @private */ _setPositionForHierarchy(node, position, level, doNotUpdate = false) { + //console.log('_setPositionForHierarchy',node.id, position) if (doNotUpdate !== true) { if (this.distributionOrdering[level] === undefined) { this.distributionOrdering[level] = []; diff --git a/test/networkTest.html b/test/networkTest.html index d4f3d0ef..71208a78 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -7,7 +7,7 @@