Browse Source

numerous bugfixes, partial progress with examples

flowchartTest
Alex de Mulder 9 years ago
parent
commit
c3883970e4
34 changed files with 814 additions and 509 deletions
  1. +514
    -425
      dist/vis.js
  2. +9
    -3
      docs/network/physics.html
  3. +4
    -4
      examples/network/categories/02_random_nodes.html
  4. +8
    -4
      examples/network/categories/28_world_cup_network_performance.html
  5. +14
    -15
      examples/network/categories/labels/label_alignment.html
  6. +57
    -0
      examples/network/categories/labels/label_background.html
  7. +61
    -0
      examples/network/categories/labels/label_color_and_size.html
  8. +10
    -8
      examples/network/categories/labels/label_stroke.html
  9. +14
    -8
      examples/network/categories/nodeStyles/colors.html
  10. +15
    -3
      examples/network/categories/nodeStyles/icons.html
  11. +1
    -1
      lib/network/Network.js
  12. +38
    -0
      lib/network/modules/CanvasRenderer.js
  13. +0
    -1
      lib/network/modules/LayoutEngine.js
  14. +14
    -5
      lib/network/modules/PhysicsEngine.js
  15. +1
    -1
      lib/network/modules/View.js
  16. +0
    -1
      lib/network/modules/components/Edge.js
  17. +6
    -0
      lib/network/modules/components/Node.js
  18. +1
    -0
      lib/network/modules/components/nodes/shapes/Box.js
  19. +1
    -0
      lib/network/modules/components/nodes/shapes/Circle.js
  20. +1
    -0
      lib/network/modules/components/nodes/shapes/CircularImage.js
  21. +1
    -0
      lib/network/modules/components/nodes/shapes/Database.js
  22. +1
    -0
      lib/network/modules/components/nodes/shapes/Ellipse.js
  23. +0
    -21
      lib/network/modules/components/nodes/shapes/Empty.js
  24. +1
    -0
      lib/network/modules/components/nodes/shapes/Icon.js
  25. +1
    -0
      lib/network/modules/components/nodes/shapes/Text.js
  26. +1
    -0
      lib/network/modules/components/nodes/util/CircleImageBase.js
  27. +2
    -0
      lib/network/modules/components/nodes/util/NodeBase.js
  28. +1
    -0
      lib/network/modules/components/nodes/util/ShapeBase.js
  29. +7
    -1
      lib/network/modules/components/physics/BarnesHutSolver.js
  30. +5
    -0
      lib/network/modules/components/physics/FA2BasedRepulsionSolver.js
  31. +13
    -2
      lib/network/modules/components/physics/SpringSolver.js
  32. +1
    -1
      lib/network/modules/components/shared/Label.js
  33. +9
    -3
      lib/network/options.js
  34. +2
    -2
      test/timeline_groups.html

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


+ 9
- 3
docs/network/physics.html View File

