Browse Source

Introduced a new layout algorithm for the hierarchical system based on the direction of the edges.

v3_develop
Alex de Mulder 10 years ago
parent
commit
4751cd5258
9 changed files with 2326 additions and 2123 deletions
  1. +1
    -0
      HISTORY.md
  2. +2064
    -1973
      dist/vis.js
  3. +16
    -8
      docs/network.html
  4. +11
    -11
      examples/network/24_hierarchical_layout_userdefined.html
  5. +140
    -0
      examples/network/32_hierarchicaLayoutMethods.html
  6. +0
    -128
      examples/network/ex.html
  7. +2
    -1
      lib/network/Network.js
  8. +1
    -0
      lib/network/Node.js
  9. +91
    -2
      lib/network/mixins/HierarchicalLayoutMixin.js

+ 1
- 0
HISTORY.md View File

@ -36,6 +36,7 @@ http://visjs.org
using arrow keys.
- Fixed an initial rendering before the graph has been stabilized.
- Fixed bug where loading hierarchical data after initialization crashed network.
- Added different layout method to the hierarchical system based on the direction of the edges.
### Graph2D

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


+ 16
- 8
docs/network.html View File

@ -33,7 +33,7 @@
<p>
Every dataset is different. Nodes can have different sizes based on content, interconnectivity can be high or low etc. Because of this, network has a special option
that the user can use to explore which settings may be good for you. Use configurePhysics as described in the <u><a href="#PhysicsConfiguration">Physics</a></u> section or by
<u><a href="../examples/Network/25_physics_configuration.html">example 25</a></u>.
<u><a href="../examples/network/25_physics_configuration.html">example 25</a></u>.
</p>
<p>
@ -765,7 +765,7 @@ var network = new vis.Network(container, data);
<p>
network can import data straight from an exported json file from gephi. You can get the JSON exporter here:
<a href="https://marketplace.gephi.org/plugin/json-exporter/" target="_blank">https://marketplace.gephi.org/plugin/json-exporter/</a>.
An example exists showing how to get a JSON file into Vis: <a href="../examples/Network/30_importing_from_gephi.html">30_importing_from_gephi</a>.
An example exists showing how to get a JSON file into Vis: <a href="../examples/network/30_importing_from_gephi.html">30_importing_from_gephi</a>.
</p>
<p>
@ -1037,7 +1037,7 @@ var options = {
<td>String</td>
<td>"continuous"</td>
<td>This option only affects NONdynamic smooth curves. The supported types are: <code>continuous, discrete, diagonalCross, straightCross, horizontal, vertical</code>. The effects of these types
are shown in examples <a href="../examples/Network/26_staticSmoothCurves.html">26</a> and <a href="../examples/Network/27_world_cup_network.html">27</a></td>
are shown in examples <a href="../examples/network/26_staticSmoothCurves.html">26</a> and <a href="../examples/network/27_world_cup_network.html">27</a></td>
</tr>
<tr>
<td>smoothCurves.roundness</td>
@ -1538,7 +1538,7 @@ var nodes = [
If no options for the physics system are supplied, the Barnes-Hut method will be used with the default parameters. If you want to customize the physics system easily, you can use the configurePhysics option. <br/>
When using the hierarchical display option, hierarchicalRepulsion is automatically used as the physics solver. Similarly, if you use the hierarchicalRepulsion physics option, hierarchical display is automatically turned on with default settings.
<p class="important_note">Note: if the behaviour of your network is not the way you want it, use configurePhysics as described <u><a href="#PhysicsConfiguration">below</a></u> or by <u><a href="../examples/Network/25_physics_configuration.html">example 25</a></u>.</p>
<p class="important_note">Note: if the behaviour of your network is not the way you want it, use configurePhysics as described <u><a href="#PhysicsConfiguration">below</a></u> or by <u><a href="../examples/network/25_physics_configuration.html">example 25</a></u>.</p>
</p>
<pre class="prettyprint">
// These variables must be defined in an options object named physics.
@ -1716,7 +1716,7 @@ var options = {
<p>
By using the data manipulation feature of the network you can dynamically create nodes, connect nodes with edges, edit nodes or delete nodes and edges.
The toolbar is fully HTML and CSS so the user can style this to their preference. To control the behaviour of the data manipulation, users can insert custom functions
into the data manipulation process. For example, an injected function can show an detailed pop-up when a user wants to add a node. In <a href="../examples/Network/21_data_manipulation.html">example 21</a>,
into the data manipulation process. For example, an injected function can show an detailed pop-up when a user wants to add a node. In <a href="../examples/network/21_data_manipulation.html">example 21</a>,
two functions have been injected into the add and edit functionality. This is described in more detail in the next subsection. To correctly display the manipulation icons, the <b>vis.css</b> file must be included.
The user is free to alter or overload the CSS classes but without them the navigation icons are not visible.
</p>
@ -1759,7 +1759,7 @@ var options: {
<h4 id="Data_manipulation_custom">Data manipulation: custom functionality</h4>
<p>
Users can insert custom functions into the add node, edit node, connect nodes, and delete selected operations. This is done by supplying them in the options.
If the callback is NOT called, nothing happens. <a href="../examples/Network/21_data_manipulation.html">Example 21</a> has two working examples
If the callback is NOT called, nothing happens. <a href="../examples/network/21_data_manipulation.html">Example 21</a> has two working examples
for the add and edit functions. The data the user is supplied with in these functions has been described in the code below.
For the add data, you can add any and all options that are accepted for node creation as described above. The same goes for edit, however only the fields described
in the code below contain information on the selected node. The callback for connect accepts any options that are used for edge creation. Only the callback for delete selected
@ -2092,7 +2092,7 @@ var options: {
<p>
The network can be used to display nodes in a hierarchical way. This can be determined automatically, based on the amount of edges connected to each node, or defined by the user.
If the user wants to manually determine the hierarchy, each node has to be supplied with a level (from 0 being heighest to n). The automatic method
is shown in <a href="../examples/Network/23_hierarchical_layout.html">example 23</a> and the user-defined method is shown in <a href="../examples/network/24_hierarchical_layout_userdefined.html">example 24</a>.
is shown in <a href="../examples/network/23_hierarchical_layout.html">example 23</a> and the user-defined method is shown in <a href="../examples/network/24_hierarchical_layout_userdefined.html">example 24</a>.
This layout method does not support smooth curves or clustering. It automatically turns these features off.
</p>
@ -2108,7 +2108,8 @@ var options: {
enabled:false,
levelSeparation: 150,
nodeSpacing: 100,
direction: "UD"
direction: "UD",
layout: "hubsize"
}
}
// partial configuration automatically sets enabled to true
@ -2155,6 +2156,13 @@ var options: {
<td>This defines the direction the network is drawn in. The supported directions are: Up-Down (UD), Down-Up (DU), Left-Right (LR) and Right-Left (RL).
These need to be supplied by the acronyms in parentheses.</td>
</tr>
<tr>
<td>layout</td>
<td>String</td>
<td>hubsize</td>
<td>This defines the way the nodes are distributed. Available options are <code>hubsize</code> and <code>direction</code>. The default value is hubsize, meaning the node with the most edges connected to it (largest hub) is on top.
Alternatively, direction arranges the nodes based on the direction of the edges. See <a href="../examples/network/32_hierarchicaLayoutMethods.html">example 32</a> for more information.</td>
</tr>
</table>
<h3 id="Localization">Localization</h3>

+ 11
- 11
examples/network/24_hierarchical_layout_userdefined.html View File

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Network | Random nodes</title>
<title>Network | Hierarchical Layout, userDefined</title>
<style type="text/css">
body {
@ -28,17 +28,17 @@
edges = [];
var connectionCount = [];
// randomly create some nodes and edges
for (var i = 0; i < 15; i++) {
nodes.push({
id: i,
label: String(i)
// randomly create some nodes and edges
for (var i = 0; i < 15; i++) {
nodes.push({
id: i,
label: String(i)
});
}
edges.push({
from: 0,
to: 1
});
}
edges.push({
from: 0,
to: 1
});
edges.push({
from: 0,
to: 6

+ 140
- 0
examples/network/32_hierarchicaLayoutMethods.html View File

@ -0,0 +1,140 @@
<!doctype html>
<html>
<head>
<title>Network | Hierarchical layout difference</title>
<style type="text/css">
body {
font: 10pt sans;
}
#mynetwork {
width: 800px;
height: 500px;
border: 1px solid lightgray;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var network = null;
var layoutMethod = "hubsize";
function draw() {
var nodes = [];
var edges = [];
// randomly create some nodes and edges
for (var i = 0; i < 15; i++) {
nodes.push({
id: i,
label: String(i)
});
}
edges.push({
from: 0,
to: 1
});
edges.push({
from: 0,
to: 6
});
edges.push({
from: 0,
to: 13
});edges.push({
from: 0,
to: 11
});
edges.push({
from: 1,
to: 2
});
edges.push({
from: 2,
to: 3
});
edges.push({
from: 2,
to: 4
});
edges.push({
from: 3,
to: 5
});
edges.push({
from: 1,
to: 10
});
edges.push({
from: 1,
to: 7
});
edges.push({
from: 2,
to: 8
});
edges.push({
from: 2,
to: 9
});
edges.push({
from: 3,
to: 14
});
edges.push({
from: 1,
to: 12
});
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
hierarchicalLayout: {
layout: layoutMethod
},
edges: {style:"arrow"},
smoothCurves:false
};
network = new vis.Network(container, data, options);
// add event listeners
network.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
}
</script>
</head>
<body onload="draw();">
<h2>Hierarchical Layout - User-defined</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows a the effect of the different hierarchical layout methods. Hubsize is based on the amount of edges connected to a node.
The node with the most connections (the largest hub) is drawn at the top of the tree. The direction method is based on the direction of the edges.
Try switching between the methods with the dropdown box below.
</div>
Layout method:
<select id="layout">
<option value="hubsize">hubsize</option>
<option value="direction">direction</option>
</select><br/>
<br />
<div id="mynetwork"></div>
<p id="selection"></p>
<script language="JavaScript">
var dropdown = document.getElementById("layout");
dropdown.onchange = function() {
layoutMethod = dropdown.value;
draw();
}
</script>
</body>
</html>

+ 0
- 128
examples/network/ex.html View File

@ -1,128 +0,0 @@
<!doctype html>
<html>
<head>
<title>Network | Basic usage</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<style type="text/css">
#mynetwork {
width: 400px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="tabs-2"></div>
<script type="text/javascript">
function drawNetwork() {
var nodes = null;
var edges = null;
var network = null;
var LENGTH_MAIN = 350,
WIDTH_SCALE = 2,
GRAY = 'gray',
BLACK = '#2B1B17';
function draw() {
nodes = [];
edges = [];
var parentDeviceName = "Windows Host";
var parentName = "john";
var parentId = 1;
if (parentDeviceName == "Windows Host") {
group = 'windowsHost';
} else if (parentDeviceName == "Sun Host") {
group = 'sunHost';
} else if (parentDeviceName == "Net-SNMP Linux") {
group = 'linuxHost';
} else if (parentDeviceName == "Cisco 2901 K9") {
group = 'router';
} else {
group = 'switch';
}
nodes.push({id: parentId, label: parentName + "\n" + parentDeviceName, group: group, value: 10});
nodes.push({id: 2, label: "bob\nbobby", group: group, value: 10});
edges.push({from: parentId, to: 2, length: LENGTH_MAIN, width: WIDTH_SCALE * 2, label: ''});
var container = document.getElementById('tabs-2');
var data = {
nodes: nodes,
edges: edges
};
var options = {
stabilize: false, // stabilize positions before displaying
nodes: {
radiusMin: 16,
radiusMax: 32,
fontColor: BLACK
},
edges: {
color: GRAY
},
groups: {
'switch': {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
'router': {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
'firewall': {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
desktop: {
shape: 'dot',
color: "#2B7CE9" // blue
},
mobile: {
shape: 'dot',
color: "#5A1E5C" // purple
},
sunHost: {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
windowsHost: {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
linuxHost: {
shape: 'image',
image: '/vis.js/examples/network/img/refresh-cl/Hardware-Laptop-1-icon.png'
},
server: {
shape: 'square',
color: "#C5000B" // red
},
internet: {
shape: 'square',
color: "#109618" // green
}
},
physics: {
barnesHut: {
enabled: true
}
}
};
network = new vis.Network(container, data, options);
}
draw();
}
drawNetwork();
</script>
</body>
</html>

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

@ -176,7 +176,8 @@ function Network (container, data, options) {
enabled:false,
levelSeparation: 150,
nodeSpacing: 100,
direction: "UD" // UD, DU, LR, RL
direction: "UD", // UD, DU, LR, RL
layout: "hubsize" // hubsize, directed
},
freezeForStabilization: false,
smoothCurves: {

+ 1
- 0
lib/network/Node.js View File

@ -50,6 +50,7 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.radiusFixed = false;
this.level = -1;
this.preassignedLevel = false;
this.hierarchyEnumerated = false;
this.imagelist = imagelist;

+ 91
- 2
lib/network/mixins/HierarchicalLayoutMixin.js View File

@ -4,6 +4,7 @@ exports._resetLevels = function() {
var node = this.nodes[nodeId];
if (node.preassignedLevel == false) {
node.level = -1;
node.hierarchyEnumerated = false;
}
}
}
@ -69,7 +70,13 @@ exports._setupHierarchicalLayout = function() {
// define levels if undefined by the users. Based on hubsize
if (undefinedLevel == true) {
this._determineLevels(hubsize);
if (this.constants.hierarchicalLayout.layout == "hubsize") {
this._determineLevels(hubsize);
}
else {
this._determineLevelsDirected();
}
}
// check the distribution of the nodes per level.
var distribution = this._getDistribution();
@ -211,6 +218,52 @@ exports._determineLevels = function(hubsize) {
}
};
/**
* this function allocates nodes in levels based on the recursive branching from the largest hubs.
*
* @param hubsize
* @private
*/
exports._determineLevelsDirected = function() {
var nodeId, node;
// set first node to source
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
this.nodes[nodeId].level = 10000;
break;
}
}
// branch from hubs
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
if (node.level == 10000) {
this._setLevelDirected(10000,node.edges,node.id);
}
}
}
// branch from hubs
var minLevel = 10000;
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
minLevel = node.level < minLevel ? node.level : minLevel;
}
}
// branch from hubs
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
node.level -= minLevel;
}
}
};
/**
* Since hierarchical layout does not support:
@ -299,7 +352,7 @@ exports._setLevel = function(level, edges, parentId) {
}
if (childNode.level == -1 || childNode.level > level) {
childNode.level = level;
if (edges.length > 1) {
if (childNode.edges.length > 1) {
this._setLevel(level+1, childNode.edges, childNode.id);
}
}
@ -307,6 +360,42 @@ exports._setLevel = function(level, edges, parentId) {
};
/**
* this function is called recursively to enumerate the barnches of the largest hubs and give each node a level.
*
* @param level
* @param edges
* @param parentId
* @private
*/
exports._setLevelDirected = function(level, edges, parentId) {
this.nodes[parentId].hierarchyEnumerated = true;
for (var i = 0; i < edges.length; i++) {
var childNode = null;
var direction = 1;
if (edges[i].toId == parentId) {
childNode = edges[i].from;
direction = -1;
}
else {
childNode = edges[i].to;
}
if (childNode.level == -1) {
childNode.level = level + direction;
}
}
for (var i = 0; i < edges.length; i++) {
var childNode = null;
if (edges[i].toId == parentId) {childNode = edges[i].from;}
else {childNode = edges[i].to;}
if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) {
this._setLevelDirected(childNode.level, childNode.edges, childNode.id);
}
}
};
/**
* Unfix nodes
*

Loading…
Cancel
Save