Browse Source

fixed numerous clustering bugs, added more examples, numerous other bug fixes

flowchartTest
Alex de Mulder 9 years ago
parent
commit
ad2d70ed8b
25 changed files with 3634 additions and 3353 deletions
  1. +2846
    -2768
      dist/vis.js
  2. +12
    -6
      docs/network/index.html
  3. +0
    -246
      examples/network/categories/31_manipulation_and_localization.html
  4. +0
    -105
      examples/network/categories/39_newClustering.html
  5. +75
    -0
      examples/network/categories/events/clickEvents.html
  6. +73
    -0
      examples/network/categories/events/physicsEvents.html
  7. +83
    -0
      examples/network/categories/events/renderEvents.html
  8. +8
    -8
      examples/network/categories/labels/multiline_text.html
  9. +0
    -0
      examples/network/categories/physics/25_physics_configuration.html
  10. +35
    -88
      examples/network/categories/rest/animationShowcase.html
  11. +141
    -0
      examples/network/categories/rest/clustering.html
  12. +167
    -0
      examples/network/categories/rest/manipulation.html
  13. +11
    -11
      examples/network/categories/rest/navigation.html
  14. +42
    -42
      lib/network/Network.js
  15. +6
    -5
      lib/network/modules/CanvasRenderer.js
  16. +97
    -54
      lib/network/modules/Clustering.js
  17. +2
    -1
      lib/network/modules/LayoutEngine.js
  18. +6
    -3
      lib/network/modules/ManipulationSystem.js
  19. +11
    -8
      lib/network/modules/PhysicsEngine.js
  20. +5
    -0
      lib/network/modules/components/Edge.js
  21. +3
    -0
      lib/network/modules/components/Node.js
  22. +1
    -1
      lib/network/options.js
  23. +4
    -1
      lib/network/shapes.js
  24. +2
    -4
      lib/shared/Validator.js
  25. +4
    -2
      lib/util.js

+ 2846
- 2768
dist/vis.js
File diff suppressed because it is too large
View File


+ 12
- 6
docs/network/index.html View File

@ -551,16 +551,18 @@ var locales = {
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterByHubsize', this);">
<td colspan="2"><span parent="clusterByHubsize" class="right-caret"></span> clusterByHubsize(
<code>Number hubsize</code>,
<code>[Number hubsize]</code>,
<code>[Object options]</code>)
</td>
</tr>
<tr class="hidden" parent="clusterByHubsize">
<td class="midMethods">Returns: none</td>
<td>This method checks all nodes in the network and those with a equal or higher amount of edges than
specified with the <code>hubsize</code> qualify. Cluster by connection is performed on each of them.
The
options object is described for <code>clusterByConnection</code> and does the same here.
specified with the <code>hubsize</code> qualify. If a hubsize is not defined, the hubsize will be determined as the average
value plus two standard deviations. <br><br>
For all qualifying nodes, clusterByConnection is performed on each of them.
The options object is described for <code>clusterByConnection</code> and does the same here.
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterOutliers', this);">
@ -569,7 +571,9 @@ var locales = {
</tr>
<tr class="hidden" parent="clusterOutliers">
<td class="midMethods">Returns: none</td>
<td>This method will cluster all nodes with 1 edge with their respective connected node.</td>
<td>This method will cluster all nodes with 1 edge with their respective connected node.
The options object is explained in full <a data-scroll="" data-options="{ &quot;easing&quot;: &quot;easeInCubic&quot; }" href="#optionsObject">below</a>.
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','findNode', this);">
<td colspan="2"><span parent="findNode" class="right-caret"></span> findNode(
@ -1055,7 +1059,9 @@ network.clustering.cluster(options);
</td>
</tr>
<tr>
<td>processProperties(<br>&nbsp;&nbsp;<code>nodeOptions:&nbsp;Object</code><br>)</td>
<td>processProperties(<br>&nbsp;&nbsp;<code>clusterOptions:&nbsp;Object</code>,<br>
&nbsp;&nbsp;<code>childNodesOptions:&nbsp;Array</code>,<br>
&nbsp;&nbsp;<code>childEdgesOptions:&nbsp;Array</code><br>)</td>
<td>Function</td>
<td><i>Optional. </i> Before creating the new cluster node, this (optional) function will be called with
the

+ 0
- 246
examples/network/categories/31_manipulation_and_localization.html View File

@ -1,246 +0,0 @@
<!doctype html>
<html>
<head>
<title>Network | Localization</title>
<style type="text/css">
body, select {
font: 10pt sans;
}
#mynetwork {
position:relative;
width: 800px;
height: 600px;
border: 1px solid lightgray;
}
table.legend_table {
font-size: 11px;
border-width:1px;
border-color:#d3d3d3;
border-style:solid;
}
table.legend_table,td {
border-width:1px;
border-color:#d3d3d3;
border-style:solid;
padding: 2px;
}
div.table_content {
width:80px;
text-align:center;
}
div.table_description {
width:100px;
}
#operation {
font-size:28px;
}
#network-popUp {
display:none;
position:absolute;
top:350px;
left:170px;
z-index:299;
width:250px;
height:120px;
background-color: #f9f9f9;
border-style:solid;
border-width:3px;
border-color: #5394ed;
padding:10px;
text-align: center;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link type="text/css" rel="stylesheet" href="../../dist/vis.css">
<script type="text/javascript">
var nodes = null;
var edges = null;
var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() {
destroy();
nodes = [];
edges = [];
var connectionCount = [];
// randomly create some nodes and edges
var nodeCount = 25;
for (var i = 0; i < nodeCount; i++) {
nodes.push({
id: i,
label: String(i)
});
connectionCount[i] = 0;
// create edges in a scale-free-network way
if (i == 1) {
var from = i;
var to = 0;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
else if (i > 1) {
var conn = edges.length * 2;
var rand = Math.floor(Math.random() * conn);
var cum = 0;
var j = 0;
while (j < connectionCount.length && cum < rand) {
cum += connectionCount[j];
j++;
}
var from = i;
var to = j;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
}
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics: {
stabilization: false
},
manipulation: {
//handlerFunctions: {
addNode: function(data,callback) {
var span = document.getElementById('operation');
var idInput = document.getElementById('node-id');
var labelInput = document.getElementById('node-label');
var saveButton = document.getElementById('saveButton');
var cancelButton = document.getElementById('cancelButton');
var div = document.getElementById('network-popUp');
span.innerHTML = "Add Node";
idInput.value = data.id;
labelInput.value = data.label;
saveButton.onclick = saveData.bind(this,data,callback);
cancelButton.onclick = clearPopUp.bind();
div.style.display = 'block';
},
editNode: function(data,callback) {
var span = document.getElementById('operation');
var idInput = document.getElementById('node-id');
var labelInput = document.getElementById('node-label');
var saveButton = document.getElementById('saveButton');
var cancelButton = document.getElementById('cancelButton');
var div = document.getElementById('network-popUp');
span.innerHTML = "Edit Node";
idInput.value = data.id;
labelInput.value = data.label;
saveButton.onclick = saveData.bind(this,data,callback);
cancelButton.onclick = clearPopUp.bind();
div.style.display = 'block';
},
addEdge: function(data,callback) {
if (data.from == data.to) {
var r=confirm("Do you want to connect the node to itself?");
if (r==true) {
callback(data);
}
}
else {
callback(data);
}
}
//}
}
};
network = new vis.Network(container, data, options);
// add event listeners
network.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
network.on("resize", function(params) {console.log(params.width,params.height)});
function clearPopUp() {
var saveButton = document.getElementById('saveButton');
var cancelButton = document.getElementById('cancelButton');
saveButton.onclick = null;
cancelButton.onclick = null;
var div = document.getElementById('network-popUp');
div.style.display = 'none';
}
function saveData(data,callback) {
var idInput = document.getElementById('node-id');
var labelInput = document.getElementById('node-label');
data.id = idInput.value;
data.label = labelInput.value;
clearPopUp();
callback(data);
}
// update the locale when changing the select box value
var select = document.getElementById('locale');
select.onchange = function () {
network.setOptions({
manipulation: {
locale: this.value
}
});
};
select.onchange();
}
</script>
<script src="../googleAnalytics.js"></script>
</head>
<body onload="draw();">
<h2>Editing the dataset (localized)</h2>
<p style="width: 700px; font-size:14px; text-align: justify;">
This is the same example as <a href="21_data_manipulation.html">21_data_manipulation.html</a>, except that there is a select box added which allows to switch locale. The localization is only relevant to the manipulation buttons.
</p>
<p>
<label for="locale">Select a locale:</label>
<select id="locale">
<option value="en" selected>en</option>
<option value="nl">nl</option>
</select>
</p>
<div id="network-popUp">
<span id="operation">node</span> <br>
<table style="margin:auto;"><tr>
<td>id</td><td><input id="node-id" value="new value" /></td>
</tr>
<tr>
<td>label</td><td><input id="node-label" value="new value" /></td>
</tr></table>
<input type="button" value="save" id="saveButton" />
<input type="button" value="cancel" id="cancelButton" />
</div>
<br />
<div id="mynetwork"></div>
<p id="selection"></p>
</body>
</html>

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

@ -1,105 +0,0 @@
<!doctype html>
<html>
<head>
<title>Network | Clustering</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>
<script src="../googleAnalytics.js"></script>
</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:2},
// }
var clusterOptionsByData = {
joinCondition:function(childOptions) {
console.log(childOptions.id)
return childOptions.cid == 1;
},
processProperties: function (properties, childNodes, childEdges) {
return properties;
},
clusterNodeProperties: {id:'bla', borderWidth:2}
}
network.clustering.cluster(clusterOptionsByData)
// network.clustering.clusterOutliers({clusterNodeProperties: {shape:'database',borderWidth:3}})
// 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.body.emitter.on("select", function(params) {
if (params.nodes.length == 1) {
if (network.clustering.isCluster(params.nodes[0]) == true) {
network.clustering.openCluster(params.nodes[0])
}
}
})
// network.openCluster('bla');
// network.openCluster('bla2');
</script>
</body>
</html>