@ -90,7 +90,8 @@ var options = {
centralGravity: 0.3, centralGravity: 0.3,
springLength: 95, springLength: 95,
springConstant: 0.04, springConstant: 0.04,
damping: 0.09
damping: 0.09,
avoidOverlap: false
}, },
forceAtlas2Based: { forceAtlas2Based: {
theta: 0.5, theta: 0.5,
@ -98,14 +99,16 @@ var options = {
centralGravity: 0.01, centralGravity: 0.01,
springConstant: 0.08, springConstant: 0.08,
springLength: 100, springLength: 100,
damping: 0.4
damping: 0.4,
avoidOverlap: false
}, },
repulsion: { repulsion: {
centralGravity: 0.2, centralGravity: 0.2,
springLength: 200, springLength: 200,
springConstant: 0.05, springConstant: 0.05,
nodeDistance: 100, nodeDistance: 100,
damping: 0.09
damping: 0.09,
avoidOverlap: false
}, },
hierarchicalRepulsion: { hierarchicalRepulsion: {
centralGravity: 0.0, centralGravity: 0.0,
@ -149,6 +152,7 @@ network.setOptions(options);
<tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.springLength</td> <td class="mid">Number</td> <td class="mid"><code>95</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr> <tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.springLength</td> <td class="mid">Number</td> <td class="mid"><code>95</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr>
<tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.04</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr> <tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.04</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr>
<tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.09</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr> <tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.09</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr>
<tr parent="barnesHut" class="hidden"><td class="indent">barnesHut.avoidOverlap</td> <td class="mid">Number</td> <td class="mid"><code>0</code></td> <td>Accepted range: <code>[0 .. 1]</code>. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model and the spring model. Value 1 is maximum overlap avoidance.</td></tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','forceAtlas2Based', this);"><td><span parent="forceAtlas2Based" class="right-caret"></span> forceAtlas2Based</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>Force Atlas 2 has been developed by <a href="http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679" target="_blank">Jacomi <i>et al</i> (2014)</a> for use with Gephi. The forceAtlas2Based solver makes use of some of the equations provided <tr class='toggle collapsible' onclick="toggleTable('optionTable','forceAtlas2Based', this);"><td><span parent="forceAtlas2Based" class="right-caret"></span> forceAtlas2Based</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>Force Atlas 2 has been developed by <a href="http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679" target="_blank">Jacomi <i>et al</i> (2014)</a> for use with Gephi. The forceAtlas2Based solver makes use of some of the equations provided
by them and makes use of the barnesHut implementation in vis. The main differences are the central gravity model, by them and makes use of the barnesHut implementation in vis. The main differences are the central gravity model,
@ -159,6 +163,7 @@ network.setOptions(options);
<tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.springLength</td> <td class="mid">Number</td> <td class="mid"><code>100</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr> <tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.springLength</td> <td class="mid">Number</td> <td class="mid"><code>100</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr>
<tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.08</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr> <tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.08</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr>
<tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.4</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr> <tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.4</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr>
<tr parent="forceAtlas2Based" class="hidden"><td class="indent">forceAtlas2Based.avoidOverlap</td> <td class="mid">Number</td> <td class="mid"><code>0</code></td> <td>Accepted range: <code>[0 .. 1]</code>. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model and the spring model. Value 1 is maximum overlap avoidance.</td></tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','repulsion', this);"><td><span parent="repulsion" class="right-caret"></span> repulsion</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>The repulsion model assumes nodes have a simplified repulsion field around them. It's force linearly decreases from 1 (at 0.5*nodeDistance and smaller) to 0 (at 2*nodeDistance).</td></tr> <tr class='toggle collapsible' onclick="toggleTable('optionTable','repulsion', this);"><td><span parent="repulsion" class="right-caret"></span> repulsion</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>The repulsion model assumes nodes have a simplified repulsion field around them. It's force linearly decreases from 1 (at 0.5*nodeDistance and smaller) to 0 (at 2*nodeDistance).</td></tr>
<tr parent="repulsion" class="hidden"><td class="indent">repulsion.nodeDistance</td> <td class="mid">Number</td> <td class="mid"><code>100</code></td> <td>This is the range of influence for the repulsion.</td></tr> <tr parent="repulsion" class="hidden"><td class="indent">repulsion.nodeDistance</td> <td class="mid">Number</td> <td class="mid"><code>100</code></td> <td>This is the range of influence for the repulsion.</td></tr>
@ -166,6 +171,7 @@ network.setOptions(options);
<tr parent="repulsion" class="hidden"><td class="indent">repulsion.springLength</td> <td class="mid">Number</td> <td class="mid"><code>200</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr> <tr parent="repulsion" class="hidden"><td class="indent">repulsion.springLength</td> <td class="mid">Number</td> <td class="mid"><code>200</code></td> <td>The edges are modelled as springs. This springLength here is the the rest length of the spring.</td></tr>
<tr parent="repulsion" class="hidden"><td class="indent">repulsion.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.05</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr> <tr parent="repulsion" class="hidden"><td class="indent">repulsion.springConstant</td> <td class="mid">Number</td> <td class="mid"><code>0.05</code></td> <td>This is how 'sturdy' the springs are. Higher values mean stronger springs.</td></tr>
<tr parent="repulsion" class="hidden"><td class="indent">repulsion.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.09</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr> <tr parent="repulsion" class="hidden"><td class="indent">repulsion.damping</td> <td class="mid">Number</td> <td class="mid"><code>0.09</code></td> <td>Accepted range: <code>[0 .. 1]</code>. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.</td></tr>
<tr parent="repulsion" class="hidden"><td class="indent">repulsion.avoidOverlap</td> <td class="mid">Number</td> <td class="mid"><code>0</code></td> <td>Accepted range: <code>[0 .. 1]</code>. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for the spring model. Value 1 is maximum overlap avoidance.</td></tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','hierarchicalRepulsion', this);"><td><span parent="hierarchicalRepulsion" class="right-caret"></span> hierarchicalRepulsion</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>This model is based on the repulsion solver but the levels are taken into account and the forces are normalized.</td></tr> <tr class='toggle collapsible' onclick="toggleTable('optionTable','hierarchicalRepulsion', this);"><td><span parent="hierarchicalRepulsion" class="right-caret"></span> hierarchicalRepulsion</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>This model is based on the repulsion solver but the levels are taken into account and the forces are normalized.</td></tr>
<tr parent="hierarchicalRepulsion" class="hidden"><td class="indent">hierarchicalRepulsion.nodeDistance</td> <td class="mid">Number</td> <td class="mid"><code>120</code></td> <td>This is the range of influence for the repulsion.</td></tr> <tr parent="hierarchicalRepulsion" class="hidden"><td class="indent">hierarchicalRepulsion.nodeDistance</td> <td class="mid">Number</td> <td class="mid"><code>120</code></td> <td>This is the range of influence for the repulsion.</td></tr>

+ 4
- 4
examples/network/categories/02_random_nodes.html View File

@ -14,8 +14,8 @@
} }
</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">
var nodes = null; var nodes = null;
@ -85,14 +85,14 @@
edges: edges edges: edges
}; };
var options = { var options = {
physics: { stabilization: false }
physics: { stabilization: true }
}; };
network = new vis.Network(container, data, options); 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();">
<p> <p>

