Settings for manipulating the Dataset. See section <ahref="#Data_manipulation">Data manipulation</a> for an overview of the available options.
</td>
</tr>
<tr>
<tr>
<td><ahref="#Clustering">clustering</a></td>
<td><ahref="#Clustering">clustering</a></td>
<td>Object</td>
<td>Object</td>
@ -710,6 +724,13 @@ var options = {
</td>
</td>
</tr>
</tr>
<tr>
<td>smoothCurves</td>
<td>Boolean</td>
<td>true</td>
<td>If true, edges are drawn as smooth curves. This is more computationally intensive since the edge now is a quadratic Bezier curve with control points on both nodes and an invisible node in the center of the edge. This support node is also handed by the physics simulation.</td>
</tr>
<tr>
<tr>
<td>selectable</td>
<td>selectable</td>
<td>Boolean</td>
<td>Boolean</td>
@ -964,12 +985,6 @@ var options = {
Only applicable when the line style is <code>dash-line</code>.</td>
Only applicable when the line style is <code>dash-line</code>.</td>
</tr>
</tr>
<tr>
<td>length</td>
<td>Number</td>
<td>100</td>
<td>The default length of a edge.</td>
</tr>
<tr>
<tr>
<td>style</td>
<td>style</td>
<td>String</td>
<td>String</td>
@ -1122,6 +1137,235 @@ var nodes = [
</table>
</table>
<h3id="Physics">Physics</h3>
<p>
The physics system has been overhauled to increase performance. The original simulation method was based on particel physics with a repulsion field (potential) around each node,
and the edges were modelled as springs. The new system employed the <ahref="http://en.wikipedia.org/wiki/Barnes%E2%80%93Hut_simulation">Barnes-Hut</a> gravitational simulation model. The edges are still modelled as springs.
To unify the physics system, the damping, repulsion distance and edge length have been combined in an physics option. To retain good behaviour, both the old repulsion model and the Barnes-Hut model have their own parameters.
If no options for the physics system are supplied, the Barnes-Hut method will be used with the default parameters.
</p>
<preclass="prettyprint">
// These variables must be defined in an options object named physics.
// If a variable is not supplied, the default value is used.
var options = {
physics: {
barnesHut: {
enabled: true,
gravitationalConstant: -2000,
centralGravity: 0.1,
springLength: 100,
springConstant: 0.05,
damping: 0.09
},
repulsion: {
centralGravity: 0.1,
springLength: 50,
springConstant: 0.05,
nodeDistance: 100,
damping: 0.09
},
}
</pre>
<h5>barnesHut:</h5>
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>enabled</td>
<td>Boolean</td>
<td>true</td>
<td>This switches the Barnes-Hut simulation on or off. If it is turned off, the old repulsion model is used. Barnes-Hut is generally faster and yields better results.</td>
</tr>
<tr>
<td>gravitationalConstant</td>
<td>Number</td>
<td>-2000</td>
<td>This is the gravitational constand used to calculate the gravity forces. More information is available <ahref="http://en.wikipedia.org/wiki/Newton's_law_of_universal_gravitation"target="_blank">here</a>.</td>
</tr>
<tr>
<td>centralGravity</td>
<td>Number</td>
<td>0.1</td>
<td>The central gravity is a force that pulls all nodes to the center. This ensures independent groups do not float apart.</td>
</tr>
<tr>
<td>springLength</td>
<td>Number</td>
<td>100</td>
<td>In the previous versions this was a property of the edges, called length. This is the length of the springs when they are at rest. During the simulation they will be streched by the gravitational fields.
To greatly reduce the edge length, the gravitationalConstant has to be reduced as well.</td>
</tr>
<tr>
<td>springConstant</td>
<td>Number</td>
<td>0.05</td>
<td>This is the spring constant used to calculate the spring forces based on Hooke′s Law. More information is available <ahref="http://en.wikipedia.org/wiki/Hooke's_law"target="_blank">here</a>.</td>
</tr>
<tr>
<td>damping</td>
<td>Number</td>
<td>0.09</td>
<td>This is the damping constant. It is used to dissipate energy from the system to have it settle in an equilibrium. More information is available <ahref="http://en.wikipedia.org/wiki/Damping"target="_blank">here</a>.</td>
</tr>
</table>
<h5>repulsion:</h5>
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>centralGravity</td>
<td>Number</td>
<td>0.1</td>
<td>The central gravity is a force that pulls all nodes to the center. This ensures independent groups do not float apart.</td>
</tr>
<tr>
<td>springLength</td>
<td>Number</td>
<td>50</td>
<td>In the previous versions this was a property of the edges, called length. This is the length of the springs when they are at rest. During the simulation they will be streched by the gravitational fields.
To greatly reduce the edge length, the gravitationalConstant has to be reduced as well.</td>
</tr>
<tr>
<td>nodeDistance</td>
<td>Number</td>
<td>100</td>
<td>This parameter is used to define the distance of influence of the repulsion field of the nodes. Below half this distance, the repulsion is maximal and beyond twice this distance the repulsion is zero.</td>
</tr>
<tr>
<td>springConstant</td>
<td>Number</td>
<td>0.05</td>
<td>This is the spring constant used to calculate the spring forces based on Hooke′s Law. More information is available <ahref="http://en.wikipedia.org/wiki/Hooke's_law"target="_blank">here</a>.</td>
</tr>
<tr>
<td>damping</td>
<td>Number</td>
<td>0.09</td>
<td>This is the damping constant. It is used to dissipate energy from the system to have it settle in an equilibrium. More information is available <ahref="http://en.wikipedia.org/wiki/Damping"target="_blank">here</a>.</td>
</tr>
</table>
<h3id="Data_manipulation">Data manipulation</h3>
<p>
By using the data manipulation feature of the graph 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 <ahref="../examples/graph/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.
</p>
<preclass="prettyprint">
// These variables must be defined in an options object named dataManipulation.
// If a variable is not supplied, the default value is used.
var options = {
dataManipulation: {
enabled: false,
initiallyVisible: false
}
}
// OR to just load the module with default values:
var options: {
dataManipulation: true
}
</pre>
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>enabled</td>
<td>Boolean</td>
<td>false</td>
<td>Enabling or disabling of the data manipulation toolbar. If it is initially hidden, an edit button appears in the top left corner.</td>
</tr>
<tr>
<td>initiallyVisible</td>
<td>Boolean</td>
<td>false</td>
<td>Initially hide or show the data manipulation toolbar.</td>
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. <ahref="../examples/graph/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
requires the same data structure that is supplied to the user.
</p>
<preclass="prettyprint">
// If a variable is not supplied, the default value is used.
var options: {
dataManipulation: true,
onAdd: function(data,callback) {
// fixed must be false because we define a set x and y position.
// If fixed is not false, the node cannot move.
/** data = {id: random unique id,
* label: new,
* x: x position of click (canvas space),
* y: y position of click (canvas space),
* fixed: false
* };
*/
var newData = {..}; // alter the data as you want.
// all fields normally accepted by a node can be used.
callback(newData); // call the callback to add a node.
},
onEdit: function(data,callback) {
/** data = {id:...,
* label: ...,
* group: ...,
* shape: ...,
* color: {
* background:...,
* border:...,
* highlight: {
* background:...,
* border:...
* }
* }
* };
*/
var newData = {..}; // alter the data as you want.
// all fields normally accepted by a node can be used.
callback(newData); // call the callback with the new data to edit the node.
}
onConnect: function(data,callback) {
// data = {from: nodeId1, to: nodeId2};
var newData = {..}; // check or alter data as you see fit.
callback(newData); // call the callback to connect the nodes.
},
onDelete: function(data,callback) {
// data = {nodes: [selectedNodeIds], edges: [selectedEdgeIds]};
var newData = {..}; // alter the data as you want.
// the same data structure is required.
callback(newData); // call the callback to delete the objects.
}
};
</pre>
<p>
Because the interface elements are CSS and HTML, the user will have to correct for size changes of the canvas. To facilitate this, a new event has been added called frameResize.
A function can be bound to this event. This function is supplied with the new widht and height of the canvas. The CSS can then be updated accordingly.
The graph now supports dynamic clustering of nodes. This allows a user to view a very large dataset (> 50.000 nodes) without
The graph now supports dynamic clustering of nodes. This allows a user to view a very large dataset (> 50.000 nodes) without
@ -1150,16 +1394,19 @@ var options = {
reduceToNodes:300,
reduceToNodes:300,
chainThreshold: 0.4,
chainThreshold: 0.4,
clusterEdgeThreshold: 20,
clusterEdgeThreshold: 20,
sectorThreshold: 50,
sectorThreshold: 100,
screenSizeThreshold: 0.2,
screenSizeThreshold: 0.2,
fontSizeMultiplier: 4.0,
fontSizeMultiplier: 4.0,
forceAmplification: 0.6,
distanceAmplification: 0.2,
edgeGrowth: 11,
nodeScaling: {width: 10,
height: 10,
radius: 10},
activeAreaBoxSize: 100
maxFontSize: 1000,
forceAmplification: 0.1,
distanceAmplification: 0.1,
edgeGrowth: 20,
nodeScaling: {width: 1,
height: 1,
radius: 1},
maxNodeSizeIncrements: 600,
activeAreaBoxSize: 100,
clusterLevelDifference: 2
}
}
}
}
// OR to just load the module with default values:
// OR to just load the module with default values:
@ -1233,6 +1480,12 @@ var options: {
<td>4.0</td>
<td>4.0</td>
<td>This parameter denotes the increase in fontSize of the cluster when a single node is added to it.</td>
<td>This parameter denotes the increase in fontSize of the cluster when a single node is added to it.</td>
</tr>
</tr>
<tr>
<td>maxFontSize</td>
<td>Number</td>
<td>1000</td>
<td>This parameter denotes the largest allowed font size. If the font becomes too large, some browsers experience problems displaying this.</td>
</tr>
<tr>
<tr>
<td>forceAmplification</td>
<td>forceAmplification</td>
<td>Number</td>
<td>Number</td>
@ -1251,7 +1504,7 @@ var options: {
<tr>
<tr>
<td>edgeGrowth</td>
<td>edgeGrowth</td>
<td>Number</td>
<td>Number</td>
<td>11</td>
<td>20</td>
<td>This factor determines the elongation of edges connected to a cluster.</td>
<td>This factor determines the elongation of edges connected to a cluster.</td>
</tr>
</tr>
<tr>
<tr>
@ -1272,13 +1525,29 @@ var options: {
<td>10</td>
<td>10</td>
<td>This factor determines how much the radius of a cluster increases in pixels per added node.</td>
<td>This factor determines how much the radius of a cluster increases in pixels per added node.</td>
</tr>
</tr>
<tr>
<td>activeAreaBoxSize</td>
<tr>
<td>maxNodeSizeIncrements</td>
<td>Number</td>
<td>600</td>
<td>This limits the size clusters can grow to. The default value, 600, implies that if a cluster contains more than 600 nodes, it will no longer grow.</td>
</tr>
<tr>
<td>activeAreaBoxSize</td>
<td>Number</td>
<td>100</td>
<td>Imagine a square with an edge length of <code>activeAreaBoxSize</code> pixels around your cursor.
If a cluster is in this box as you zoom in, the cluster can be opened in a seperate sector.
This is regardless of the zoom level.</td>
</tr>
<tr>
<td>clusterLevelDifference</td>
<td>Number</td>
<td>Number</td>
<td>100</td>
<td>Imagine a square with an edge length of <code>activeAreaBoxSize</code> pixels around your cursor.
If a cluster is in this box as you zoom in, the cluster can be opened in a seperate sector.
This is regardless of the zoom level.</td>
<td>2</td>
<td>At every clustering session, Graph will check if the difference between cluster levels is
acceptable. When a cluster is formed when zooming out, that is one cluster level.
If you zoom out further and it encompasses more nodes, that is another level. For example:
If the highest level of your graph at any given time is 3, nodes that have not clustered or
have clustered only once will join their neighbour with the lowest cluster level.</td>
<h2>Navigation controls and keyboad navigation</h2>
<h2>Navigation controls and keyboad navigation</h2>
<divstyle="width: 700px; font-size:14px;">
<divstyle="width: 700px; font-size:14px;">
This example is the same as example 2, except for the navigation controls that has been activated. The navigation controls are described below. <br/><br/>
This example is the same as example 2, except for the navigation controls that have been activated. The navigation controls are described below. <br/><br/>
var saveButton = document.getElementById('saveButton');
var cancelButton = document.getElementById('cancelButton');
saveButton.onclick = null;
cancelButton.onclick = null;
var div = document.getElementById('graph-popUp');
div.style.display = 'none';
}
function saveData(data,callback) {
var idInput = document.getElementById('node-id');
var labelInput = document.getElementById('node-label');
var div = document.getElementById('graph-popUp');
data.id = idInput.value;
data.label = labelInput.value;
clearPopUp();
callback(data);
}
}
</script>
</head>
</head>
<bodyonload="draw();">
<bodyonload="draw();">
<h2>Navigation controls and keyboad navigation</h2>
<h2>Editing the dataset</h2>
<divstyle="width: 700px; font-size:14px;">
<divstyle="width: 700px; font-size:14px;">
This example is the same as example 2, except for the navigation controls that has been activated. The navigation controls are described below. <br/><br/>
Apart from clicking the icons, you can also navigate using the keyboard. The buttons are in table above.
Zoom Extends changes the zoom and position of the camera to encompass all visible nodes.
In this example we have enabled the data manipulation setting. If the dataManipulation option is set to true, the edit button will appear.
If you prefer to have the toolbar visible initially, you can set the initiallyVisible option to true. The exact method is described in the docs.
<br/><br/>
The data manipulation allows the user to add nodes, connect them, edit them and delete any selected items. In this example we have created trigger functions
for the add and edit operations. By settings these trigger functions the user can direct the way the data is manipulated. In this example we have created a simple
pop-up that allows us to edit some of the properties.
@ -61,8 +66,6 @@ function Graph (container, data, options) {
fontColor:'#343434',
fontColor:'#343434',
fontSize:14,// px
fontSize:14,// px
fontFace:'arial',
fontFace:'arial',
//distance: 100, //px
length:100,// px
dash:{
dash:{
length:10,
length:10,
gap:5,
gap:5,
@ -72,18 +75,21 @@ function Graph (container, data, options) {
physics:{
physics:{
barnesHut:{
barnesHut:{
enabled:true,
enabled:true,
theta:1/0.5,// inverted to save time during calculation
gravitationalConstant:-3000,
centralGravity:0.9,
springLength:40,
springConstant:0.04
theta:1/0.6,// inverted to save time during calculation
gravitationalConstant:-2000,
centralGravity:0.1,
springLength:100,
springConstant:0.05,
damping:0.09
},
},
repulsion:{
repulsion:{
centralGravity:0.01,
springLength:80,
centralGravity:0.1,
springLength:50,
springConstant:0.05,
springConstant:0.05,
nodeDistance:100
nodeDistance:100,
damping:0.09
},
},
damping:null,
centralGravity:null,
centralGravity:null,
springLength:null,
springLength:null,
springConstant:null
springConstant:null
@ -98,8 +104,8 @@ function Graph (container, data, options) {
sectorThreshold:100,// (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
sectorThreshold:100,// (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
screenSizeThreshold:0.2,// (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
screenSizeThreshold:0.2,// (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
fontSizeMultiplier:4.0,// (px PNiC) | how much the cluster font size grows per node in cluster (in px).
fontSizeMultiplier:4.0,// (px PNiC) | how much the cluster font size grows per node in cluster (in px).
forceAmplification:0.1,// (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
maxFontSize:1000,
maxFontSize:1000,
forceAmplification:0.1,// (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
distanceAmplification:0.1,// (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
distanceAmplification:0.1,// (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
edgeGrowth:20,// (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
edgeGrowth:20,// (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
nodeScaling:{width:1,// (px PNiC) | growth of the width per node in cluster.
nodeScaling:{width:1,// (px PNiC) | growth of the width per node in cluster.
@ -117,104 +123,101 @@ function Graph (container, data, options) {
enabled:false,
enabled:false,
speed:{x:10,y:10,zoom:0.02}
speed:{x:10,y:10,zoom:0.02}
},
},
dataManipulationToolbar:{
dataManipulation:{
enabled:false,
enabled:false,
initiallyVisible:false
initiallyVisible:false
},
},
smoothCurves:true,
smoothCurves:true,
maxVelocity:25,
minVelocity:0.1,// px/s
maxVelocity:10,
minVelocity:0.1,// px/s
maxIterations:1000// maximum number of iteration to stabilize
maxIterations:1000// maximum number of iteration to stabilize
zoomLevel=38.8467/(numberOfNodes-14.50184)+0.0116;// this is obtained from fitting a dataset from 5 points with scale levels that looked good.
zoomLevel=77.5271985/(numberOfNodes+187.266146)+4.76710517e-05;// this is obtained from fitting a dataset from 5 points with scale levels that looked good.
}
}
else{
else{
zoomLevel=42.54117319/(numberOfNodes+39.31966387)+0.1944405;// this is obtained from fitting a dataset from 5 points with scale levels that looked good.
zoomLevel=30.5062972/(numberOfNodes+19.93597763)+0.08413486;// this is obtained from fitting a dataset from 5 points with scale levels that looked good.
}
}
}
}
else{
else{
@ -418,11 +421,22 @@ Graph.prototype.setOptions = function (options) {
this.timer=window.setTimeout(this._animationStep.bind(this),this.renderTimestep);// wait this.renderTimeStep milliseconds and perform the animation step function
"<span class='manipulationUI none' id='manipulate-back'><span id='manipulatorLabel' class='manipulationLabel'>Click on a node and drag the edge to another node.</span></span>";