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>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterByHubsize', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterByHubsize', this);">
<td colspan="2"><span parent="clusterByHubsize" class="right-caret"></span> clusterByHubsize( <td colspan="2"><span parent="clusterByHubsize" class="right-caret"></span> clusterByHubsize(
<code>Number hubsize</code>,
<code>[Number hubsize]</code>,
<code>[Object options]</code>) <code>[Object options]</code>)
</td> </td>
</tr> </tr>
<tr class="hidden" parent="clusterByHubsize"> <tr class="hidden" parent="clusterByHubsize">
<td class="midMethods">Returns: none</td> <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 <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> </td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterOutliers', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','clusterOutliers', this);">
@ -569,7 +571,9 @@ var locales = {
</tr> </tr>
<tr class="hidden" parent="clusterOutliers"> <tr class="hidden" parent="clusterOutliers">
<td class="midMethods">Returns: none</td> <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>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','findNode', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','findNode', this);">
<td colspan="2"><span parent="findNode" class="right-caret"></span> findNode( <td colspan="2"><span parent="findNode" class="right-caret"></span> findNode(
@ -1055,7 +1059,9 @@ network.clustering.cluster(options);
</td> </td>
</tr> </tr>
<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>Function</td>
<td><i>Optional. </i> Before creating the new cluster node, this (optional) function will be called with <td><i>Optional. </i> Before creating the new cluster node, this (optional) function will be called with
the 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> </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"> <script type="text/javascript">
function draw() { function draw() {
// create some nodes // create some nodes
var 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: 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: 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'}, {id: 4, label: 'This is text\non multiple lines', shape: 'box'},
@ -27,10 +27,10 @@
// create some edges // create some edges
var 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 // create a network
@ -43,7 +43,7 @@
var network = new vis.Network(container, data, options); var network = new vis.Network(container, data, options);
} }
</script> </script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head> </head>
<body onload="draw()"> <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> </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"> <script type="text/javascript">
var nodes = null; var nodes = null;
var edges = null; var edges = null;
var network = null; var network = null;
var doButton, focusButton, showButton;
var offsetx, offsety, scale, positionx, positiony, duration, easingFunction, doButton, focusButton, showButton;
var statusUpdateSpan; var statusUpdateSpan;
var finishMessage = ''; var finishMessage = '';
var showInterval = false;
var showPhase = 1;
var amountOfNodes = 25;
function destroy() { function destroy() {
if (network !== null) { 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() { function draw() {
destroy(); destroy();
statusUpdateSpan = document.getElementById('statusUpdate'); statusUpdateSpan = document.getElementById('statusUpdate');
doButton = document.getElementById('btnDo'); doButton = document.getElementById('btnDo');
focusButton = document.getElementById('btnFocus'); focusButton = document.getElementById('btnFocus');
showButton = document.getElementById('btnShow'); showButton = document.getElementById('btnShow');
nodes = [];
edges = [];
var connectionCount = [];
// randomly create some nodes and edges // 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 // create a network
var container = document.getElementById('mynetwork'); var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = { var options = {
physics: { physics: {
stabilization: { 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}, var options = {offset: {x:offsetx,y:offsety},
duration: duration, duration: duration,
easingFunction: easingFunction easingFunction: easingFunction
}; };
statusUpdateSpan.innerHTML = 'Doing ZoomExtent() Animation.';
statusUpdateSpan.innerHTML = 'Doing fit() Animation.';
finishMessage = 'Animation finished.'; finishMessage = 'Animation finished.';
network.fit(options);
network.fit({animation:options});
} }
function doDefaultAnimation() { 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 = { var options = {
position: {x:positionx,y:positiony}, position: {x:positionx,y:positiony},
@ -168,13 +126,7 @@
} }
function doAnimation() { 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 = { var options = {
position: {x:positionx,y:positiony}, position: {x:positionx,y:positiony},
@ -191,13 +143,9 @@
} }
function focusRandom() { 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 = { var options = {
// position: {x:positionx,y:positiony}, // this is not relevant when focusing on nodes // position: {x:positionx,y:positiony}, // this is not relevant when focusing on nodes
scale: scale, scale: scale,
@ -213,9 +161,9 @@
} }
var showInterval = false;
var showPhase = 1;
function startShow() { function startShow() {
updateValues();
if (showInterval !== false) { if (showInterval !== false) {
showInterval = false; showInterval = false;
showButton.value = 'Start a show!'; showButton.value = 'Start a show!';
@ -223,7 +171,6 @@
} }
else { else {
showButton.value = 'Stop the show!'; showButton.value = 'Stop the show!';
var duration = parseInt(document.getElementById('duration').value);
focusRandom(); focusRandom();
setTimeout(doTheShow, duration); setTimeout(doTheShow, duration);
showInterval = true; showInterval = true;
@ -231,31 +178,31 @@
} }
function doTheShow() { function doTheShow() {
updateValues();
if (showInterval == true) { if (showInterval == true) {
var duration = parseInt(document.getElementById('duration').value);
if (showPhase == 0) { if (showPhase == 0) {
focusRandom(); focusRandom();
showPhase = 1; showPhase = 1;
} }
else { else {
zoomExtentAnimated();
fitAnimated();
showPhase = 0; showPhase = 0;
} }
setTimeout(doTheShow, duration); setTimeout(doTheShow, duration);
} }
} }
</script> </script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head> </head>
<body onload="draw();"> <body onload="draw();">
<h2>Camera animations</h2> <h2>Camera animations</h2>
<div style="width:700px; font-size:14px;"> <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 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/> <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 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/> <br/><br/>
Here you can see a full description of the options you can supply to moveTo: Here you can see a full description of the options you can supply to moveTo:
</div> </div>
@ -290,7 +237,7 @@ var moveToOptions = {
<td>offset y</td><td><input type="text" value="0" id="offsety" style="width:170px;"> px</td> <td>offset y</td><td><input type="text" value="0" id="offsety" style="width:170px;"> px</td>
</tr> </tr>
<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>
<tr> <tr>
<td>easingFunction</td><td> <td>easingFunction</td><td>
@ -317,7 +264,7 @@ var moveToOptions = {
Examples: Examples:
<input type="button" onclick="doAnimation();" value="Animate with above settings." style="width:300px;" id="btnDo"> <br/> <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="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="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/> <input type="button" onclick="startShow();" value="Start a show!" style="width:300px;" id="btnShow"><br/>
</div> </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 { #mynetwork {
width: 600px; width: 600px;
height: 600px; height: 600px;
margin:100px;
border: 1px solid lightgray; border: 1px solid lightgray;
} }
table.legend_table { table.legend_table {
@ -28,8 +27,9 @@
} }
</style> </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"> <script type="text/javascript">
var nodes = null; var nodes = null;
@ -84,7 +84,7 @@
}); });
} }
</script> </script>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head> </head>
<body onload="draw();"> <body onload="draw();">
@ -94,13 +94,13 @@
<table class="legend_table"> <table class="legend_table">
<tr> <tr>
<th>Icons: </th> <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>
<tr> <tr>
<th>Keyboard shortcuts:</th> <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.nodesHandler.setOptions(options.nodes);
this.edgesHandler.setOptions(options.edges); this.edgesHandler.setOptions(options.edges);
this.physics.setOptions(options.physics); 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.interactionHandler.setOptions(options.interaction);
this.renderer.setOptions(options.interaction); // options for rendering are in 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; 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; let h = this.canvas.frame.canvas.clientHeight;
ctx.clearRect(0, 0, w, h); ctx.clearRect(0, 0, w, h);
this.body.emitter.emit("beforeDrawing", ctx);
// set scaling and translation // set scaling and translation
ctx.save(); ctx.save();
ctx.translate(this.body.view.translation.x, this.body.view.translation.y); ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
ctx.scale(this.body.view.scale, this.body.view.scale); ctx.scale(this.body.view.scale, this.body.view.scale);
ctx.beginPath();
this.body.emitter.emit("beforeDrawing", ctx);
ctx.closePath();
if (hidden === false) { if (hidden === false) {
if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) { if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) {
this._drawEdges(ctx); this._drawEdges(ctx);
@ -182,10 +183,10 @@ class CanvasRenderer {
this._drawControlNodes(ctx); this._drawControlNodes(ctx);
} }
ctx.beginPath();
//this.physics.nodesSolver._debug(ctx,"#F00F0F"); //this.physics.nodesSolver._debug(ctx,"#F00F0F");
this.body.emitter.emit("afterDrawing", ctx); this.body.emitter.emit("afterDrawing", ctx);
ctx.closePath();
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); 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++) { 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'); this.body.emitter.emit('_dataChanged');
} }
@ -64,9 +63,16 @@ class ClusterEngine {
// collect the nodes that will be in the cluster // collect the nodes that will be in the cluster
for (let i = 0; i < this.body.nodeIndices.length; i++) { for (let i = 0; i < this.body.nodeIndices.length; i++) {
let nodeId = this.body.nodeIndices[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) { if (options.joinCondition(clonedOptions) === true) {
childNodesObj[nodeId] = this.body.nodes[nodeId]; 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 childEdgesObj = {};
let nodeId = this.body.nodeIndices[i]; let nodeId = this.body.nodeIndices[i];
if (this.body.nodes[nodeId].edges.length === 1) { if (this.body.nodes[nodeId].edges.length === 1) {
// this is an outlier
let edge = this.body.nodes[nodeId].edges[0]; let edge = this.body.nodes[nodeId].edges[0];
let childNodeId = this._getConnectedId(edge, nodeId); let childNodeId = this._getConnectedId(edge, nodeId);
if (childNodeId != nodeId) {
if (childNodeId !== nodeId) {
if (options.joinCondition === undefined) { if (options.joinCondition === undefined) {
childEdgesObj[edge.id] = edge;
childNodesObj[nodeId] = this.body.nodes[nodeId]; childNodesObj[nodeId] = this.body.nodes[nodeId];
childNodesObj[childNodeId] = this.body.nodes[childNodeId]; childNodesObj[childNodeId] = this.body.nodes[childNodeId];
} }
else { else {
let clonedOptions = this._cloneOptions(nodeId);
let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]);
if (options.joinCondition(clonedOptions) === true) { if (options.joinCondition(clonedOptions) === true) {
childEdgesObj[edge.id] = edge;
childNodesObj[nodeId] = this.body.nodes[nodeId]; childNodesObj[nodeId] = this.body.nodes[nodeId];
} }
clonedOptions = this._cloneOptions(childNodeId);
clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
if (options.joinCondition(clonedOptions) === true) { if (options.joinCondition(clonedOptions) === true) {
childEdgesObj[edge.id] = edge;
childNodesObj[childNodeId] = this.body.nodes[childNodeId]; childNodesObj[childNodeId] = this.body.nodes[childNodeId];
} }
} }
@ -145,7 +155,7 @@ class ClusterEngine {
let childNodesObj = {}; let childNodesObj = {};
let childEdgesObj = {}; let childEdgesObj = {};
let parentNodeId = node.id; let parentNodeId = node.id;
let parentClonedOptions = this._cloneOptions(parentNodeId);
let parentClonedOptions = this._cloneOptions(node);
childNodesObj[parentNodeId] = node; childNodesObj[parentNodeId] = node;
// collect the nodes that will be in the cluster // collect the nodes that will be in the cluster
@ -160,7 +170,7 @@ class ClusterEngine {
} }
else { else {
// clone the options and insert some additional parameters that could be interesting. // 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) { if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) {
childEdgesObj[edge.id] = edge; childEdgesObj[edge.id] = edge;
childNodesObj[childNodeId] = this.body.nodes[childNodeId]; childNodesObj[childNodeId] = this.body.nodes[childNodeId];
@ -183,16 +193,16 @@ class ClusterEngine {
* @returns {{}} * @returns {{}}
* @private * @private
*/ */
_cloneOptions(objId, type) {
_cloneOptions(item, type) {
let clonedOptions = {}; let clonedOptions = {};
if (type === undefined || type === 'node') { 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 { else {
util.deepExtend(clonedOptions, this.body.edges[objId].options, true);
util.deepExtend(clonedOptions, item.options, true);
} }
return clonedOptions; return clonedOptions;
} }
@ -207,41 +217,37 @@ class ClusterEngine {
* @param options * @param options
* @private * @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); let childKeys = Object.keys(childNodesObj);
for (let i = 0; i < childKeys.length; i++) { for (let i = 0; i < childKeys.length; i++) {
childNodeId = childKeys[i]; childNodeId = childKeys[i];
childNode = childNodesObj[childNodeId]; 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++) { for (let j = 0; j < childNode.edges.length; j++) {
edge = childNode.edges[j]; edge = childNode.edges[j];
childEdgesObj[edge.id] = edge; 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) { 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(); clonedOptions.id = 'clusterEdge:' + util.randomUUID();
newEdges.push(this.body.functions.createEdge(clonedOptions)) 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 * This function checks the options that can be supplied to the different cluster functions
* for certain fields and inserts defaults if needed * for certain fields and inserts defaults if needed
@ -276,32 +281,34 @@ class ClusterEngine {
// kill condition: no children so cant cluster // kill condition: no children so cant cluster
if (Object.keys(childNodesObj).length === 0) {return;} if (Object.keys(childNodesObj).length === 0) {return;}
let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties);
// check if we have an unique id; // 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 // construct the clusterNodeProperties
let clusterNodeProperties = options.clusterNodeProperties;
if (options.processProperties !== undefined) { if (options.processProperties !== undefined) {
// get the childNode options // get the childNode options
let childNodesOptions = []; let childNodesOptions = [];
for (let nodeId in childNodesObj) { for (let nodeId in childNodesObj) {
let clonedOptions = this._cloneOptions(nodeId);
let clonedOptions = this._cloneOptions(childNodesObj[nodeId]);
childNodesOptions.push(clonedOptions); childNodesOptions.push(clonedOptions);
} }
// get clusterproperties based on childNodes // get clusterproperties based on childNodes
let childEdgesOptions = []; let childEdgesOptions = [];
for (let edgeId in childEdgesObj) { for (let edgeId in childEdgesObj) {
let clonedOptions = this._cloneOptions(edgeId, 'edge');
let clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge');
childEdgesOptions.push(clonedOptions); childEdgesOptions.push(clonedOptions);
} }
clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions); clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions);
if (!clusterNodeProperties) { 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) { if (clusterNodeProperties.label === undefined) {
clusterNodeProperties.label = 'cluster'; clusterNodeProperties.label = 'cluster';
} }
@ -320,25 +327,23 @@ class ClusterEngine {
clusterNodeProperties.y = pos.y; clusterNodeProperties.y = pos.y;
} }
// force the ID to remain the same // force the ID to remain the same
clusterNodeProperties.id = clusterId; clusterNodeProperties.id = clusterId;
// create the clusterNode // create the clusterNode
let clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster); let clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster);
clusterNode.isCluster = true; clusterNode.isCluster = true;
clusterNode.containedNodes = childNodesObj; clusterNode.containedNodes = childNodesObj;
clusterNode.containedEdges = childEdgesObj; 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 // finally put the cluster node into global
this.body.nodes[clusterNodeProperties.id] = clusterNode; this.body.nodes[clusterNodeProperties.id] = clusterNode;
// create the new edges that will connect to the cluster // create the new edges that will connect to the cluster
let newEdges = []; let newEdges = [];
this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, options);
this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, options.clusterEdgeProperties);
// disable the childEdges // disable the childEdges
for (let edgeId in childEdgesObj) { for (let edgeId in childEdgesObj) {
@ -360,7 +365,6 @@ class ClusterEngine {
} }
} }
// push new edges to global // push new edges to global
for (let i = 0; i < newEdges.length; i++) { for (let i = 0; i < newEdges.length; i++) {
this.body.edges[newEdges[i].id] = newEdges[i]; this.body.edges[newEdges[i].id] = newEdges[i];
@ -370,7 +374,6 @@ class ClusterEngine {
// set ID to undefined so no duplicates arise // set ID to undefined so no duplicates arise
clusterNodeProperties.id = undefined; clusterNodeProperties.id = undefined;
// wrap up // wrap up
if (refreshData === true) { if (refreshData === true) {
this.body.emitter.emit('_dataChanged'); this.body.emitter.emit('_dataChanged');
@ -406,17 +409,20 @@ class ClusterEngine {
let minY = childNodesObj[childKeys[0]].y; let minY = childNodesObj[childKeys[0]].y;
let maxY = childNodesObj[childKeys[0]].y; let maxY = childNodesObj[childKeys[0]].y;
let node; 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; minX = node.x < minX ? node.x : minX;
maxX = node.x > maxX ? node.x : maxX; maxX = node.x > maxX ? node.x : maxX;
minY = node.y < minY ? node.y : minY; minY = node.y < minY ? node.y : minY;
maxY = node.y > maxY ? node.y : maxY; maxY = node.y > maxY ? node.y : maxY;
} }
return {x: 0.5*(minX + maxX), y: 0.5*(minY + maxY)}; return {x: 0.5*(minX + maxX), y: 0.5*(minY + maxY)};
} }
/** /**
* Open a cluster by calling this function. * Open a cluster by calling this function.
* @param {String} clusterNodeId | the ID of the cluster node * @param {String} clusterNodeId | the ID of the cluster node
@ -457,9 +463,46 @@ class ClusterEngine {
// release edges // release edges
for (let edgeId in containedEdges) { for (let edgeId in containedEdges) {
if (containedEdges.hasOwnProperty(edgeId)) { 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'); util.mergeOptions(this.options, options, 'hierarchical');
if (options.randomSeed !== undefined) { if (options.randomSeed !== undefined) {
this.randomSeed = options.randomSeed;
this.initialRandomSeed = options.randomSeed;
} }
if (this.options.hierarchical.enabled === true) { if (this.options.hierarchical.enabled === true) {
@ -129,6 +129,7 @@ class LayoutEngine {
positionInitially(nodesArray) { positionInitially(nodesArray) {
if (this.options.hierarchical.enabled !== true) { if (this.options.hierarchical.enabled !== true) {
this.randomSeed = this.initialRandomSeed;
for (let i = 0; i < nodesArray.length; i++) { for (let i = 0; i < nodesArray.length; i++) {
let node = nodesArray[i]; let node = nodesArray[i];
if ((!node.isFixed()) && (node.x === undefined || node.y === undefined)) { 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 * Set the Options
* @param options * @param options
*/ */
setOptions(options, allOptions) {
setOptions(options, allOptions, globalOptions) {
if (allOptions !== undefined) { 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) { if (options !== undefined) {
@ -439,6 +439,7 @@ class ManipulationSystem {
this.body.data.edges.remove(finalizedData.edges); this.body.data.edges.remove(finalizedData.edges);
this.body.data.nodes.remove(finalizedData.nodes); this.body.data.nodes.remove(finalizedData.nodes);
this.body.emitter.emit('startSimulation'); this.body.emitter.emit('startSimulation');
this.showManipulatorToolbar();
} }
}); });
} }
@ -450,6 +451,7 @@ class ManipulationSystem {
this.body.data.edges.remove(selectedEdges); this.body.data.edges.remove(selectedEdges);
this.body.data.nodes.remove(selectedNodes); this.body.data.nodes.remove(selectedNodes);
this.body.emitter.emit('startSimulation'); this.body.emitter.emit('startSimulation');
this.showManipulatorToolbar();
} }
} }
@ -559,6 +561,7 @@ class ManipulationSystem {
// empty the editModeDiv // empty the editModeDiv
util.recursiveDOMDelete(this.editModeDiv); util.recursiveDOMDelete(this.editModeDiv);
// create the contents for the editMode button // create the contents for the editMode button
let locale = this.options.locales[this.options.locale]; 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']); 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.renderTimer = undefined;
this.stabilized = false; this.stabilized = false;
this.startedStabilization = false;
this.stabilizationIterations = 0; this.stabilizationIterations = 0;
this.ready = false; // will be set to true if the stabilize 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 // 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 // it is fired while initializing the Network, in which case you would not
// be able to catch it // be able to catch it
this.stabilizationIterations = 0;
this.startedStabilization = false; this.startedStabilization = false;
this._emitStabilized();
}
else {
this.stabilizationIterations = 0;
//this._emitStabilized();
} }
this.stopSimulation(); this.stopSimulation();
} }
@ -244,6 +241,7 @@ class PhysicsEngine {
if (this.stabilizationIterations > 1) { if (this.stabilizationIterations > 1) {
setTimeout(() => { setTimeout(() => {
this.body.emitter.emit('stabilized', {iterations: this.stabilizationIterations}); this.body.emitter.emit('stabilized', {iterations: this.stabilizationIterations});
this.stabilizationIterations = 0;
}, 0); }, 0);
} }
} }
@ -495,8 +493,6 @@ class PhysicsEngine {
// block redraw requests // block redraw requests
this.body.emitter.emit('_blockRedrawRequests'); this.body.emitter.emit('_blockRedrawRequests');
this.body.emitter.emit('startStabilizing');
this.startedStabilization = true;
this.targetIterations = iterations; this.targetIterations = iterations;
// start the stabilization // start the stabilization
@ -505,7 +501,7 @@ class PhysicsEngine {
} }
this.stabilizationIterations = 0; this.stabilizationIterations = 0;
setTimeout(this._stabilizationBatch.bind(this),0);
setTimeout(() => this._stabilizationBatch(),0);
} }
_stabilizationBatch() { _stabilizationBatch() {
@ -538,6 +534,13 @@ class PhysicsEngine {
this.body.emitter.emit('stabilizationIterationsDone'); this.body.emitter.emit('stabilizationIterationsDone');
this.body.emitter.emit('_requestRedraw'); this.body.emitter.emit('_requestRedraw');
if (this.stabilized === true) {
this._emitStabilized();
}
else {
this.startSimulation();
}
this.ready = true; this.ready = true;
} }

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

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

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

@ -165,7 +165,7 @@ let allOptions = {
size: {number}, // px size: {number}, // px
face: {string}, face: {string},
background: {string}, background: {string},
stroke: {number}, // px
strokeWidth: {number}, // px
strokeColor: {string}, strokeColor: {string},
__type__: {object,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) { CanvasRenderingContext2D.prototype.circle = function (x, y, r) {
this.beginPath(); this.beginPath();
this.arc(x, y, r, 0, 2 * Math.PI, false); 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) { CanvasRenderingContext2D.prototype.square = function (x, y, r) {
this.beginPath(); this.beginPath();
this.rect(x - r, y - r, r * 2, r * 2); 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.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false);
this.lineTo(x, y + r); this.lineTo(x, y + r);
this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false); 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(xm + ox, y, xe, ym - oy, xe, ym);
this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); 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); var yt = y - length * Math.sin(angle);
// inner tail // inner tail
// TODO: allow to customize different shapes
var xi = x - length * 0.9 * Math.cos(angle); var xi = x - length * 0.9 * Math.cos(angle);
var yi = y - length * 0.9 * Math.sin(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}} * @returns {{closestMatch: string, path: Array, distance: number}}
*/ */
static findInOptions(option, options, path, recursive = false) { static findInOptions(option, options, path, recursive = false) {
//console.log(option, options, path)
let min = 1e9; let min = 1e9;
let closestMatch = ''; let closestMatch = '';
let closestMatchPath = []; let closestMatchPath = [];
let lowerCaseOption = option.toLowerCase(); let lowerCaseOption = option.toLowerCase();
let indexMatch = undefined; let indexMatch = undefined;
for (let op in options) { for (let op in options) {
let type = Validator.getType(options[op]);
let distance; 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)); let result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path,op));
if (min > result.distance) { if (min > result.distance) {
closestMatch = result.closestMatch; 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') { 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 { else {
if (options[option] !== undefined) { if (options[option] !== undefined) {
if (typeof options[option] == 'boolean') {
if (typeof options[option] === 'boolean') {
mergeTarget[option].enabled = options[option]; mergeTarget[option].enabled = options[option];
} }
else { else {
mergeTarget[option].enabled = true;
if (options[option].enabled === undefined) {
mergeTarget[option].enabled = true;
}
for (var prop in options[option]) { for (var prop in options[option]) {
if (options[option].hasOwnProperty(prop)) { if (options[option].hasOwnProperty(prop)) {
mergeTarget[option][prop] = options[option][prop]; mergeTarget[option][prop] = options[option][prop];

Loading…
Cancel
Save