+ 8
- 4
examples/network/categories/28_world_cup_network_performance.html View File

@ -5,10 +5,10 @@
<meta http-equiv="content-type" content="text/html; charset=UTF8"> <meta http-equiv="content-type" content="text/html; charset=UTF8">
<title>Network | Static smooth curves - World Cup Network</title> <title>Network | Static smooth curves - World Cup Network</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link type="text/css" rel="stylesheet" href="../../dist/vis.css">
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link type="text/css" rel="stylesheet" href="../../../dist/vis.css">
<script src="./data/WorldCup2014.js"></script>
<script src="../data/WorldCup2014.js"></script>
<style type="text/css"> <style type="text/css">
#mynetwork { #mynetwork {
@ -45,6 +45,11 @@
function redrawAll() { function redrawAll() {
network = null; network = null;
for (var i = 0; i < nodes.length; i++) {
delete nodes[i].x;
delete nodes[i].y;
}
// create a network // create a network
var container = document.getElementById('mynetwork'); var container = document.getElementById('mynetwork');
var data = { var data = {
@ -67,7 +72,6 @@
width: 0.15, width: 0.15,
color: {inherit: 'from'}, color: {inherit: 'from'},
smooth: { smooth: {
dynamic: false,
type: 'continuous' type: 'continuous'
} }
}, },

examples/network/categories/37_label_alignment.html → examples/network/categories/labels/label_alignment.html View File

@ -3,22 +3,25 @@
<head> <head>
<title>Network | Label alignment</title> <title>Network | Label alignment</title>
<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" />
<style type="text/css"> <style type="text/css">
#mynetwork { #mynetwork {
width: 400px;
height: 400px;
width: 600px;
height: 600px;
border: 1px solid lightgray; border: 1px solid lightgray;
} }
p {
max-width:600px;
}
</style> </style>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head> </head>
<body> <body>
<p>Labels can be aligned to edges in various ways.</p>
<p>Labels of edges can be aligned to edges in various ways. <br>Label alignment for nodes (top, bottom, left, right, inside) is planned but not in vis yet.</p>
<div id="mynetwork"></div> <div id="mynetwork"></div>
@ -34,10 +37,10 @@
// create an array with edges // create an array with edges
var edges = [ var edges = [
{from: 1, to: 2, label: 'middle', font: {align:'middle', background:'orange'}},
{from: 1, to: 3, label: 'top', font: {align: 'top', background:'green'}},
{from: 2, to: 4, label: 'horizontal', font: {align: 'horizontal', background: 'red'}},
{from: 2, to: 5, label: 'bottom', font: { align:'bottom', background: '#ccd' }}
{from: 1, to: 2, label: 'middle', font: {align: 'middle'}},
{from: 1, to: 3, label: 'top', font: {align: 'top'}},
{from: 2, to: 4, label: 'horizontal', font: {align: 'horizontal'}},
{from: 2, to: 5, label: 'bottom', font: {align: 'bottom'}}
]; ];
// create a network // create a network
@ -46,11 +49,7 @@
nodes: nodes, nodes: nodes,
edges: edges edges: edges
}; };
var options = {
edges: {
font: {stroke: 0}
}
};
var options = {};
var network = new vis.Network(container, data, options); var network = new vis.Network(container, data, options);
</script> </script>

+ 57
- 0
examples/network/categories/labels/label_background.html View File

@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<title>Network | Label alignment</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;
}
</style>
<script src="../../../googleAnalytics.js"></script>
</head>
<body>
<p>Labels can have any color background.</p>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1', font: {background: 'red'}},
{id: 2, label: 'Node 2', font: {background: 'white'}},
{id: 3, label: 'Node 3', font: {background: 'cyan'}},
{id: 4, label: 'Node 4', font: {background: 'lime'}},
{id: 5, label: 'Node 5', font: {background: 'pink'}}
];
// create an array with edges
var edges = [
{from: 1, to: 2, label: 'label1', font: {background: '#ff0000'}},
{from: 1, to: 3, label: 'label2', font: {background: 'yellow'}},
{from: 2, to: 4, label: 'label3', font: {background: 'lime'}},
{from: 2, to: 5, label: 'label3', font: {background: 'pink'}}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {nodes:{font:{strokeWidth:0}}, edges:{font:{strokeWidth:0}}};
var network = new vis.Network(container, data, options);
</script>
</body>
</html>

+ 61
- 0
examples/network/categories/labels/label_color_and_size.html View File

@ -0,0 +1,61 @@
<!doctype html>
<html>
<head>
<title>Network | Label stroke</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;
background:#d1d1d1;
}
p {
max-width:600px;
}
</style>
<script src="../../../googleAnalytics.js"></script>
</head>
<body>
<p>The style of the edges can be fully customized.</p>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1', font: '12px arial red'},
{id: 2, label: 'Node 2', font: {size:12, color:'lime', face:'arial'}},
{id: 3, label: 'Node 3', font: '18px verdana blue'},
{id: 4, label: 'Node 4', font: {size:12, color:'red', face:'sans', background:'white'}},
{id: 5, label: 'Node 5', font: {size:15, color:'red', face:'courier', strokeWidth:3, strokeColor:'#ffffff'}}
];
// create an array with edges
var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 2, to: 4},
{from: 2, to: 5}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
nodes : {
shape: 'dot',
size: 10
}
};
var network = new vis.Network(container, data, options);
</script>
</body>
</html>

