Browse Source

Merge branch 'clusterRewrite' into v4

Conflicts:
	HISTORY.md
	dist/vis.js
	dist/vis.map
	dist/vis.min.css
	dist/vis.min.js
flowchartTest
Alex de Mulder 9 years ago
parent
commit
dc977a7386
35 changed files with 2503 additions and 1281 deletions
  1. +26
    -1
      HISTORY.md
  2. +6
    -0
      docs/graph2d.html
  3. +25
    -2
      docs/network.html
  4. +65
    -0
      examples/graph2d/19_labels.html
  5. +11
    -7
      examples/network/06_groups.html
  6. +0
    -1
      examples/network/25_physics_configuration.html
  7. +17
    -4
      examples/network/26_staticSmoothCurves.html
  8. +2
    -0
      examples/network/27_world_cup_network.html
  9. +166
    -0
      examples/network/38_node_as_icon.html
  10. +103
    -0
      examples/network/39_newClustering.html
  11. +1
    -0
      examples/network/index.html
  12. +24
    -1
      lib/DOMutil.js
  13. +65
    -21
      lib/network/Edge.js
  14. +46
    -19
      lib/network/Groups.js
  15. +182
    -98
      lib/network/Network.js
  16. +67
    -151
      lib/network/Node.js
  17. +4
    -1
      lib/network/Popup.js
  18. +432
    -933
      lib/network/mixins/ClusterMixin.js
  19. +1
    -4
      lib/network/mixins/HierarchicalLayoutMixin.js
  20. +1
    -2
      lib/network/mixins/MixinLoader.js
  21. +8
    -8
      lib/network/mixins/SelectionMixin.js
  22. +2
    -2
      lib/network/mixins/physics/BarnesHutMixin.js
  23. +1
    -1
      lib/network/mixins/physics/HierarchialRepulsionMixin.js
  24. +3
    -14
      lib/network/mixins/physics/PhysicsMixin.js
  25. +1
    -1
      lib/network/mixins/physics/RepulsionMixin.js
  26. +647
    -0
      lib/network/modules/ClusterEngine.js
  27. +33
    -0
      lib/network/modules/PhysicsEngine.js
  28. +409
    -0
      lib/network/modules/components/BarnesHutSolver.js
  29. +32
    -0
      lib/network/modules/components/CentralGravitySolver.js
  30. +101
    -0
      lib/network/modules/components/SpringSolver.js
  31. +3
    -3
      lib/timeline/Timeline.js
  32. +9
    -1
      lib/timeline/component/LineGraph.js
  33. +4
    -2
      lib/timeline/component/graph2d_types/bar.js
  34. +1
    -1
      lib/timeline/component/graph2d_types/points.js
  35. +5
    -3
      lib/util.js

+ 26
- 1
HISTORY.md View File

@ -22,8 +22,33 @@ http://visjs.org
- Fixed invalid css names for time axis grid, renamed hours class names from
`4-8h` to `h4-h8`.
### Network
- Rebuilt the cluster system
## not yet released, version 3.10.1-SNAPSHOT
### Network
- (added gradient coloring for lines, but set for release in 4.0 due to required refactoring of options)
- Fixed bug where a network that has frozen physics would resume redrawing after setData, setOptions etc.
- (add docs) Added option to bypass default groups. If more groups are specified in the nodes than there are in the groups, loop over supplied groups instead of default.
- (add docs) Added two new static smooth curves modes: curveCW and curve CCW.
- Added request redraw for certain internal processes to reduce number of draw calls.
- Added pull request for usage of Icons. Thanks @Dude9177!
- Allow hierarchical view to be set in setOptions.
### Graph2d
### Timeline
- Fixed not property initializing with a DataView for groups.
## not yet released, version 3.9.2-SNAPSHOT
## 2015-02-11, version 3.10.0
### Network

+ 6
- 0
docs/graph2d.html View File