+ 75
- 0
examples/network/categories/events/clickEvents.html View File

@ -0,0 +1,75 @@
<!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: 600px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<p>
Create a simple network with some nodes and edges.
</p>
<div id="mynetwork"></div>
<pre id="eventSpan"></pre>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'Node 1', title: 'I have a popup!'},
{id: 2, label: 'Node 2', title: 'I have a popup!'},
{id: 3, label: 'Node 3', title: 'I have a popup!'},
{id: 4, label: 'Node 4', title: 'I have a popup!'},
{id: 5, label: 'Node 5', title: 'I have a popup!'}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);
network.on("click", function (params) {
params.event = "[original event]";
document.getElementById('eventSpan').innerHTML = '<h2>Click event:</h2>' + JSON.stringify(params, null, 4);
});
network.on("doubleClick", function (params) {
params.event = "[original event]";
document.getElementById('eventSpan').innerHTML = '<h2>doubleClick event:</h2>' + JSON.stringify(params, null, 4);
});
network.on("oncontext", function (params) {
params.event = "[original event]";
document.getElementById('eventSpan').innerHTML = '<h2>oncontext (right click) event:</h2>' + JSON.stringify(params, null, 4);
});
network.on("zoom", function (params) {
document.getElementById('eventSpan').innerHTML = '<h2>zoom event:</h2>' + JSON.stringify(params, null, 4);
});
network.on("showPopup", function (params) {
document.getElementById('eventSpan').innerHTML = '<h2>showPopup event: </h2>' + JSON.stringify(params, null, 4);
});
</script>
<script src="../../../googleAnalytics.js"></script>
</body>
</html>

+ 73
- 0
examples/network/categories/events/physicsEvents.html View File