examples/network/categories/35_label_stroke.html → examples/network/categories/labels/label_stroke.html View File

@ -2,23 +2,25 @@
<html> <html>
<head> <head>
<title>Network | Label stroke</title> <title>Network | Label stroke</title>
<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" />
<style type="text/css"> <style type="text/css">
#mynetwork { #mynetwork {
width: 400px;
height: 400px;
width: 600px;
height: 600px;
border: 1px solid lightgray; border: 1px solid lightgray;
background: #d1d1d1;
background:#d1d1d1;
}
p {
max-width:600px;
} }
</style> </style>
<script src="../googleAnalytics.js"></script>
<script src="../../../googleAnalytics.js"></script>
</head> </head>
<body> <body>
<p>The font, size, and stroke of labels is fully customizable.</p>
<p>The stroke of labels is fully can have a width and color. Edgelabels by default have a white stroke for clarity.</p>
<div id="mynetwork"></div> <div id="mynetwork"></div>

+ 14
- 8
examples/network/categories/nodeStyles/colors.html View File

@ -12,6 +12,10 @@
height: 400px; height: 400px;
border: 1px solid lightgray; border: 1px solid lightgray;
} }
p {
max-width:700px;
}
</style> </style>
</head> </head>
<body> <body>
@ -26,12 +30,12 @@
<script type="text/javascript"> <script type="text/javascript">
// create an array with nodes // create an array with nodes
var nodes = new vis.DataSet([ var nodes = new vis.DataSet([
{id: 1, label:'html color', color: 'red'},
{id: 2, label:'rgb color', color: 'rgb(200,12,31)'},
{id: 3, label:'hex color', color: '#ff00dd'},
{id: 4, label:'rgba color', color: 'rgba(230,53,94,0.5)'},
{id: 5, label:'colorObject', color: {background:'red', border:'blue'}},
{id: 6, label:'colorObject + highlight', color: {background:'orange', border:'blue',highlight:{background:'red',border:'blue'}}},
{id: 1, label:'html color', color: 'lime'},
{id: 2, label:'rgb color', color: 'rgb(255,168,7)'},
{id: 3, label:'hex color', color: '#7BE141'},
{id: 4, label:'rgba color', color: 'rgba(97,195,238,0.5)'},
{id: 5, label:'colorObject', color: {background:'pink', border:'purple'}},
{id: 6, label:'colorObject + highlight', color: {background:'#F03967', border:'#713E7F',highlight:{background:'red',border:'black'}}},
{id: 7, label:'colorObject + highlight + hover', color: {background:'cyan', border:'blue',highlight:{background:'red',border:'blue'},hover:{background:'white',border:'red'}}} {id: 7, label:'colorObject + highlight + hover', color: {background:'cyan', border:'blue',highlight:{background:'red',border:'blue'},hover:{background:'white',border:'red'}}}
]) ])
@ -40,7 +44,9 @@
{from: 1, to: 3}, {from: 1, to: 3},
{from: 1, to: 2}, {from: 1, to: 2},
{from: 2, to: 4}, {from: 2, to: 4},
{from: 2, to: 5}
{from: 2, to: 5},
{from: 2, to: 6},
{from: 4, to: 7},
]); ]);
// create a network // create a network
@ -49,7 +55,7 @@
nodes: nodes, nodes: nodes,
edges: edges edges: edges
}; };
var options = {interaction:{hover:true}};
var options = {nodes:{borderWidth:2},interaction:{hover:true}};
var network = new vis.Network(container, data, options); var network = new vis.Network(container, data, options);
</script> </script>

+ 15
- 3
examples/network/categories/nodeStyles/icons.html View File