@ -175,6 +175,12 @@ var items = [
<td>no</td>
<td>The ID of the group this point belongs to.</td>
</tr>
<tr>
<td>label</td>
<td>object</td>
<td>no</td>
<td>A label object which will be displayed near to the item. A label object has one requirement - a <b> content </b> property. In addition you can set the <b> xOffset, yOffset and className </b> for further appearance customisations </td>
</tr>
</table>
<h3 id="groups">Groups</h3>

+ 25
- 2
docs/network.html View File

@ -1009,7 +1009,6 @@ mySize = minSize + diff * scale;
<td>'white'</td>
<td>The color of the label stroke.</td>
</tr>
<tr>
<td class="greenField">shape</td>
<td>string</td>
@ -1018,7 +1017,7 @@ mySize = minSize + diff * scale;
Choose from
<code>ellipse</code> (default), <code>circle</code>, <code>box</code>,
<code>database</code>, <code>image</code>, <code>circularImage</code>, <code>label</code>, <code>dot</code>,
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>.
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, <code>square</code> and <code>icon</code>.
<br><br>
In case of <code>image</code> and <code>circularImage</code>, a property with name <code>image</code> must
@ -1095,6 +1094,30 @@ mySize = minSize + diff * scale;
<td>The maximum radius for a scaled node. Only applicable to shapes <code>dot</code>,
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>. This only does something if you supply a value.</td>
</tr>
<tr>
<td class="greenField">iconFontFace</td>
<td>String</td>
<td>undefined</td>
<td>Font face for icons, for example <code>FontAwesome</code> or <code>Ionicon</code>.<br /><em>You have to link to the css defining the font by yourself (see Examples)</em></td>
</tr>
<tr>
<td class="greenField">icon</td>
<td>String</td>
<td>undefined</td>
<td>Unicode of the icon f.e. <code>\uf0c0</code> (user-icon in FontAwesome)</td>
</tr>
<tr>
<td class="greenField">iconSize</td>
<td>Number</td>
<td>50</td>
<td>Size of the icon</td>
</tr>
<tr>
<td class="greenField">color</td>
<td>String</td>
<td>black</td>
<td>Color of the icon</td>
</tr>
</table>

+ 65
- 0
examples/graph2d/19_labels.html View File

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Graph2d | Basic Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
.red {
fill:red;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2d | Label Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the how to add a label to each point in Graph2d. Each item can have a label object which contains the content and CSS class.In addition, xOffset and yOffset will adjust the location of the label relative to the point being labelled.
<br /><br />
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var label1 = {
content: "offset label",
xOffset: 20,
yOffset: 20
}
var label2 = {
content: "Label2",
className: "red"
}
var items = [
{x: '2014-06-11', y: 10,label:label1},
{x: '2014-06-12', y: 25,label:label2},
{x: '2014-06-13', y: 30},
{x: '2014-06-14', y: 10},
{x: '2014-06-15', y: 15},
{x: '2014-06-16', y: 30}
];
var dataset = new vis.DataSet(items);
var options = {
start: '2014-06-10',
end: '2014-06-18',
style:'bar'
};
var graph2d = new vis.Graph2d(container, dataset, options);
</script>
</body>
</html>

+ 11
- 7
examples/network/06_groups.html View File

@ -8,9 +8,10 @@
font: 10pt arial;
}
#mynetwork {
width: 600px;
height: 600px;
width: 1900px;
height: 900px;
border: 1px solid lightgray;
background-color:#222222;
}
</style>
@ -139,11 +140,14 @@
edges: edges
};
var options = {
stabilize: false,
stabilize: true,
nodes: {
shape: 'dot'
shape: 'dot',
radius:30,
fontColor:'#ffffff',
borderWidth:2
},
physics: {barnesHut:{springLength: 200}}
physics: {barnesHut:{springLength: 100}}
};
network = new vis.Network(container, data, options);
}
@ -154,9 +158,9 @@
<body onload="draw()">
<form onsubmit= "javascript: draw(); return false;">
Number of groups:
<input type="text" value="6" id="groupCount" style="width: 50px;">
<input type="text" value="20" id="groupCount" style="width: 50px;">
Number of nodes per group:
<input type="text" value="7" id="nodeCount" style="width: 50px;">
<input type="text" value="1" id="nodeCount" style="width: 50px;">
<input type="submit" value="Go">
</form>
<br>

+ 0
- 1
examples/network/25_physics_configuration.html View File

@ -78,7 +78,6 @@
};
var options = {
edges:{opacity:0.2},
stabilize: false,
configurePhysics:true
};

+ 17
- 4
examples/network/26_staticSmoothCurves.html View File

@ -32,19 +32,26 @@
Smooth curve type:
<select id="dropdownID">
<option value="continuous">continuous</option>
<option value="continuous" selected="selected">continuous</option>
<option value="discrete">discrete</option>
<option value="diagonalCross">diagonalCross</option>
<option value="straightCross">straightCross</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
</select>
<option value="curvedCW">curvedCW</option>
<option value="curvedCCW">curvedCCW</option>
</select><br/>
Roundness (0..1): <input type="range" min="0" max="1" value="0.5" step="0.05" style="width:200px" id="roundnessSlider"> <input id="roundnessScreen" value="0.5"> (0.5 is max roundness for continuous, 1.0 for the others)
<div id="mynetwork"></div>
<script type="text/javascript">
var dropdown = document.getElementById("dropdownID");
dropdown.onchange = update;
var roundnessSlider = document.getElementById("roundnessSlider");
roundnessSlider.onchange = update;
var roundnessScreen = document.getElementById("roundnessScreen");
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
@ -53,7 +60,7 @@ dropdown.onchange = update;
// create an array with edges
var edges = [
{from: 1, to: 2}
{from: 1, to: 2, style:"arrow"}
];
// create a network
@ -68,8 +75,14 @@ dropdown.onchange = update;
function update() {
var type = dropdown.value;
network.setOptions({smoothCurves:{type:type}});
var roundness = roundnessSlider.value;
roundnessScreen.value = roundness;
var options = {smoothCurves:{type:type, roundness:roundness}}
network.setOptions(options);
}
update();
</script>
</body>