@ -0,0 +1,73 @@
<!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: 600px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<p>
Create a simple network with some nodes and edges.
</p>
<div id="mynetwork"></div>
<pre id="eventSpan"></pre>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{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'}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);
network.on("startStabilizing", function (params) {
document.getElementById('eventSpan').innerHTML = '<h3>Starting Stabilization</h3>';
console.log("started")
});
network.on("stabilizationProgress", function (params) {
document.getElementById('eventSpan').innerHTML = '<h3>Stabilization progress</h3>' + JSON.stringify(params, null, 4);
console.log("progress:",params);
});
network.on("stabilizationIterationsDone", function (params) {
document.getElementById('eventSpan').innerHTML = '<h3>Stabilization iterations complete</h3>';
console.log("finished stabilization interations");
});
network.on("stabilized", function (params) {
document.getElementById('eventSpan').innerHTML = '<h3>Stabilized!</h3>' + JSON.stringify(params, null, 4);
console.log("stabilized!", params);
});
</script>
<script src="../../../googleAnalytics.js"></script>
</body>
</html>

+ 83
- 0
examples/network/categories/events/renderEvents.html View File

@ -0,0 +1,83 @@
<!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: 600px;
height: 400px;
border: 1px solid lightgray;
}
p {
max-width:600px;
}
</style>
</head>
<body>
<p>
You can draw on the canvas using normal HTML5 canvas functions. The before drawing will be behind the network, the after drawing will be in front of the network.
</p>
<div id="mynetwork"></div>
<pre id="eventSpan"></pre>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{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'}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);
network.on("initRedraw", function () {
// do something like move some custom elements?
});
network.on("beforeDrawing", function (ctx) {
var nodeId = 1;
var nodePosition = network.getPositions([nodeId]);
ctx.strokeStyle = '#A6D5F7';
ctx.fillStyle = '#294475';
ctx.circle(nodePosition[nodeId].x, nodePosition[nodeId].y,50);
ctx.fill();
ctx.stroke();
});
network.on("afterDrawing", function (ctx) {
var nodeId = 1;
var nodePosition = network.getPositions([nodeId]);
ctx.strokeStyle = '#294475';
ctx.lineWidth = 4;
ctx.fillStyle = '#A6D5F7';
ctx.circle(nodePosition[nodeId].x, nodePosition[nodeId].y,20);
ctx.fill();
ctx.stroke();
});
</script>
<script src="../../../googleAnalytics.js"></script>
</body>
</html>

examples/network/categories/10_multiline_text.html → examples/network/categories/labels/multiline_text.html View File

@ -11,14 +11,14 @@
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../../../dist/vis.js"></script>
<link href="../../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
function draw() {
// create some nodes
var nodes = [
{id: 1, label: 'Node in\nthe center', shape: 'text'},
{id: 1, label: 'Node in\nthe center', shape: 'text', font:{strokeWidth:4}},
{id: 2, label: 'Node\nwith\nmultiple\nlines', shape: 'circle'},
{id: 3, label: 'This is a lot of text\nbut luckily we can spread\nover multiple lines', shape: 'database'},
{id: 4, label: 'This is text\non multiple lines', shape: 'box'},
@ -27,10 +27,10 @@
// create some edges
var edges = [
{from: 1, to: 2, style: 'line', color: 'red', width: 3, length: 200}, // individual length definition is possible
{from: 1, to: 3, style: 'dash-line', width: 1, length: 200},
{from: 1, to: 4, style: 'line', width: 1, length: 200, label:'I\'m an edge!'},
{from: 1, to: 5, style: 'arrow', width: 3, length: 200, label:'arrows\nare cool'}
{from: 1, to: 2, color: 'red', width: 3, length: 200}, // individual length definition is possible
{from: 1, to: 3, dashes:true, width: 1, length: 200},
{from: 1, to: 4, width: 1, length: 200, label:'I\'m an edge!'},
{from: 1, to: 5, arrows:'to', width: 3, length: 200, label:'arrows\nare cool'}
];
// create a network
@ -43,7 +43,7 @@
var network = new vis.Network(container, data, options);
}
</script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head>
<body onload="draw()">

examples/network/categories/25_physics_configuration.html → examples/network/categories/physics/25_physics_configuration.html View File


examples/network/categories/33_animation.html → examples/network/categories/rest/animationShowcase.html View File

@ -34,17 +34,20 @@
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../exampleUtil.js"></script>
<script type="text/javascript" src="../../../../dist/vis.js"></script>
<link href="../../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var nodes = null;
var edges = null;
var network = null;
var doButton, focusButton, showButton;
var offsetx, offsety, scale, positionx, positiony, duration, easingFunction, doButton, focusButton, showButton;
var statusUpdateSpan;
var finishMessage = '';
var showInterval = false;
var showPhase = 1;
var amountOfNodes = 25;
function destroy() {
if (network !== null) {
@ -53,65 +56,28 @@
}
}
function updateValues() {
offsetx = parseInt(document.getElementById('offsetx').value);
offsety = parseInt(document.getElementById('offsety').value);
duration = parseInt(document.getElementById('duration').value);
scale = parseFloat(document.getElementById('scale').value);
positionx = parseInt(document.getElementById('positionx').value);
positiony = parseInt(document.getElementById('positiony').value);
easingFunction = document.getElementById('easingFunction').value;
}
function draw() {
destroy();
statusUpdateSpan = document.getElementById('statusUpdate');
doButton = document.getElementById('btnDo');
focusButton = document.getElementById('btnFocus');
showButton = document.getElementById('btnShow');
nodes = [];
edges = [];
var connectionCount = [];
// randomly create some nodes and edges
var nodeCount = 25;
for (var i = 0; i < nodeCount; i++) {
nodes.push({
id: i,
label: String(i)
});
connectionCount[i] = 0;
// create edges in a scale-free-network way
if (i == 1) {
var from = i;
var to = 0;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
else if (i > 1) {
var conn = edges.length * 2;
var rand = Math.floor(Math.random() * conn);
var cum = 0;
var j = 0;
while (j < connectionCount.length && cum < rand) {
cum += connectionCount[j];
j++;
}
var from = i;
var to = j;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
}
var data = getScaleFreeNetwork(amountOfNodes);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics: {
stabilization: {
@ -133,28 +99,20 @@
})
}
function zoomExtentAnimated() {
var offsetx = parseInt(document.getElementById('offsetx').value);
var offsety = parseInt(document.getElementById('offsety').value);
var duration = parseInt(document.getElementById('duration').value);
var easingFunction = document.getElementById('easingFunction').value;
function fitAnimated() {
updateValues();
var options = {offset: {x:offsetx,y:offsety},
duration: duration,
easingFunction: easingFunction
};
statusUpdateSpan.innerHTML = 'Doing ZoomExtent() Animation.';
statusUpdateSpan.innerHTML = 'Doing fit() Animation.';
finishMessage = 'Animation finished.';
network.fit(options);
network.fit({animation:options});
}
function doDefaultAnimation() {
var offsetx = parseInt(document.getElementById('offsetx').value);
var offsety = parseInt(document.getElementById('offsety').value);
var scale = parseFloat(document.getElementById('scale').value);
var positionx = parseInt(document.getElementById('positionx').value);
var positiony = parseInt(document.getElementById('positiony').value);
var easingFunction = document.getElementById('easingFunction').value;
updateValues();
var options = {
position: {x:positionx,y:positiony},
@ -168,13 +126,7 @@
}
function doAnimation() {
var offsetx = parseInt(document.getElementById('offsetx').value);
var offsety = parseInt(document.getElementById('offsety').value);
var duration = parseInt(document.getElementById('duration').value);
var scale = parseFloat(document.getElementById('scale').value);
var positionx = parseInt(document.getElementById('positionx').value);
var positiony = parseInt(document.getElementById('positiony').value);
var easingFunction = document.getElementById('easingFunction').value;
updateValues();
var options = {
position: {x:positionx,y:positiony},
@ -191,13 +143,9 @@
}
function focusRandom() {
var nodeId = Math.floor(Math.random() * 25);
var offsetx = parseInt(document.getElementById('offsetx').value);
var offsety = parseInt(document.getElementById('offsety').value);
var duration = parseInt(document.getElementById('duration').value);
var scale = parseFloat(document.getElementById('scale').value);
var easingFunction = document.getElementById('easingFunction').value;
updateValues();
var nodeId = Math.floor(Math.random() * amountOfNodes);
var options = {
// position: {x:positionx,y:positiony}, // this is not relevant when focusing on nodes
scale: scale,
@ -213,9 +161,9 @@
}
var showInterval = false;
var showPhase = 1;
function startShow() {
updateValues();
if (showInterval !== false) {
showInterval = false;
showButton.value = 'Start a show!';
@ -223,7 +171,6 @@
}
else {
showButton.value = 'Stop the show!';
var duration = parseInt(document.getElementById('duration').value);
focusRandom();
setTimeout(doTheShow, duration);
showInterval = true;
@ -231,31 +178,31 @@
}
function doTheShow() {
updateValues();
if (showInterval == true) {
var duration = parseInt(document.getElementById('duration').value);
if (showPhase == 0) {
focusRandom();
showPhase = 1;
}
else {
zoomExtentAnimated();
fitAnimated();
showPhase = 0;
}
setTimeout(doTheShow, duration);
}
}
</script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head>
<body onload="draw();">
<h2>Camera animations</h2>
<div style="width:700px; font-size:14px;">
You can move the view around programmatically using the .moveTo(options) function. The options supplied to this function can
also be (partially) supplied to the .zoomExtent() and .focusOnNode() methods. These are explained in the docs.
also be (partially) supplied to the .fit() and .focusOnNode() methods. These are explained in the docs.
<br /><br/>
The buttons below take the fields from the table when they can. For instance, the "Animate with default settings." takes the position, scale and offset while using
the default animation values for duration and easing function. The focusOnNode takes everything except the position and the zoomExtent takes only the duration and easing function.
the default animation values for duration and easing function. The focusOnNode takes everything except the position and the fit takes only the duration and easing function.
<br/><br/>
Here you can see a full description of the options you can supply to moveTo:
</div>
@ -290,7 +237,7 @@ var moveToOptions = {
<td>offset y</td><td><input type="text" value="0" id="offsety" style="width:170px;"> px</td>
</tr>
<tr>
<td>duration</td><td><input type="text" value="2500" id="duration" style="width:170px;"> ms</td>
<td>duration</td><td><input type="text" value="1000" id="duration" style="width:170px;"> ms</td>
</tr>
<tr>
<td>easingFunction</td><td>
@ -317,7 +264,7 @@ var moveToOptions = {
Examples:
<input type="button" onclick="doAnimation();" value="Animate with above settings." style="width:300px;" id="btnDo"> <br/>
<input type="button" onclick="doDefaultAnimation();" value="Animate with default settings." style="width:300px;" id="btnDoDefault"> <br/>
<input type="button" onclick="zoomExtentAnimated();" value="Animated ZoomExtent()." style="width:300px;" id="btnZoom"> <br/>
<input type="button" onclick="fitAnimated();" value="Animated fit()." style="width:300px;" id="btnZoom"> <br/>
<input type="button" onclick="focusRandom();" value="Focus on random node." style="width:300px;" id="btnFocus"><br/>
<input type="button" onclick="startShow();" value="Start a show!" style="width:300px;" id="btnShow"><br/>
</div>

+ 141
- 0
examples/network/categories/rest/clustering.html View File

@ -0,0 +1,141 @@
<!doctype html>
<html>
<head>
<title>Network | Clustering</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: 600px;
height: 600px;
border: 1px solid lightgray;
}
p {
max-width:600px;
}
h4 {
margin-bottom:3px;
}
</style>
<script src="../../../googleAnalytics.js"></script>
</head>
<body>
<p>
Click any of the buttons below to cluster the network. On every push the network will be reinitialized first. You can click on a cluster to open it.
</p>
<input type="button" onclick="clusterByCid()" value="Cluster all nodes with CID = 1"> <br />
<input type="button" onclick="clusterByColor()" value="Cluster by color"> <br />
<input type="button" onclick="clusterByConnection()" value="Cluster 'node 1' by connections"> <br />
<input type="button" onclick="clusterOutliers()" value="Cluster outliers"> <br />
<input type="button" onclick="clusterByHubsize()" value="Cluster by hubsize"> <br />
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1', color:'orange'},
{id: 2, label: 'Node 2', color:'DarkViolet'},
{id: 3, label: 'Node 3', color:'orange'},
{id: 4, label: 'Node 4', color:'DarkViolet'},
{id: 5, label: 'Node 5', color:'orange'},
{id: 6, label: 'cid = 1', cid:1, color:'orange'},
{id: 7, label: 'cid = 1', cid:1, color:'DarkViolet'},
{id: 8, label: 'cid = 1', cid:1, color:'lime'},
{id: 9, label: 'cid = 1', cid:1, color:'orange'},
{id: 10, label: 'cid = 1', cid:1, color:'lime'}
];
// 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 = {layout:{randomSeed:8},nodes:{font:{strokeWidth:5}}};
var network = new vis.Network(container, data, options);
network.on("selectNode", function(params) {
if (params.nodes.length == 1) {
if (network.isCluster(params.nodes[0]) == true) {
network.openCluster(params.nodes[0])
}
}
})
function clusterByCid() {
network.setData(data);
var clusterOptionsByData = {
joinCondition:function(childOptions) {
return childOptions.cid == 1;
},
clusterNodeProperties: {id:'cidCluster', borderWidth:3, shape:'database'}
}
network.cluster(clusterOptionsByData);
}
function clusterByColor() {
network.setData(data);
var colors = ['orange','lime','DarkViolet'];
var clusterOptionsByData;
for (var i = 0; i < colors.length; i++) {
var color = colors[i];
clusterOptionsByData = {
joinCondition: function (childOptions) {
return childOptions.color.background == color; // the color is fully defined in the node.
},
processProperties: function (clusterOptions, childNodes, childEdges) {
var totalMass = 0;
for (var i = 0; i < childNodes.length; i++) {
totalMass += childNodes[i].mass;
}
clusterOptions.mass = totalMass;
return clusterOptions;
},
clusterNodeProperties: {id: 'cluster:' + color, borderWidth: 3, shape: 'database', color:color, label:'color:' + color}
}
network.cluster(clusterOptionsByData);
}
}
function clusterByConnection() {
network.setData(data);
network.clusterByConnection(1)
}
function clusterOutliers() {
network.setData(data);
network.clusterOutliers();
}
function clusterByHubsize() {
network.setData(data);
var clusterOptionsByData = {
processProperties: function(clusterOptions, childNodes) {
clusterOptions.label = "[" + childNodes.length + "]";
return clusterOptions;
},
clusterNodeProperties: {borderWidth:3, shape:'box', font:{size:30}}
}
network.clusterByHubsize(undefined, clusterOptionsByData);
}
</script>
</body>
</html>

+ 167
- 0
examples/network/categories/rest/manipulation.html View File

@ -0,0 +1,167 @@
<!doctype html>
<html>
<head>
<title>Network | Localization</title>
<style type="text/css">
body, select {
font: 10pt sans;
}
#mynetwork {
position:relative;
width: 800px;
height: 600px;
border: 1px solid lightgray;
}
table.legend_table {
font-size: 11px;
border-width:1px;
border-color:#d3d3d3;
border-style:solid;
}
table.legend_table,td {
border-width:1px;
border-color:#d3d3d3;
border-style:solid;
padding: 2px;
}
div.table_content {
width:80px;
text-align:center;
}
div.table_description {
width:100px;
}
#operation {
font-size:28px;
}
#network-popUp {
display:none;
position:absolute;
top:350px;
left:170px;
z-index:299;
width:250px;
height:120px;
background-color: #f9f9f9;
border-style:solid;
border-width:3px;
border-color: #5394ed;
padding:10px;
text-align: center;
}
</style>
<script type="text/javascript" src="../../exampleUtil.js"></script>
<script type="text/javascript" src="../../../../dist/vis.js"></script>
<link href="../../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var nodes = null;
var edges = null;
var network = null;
// randomly create some nodes and edges
var data = getScaleFreeNetwork(25);
var seed = 2;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() {
destroy();
nodes = [];
edges = [];
// create a network
var container = document.getElementById('mynetwork');
var options = {
layout: {randomSeed:seed}, // just to make sure the layout is the same when the locale is changed
locale: document.getElementById('locale').value,
manipulation: {
addNode: function (data, callback) {
// filling in the popup DOM elements
document.getElementById('operation').innerHTML = "Add Node";
document.getElementById('node-id').value = data.id;
document.getElementById('node-label').value = data.label;
document.getElementById('saveButton').onclick = saveData.bind(this, data, callback);
document.getElementById('cancelButton').onclick = clearPopUp.bind();
document.getElementById('network-popUp').style.display = 'block';
},
editNode: function (data, callback) {
// filling in the popup DOM elements
document.getElementById('operation').innerHTML = "Edit Node";
document.getElementById('node-id').value = data.id;
document.getElementById('node-label').value = data.label;
document.getElementById('saveButton').onclick = saveData.bind(this, data, callback);
document.getElementById('cancelButton').onclick = clearPopUp.bind();
document.getElementById('network-popUp').style.display = 'block';
},
addEdge: function (data, callback) {
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r == true) {
callback(data);
}
}
else {
callback(data);
}
}
}
};
network = new vis.Network(container, data, options);
}
function clearPopUp() {
document.getElementById('saveButton').onclick = null;
document.getElementById('cancelButton').onclick = null;
document.getElementById('network-popUp').style.display = 'none';
}
function saveData(data,callback) {
data.id = document.getElementById('node-id').value;
data.label = document.getElementById('node-label').value;
clearPopUp();
callback(data);
}
</script>
<script src="../../../googleAnalytics.js"></script>
</head>
<body onload="draw();">
<h2>Editing the nodes and edges (localized)</h2>
<p style="width: 700px; font-size:14px; text-align: justify;">
The localization is only relevant to the manipulation buttons.
</p>
<p>
<label for="locale">Select a locale:</label>
<select id="locale" onchange="draw();">
<option value="en" selected>en</option>
<option value="nl">nl</option>
</select>
</p>
<div id="network-popUp">
<span id="operation">node</span> <br>
<table style="margin:auto;"><tr>
<td>id</td><td><input id="node-id" value="new value" /></td>
</tr>
<tr>
<td>label</td><td><input id="node-label" value="new value" /></td>
</tr></table>
<input type="button" value="save" id="saveButton" />
<input type="button" value="cancel" id="cancelButton" />
</div>
<br />
<div id="mynetwork"></div>
</body>
</html>

examples/network/categories/20_navigation.html → examples/network/categories/rest/navigation.html View File

@ -10,7 +10,6 @@
#mynetwork {
width: 600px;
height: 600px;
margin:100px;
border: 1px solid lightgray;
}
table.legend_table {
@ -28,8 +27,9 @@
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link type="text/css" rel="stylesheet" href="../../dist/vis.css">
<script type="text/javascript" src="../../exampleUtil.js"></script>
<script type="text/javascript" src="../../../../dist/vis.js"></script>
<link href="../../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var nodes = null;
@ -84,7 +84,7 @@
});
}
</script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head>
<body onload="draw();">
@ -94,13 +94,13 @@
<table class="legend_table">
<tr>
<th>Icons: </th>
<td><img src="../../dist/img/network/upArrow.png" /> </td>
<td><img src="../../dist/img/network/downArrow.png" /> </td>
<td><img src="../../dist/img/network/leftArrow.png" /> </td>
<td><img src="../../dist/img/network/rightArrow.png" /> </td>
<td><img src="../../dist/img/network/plus.png" /> </td>
<td><img src="../../dist/img/network/minus.png" /> </td>
<td><img src="../../dist/img/network/zoomExtends.png" /> </td>
<td><img src="../../../../dist/img/network/upArrow.png" /> </td>
<td><img src="../../../../dist/img/network/downArrow.png" /> </td>
<td><img src="../../../../dist/img/network/leftArrow.png" /> </td>
<td><img src="../../../../dist/img/network/rightArrow.png" /> </td>
<td><img src="../../../../dist/img/network/plus.png" /> </td>
<td><img src="../../../../dist/img/network/minus.png" /> </td>
<td><img src="../../../../dist/img/network/zoomExtends.png" /> </td>
</tr>
<tr>
<th>Keyboard shortcuts:</th>

+ 42
- 42
lib/network/Network.js View File

@ -163,7 +163,7 @@ Network.prototype.setOptions = function (options) {
this.nodesHandler.setOptions(options.nodes);
this.edgesHandler.setOptions(options.edges);
this.physics.setOptions(options.physics);
this.manipulation.setOptions(options.manipulation,options); // manipulation uses the locales in the globals
this.manipulation.setOptions(options.manipulation, options, this.options); // manipulation uses the locales in the globals
this.interactionHandler.setOptions(options.interaction);
this.renderer.setOptions(options.interaction); // options for rendering are in interaction
@ -428,46 +428,46 @@ Network.prototype.isActive = function () {
};
Network.prototype.setSize = function() {this.canvas.setSize.apply(this.canvas,arguments);};
Network.prototype.canvasToDOM = function() {this.canvas.canvasToDOM.apply(this.canvas,arguments);};
Network.prototype.DOMtoCanvas = function() {this.canvas.setSize.DOMtoCanvas(this.canvas,arguments);};
Network.prototype.findNode = function() {this.clustering.findNode.apply(this.clustering,arguments);};
Network.prototype.isCluster = function() {this.clustering.isCluster.apply(this.clustering,arguments);};
Network.prototype.openCluster = function() {this.clustering.openCluster.apply(this.clustering,arguments);};
Network.prototype.cluster = function() {this.clustering.cluster.apply(this.clustering,arguments);};
Network.prototype.clusterByConnection = function() {this.clustering.clusterByConnection.apply(this.clustering,arguments);};
Network.prototype.clusterByHubsize = function() {this.clustering.clusterByHubsize.apply(this.clustering,arguments);};
Network.prototype.clusterOutliers = function() {this.clustering.clusterOutliers.apply(this.clustering,arguments);};
Network.prototype.getSeed = function() {this.layoutEngine.getSeed.apply(this.layoutEngine,arguments);};
Network.prototype.enableEditMode = function() {this.manipulation.enableEditMode.apply(this.manipulation,arguments);};
Network.prototype.disableEditMode = function() {this.manipulation.disableEditMode.apply(this.manipulation,arguments);};
Network.prototype.addNodeMode = function() {this.manipulation.addNodeMode.apply(this.manipulation,arguments);};
Network.prototype.editNodeMode = function() {this.manipulation.editNodeMode.apply(this.manipulation,arguments);};
Network.prototype.addEdgeMode = function() {this.manipulation.addEdgeMode.apply(this.manipulation,arguments);};
Network.prototype.editEdgeMode = function() {this.manipulation.editEdgeMode.apply(this.manipulation,arguments);};
Network.prototype.deleteSelected = function() {this.manipulation.deleteSelected.apply(this.manipulation,arguments);};
Network.prototype.getPositions = function() {this.nodesHandler.getPositions.apply(this.nodesHandler,arguments);};
Network.prototype.storePositions = function() {this.nodesHandler.storePositions.apply(this.nodesHandler,arguments);};
Network.prototype.getBoundingBox = function() {this.nodesHandler.getBoundingBox.apply(this.nodesHandler,arguments);};
Network.prototype.getConnectedNodes = function() {this.nodesHandler.getConnectedNodes.apply(this.nodesHandler,arguments);};
Network.prototype.getEdges = function() {this.nodesHandler.getEdges.apply(this.nodesHandler,arguments);};
Network.prototype.startSimulation = function() {this.physics.startSimulation.apply(this.physics,arguments);};
Network.prototype.stopSimulation = function() {this.physics.stopSimulation.apply(this.physics,arguments);};
Network.prototype.stabilize = function() {this.physics.stabilize.apply(this.physics,arguments);};
Network.prototype.getSelection = function() {this.selectionHandler.getSelection.apply(this.selectionHandler,arguments);};
Network.prototype.getSelectedNodes = function() {this.selectionHandler.getSelectedNodes.apply(this.selectionHandler,arguments);};
Network.prototype.getSelectedEdges = function() {this.selectionHandler.getSelectedEdges.apply(this.selectionHandler,arguments);};
Network.prototype.getNodeAt = function() {this.selectionHandler.getNodeAt.apply(this.selectionHandler,arguments);};
Network.prototype.getEdgeAt = function() {this.selectionHandler.getEdgeAt.apply(this.selectionHandler,arguments);};
Network.prototype.selectNodes = function() {this.selectionHandler.selectNodes.apply(this.selectionHandler,arguments);};
Network.prototype.selectEdges = function() {this.selectionHandler.selectEdges.apply(this.selectionHandler,arguments);};
Network.prototype.unselectAll = function() {this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);};
Network.prototype.redraw = function() {this.renderer.redraw.apply(this.renderer,arguments);};
Network.prototype.getScale = function() {this.view.getScale.apply(this.view,arguments);};
Network.prototype.getPosition = function() {this.view.getPosition.apply(this.view,arguments);};
Network.prototype.fit = function() {this.view.fit.apply(this.view,arguments);};
Network.prototype.moveTo = function() {this.view.moveTo.apply(this.view,arguments);};
Network.prototype.focus = function() {this.view.focus.apply(this.view,arguments);};
Network.prototype.releaseNode = function() {this.view.releaseNode.apply(this.view,arguments);};
Network.prototype.setSize = function() {return this.canvas.setSize.apply(this.canvas,arguments);};
Network.prototype.canvasToDOM = function() {return this.canvas.canvasToDOM.apply(this.canvas,arguments);};
Network.prototype.DOMtoCanvas = function() {return this.canvas.setSize.DOMtoCanvas(this.canvas,arguments);};
Network.prototype.findNode = function() {return this.clustering.findNode.apply(this.clustering,arguments);};
Network.prototype.isCluster = function() {return this.clustering.isCluster.apply(this.clustering,arguments);};
Network.prototype.openCluster = function() {return this.clustering.openCluster.apply(this.clustering,arguments);};
Network.prototype.cluster = function() {return this.clustering.cluster.apply(this.clustering,arguments);};
Network.prototype.clusterByConnection = function() {return this.clustering.clusterByConnection.apply(this.clustering,arguments);};
Network.prototype.clusterByHubsize = function() {return this.clustering.clusterByHubsize.apply(this.clustering,arguments);};
Network.prototype.clusterOutliers = function() {return this.clustering.clusterOutliers.apply(this.clustering,arguments);};
Network.prototype.getSeed = function() {return this.layoutEngine.getSeed.apply(this.layoutEngine,arguments);};
Network.prototype.enableEditMode = function() {return this.manipulation.enableEditMode.apply(this.manipulation,arguments);};
Network.prototype.disableEditMode = function() {return this.manipulation.disableEditMode.apply(this.manipulation,arguments);};
Network.prototype.addNodeMode = function() {return this.manipulation.addNodeMode.apply(this.manipulation,arguments);};
Network.prototype.editNodeMode = function() {return this.manipulation.editNodeMode.apply(this.manipulation,arguments);};
Network.prototype.addEdgeMode = function() {return this.manipulation.addEdgeMode.apply(this.manipulation,arguments);};
Network.prototype.editEdgeMode = function() {return this.manipulation.editEdgeMode.apply(this.manipulation,arguments);};
Network.prototype.deleteSelected = function() {return this.manipulation.deleteSelected.apply(this.manipulation,arguments);};
Network.prototype.getPositions = function() {return this.nodesHandler.getPositions.apply(this.nodesHandler,arguments);};
Network.prototype.storePositions = function() {return this.nodesHandler.storePositions.apply(this.nodesHandler,arguments);};
Network.prototype.getBoundingBox = function() {return this.nodesHandler.getBoundingBox.apply(this.nodesHandler,arguments);};
Network.prototype.getConnectedNodes = function() {return this.nodesHandler.getConnectedNodes.apply(this.nodesHandler,arguments);};
Network.prototype.getEdges = function() {return this.nodesHandler.getEdges.apply(this.nodesHandler,arguments);};
Network.prototype.startSimulation = function() {return this.physics.startSimulation.apply(this.physics,arguments);};
Network.prototype.stopSimulation = function() {return this.physics.stopSimulation.apply(this.physics,arguments);};
Network.prototype.stabilize = function() {return this.physics.stabilize.apply(this.physics,arguments);};
Network.prototype.getSelection = function() {return this.selectionHandler.getSelection.apply(this.selectionHandler,arguments);};
Network.prototype.getSelectedNodes = function() {return this.selectionHandler.getSelectedNodes.apply(this.selectionHandler,arguments);};
Network.prototype.getSelectedEdges = function() {return this.selectionHandler.getSelectedEdges.apply(this.selectionHandler,arguments);};
Network.prototype.getNodeAt = function() {return this.selectionHandler.getNodeAt.apply(this.selectionHandler,arguments);};
Network.prototype.getEdgeAt = function() {return this.selectionHandler.getEdgeAt.apply(this.selectionHandler,arguments);};
Network.prototype.selectNodes = function() {return this.selectionHandler.selectNodes.apply(this.selectionHandler,arguments);};
Network.prototype.selectEdges = function() {return this.selectionHandler.selectEdges.apply(this.selectionHandler,arguments);};
Network.prototype.unselectAll = function() {return this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);};
Network.prototype.redraw = function() {return this.renderer.redraw.apply(this.renderer,arguments);};
Network.prototype.getScale = function() {return this.view.getScale.apply(this.view,arguments);};
Network.prototype.getPosition = function() {return this.view.getPosition.apply(this.view,arguments);};
Network.prototype.fit = function() {return this.view.fit.apply(this.view,arguments);};
Network.prototype.moveTo = function() {return this.view.moveTo.apply(this.view,arguments);};
Network.prototype.focus = function() {return this.view.focus.apply(this.view,arguments);};
Network.prototype.releaseNode = function() {return this.view.releaseNode.apply(this.view,arguments);};
module.exports = Network;