@ -15,6 +15,12 @@
#mynetworkFA, #mynetworkFA,
#mynetworkIO { #mynetworkIO {
height: 300px; height: 300px;
width: 700px;
border:1px solid lightgrey;
}
p {
max-width:700px;
} }
</style> </style>
@ -170,13 +176,19 @@
</script> </script>
<script src="../../../googleAnalytics.js"></script> <script src="../../../googleAnalytics.js"></script>
</head> </head>
<body onload="draw()"> <body onload="draw()">
<p>
Icons can be used for nodes as well. This example shows Icons from fontAwesome and Ionicons but it should work with similar packages as well.
It uses unicode and css to define the icons.<br><br> <b>Remember! Unicode in javascript is done like this: \uf274 for the unicode f274.</b>
<br> If a node is shown as a rectangle, it means the css is not loaded (or not yet loaded). A redraw will fix that.
</p>
<h2> <h2>
<i class="fa fa-flag"></i> Use FontAwesome-icons for node</h2>
<i class="fa fa-flag"></i> Use FontAwesome-icons for nodes</h2>
<div id="mynetworkFA"></div> <div id="mynetworkFA"></div>
<h2> <h2>
<i class="ion ion-ionic"></i> Use Ionicons-icons for node</h2>
<i class="ion ion-ionic"></i> Use Ionicons-icons for nodes</h2>
<div id="mynetworkIO"></div> <div id="mynetworkIO"></div>
</body> </body>

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