+ 2
- 0
examples/network/27_world_cup_network.html View File

@ -39,6 +39,8 @@ Smooth curve type:
<option value="straightCross">straightCross</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
<option value="curvedCW">curvedCW</option>
<option value="curvedCCW">curvedCCW</option>
</select><br/>
inheritColor option:
<select id="inheritColor">

+ 166
- 0
examples/network/38_node_as_icon.html View File

@ -0,0 +1,166 @@
๏ปฟ<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Network | node as icon</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<script language="JavaScript">
function draw() {
/*
* Example for FontAwesome
*/
var optionsFA = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf0c0',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf007',
iconSize: 50,
iconColor: '#aa00ff'
}
}
};
// create an array with nodes
var nodesFA = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf1ad',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create an array with edges
var edges = [{
from: 1,
to: 3
}, {
from: 1,
to: 4
}, {
from: 2,
to: 4
}, {
from: 3,
to: 5
}, {
from: 4,
to: 5
}];
// create a network
var containerFA = document.getElementById('mynetworkFA');
var dataFA = {
nodes: nodesFA,
edges: edges
};
var networkFA = new vis.Network(containerFA, dataFA, optionsFA);
/*
* Example for Ionicons
*/
var optionsIO = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47c',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47e',
iconSize: 50,
iconColor: '#aa00ff'
}
}
};
// create an array with nodes
var nodesIO = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf276',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create a network
var containerIO = document.getElementById('mynetworkIO');
var dataIO = {
nodes: nodesIO,
edges: edges
};
var networkIO = new vis.Network(containerIO, dataIO, optionsIO);
}
</script>
</head>
<body onload="draw()">
<h2>
<i class="fa fa-flag"></i> Use FontAwesome-icons for node</h2>
<div id="mynetworkFA"></div>
<h2>
<i class="ion ion-ionic"></i> Use Ionicons-icons for node</h2>
<div id="mynetworkIO"></div>
</body>
</html>

+ 103
- 0
examples/network/39_newClustering.html View File

@ -0,0 +1,103 @@
<!doctype html>
<html>
<head>
<title>Network | Basic usage</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
#mynetwork {
width: 400px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'},
{id: 6, label: 'Node 6', cid:1},
{id: 7, label: 'Node 7', cid:1},
{id: 8, label: 'Node 8', cid:1},
{id: 9, label: 'Node 9', cid:1},
{id: 10, label: 'Node 10', cid:1}
];
// create an array with edges
var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 10, to: 4},
{from: 2, to: 5},
{from: 6, to: 2},
{from: 7, to: 5},
{from: 8, to: 6},
{from: 9, to: 7},
{from: 10, to: 9}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);
var clusterOptions = {
joinCondition:function(parentOptions,childOptions) {
return true;
},
processClusterProperties: function (properties, childNodes, childEdges) {
return properties;
},
clusterNodeProperties: {id:'bla', borderWidth:8},
}
var clusterOptionsByData = {
joinCondition:function(childOptions) {
return childOptions.cid == 1;
},
processClusterProperties: function (properties, childNodes, childEdges) {
return properties;
},
clusterNodeProperties: {id:'bla', borderWidth:8}
}
// network.clusterByNodeData(clusterOptionsByData)
// network.clusterOutliers({clusterNodeProperties: {borderWidth:8}})
network.clusterByConnection(2, clusterOptions);
// network.clusterByConnection(9, {
// joinCondition:function(parentOptions,childOptions) {return true;},
// processProperties:function (properties, childNodes, childEdges) {
// return properties;
// },
// clusterNodeProperties: {id:'bla2', label:"bla2", borderWidth:8}
// });
network.on("select", function(params) {
if (params.nodes.length == 1) {
if (network.isCluster(params.nodes[0]) == true) {
network.openCluster(params.nodes[0])
}
}
})
// network.openCluster('bla');
// network.openCluster('bla2');
</script>
</body>
</html>

+ 1
- 0
examples/network/index.html View File

@ -49,6 +49,7 @@
<p><a href="35_label_stroke.html">35_label_stroke.html</a></p>
<p><a href="36_HTML_in_Nodes.html">36_HTML_in_Nodes.html</a></p>
<p><a href="37_label_alignment.html">37_label_alignment.html</a></p>
<p><a href="38_node_as_icon.html">38_node_as_icon.html</a></p>
<p><a href="graphviz/graphviz_gallery.html">graphviz_gallery.html</a></p>
</div>

+ 24
- 1
lib/DOMutil.js View File