+ 6
- 5
lib/network/modules/CanvasRenderer.js View File

@ -160,14 +160,15 @@ class CanvasRenderer {
let h = this.canvas.frame.canvas.clientHeight;
ctx.clearRect(0, 0, w, h);
this.body.emitter.emit("beforeDrawing", ctx);
// set scaling and translation
ctx.save();
ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
ctx.scale(this.body.view.scale, this.body.view.scale);
ctx.beginPath();
this.body.emitter.emit("beforeDrawing", ctx);
ctx.closePath();
if (hidden === false) {
if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) {
this._drawEdges(ctx);
@ -182,10 +183,10 @@ class CanvasRenderer {
this._drawControlNodes(ctx);
}
ctx.beginPath();
//this.physics.nodesSolver._debug(ctx,"#F00F0F");
this.body.emitter.emit("afterDrawing", ctx);
ctx.closePath();
// restore original scaling and translation
ctx.restore();

+ 97
- 54
lib/network/modules/Clustering.js View File

@ -40,8 +40,7 @@ class ClusterEngine {
}
for (let i = 0; i < nodesToCluster.length; i++) {
let node = this.body.nodes[nodesToCluster[i]];
this.clusterByConnection(node,options,false);
this.clusterByConnection(nodesToCluster[i],options,false);
}
this.body.emitter.emit('_dataChanged');
}
@ -64,9 +63,16 @@ class ClusterEngine {
// collect the nodes that will be in the cluster
for (let i = 0; i < this.body.nodeIndices.length; i++) {
let nodeId = this.body.nodeIndices[i];
let clonedOptions = this._cloneOptions(nodeId);
let node = this.body.nodes[nodeId];
let clonedOptions = this._cloneOptions(node);
if (options.joinCondition(clonedOptions) === true) {
childNodesObj[nodeId] = this.body.nodes[nodeId];
// collect the nodes that will be in the cluster
for (let i = 0; i < node.edges.length; i++) {
let edge = node.edges[i];
childEdgesObj[edge.id] = edge;
}
}
}
@ -89,20 +95,24 @@ class ClusterEngine {
let childEdgesObj = {};
let nodeId = this.body.nodeIndices[i];
if (this.body.nodes[nodeId].edges.length === 1) {
// this is an outlier
let edge = this.body.nodes[nodeId].edges[0];
let childNodeId = this._getConnectedId(edge, nodeId);
if (childNodeId != nodeId) {
if (childNodeId !== nodeId) {
if (options.joinCondition === undefined) {
childEdgesObj[edge.id] = edge;
childNodesObj[nodeId] = this.body.nodes[nodeId];
childNodesObj[childNodeId] = this.body.nodes[childNodeId];
}
else {
let clonedOptions = this._cloneOptions(nodeId);
let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]);
if (options.joinCondition(clonedOptions) === true) {
childEdgesObj[edge.id] = edge;
childNodesObj[nodeId] = this.body.nodes[nodeId];
}
clonedOptions = this._cloneOptions(childNodeId);
clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
if (options.joinCondition(clonedOptions) === true) {
childEdgesObj[edge.id] = edge;
childNodesObj[childNodeId] = this.body.nodes[childNodeId];
}
}
@ -145,7 +155,7 @@ class ClusterEngine {
let childNodesObj = {};
let childEdgesObj = {};
let parentNodeId = node.id;
let parentClonedOptions = this._cloneOptions(parentNodeId);
let parentClonedOptions = this._cloneOptions(node);
childNodesObj[parentNodeId] = node;
// collect the nodes that will be in the cluster
@ -160,7 +170,7 @@ class ClusterEngine {
}
else {
// clone the options and insert some additional parameters that could be interesting.
let childClonedOptions = this._cloneOptions(childNodeId);
let childClonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) {
childEdgesObj[edge.id] = edge;
childNodesObj[childNodeId] = this.body.nodes[childNodeId];
@ -183,16 +193,16 @@ class ClusterEngine {
* @returns {{}}
* @private
*/
_cloneOptions(objId, type) {
_cloneOptions(item, type) {
let clonedOptions = {};
if (type === undefined || type === 'node') {
util.deepExtend(clonedOptions, this.body.nodes[objId].options, true);
clonedOptions.x = this.body.nodes[objId].x;
clonedOptions.y = this.body.nodes[objId].y;
clonedOptions.amountOfConnections = this.body.nodes[objId].edges.length;
util.deepExtend(clonedOptions, item.options, true);
clonedOptions.x = item.x;
clonedOptions.y = item.y;
clonedOptions.amountOfConnections = item.edges.length;
}
else {
util.deepExtend(clonedOptions, this.body.edges[objId].options, true);
util.deepExtend(clonedOptions, item.options, true);
}
return clonedOptions;
}
@ -207,41 +217,37 @@ class ClusterEngine {
* @param options
* @private
*/
_createClusterEdges (childNodesObj, childEdgesObj, newEdges, options) {
let edge, childNodeId, childNode;
_createClusterEdges (childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, clusterEdgeProperties) {
let edge, childNodeId, childNode, toId, fromId, otherNodeId;
let childKeys = Object.keys(childNodesObj);
for (let i = 0; i < childKeys.length; i++) {
childNodeId = childKeys[i];
childNode = childNodesObj[childNodeId];
// mark all edges for removal from global and construct new edges from the cluster to others
// construct new edges from the cluster to others
for (let j = 0; j < childNode.edges.length; j++) {
edge = childNode.edges[j];
childEdgesObj[edge.id] = edge;
let otherNodeId = edge.toId;
let otherOnTo = true;
if (edge.toId != childNodeId) {
otherNodeId = edge.toId;
otherOnTo = true;
// childNodeId position will be replaced by the cluster.
if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here.
toId = clusterNodeProperties.id;
fromId = edge.fromId;
otherNodeId = fromId;
}
else if (edge.fromId != childNodeId) {
otherNodeId = edge.fromId;
otherOnTo = false;
else {
toId = edge.toId;
fromId = clusterNodeProperties.id;
otherNodeId = toId;
}
// if the node connected to the cluster is also in the cluster we do not need a new edge.
if (childNodesObj[otherNodeId] === undefined) {
let clonedOptions = this._cloneOptions(edge.id, 'edge');
util.deepExtend(clonedOptions, options.clusterEdgeProperties);
if (otherOnTo === true) {
clonedOptions.from = options.clusterNodeProperties.id;
clonedOptions.to = otherNodeId;
}
else {
clonedOptions.from = otherNodeId;
clonedOptions.to = options.clusterNodeProperties.id;
}
let clonedOptions = this._cloneOptions(edge, 'edge');
util.deepExtend(clonedOptions, clusterEdgeProperties);
clonedOptions.from = fromId;
clonedOptions.to = toId;
clonedOptions.id = 'clusterEdge:' + util.randomUUID();
newEdges.push(this.body.functions.createEdge(clonedOptions))
}
@ -249,7 +255,6 @@ class ClusterEngine {
}
}
/**
* This function checks the options that can be supplied to the different cluster functions
* for certain fields and inserts defaults if needed
@ -276,32 +281,34 @@ class ClusterEngine {
// kill condition: no children so cant cluster
if (Object.keys(childNodesObj).length === 0) {return;}
let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties);
// check if we have an unique id;
if (options.clusterNodeProperties.id === undefined) {options.clusterNodeProperties.id = 'cluster:' + util.randomUUID();}
let clusterId = options.clusterNodeProperties.id;
if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();}
let clusterId = clusterNodeProperties.id;
// construct the clusterNodeProperties
let clusterNodeProperties = options.clusterNodeProperties;
if (options.processProperties !== undefined) {
// get the childNode options
let childNodesOptions = [];
for (let nodeId in childNodesObj) {
let clonedOptions = this._cloneOptions(nodeId);
let clonedOptions = this._cloneOptions(childNodesObj[nodeId]);
childNodesOptions.push(clonedOptions);
}
// get clusterproperties based on childNodes
let childEdgesOptions = [];
for (let edgeId in childEdgesObj) {
let clonedOptions = this._cloneOptions(edgeId, 'edge');
let clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge');
childEdgesOptions.push(clonedOptions);
}
clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions);
if (!clusterNodeProperties) {
throw new Error("The processClusterProperties function does not return properties!");
throw new Error("The processProperties function does not return properties!");
}
}
if (clusterNodeProperties.label === undefined) {
clusterNodeProperties.label = 'cluster';
}
@ -320,25 +327,23 @@ class ClusterEngine {
clusterNodeProperties.y = pos.y;
}
// force the ID to remain the same
clusterNodeProperties.id = clusterId;
// create the clusterNode
let clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster);
clusterNode.isCluster = true;
clusterNode.containedNodes = childNodesObj;
clusterNode.containedEdges = childEdgesObj;
// cache a copy from the cluster edge properties if we have to reconnect others later on
clusterNode.clusterEdgeProperties = options.clusterEdgeProperties;
// finally put the cluster node into global
this.body.nodes[clusterNodeProperties.id] = clusterNode;
// create the new edges that will connect to the cluster
let newEdges = [];
this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, options);
this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, options.clusterEdgeProperties);
// disable the childEdges
for (let edgeId in childEdgesObj) {
@ -360,7 +365,6 @@ class ClusterEngine {
}
}
// push new edges to global
for (let i = 0; i < newEdges.length; i++) {
this.body.edges[newEdges[i].id] = newEdges[i];
@ -370,7 +374,6 @@ class ClusterEngine {
// set ID to undefined so no duplicates arise
clusterNodeProperties.id = undefined;
// wrap up
if (refreshData === true) {
this.body.emitter.emit('_dataChanged');
@ -406,17 +409,20 @@ class ClusterEngine {
let minY = childNodesObj[childKeys[0]].y;
let maxY = childNodesObj[childKeys[0]].y;
let node;
for (let i = 0; i < childKeys.lenght; i++) {
node = childNodesObj[childKeys[0]];
for (let i = 1; i < childKeys.length; i++) {
node = childNodesObj[childKeys[i]];
minX = node.x < minX ? node.x : minX;
maxX = node.x > maxX ? node.x : maxX;
minY = node.y < minY ? node.y : minY;
maxY = node.y > maxY ? node.y : maxY;
}
return {x: 0.5*(minX + maxX), y: 0.5*(minY + maxY)};
}
/**
* Open a cluster by calling this function.
* @param {String} clusterNodeId | the ID of the cluster node
@ -457,9 +463,46 @@ class ClusterEngine {
// release edges
for (let edgeId in containedEdges) {
if (containedEdges.hasOwnProperty(edgeId)) {
let edge = this.body.edges[edgeId];
edge.options.hidden = false;
edge.togglePhysics(true);
let edge = containedEdges[edgeId];
// if this edge was a temporary edge and it's connected nodes do not exist anymore, we remove it from the data
if (this.body.nodes[edge.fromId] === undefined || this.body.nodes[edge.toId] === undefined) {
edge.edgeType.cleanup();
// this removes the edge from node.edges, which is why edgeIds is formed
edge.disconnect();
delete this.body.edges[edgeId];
}
else {
// one of the nodes connected to this edge is in a cluster. We give the edge to that cluster and make a new temporary edge.
if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) {
let fromId, toId;
let clusteredNode = this.clusteredNodes[edge.fromId] || this.clusteredNodes[edge.toId];
let clusterId = clusteredNode.clusterId;
let clusterNode = this.body.nodes[clusterId];
clusterNode.containedEdges[edgeId] = edge;
if (this.clusteredNodes[edge.fromId] !== undefined) {
fromId = clusterId;
toId = edge.toId;
}
else {
fromId = edge.fromId;
toId = clusterId;
}
let clonedOptions = this._cloneOptions(edge, 'edge');
let id = 'clusterEdge:' + util.randomUUID();
util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties);
util.deepExtend(clonedOptions, {from:fromId, to:toId, hidden:false, physics:true, id: id});
let newEdge = this.body.functions.createEdge(clonedOptions);
this.body.edges[id] = newEdge;
this.body.edges[id].connect();
}
else {
edge.options.hidden = false;
edge.togglePhysics(true);
}
}
}
}

+ 2
- 1
lib/network/modules/LayoutEngine.js View File

@ -40,7 +40,7 @@ class LayoutEngine {
util.mergeOptions(this.options, options, 'hierarchical');
if (options.randomSeed !== undefined) {
this.randomSeed = options.randomSeed;
this.initialRandomSeed = options.randomSeed;
}
if (this.options.hierarchical.enabled === true) {
@ -129,6 +129,7 @@ class LayoutEngine {
positionInitially(nodesArray) {
if (this.options.hierarchical.enabled !== true) {
this.randomSeed = this.initialRandomSeed;
for (let i = 0; i < nodesArray.length; i++) {
let node = nodesArray[i];
if ((!node.isFixed()) && (node.x === undefined || node.y === undefined)) {

+ 6
- 3
lib/network/modules/ManipulationSystem.js View File

@ -74,10 +74,10 @@ class ManipulationSystem {
* Set the Options
* @param options
*/
setOptions(options, allOptions) {
setOptions(options, allOptions, globalOptions) {
if (allOptions !== undefined) {
if (allOptions.locale !== undefined) {this.options.locale = allOptions.locale};
if (allOptions.locales !== undefined) {this.options.locales = allOptions.locales};
if (allOptions.locale !== undefined) {this.options.locale = allOptions.locale} else {this.options.locale = globalOptions.locale;}
if (allOptions.locales !== undefined) {this.options.locales = allOptions.locales} else {this.options.locales = globalOptions.locales;}
}
if (options !== undefined) {
@ -439,6 +439,7 @@ class ManipulationSystem {
this.body.data.edges.remove(finalizedData.edges);
this.body.data.nodes.remove(finalizedData.nodes);
this.body.emitter.emit('startSimulation');
this.showManipulatorToolbar();
}
});
}
@ -450,6 +451,7 @@ class ManipulationSystem {
this.body.data.edges.remove(selectedEdges);
this.body.data.nodes.remove(selectedNodes);
this.body.emitter.emit('startSimulation');
this.showManipulatorToolbar();
}
}
@ -559,6 +561,7 @@ class ManipulationSystem {
// empty the editModeDiv
util.recursiveDOMDelete(this.editModeDiv);
// create the contents for the editMode button
let locale = this.options.locales[this.options.locale];
let button = this._createButton('editMode', 'vis-button vis-edit vis-edit-mode', locale['edit'] || this.options.locales['en']['edit']);

+ 11
- 8
lib/network/modules/PhysicsEngine.js View File

@ -23,6 +23,7 @@ class PhysicsEngine {
this.renderTimer = undefined;
this.stabilized = false;
this.startedStabilization = false;
this.stabilizationIterations = 0;
this.ready = false; // will be set to true if the stabilize
@ -229,12 +230,8 @@ class PhysicsEngine {
// The event is triggered on the next tick, to prevent the case that
// it is fired while initializing the Network, in which case you would not
// be able to catch it
this.stabilizationIterations = 0;
this.startedStabilization = false;
this._emitStabilized();
}
else {
this.stabilizationIterations = 0;
//this._emitStabilized();
}
this.stopSimulation();
}
@ -244,6 +241,7 @@ class PhysicsEngine {
if (this.stabilizationIterations > 1) {
setTimeout(() => {
this.body.emitter.emit('stabilized', {iterations: this.stabilizationIterations});
this.stabilizationIterations = 0;
}, 0);
}
}
@ -495,8 +493,6 @@ class PhysicsEngine {
// block redraw requests
this.body.emitter.emit('_blockRedrawRequests');
this.body.emitter.emit('startStabilizing');
this.startedStabilization = true;
this.targetIterations = iterations;
// start the stabilization
@ -505,7 +501,7 @@ class PhysicsEngine {
}
this.stabilizationIterations = 0;
setTimeout(this._stabilizationBatch.bind(this),0);
setTimeout(() => this._stabilizationBatch(),0);
}
_stabilizationBatch() {
@ -538,6 +534,13 @@ class PhysicsEngine {
this.body.emitter.emit('stabilizationIterationsDone');
this.body.emitter.emit('_requestRedraw');
if (this.stabilized === true) {
this._emitStabilized();
}
else {
this.startSimulation();
}
this.ready = true;
}

+ 5
- 0
lib/network/modules/components/Edge.js View File

@ -178,6 +178,11 @@ class Edge {
parentOptions.color = undefined;
delete parentOptions.color;
}
// handle the font settings
if (newOptions.font !== undefined) {
Label.parseOptions(parentOptions.font, newOptions);
}
}

+ 3
- 0
lib/network/modules/components/Node.js View File

@ -180,6 +180,7 @@ class Node {
delete parentOptions.color;
}
// handle the fixed options
if (newOptions.fixed !== undefined && newOptions.fixed !== null) {
if (typeof newOptions.fixed === 'boolean') {
parentOptions.fixed.x = newOptions.fixed;
@ -195,10 +196,12 @@ class Node {
}
}
// handle the font options
if (newOptions.font !== undefined) {
Label.parseOptions(parentOptions.font, newOptions);
}
// handle the scaling options, specifically the label part
if (newOptions.scaling !== undefined) {
util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label');
}

+ 1
- 1
lib/network/options.js View File

@ -165,7 +165,7 @@ let allOptions = {
size: {number}, // px
face: {string},
background: {string},
stroke: {number}, // px
strokeWidth: {number}, // px
strokeColor: {string},
__type__: {object,string}
},

+ 4
- 1
lib/network/shapes.js View File

@ -9,6 +9,7 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
CanvasRenderingContext2D.prototype.circle = function (x, y, r) {
this.beginPath();
this.arc(x, y, r, 0, 2 * Math.PI, false);
this.closePath();
};
/**
@ -20,6 +21,7 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
CanvasRenderingContext2D.prototype.square = function (x, y, r) {
this.beginPath();
this.rect(x - r, y - r, r * 2, r * 2);
this.closePath();
};
/**
@ -142,6 +144,7 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
this.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false);
this.lineTo(x, y + r);
this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false);
this.closePath();
};
/**
@ -162,6 +165,7 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
this.closePath();
};
@ -210,7 +214,6 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
var yt = y - length * Math.sin(angle);
// inner tail
// TODO: allow to customize different shapes
var xi = x - length * 0.9 * Math.cos(angle);
var yi = y - length * 0.9 * Math.sin(angle);

+ 2
- 4
lib/shared/Validator.js View File

@ -195,16 +195,14 @@ class Validator {
* @returns {{closestMatch: string, path: Array, distance: number}}
*/
static findInOptions(option, options, path, recursive = false) {
//console.log(option, options, path)
let min = 1e9;
let closestMatch = '';
let closestMatchPath = [];
let lowerCaseOption = option.toLowerCase();
let indexMatch = undefined;
for (let op in options) {
let type = Validator.getType(options[op]);
let distance;
if (type === 'object' && recursive === true) {
if (options[op].__type__ !== undefined && recursive === true) {
let result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path,op));
if (min > result.distance) {
closestMatch = result.closestMatch;
@ -225,7 +223,7 @@ class Validator {
}
}
}
return {closestMatch:closestMatch, path:closestMatchPath, distance:min, indexMatch: indexMatch}
return {closestMatch:closestMatch, path:closestMatchPath, distance:min, indexMatch: indexMatch};
}
static printLocation(path, option, prefix = 'Problem value found at: \n') {

+ 4
- 2
lib/util.js View File

@ -1218,11 +1218,13 @@ exports.mergeOptions = function (mergeTarget, options, option, allowDeletion = f
}
else {
if (options[option] !== undefined) {
if (typeof options[option] == 'boolean') {
if (typeof options[option] === 'boolean') {
mergeTarget[option].enabled = options[option];
}
else {
mergeTarget[option].enabled = true;
if (options[option].enabled === undefined) {
mergeTarget[option].enabled = true;
}
for (var prop in options[option]) {
if (options[option].hasOwnProperty(prop)) {
mergeTarget[option][prop] = options[option][prop];

Loading…
Cancel
Save