@ -260,7 +260,7 @@ Network.prototype.bindEventListeners = function () {
this.body.emitter.on("_dataChanged", () => { this.body.emitter.on("_dataChanged", () => {
// update shortcut lists // update shortcut lists
this._updateVisibleIndices(); this._updateVisibleIndices();
this.physics.updatePhysicsIndices();
this.physics.updatePhysicsData();
// call the dataUpdated event because the only difference between the two is the updating of the indices // call the dataUpdated event because the only difference between the two is the updating of the indices
this.body.emitter.emit("_dataUpdated"); this.body.emitter.emit("_dataUpdated");

+ 38
- 0
lib/network/modules/CanvasRenderer.js View File

@ -36,6 +36,7 @@ class CanvasRenderer {
this.dragging = true; this.dragging = true;
}); });
this.body.emitter.on("dragEnd", () => this.dragging = false); this.body.emitter.on("dragEnd", () => this.dragging = false);
this.body.emitter.on("_resizeNodes", () => this._resizeNodes());
this.body.emitter.on("_redraw", () => { this.body.emitter.on("_redraw", () => {
if (this.renderingActive === false) { if (this.renderingActive === false) {
this._redraw(); this._redraw();
@ -194,6 +195,43 @@ class CanvasRenderer {
} }
/**
* Redraw all nodes
* The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
* @param {CanvasRenderingContext2D} ctx
* @param {Boolean} [alwaysShow]
* @private
*/
_resizeNodes() {
let ctx = this.canvas.frame.canvas.getContext('2d');
if (this.pixelRatio === undefined) {
this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1);
}
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
ctx.save();
ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
ctx.scale(this.body.view.scale, this.body.view.scale);
let nodes = this.body.nodes;
let node;
// resize all nodes
for (let nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
node = nodes[nodeId];
node.resize(ctx);
node.updateBoundingBox(ctx);
}
}
// restore original scaling and translation
ctx.restore();
}
/** /**
* Redraw all nodes * Redraw all nodes
* The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');

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

@ -134,7 +134,6 @@ class LayoutEngine {
if ((!node.isFixed()) && (node.x === undefined || node.y === undefined)) { if ((!node.isFixed()) && (node.x === undefined || node.y === undefined)) {
let radius = 10 * 0.1 * nodesArray.length + 10; let radius = 10 * 0.1 * nodesArray.length + 10;
let angle = 2 * Math.PI * this.seededRandom(); let angle = 2 * Math.PI * this.seededRandom();
if (node.options.fixed.x === false) { if (node.options.fixed.x === false) {
node.x = radius * Math.cos(angle); node.x = radius * Math.cos(angle);
} }

+ 14
- 5
lib/network/modules/PhysicsEngine.js View File

@ -35,7 +35,8 @@ class PhysicsEngine {
centralGravity: 0.3, centralGravity: 0.3,
springLength: 95, springLength: 95,
springConstant: 0.04, springConstant: 0.04,
damping: 0.09
damping: 0.09,
avoidOverlap: 0
}, },
forceAtlas2Based: { forceAtlas2Based: {
theta: 0.5, theta: 0.5,
@ -43,14 +44,16 @@ class PhysicsEngine {
centralGravity: 0.01, centralGravity: 0.01,
springConstant: 0.08, springConstant: 0.08,
springLength: 100, springLength: 100,
damping: 0.4
damping: 0.4,
avoidOverlap: 0
}, },
repulsion: { repulsion: {
centralGravity: 0.2, centralGravity: 0.2,
springLength: 200, springLength: 200,
springConstant: 0.05, springConstant: 0.05,
nodeDistance: 100, nodeDistance: 100,
damping: 0.09
damping: 0.09,
avoidOverlap: 0
}, },
hierarchicalRepulsion: { hierarchicalRepulsion: {
centralGravity: 0.0, centralGravity: 0.0,
@ -159,7 +162,6 @@ class PhysicsEngine {
else { else {
this.ready = true; this.ready = true;
this.body.emitter.emit('fit'); this.body.emitter.emit('fit');
} }
} }
@ -169,6 +171,10 @@ class PhysicsEngine {
startSimulation() { startSimulation() {
if (this.physicsEnabled === true) { if (this.physicsEnabled === true) {
this.stabilized = false; this.stabilized = false;
// this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes");
if (this.viewFunction === undefined) { if (this.viewFunction === undefined) {
this.viewFunction = this.simulationStep.bind(this); this.viewFunction = this.simulationStep.bind(this);
this.body.emitter.on('initRedraw', this.viewFunction); this.body.emitter.on('initRedraw', this.viewFunction);
@ -273,7 +279,7 @@ class PhysicsEngine {
* *
* @private * @private
*/ */
updatePhysicsIndices() {
updatePhysicsData() {
this.physicsBody.forces = {}; this.physicsBody.forces = {};
this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsNodeIndices = [];
this.physicsBody.physicsEdgeIndices = []; this.physicsBody.physicsEdgeIndices = [];
@ -478,6 +484,9 @@ class PhysicsEngine {
iterations = this.options.stabilization.iterations; iterations = this.options.stabilization.iterations;
} }
// this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes");
// stop the render loop // stop the render loop
this.stopSimulation(); this.stopSimulation();

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

@ -125,7 +125,7 @@ class View {
zoomLevel *= factor; zoomLevel *= factor;
} }
else { else {
this.body.emitter.emit("_redraw", true);
this.body.emitter.emit("_resizeNodes");
range = this._getRange(options.nodes); range = this._getRange(options.nodes);
var xDistance = Math.abs(range.maxX - range.minX) * 1.1; var xDistance = Math.abs(range.maxX - range.minX) * 1.1;
var yDistance = Math.abs(range.maxY - range.minY) * 1.1; var yDistance = Math.abs(range.maxY - range.minY) * 1.1;

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

@ -90,7 +90,6 @@ class Edge {
static parseOptions(parentOptions, newOptions, allowDeletion = false) { static parseOptions(parentOptions, newOptions, allowDeletion = false) {
var fields = [ var fields = [
'id', 'id',
'font',
'from', 'from',
'hidden', 'hidden',
'hoverWidth', 'hoverWidth',

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

@ -162,6 +162,7 @@ class Node {
static parseOptions(parentOptions, newOptions, allowDeletion = false) { static parseOptions(parentOptions, newOptions, allowDeletion = false) {
var fields = [ var fields = [
'color', 'color',
'font',
'fixed', 'fixed',
'shadow' 'shadow'
]; ];
@ -194,6 +195,10 @@ class Node {
} }
} }
} }
if (newOptions.font !== undefined) {
Label.parseOptions(parentOptions.font, newOptions);
}
} }
updateLabelModule() { updateLabelModule() {
@ -368,6 +373,7 @@ class Node {
this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); this.shape.draw(ctx, this.x, this.y, this.selected, this.hover);
} }
/** /**
* Update the bounding box of the shape * Update the bounding box of the shape
*/ */

+ 1
- 0
lib/network/modules/components/nodes/shapes/Box.js View File

@ -13,6 +13,7 @@ class Box extends NodeBase {
let textSize = this.labelModule.getTextSize(ctx,selected); let textSize = this.labelModule.getTextSize(ctx,selected);
this.width = textSize.width + 2 * margin; this.width = textSize.width + 2 * margin;
this.height = textSize.height + 2 * margin; this.height = textSize.height + 2 * margin;
this.radius = 0.5*this.width;
} }
} }

+ 1
- 0
lib/network/modules/components/nodes/shapes/Circle.js View File

@ -16,6 +16,7 @@ class Circle extends CircleImageBase {
this.width = diameter; this.width = diameter;
this.height = diameter; this.height = diameter;
this.radius = 0.5*this.width;
} }
} }

+ 1
- 0
lib/network/modules/components/nodes/shapes/CircularImage.js View File

@ -17,6 +17,7 @@ class CircularImage extends CircleImageBase {
this.width = diameter; this.width = diameter;
this.height = diameter; this.height = diameter;
this._swapToImageResizeWhenImageLoaded = true; this._swapToImageResizeWhenImageLoaded = true;
this.radius = 0.5*this.width;
} }
} }
else { else {

+ 1
- 0
lib/network/modules/components/nodes/shapes/Database.js View File

@ -14,6 +14,7 @@ class Database extends NodeBase {
var size = textSize.width + 2 * margin; var size = textSize.width + 2 * margin;
this.width = size; this.width = size;
this.height = size; this.height = size;
this.radius = 0.5*this.width;
} }
} }