@ -130,9 +130,10 @@ exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, inse
* @param group
* @param JSONcontainer
* @param svgContainer
* @param labelObj
* @returns {*}
*/
exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer, labelObj) {
var point;
if (group.options.drawPoints.style == 'circle') {
point = exports.getSVGElement('circle',JSONcontainer,svgContainer);
@ -152,6 +153,28 @@ exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
point.setAttributeNS(null, "style", group.group.options.drawPoints.styles);
}
point.setAttributeNS(null, "class", group.className + " point");
//handle label
var label = exports.getSVGElement('text',JSONcontainer,svgContainer);
if (labelObj){
if (labelObj.xOffset) {
x = x + labelObj.xOffset;
}
if (labelObj.yOffset) {
y = y + labelObj.yOffset;
}
if (labelObj.content) {
label.textContent = labelObj.content;
}
if (labelObj.className) {
label.setAttributeNS(null, "class", labelObj.className + " label");
}
}
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", y);
return point;
};

+ 65
- 21
lib/network/Edge.js View File

@ -23,6 +23,7 @@ function Edge (properties, network, networkConstants) {
var fields = ['edges','physics'];
var constants = util.selectiveBridgeObject(fields,networkConstants);
this.options = constants.edges;
this.physics = constants.physics;
this.options['smoothCurves'] = networkConstants['smoothCurves'];
@ -46,13 +47,13 @@ function Edge (properties, network, networkConstants) {
this.to = null; // a node
this.via = null; // a temp node
this.fromBackup = null; // used to clean up after reconnect
this.toBackup = null;; // used to clean up after reconnect
this.fromBackup = null; // used to clean up after reconnect (used for manipulation)
this.toBackup = null; // used to clean up after reconnect (used for manipulation)
// we use this to be able to reconnect the edge to a cluster if its node is put into a cluster
// by storing the original information we can revert to the original connection when the cluser is opened.
this.originalFromId = [];
this.originalToId = [];
this.fromArray = [];
this.toArray = [];
this.connected = false;
@ -76,10 +77,11 @@ Edge.prototype.setProperties = function(properties) {
if (!properties) {
return;
}
this.properties = properties;
var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width',
'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment', 'opacity',
'customScalingFunction'
'customScalingFunction','useGradients','value'
];
util.selectiveDeepExtend(fields, this.options, properties);
@ -135,9 +137,9 @@ Edge.prototype.connect = function () {
this.from = this.network.nodes[this.fromId] || null;
this.to = this.network.nodes[this.toId] || null;
this.connected = (this.from && this.to);
this.connected = (this.from !== null && this.to !== null);
if (this.connected) {
if (this.connected === true) {
this.from.attachEdge(this);
this.to.attachEdge(this);
}
@ -234,9 +236,32 @@ Edge.prototype.isOverlappingWith = function(obj) {
}
};
Edge.prototype._getColor = function() {
Edge.prototype._getColor = function(ctx) {
var colorObj = this.options.color;
if (this.options.useGradients == true) {
var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
var fromColor, toColor;
fromColor = this.from.options.color.highlight.border;
toColor = this.to.options.color.highlight.border;
if (this.from.selected == false && this.to.selected == false) {
fromColor = util.overrideOpacity(this.from.options.color.border, this.options.opacity);
toColor = util.overrideOpacity(this.to.options.color.border, this.options.opacity);
}
else if (this.from.selected == true && this.to.selected == false) {
toColor = this.to.options.color.border;
}
else if (this.from.selected == false && this.to.selected == true) {
fromColor = this.from.options.color.border;
}
grd.addColorStop(0, fromColor);
grd.addColorStop(1, toColor);
return grd;
}
if (this.colorDirty === true) {
if (this.options.inheritColor == "to") {
colorObj = {
highlight: this.to.options.color.highlight.border,
@ -255,6 +280,8 @@ Edge.prototype._getColor = function() {
this.colorDirty = false;
}
if (this.selected == true) {return colorObj.highlight;}
else if (this.hover == true) {return colorObj.hover;}
else {return colorObj.color;}
@ -270,7 +297,7 @@ Edge.prototype._getColor = function() {
*/
Edge.prototype._drawLine = function(ctx) {
// set style
ctx.strokeStyle = this._getColor();
ctx.strokeStyle = this._getColor(ctx);
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
@ -344,7 +371,6 @@ Edge.prototype._getViaCoordinates = function () {
var yVia = null;
var factor = this.options.smoothCurves.roundness;
var type = this.options.smoothCurves.type;
var dx = Math.abs(this.from.x - this.to.x);
var dy = Math.abs(this.from.y - this.to.y);
if (type == 'discrete' || type == 'diagonalCross') {
@ -437,17 +463,39 @@ Edge.prototype._getViaCoordinates = function () {
yVia = this.to.y + (1 - factor) * dy;
}
}
else if (type == 'curvedCW') {
var dx = this.to.x - this.from.x;
var dy = this.from.y - this.to.y;
var radius = Math.sqrt(dx*dx + dy*dy);
var pi = Math.PI;
var originalAngle = Math.atan2(dy,dx);
var myAngle = (originalAngle + ((factor * 0.5) + 0.5) * pi) % (2 * pi);
xVia = this.from.x + (factor*0.5 + 0.5)*radius*Math.sin(myAngle);
yVia = this.from.y + (factor*0.5 + 0.5)*radius*Math.cos(myAngle);
}
else if (type == 'curvedCCW') {
var dx = this.to.x - this.from.x;
var dy = this.from.y - this.to.y;
var radius = Math.sqrt(dx*dx + dy*dy);
var pi = Math.PI;
var originalAngle = Math.atan2(dy,dx);
var myAngle = (originalAngle + ((-factor * 0.5) + 0.5) * pi) % (2 * pi);
xVia = this.from.x + (factor*0.5 + 0.5)*radius*Math.sin(myAngle);
yVia = this.from.y + (factor*0.5 + 0.5)*radius*Math.cos(myAngle);
}
else { // continuous
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(1)
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(2)
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
@ -455,13 +503,11 @@ Edge.prototype._getViaCoordinates = function () {
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(3)
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(4, this.from.x, this.to.x)
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
@ -471,13 +517,11 @@ Edge.prototype._getViaCoordinates = function () {
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(5)
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(6)
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
@ -485,13 +529,11 @@ Edge.prototype._getViaCoordinates = function () {
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(7)
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(8)
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
@ -527,6 +569,8 @@ Edge.prototype._line = function (ctx) {
// this.via.y = via.y;
ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y);
ctx.stroke();
//ctx.circle(via.x,via.y,2)
//ctx.stroke();
return via;
}
}
@ -715,7 +759,7 @@ Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSi
*/
Edge.prototype._drawDashLine = function(ctx) {
// set style
ctx.strokeStyle = this._getColor();
ctx.strokeStyle = this._getColor(ctx);
ctx.lineWidth = this._getLineWidth();
var via = null;
@ -820,7 +864,7 @@ Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
Edge.prototype._drawArrowCenter = function(ctx) {
var point;
// set style
ctx.strokeStyle = this._getColor();
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this._getLineWidth();
@ -956,7 +1000,7 @@ Edge.prototype._findBorderPosition = function(from,ctx) {
*/
Edge.prototype._drawArrow = function(ctx) {
// set style
ctx.strokeStyle = this._getColor();
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this._getLineWidth();

+ 46
- 19
lib/network/Groups.js View File

@ -7,6 +7,9 @@ var util = require('../util');
function Groups() {
this.clear();
this.defaultIndex = 0;
this.groupsArray = [];
this.groupIndex = 0;
this.useDefaultGroups = true;
}
@ -14,16 +17,29 @@ function Groups() {
* default constants for group colors
*/
Groups.DEFAULT = [
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // 0: blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // 1: yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // 2: red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // 3: green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // 4: magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // 5: purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // 6: orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // 7: darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // 8: pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}}, // 9: mint
{border: "#990000", background: "#EE0000", highlight: {border: "#BB0000", background: "#FF3333"}, hover: {border: "#BB0000", background: "#FF3333"}}, // 10:bright red
{border: "#FF6000", background: "#FF6000", highlight: {border: "#FF6000", background: "#FF6000"}, hover: {border: "#FF6000", background: "#FF6000"}}, // 12: real orange
{border: "#97C2FC", background: "#2B7CE9", highlight: {border: "#D2E5FF", background: "#2B7CE9"}, hover: {border: "#D2E5FF", background: "#2B7CE9"}}, // 13: blue
{border: "#399605", background: "#255C03", highlight: {border: "#399605", background: "#255C03"}, hover: {border: "#399605", background: "#255C03"}}, // 14: green
{border: "#B70054", background: "#FF007E", highlight: {border: "#B70054", background: "#FF007E"}, hover: {border: "#B70054", background: "#FF007E"}}, // 15: magenta
{border: "#AD85E4", background: "#7C29F0", highlight: {border: "#D3BDF0", background: "#7C29F0"}, hover: {border: "#D3BDF0", background: "#7C29F0"}}, // 16: purple
{border: "#4557FA", background: "#000EA1", highlight: {border: "#6E6EFD", background: "#000EA1"}, hover: {border: "#6E6EFD", background: "#000EA1"}}, // 17: darkblue
{border: "#FFC0CB", background: "#FD5A77", highlight: {border: "#FFD1D9", background: "#FD5A77"}, hover: {border: "#FFD1D9", background: "#FD5A77"}}, // 18: pink
{border: "#C2FABC", background: "#74D66A", highlight: {border: "#E6FFE3", background: "#74D66A"}, hover: {border: "#E6FFE3", background: "#74D66A"}}, // 19: mint
{border: "#EE0000", background: "#990000", highlight: {border: "#FF3333", background: "#BB0000"}, hover: {border: "#FF3333", background: "#BB0000"}}, // 20:bright red
];
@ -54,12 +70,22 @@ Groups.prototype.clear = function () {
Groups.prototype.get = function (groupname) {
var group = this.groups[groupname];
if (group == undefined) {
// create new group
var index = this.defaultIndex % Groups.DEFAULT.length;
this.defaultIndex++;
group = {};
group.color = Groups.DEFAULT[index];
this.groups[groupname] = group;
if (this.useDefaultGroups === false && this.groupsArray.length > 0) {
// create new group
var index = this.groupIndex % this.groupsArray.length;
this.groupIndex++;
group = {};
group.color = this.groups[this.groupsArray[index]];
this.groups[groupname] = group;
}
else {
// create new group
var index = this.defaultIndex % Groups.DEFAULT.length;
this.defaultIndex++;
group = {};
group.color = Groups.DEFAULT[index];
this.groups[groupname] = group;
}
}
return group;
@ -67,13 +93,14 @@ Groups.prototype.get = function (groupname) {
/**
* Add a custom group style
* @param {String} groupname
* @param {String} groupName
* @param {Object} style An object containing borderColor,
* backgroundColor, etc.
* @return {Object} group The created group object
*/
Groups.prototype.add = function (groupname, style) {
this.groups[groupname] = style;
Groups.prototype.add = function (groupName, style) {
this.groups[groupName] = style;
this.groupsArray.push(groupName);
return style;
};

+ 182
- 98
lib/network/Network.js View File

@ -45,7 +45,6 @@ function Network (container, data, options) {
this.renderRefreshRate = 60; // hz (fps)
this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
this.renderTime = 0; // measured time it takes to render a frame
this.physicsTime = 0; // measured time it takes to render a frame
this.runDoubleSpeed = false;
this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
@ -85,6 +84,7 @@ function Network (container, data, options) {
fontSizeMin: 14,
fontSizeMax: 30,
fontSizeMaxVisible: 30,
value: 1,
level: -1,
color: {
border: '#2B7CE9',
@ -109,6 +109,7 @@ function Network (container, data, options) {
width: 1,
widthSelectionMultiplier: 2,
hoverWidth: 1.5,
value:1,
style: 'line',
color: {
color:'#848484',
@ -129,7 +130,8 @@ function Network (container, data, options) {
gap: 5,
altLength: undefined
},
inheritColor: "from" // to, from, false, true (== from)
inheritColor: "from", // to, from, false, true (== from)
useGradients: false // release in 4.0
},
configurePhysics:false,
physics: {
@ -163,26 +165,7 @@ function Network (container, data, options) {
springConstant: null
},
clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px).
maxFontSize: 1000,
forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster.
height: 1, // (px PNiC) | growth of the height per node in cluster.
radius: 1}, // (px PNiC) | growth of the radius per node in cluster.
maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster.
activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open.
clusterLevelDifference: 2, // used for normalization of the cluster levels
clusterByZoom: true // enable clustering through zooming in and out
enabled: false // (Boolean) | global on/off switch for clustering.
},
navigation: {
enabled: false
@ -214,6 +197,7 @@ function Network (container, data, options) {
minVelocity: 0.1, // px/s
stabilize: true, // stabilize before displaying the network
stabilizationIterations: 1000, // maximum number of iteration to stabilize
stabilizationStepsize: 100,
zoomExtentOnStabilize: true,
locale: 'en',
locales: locales,
@ -235,7 +219,8 @@ function Network (container, data, options) {
hideNodesOnDrag: false,
width : '100%',
height : '100%',
selectable: true
selectable: true,
useDefaultGroups: true
};
this.constants = util.extend({}, this.defaultOptions);
this.pixelRatio = 1;
@ -257,13 +242,14 @@ function Network (container, data, options) {
this.lockedOnNodeId = null;
this.lockedOnNodeOffset = null;
this.touchTime = 0;
this.redrawRequested = false;
// Node variables
var network = this;
this.groups = new Groups(); // object with groups
this.images = new Images(); // object with images
this.images.setOnloadCallback(function (status) {
network._redraw();
network._requestRedraw();
});
// keyboard navigation variables
@ -300,6 +286,14 @@ function Network (container, data, options) {
this.draggingNodes = false;
// containers for nodes and edges
this.body = {
calculationNodes: {},
calculationNodeIndices: {},
nodeIndices: {},
nodes: {},
edges: {}
}
this.calculationNodes = {};
this.calculationNodeIndices = [];
this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation
@ -310,9 +304,7 @@ function Network (container, data, options) {
this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw.
this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw
this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw
this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action
this.scale = 1; // defining the global scale variable in the constructor
this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out
// datasets or dataviews
this.nodesData = null; // A DataSet or DataView
@ -353,10 +345,9 @@ function Network (container, data, options) {
this.timer = undefined; // Scheduling function. Is definded in this.start();
// load data (the disable start variable will be the same as the enabled clustering)
this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled);
this.setData(data, this.constants.hierarchicalLayout.enabled);
// hierarchical layout
this.initializing = false;
if (this.constants.hierarchicalLayout.enabled == true) {
this._setupHierarchicalLayout();
}
@ -367,10 +358,11 @@ function Network (container, data, options) {
}
}
// if clustering is disabled, the simulation will have started in the setData function
if (this.constants.clustering.enabled) {
this.startWithClustering();
if (this.constants.stabilize == false) {
this.initializing = false;
}
this.on("stabilizationIterationsDone", function () {this.initializing = false; this.start();}.bind(this));
}
// Extend Network with an Emitter mixin
@ -580,11 +572,7 @@ Network.prototype.zoomExtent = function(options, initialZoom, disableStart) {
*/
Network.prototype._updateNodeIndexList = function() {
this._clearNodeIndexList();
for (var idx in this.nodes) {
if (this.nodes.hasOwnProperty(idx)) {
this.nodeIndices.push(idx);
}
}
this.nodeIndices = Object.keys(this.nodes);
};
@ -644,6 +632,7 @@ Network.prototype.setData = function(data, disableStart) {
this._setEdges(data && data.edges);
}
this._putDataInSector();
if (disableStart == false) {
if (this.constants.hierarchicalLayout.enabled == true) {
this._resetLevels();
@ -654,10 +643,15 @@ Network.prototype.setData = function(data, disableStart) {
if (this.constants.stabilize == true) {
this._stabilize();
}
else {
this.moving = true;
this.start();
}
}
this.start();
}
this.initializing = false;
else {
this.initializing = false;
}
};
/**
@ -675,6 +669,7 @@ Network.prototype.setOptions = function (options) {
util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes);
util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges);
this.groups.useDefaultGroups = this.constants.useDefaultGroups;
if (options.physics) {
util.mergeOptions(this.constants.physics, options.physics,'barnesHut');
util.mergeOptions(this.constants.physics, options.physics,'repulsion');
@ -785,7 +780,6 @@ Network.prototype.setOptions = function (options) {
throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');
}
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
this._loadPhysicsSystem();
@ -804,8 +798,15 @@ Network.prototype.setOptions = function (options) {
this._markAllEdgesAsDirty();
this.setSize(this.constants.width, this.constants.height);
this.moving = true;
this.start();
if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
this._resetLevels();
this._setupHierarchicalLayout();
}
if (this.initializing !== true) {
this.moving = true;
this.start();
}
}
};
@ -830,7 +831,6 @@ Network.prototype._create = function () {
this.frame.style.overflow = 'hidden';
this.frame.tabIndex = 900;
//////////////////////////////////////////////////////////////////
this.frame.canvas = document.createElement("canvas");
@ -945,10 +945,6 @@ Network.prototype._createKeyBinds = function() {
this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown");
this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup");
}
//this.keycharm.bind("1",this.increaseClusterLevel.bind(me), "keydown");
//this.keycharm.bind("2",this.decreaseClusterLevel.bind(me), "keydown");
//this.keycharm.bind("3",this.forceAggregateHubs.bind(me,true),"keydown");
//this.keycharm.bind("4",this.normalizeClusterLevels.bind(me), "keydown");
if (this.constants.dataManipulation.enabled == true) {
this.keycharm.bind("esc",this._createManipulatorBar.bind(me));
@ -1284,7 +1280,6 @@ Network.prototype._zoom = function(scale, pointer) {
this._setScale(scale);
this._setTranslation(tx, ty);
this.updateClustersDefault();
if (preScaleDragPointer != null) {
var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer);
@ -1358,10 +1353,20 @@ Network.prototype._onMouseWheel = function(event) {
Network.prototype._onMouseMoveTitle = function (event) {
var gesture = hammerUtil.fakeGesture(this, event);
var pointer = this._getPointer(gesture.center);
var popupVisible = false;
// check if the previously selected node is still selected
if (this.popupObj) {
this._checkHidePopup(pointer);
if (this.popup !== undefined) {
if (this.popup.hidden === false) {
this._checkHidePopup(pointer);
}
// if the popup was not hidden above
if (this.popup.hidden === false) {
popupVisible = true;
this.popup.setPosition(pointer.x + 3,pointer.y - 5)
this.popup.show();
}
}
// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
@ -1369,20 +1374,20 @@ Network.prototype._onMouseMoveTitle = function (event) {
this.frame.focus();
}
// start a timeout that will check if the mouse is positioned above
// an element
var me = this;
var checkShow = function() {
me._checkShowPopup(pointer);
};
if (this.popupTimer) {
clearInterval(this.popupTimer); // stop any running calculationTimer
}
if (!this.drag.dragging) {
this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
// start a timeout that will check if the mouse is positioned above an element
if (popupVisible === false) {
var me = this;
var checkShow = function () {
me._checkShowPopup(pointer);
};
if (this.popupTimer) {
clearInterval(this.popupTimer); // stop any running calculationTimer
}
if (!this.drag.dragging) {
this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
}
}
/**
* Adding hover highlights
*/
@ -1434,8 +1439,9 @@ Network.prototype._checkShowPopup = function (pointer) {
};
var id;
var lastPopupNode = this.popupObj;
var previousPopupObjId = this.popupObj === undefined ? "" : this.popupObj.id;
var nodeUnderCursor = false;
var popupType = "node";
if (this.popupObj == undefined) {
// search the nodes for overlap, select the top one in case of multiple nodes
@ -1468,7 +1474,7 @@ Network.prototype._checkShowPopup = function (pointer) {
for (id in edges) {
if (edges.hasOwnProperty(id)) {
var edge = edges[id];
if (edge.connected && (edge.getTitle() !== undefined) &&
if (edge.connected === true && (edge.getTitle() !== undefined) &&
edge.isOverlappingWith(obj)) {
overlappingEdges.push(id);
}
@ -1477,23 +1483,26 @@ Network.prototype._checkShowPopup = function (pointer) {
if (overlappingEdges.length > 0) {
this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]];
popupType = "edge";
}
}
if (this.popupObj) {
// show popup message window
if (this.popupObj != lastPopupNode) {
var me = this;
if (!me.popup) {
me.popup = new Popup(me.frame, me.constants.tooltip);
if (this.popupObj.id != previousPopupObjId) {
if (this.popup === undefined) {
this.popup = new Popup(this.frame, this.constants.tooltip);
}
this.popup.popupTargetType = popupType;
this.popup.popupTargetId = this.popupObj.id;
// adjust a small offset such that the mouse cursor is located in the
// bottom left location of the popup, and you can easily move over the
// popup area
me.popup.setPosition(pointer.x - 3, pointer.y - 3);
me.popup.setText(me.popupObj.getTitle());
me.popup.show();
this.popup.setPosition(pointer.x + 3, pointer.y - 5);
this.popup.setText(this.popupObj.getTitle());
this.popup.show();
}
}
else {
@ -1505,18 +1514,38 @@ Network.prototype._checkShowPopup = function (pointer) {
/**
* Check if the popup must be hided, which is the case when the mouse is no
* Check if the popup must be hidden, which is the case when the mouse is no
* longer hovering on the object
* @param {{x:Number, y:Number}} pointer
* @private
*/
Network.prototype._checkHidePopup = function (pointer) {
if (!this.popupObj || !this._getNodeAt(pointer) ) {
this.popupObj = undefined;
if (this.popup) {
this.popup.hide();
var pointerObj = {
left: this._XconvertDOMtoCanvas(pointer.x),
top: this._YconvertDOMtoCanvas(pointer.y),
right: this._XconvertDOMtoCanvas(pointer.x),
bottom: this._YconvertDOMtoCanvas(pointer.y)
};
var stillOnObj = false;
if (this.popup.popupTargetType == 'node') {
stillOnObj = this.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
if (stillOnObj === true) {
var overNode = this._getNodeAt(pointer);
stillOnObj = overNode.id == this.popup.popupTargetId;
}
}
else {
if (this._getNodeAt(pointer) === null) {
stillOnObj = this.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
}
}
if (stillOnObj === false) {
this.popupObj = undefined;
this.popup.hide();
}
};
@ -1640,7 +1669,6 @@ Network.prototype._addNodes = function(ids) {
this._updateCalculationNodes();
this._reconnectEdges();
this._updateValueRange(this.nodes);
this.updateLabels();
};
/**
@ -1876,7 +1904,6 @@ Network.prototype._reconnectEdges = function() {
for (id in nodes) {
if (nodes.hasOwnProperty(id)) {
nodes[id].edges = [];
nodes[id].dynamicEdges = [];
}
}
@ -1940,7 +1967,23 @@ Network.prototype.redraw = function() {
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private
*/
Network.prototype._redraw = function(hidden) {
Network.prototype._requestRedraw = function(hidden) {
if (this.redrawRequested !== true) {
this.redrawRequested = true;
if (this.requiresTimeout === true) {
window.setTimeout(this._redraw.bind(this, hidden),0);
}
else {
window.requestAnimationFrame(this._redraw.bind(this, hidden, true));
}
}
};
Network.prototype._redraw = function(hidden, requested) {
if (hidden === undefined) {
hidden = false;
}
this.redrawRequested = false;
var ctx = this.frame.canvas.getContext('2d');
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
@ -1964,7 +2007,7 @@ Network.prototype._redraw = function(hidden) {
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
};
if (!(hidden == true)) {
if (hidden === false) {
this._doInAllSectors("_drawAllSectorNodes", ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges", ctx);
@ -1975,22 +2018,22 @@ Network.prototype._redraw = function(hidden) {
this._doInAllSectors("_drawNodes",ctx,false);
}
if (!(hidden == true)) {
if (hidden === false) {
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes", ctx);
}
}
// this._doInSupportSector("_drawNodes",ctx,true);