+ 1
- 0
lib/network/modules/components/nodes/shapes/Ellipse.js View File

@ -16,6 +16,7 @@ class Ellipse extends NodeBase {
if (this.width < this.height) { if (this.width < this.height) {
this.width = this.height; this.width = this.height;
} }
this.radius = 0.5*this.width;
} }
} }

+ 0
- 21
lib/network/modules/components/nodes/shapes/Empty.js View File

@ -1,21 +0,0 @@
'use strict';
import NodeBase from '../util/NodeBase'
class Empty extends NodeBase {
constructor (options, body, labelModule) {
super(options, body, labelModule);
}
setOptions() {}
resize() {}
draw() {}
distanceToBorder() {}
updateBoundingBox() {}}
export default Empty;

+ 1
- 0
lib/network/modules/components/nodes/shapes/Icon.js View File

@ -16,6 +16,7 @@ class Icon extends NodeBase {
}; };
this.width = iconSize.width + 2 * margin; this.width = iconSize.width + 2 * margin;
this.height = iconSize.height + 2 * margin; this.height = iconSize.height + 2 * margin;
this.radius = 0.5*this.width;
} }
} }

+ 1
- 0
lib/network/modules/components/nodes/shapes/Text.js View File

@ -13,6 +13,7 @@ class Text extends NodeBase {
var textSize = this.labelModule.getTextSize(ctx,selected); var textSize = this.labelModule.getTextSize(ctx,selected);
this.width = textSize.width + 2 * margin; this.width = textSize.width + 2 * margin;
this.height = textSize.height + 2 * margin; this.height = textSize.height + 2 * margin;
this.radius = 0.5*this.width;
} }
} }

+ 1
- 0
lib/network/modules/components/nodes/util/CircleImageBase.js View File

@ -25,6 +25,7 @@ class CircleImageBase extends NodeBase {
} }
this.width = width; this.width = width;
this.height = height; this.height = height;
this.radius = 0.5*this.width;
} }
} }

+ 2
- 0
lib/network/modules/components/nodes/util/NodeBase.js View File

@ -6,6 +6,8 @@ class NodeBase {
this.top = undefined; this.top = undefined;
this.left = undefined; this.left = undefined;
this.height = undefined; this.height = undefined;
this.width = undefined;
this.radius = undefined;
this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0};
} }

+ 1
- 0
lib/network/modules/components/nodes/util/ShapeBase.js View File

@ -10,6 +10,7 @@ class ShapeBase extends NodeBase {
var size = 2 * this.options.size; var size = 2 * this.options.size;
this.width = size; this.width = size;
this.height = size; this.height = size;
this.radius = 0.5*this.width;
} }
} }

+ 7
- 1
lib/network/modules/components/physics/BarnesHutSolver.js View File

@ -9,7 +9,8 @@ class BarnesHutSolver {
setOptions(options) { setOptions(options) {
this.options = options; this.options = options;
this.thetaInversed = 1/ this.options.theta;
this.thetaInversed = 1 / this.options.theta;
this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1,this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius
} }
@ -104,6 +105,11 @@ class BarnesHutSolver {
distance = 0.1 * Math.random(); distance = 0.1 * Math.random();
dx = distance; dx = distance;
} }
if (this.overlapAvoidanceFactor < 1) {
distance = Math.max(0.1 + (this.overlapAvoidanceFactor * node.shape.radius), distance - node.shape.radius);
}
// the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
// it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
let gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance,3); let gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance,3);

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

@ -20,6 +20,11 @@ class ForceAtlas2BasedRepulsionSolver extends BarnesHutSolver {
distance = 0.1 * Math.random(); distance = 0.1 * Math.random();
dx = distance; dx = distance;
} }
if (this.overlapAvoidanceFactor < 1) {
distance = Math.max(0.1 + (this.overlapAvoidanceFactor * node.shape.radius), distance - node.shape.radius);
}
let degree = (node.edges.length + 1); let degree = (node.edges.length + 1);
// the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
// it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce

+ 13
- 2
lib/network/modules/components/physics/SpringSolver.js View File

@ -7,6 +7,8 @@ class SpringSolver {
setOptions(options) { setOptions(options) {
this.options = options; this.options = options;
this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1,this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius
} }
/** /**
@ -56,8 +58,17 @@ class SpringSolver {
* @private * @private
*/ */
_calculateSpringForce(node1, node2, edgeLength) { _calculateSpringForce(node1, node2, edgeLength) {
let dx = (node1.x - node2.x);
let dy = (node1.y - node2.y);
let dx,dy;
if (this.overlapAvoidanceFactor < 1) {
let nodesWidth = this.overlapAvoidanceFactor * (node1.shape.radius + node2.shape.radius);
dx = (node1.x - node2.x) - nodesWidth;
dy = (node1.y - node2.y) - nodesWidth;
}
else {
dx = (node1.x - node2.x);
dy = (node1.y - node2.y);
}
let distance = Math.sqrt(dx * dx + dy * dy); let distance = Math.sqrt(dx * dx + dy * dy);
distance = distance === 0 ? 0.01 : distance; distance = distance === 0 ? 0.01 : distance;

+ 1
- 1
lib/network/modules/components/shared/Label.js View File

@ -92,7 +92,7 @@ class Label {
ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height); ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height);
break; break;
default: default:
ctx.fillRect(this.size.left, this.size.top, this.size.width, this.size.height);
ctx.fillRect(this.size.left, this.size.top - 0.5*lineMargin, this.size.width, this.size.height);
break; break;
} }
} }

+ 9
- 3
lib/network/options.js View File

@ -220,6 +220,7 @@ let allOptions = {
springLength: {number}, springLength: {number},
springConstant: {number}, springConstant: {number},
damping: {number}, damping: {number},
avoidOverlap: {number},
__type__: {object} __type__: {object}
}, },
forceAtlas2Based: { forceAtlas2Based: {
@ -228,6 +229,7 @@ let allOptions = {
springLength: {number}, springLength: {number},
springConstant: {number}, springConstant: {number},
damping: {number}, damping: {number},
avoidOverlap: {number},
__type__: {object} __type__: {object}
}, },
repulsion: { repulsion: {
@ -236,6 +238,7 @@ let allOptions = {
springConstant: {number}, springConstant: {number},
nodeDistance: {number}, nodeDistance: {number},
damping: {number}, damping: {number},
avoidOverlap: {number},
__type__: {object} __type__: {object}
}, },
hierarchicalRepulsion: { hierarchicalRepulsion: {
@ -426,7 +429,8 @@ let configureOptions = {
centralGravity: [0.3, 0, 10, 0.05], centralGravity: [0.3, 0, 10, 0.05],
springLength: [95, 0, 500, 5], springLength: [95, 0, 500, 5],
springConstant: [0.04, 0, 5, 0.005], springConstant: [0.04, 0, 5, 0.005],
damping: [0.09, 0, 1, 0.01]
damping: [0.09, 0, 1, 0.01],
avoidOverlap: [0, 0, 1, 0.01]
}, },
forceAtlas2Based: { forceAtlas2Based: {
//theta: [0.5, 0.1, 1, 0.05], //theta: [0.5, 0.1, 1, 0.05],
@ -434,14 +438,16 @@ let configureOptions = {
centralGravity: [0.01, 0, 1, 0.005], centralGravity: [0.01, 0, 1, 0.005],
springLength: [95, 0, 500, 5], springLength: [95, 0, 500, 5],
springConstant: [0.08, 0, 5, 0.005], springConstant: [0.08, 0, 5, 0.005],
damping: [0.4, 0, 1, 0.01]
damping: [0.4, 0, 1, 0.01],
avoidOverlap: [0, 0, 1, 0.01]
}, },
repulsion: { repulsion: {
centralGravity: [0.2, 0, 10, 0.05], centralGravity: [0.2, 0, 10, 0.05],
springLength: [200, 0, 500, 5], springLength: [200, 0, 500, 5],
springConstant: [0.05, 0, 5, 0.005], springConstant: [0.05, 0, 5, 0.005],
nodeDistance: [100, 0, 500, 5], nodeDistance: [100, 0, 500, 5],
damping: [0.09, 0, 1, 0.01]
damping: [0.09, 0, 1, 0.01],
avoidOverlap: [0, 0, 1, 0.01]
}, },
hierarchicalRepulsion: { hierarchicalRepulsion: {
centralGravity: [0.2, 0, 10, 0.05], centralGravity: [0.2, 0, 10, 0.05],

+ 2
- 2
test/timeline_groups.html View File

@ -64,7 +64,7 @@
var names = ['John (0)', 'Alston (1)', 'Lee (2)', 'Grant (3)']; var names = ['John (0)', 'Alston (1)', 'Lee (2)', 'Grant (3)'];
var groups = new vis.DataSet(); var groups = new vis.DataSet();
for (var g = 0; g < groupCount; g++) { for (var g = 0; g < groupCount; g++) {
groups.add({id: g, content: names[g], title: 'Title of group ' + g, 'className': 'myGroup'});
groups.add({id: g, content: '<a href="http://google.com"><span style="color:#97B0F8;">' + names[g] + '</a>', title: 'Title of group ' + g, 'className': 'myGroup'});
} }
// create a dataset with items // create a dataset with items
@ -77,7 +77,7 @@
id: i, id: i,
group: group, group: group,
content: 'item ' + i + content: 'item ' + i +
' <span style="color:#97B0F8;">(' + names[group] + ')</span>',
' <a href="http://google.com"><span style="color:#97B0F8;">(' + names[group] + ')</span></a> ',
start: start, start: start,
end: end, end: end,
title: 'Title for item ' + i, title: 'Title for item ' + i,

Loading…
Cancel
Save