Browse Source

refactoring, constants names, fixed trello notes

css_transitions
Alex de Mulder 10 years ago
parent
commit
5e93c44279
8 changed files with 141 additions and 162 deletions
  1. +56
    -63
      dist/vis.js
  2. +30
    -36
      docs/graph.html
  3. +1
    -2
      examples/graph/20_UI_example.html
  4. +3
    -3
      src/graph/ClusterMixin.js
  5. +35
    -29
      src/graph/Graph.js
  6. +3
    -4
      src/graph/Node.js
  7. +7
    -5
      src/graph/SelectionMixin.js
  8. +6
    -20
      src/graph/UIMixin.js

+ 56
- 63
dist/vis.js View File

@ -4,8 +4,8 @@
* *
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 0.4.0-SNAPSHOT
* @date 2014-01-29
* @version @@version
* @date @@date
* *
* @license * @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -8830,10 +8830,9 @@ function Node(properties, imagelist, grouplist, constants) {
this.resetCluster(); this.resetCluster();
this.dynamicEdgesLength = 0; this.dynamicEdgesLength = 0;
this.clusterSession = 0; this.clusterSession = 0;
this.clusterSizeWidthFactor = constants.clustering.clusterSizeWidthFactor;
this.clusterSizeHeightFactor = constants.clustering.clusterSizeHeightFactor;
this.clusterSizeRadiusFactor = constants.clustering.clusterSizeRadiusFactor;
this.clusterSizeWidthFactor = constants.clustering.nodeScaling.width;
this.clusterSizeHeightFactor = constants.clustering.nodeScaling.height;
this.clusterSizeRadiusFactor = constants.clustering.nodeScaling.radius;
// mass, force, velocity // mass, force, velocity
this.mass = 1; // kg (mass is adjusted for the number of connected edges) this.mass = 1; // kg (mass is adjusted for the number of connected edges)
@ -11157,7 +11156,7 @@ var ClusterMixin = {
* */ * */
startWithClustering : function() { startWithClustering : function() {
// cluster if the data set is big // cluster if the data set is big
this.clusterToFit(this.constants.clustering.initialMaxNumberOfNodes, true);
this.clusterToFit(this.constants.clustering.initialMaxNodes, true);
// updates the lables after clustering // updates the lables after clustering
this.updateLabels(); this.updateLabels();
@ -11171,7 +11170,7 @@ var ClusterMixin = {
}, },
/** /**
* This function clusters until the initialMaxNumberOfNodes has been reached
* This function clusters until the initialMaxNodes has been reached
* *
* @param {Number} maxNumberOfNodes * @param {Number} maxNumberOfNodes
* @param {Boolean} reposition * @param {Boolean} reposition
@ -11212,7 +11211,7 @@ var ClusterMixin = {
!(this._sector() == "default" && this.nodeIndices.length == 1)) { !(this._sector() == "default" && this.nodeIndices.length == 1)) {
this._addSector(node); this._addSector(node);
var level = 0; var level = 0;
while ((this.nodeIndices.length < this.constants.clustering.initialMaxNumberOfNodes) && (level < 10)) {
while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) {
this.decreaseClusterLevel(); this.decreaseClusterLevel();
level += 1; level += 1;
} }
@ -12253,7 +12252,7 @@ var SelectionMixin = {
_getUINodeAt : function (pointer) { _getUINodeAt : function (pointer) {
var screenPositionObject = this._pointerToScreenPositionObject(pointer); var screenPositionObject = this._pointerToScreenPositionObject(pointer);
var overlappingNodes = this._getAllUINodesOverlappingWith(screenPositionObject); var overlappingNodes = this._getAllUINodesOverlappingWith(screenPositionObject);
if (this.UIvisible && overlappingNodes.length > 0) {
if (overlappingNodes.length > 0) {
return this.sectors["navigationUI"]["nodes"][overlappingNodes[overlappingNodes.length - 1]]; return this.sectors["navigationUI"]["nodes"][overlappingNodes[overlappingNodes.length - 1]];
} }
else { else {
@ -12410,10 +12409,12 @@ var SelectionMixin = {
* @private * @private
*/ */
_handleTouch : function(pointer) { _handleTouch : function(pointer) {
var node = this._getUINodeAt(pointer);
if (node != null) {
if (this[node.triggerFunction] !== undefined) {
this[node.triggerFunction]();
if (this.constants.navigationUI.enabled == true) {
var node = this._getUINodeAt(pointer);
if (node != null) {
if (this[node.triggerFunction] !== undefined) {
this[node.triggerFunction]();
}
} }
} }
}, },
@ -12777,20 +12778,6 @@ var UIMixin = {
}, },
/**
* toggle the visibility of the navigationUI
*
* @private
*/
_toggleUI : function() {
if (this.UIvisible === undefined) {
this.UIvisible = false;
}
this.UIvisible = !this.UIvisible;
this._redraw();
},
/** /**
* un-highlight (for lack of a better term) all navigationUI elements * un-highlight (for lack of a better term) all navigationUI elements
* @private * @private
@ -12823,7 +12810,7 @@ var UIMixin = {
*/ */
_moveUp : function(event) { _moveUp : function(event) {
this._highlightUIElement("UI_up"); this._highlightUIElement("UI_up");
this.yIncrement = this.constants.keyboardNavigation.yMovementSpeed;
this.yIncrement = this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12835,7 +12822,7 @@ var UIMixin = {
*/ */
_moveDown : function(event) { _moveDown : function(event) {
this._highlightUIElement("UI_down"); this._highlightUIElement("UI_down");
this.yIncrement = -this.constants.keyboardNavigation.yMovementSpeed;
this.yIncrement = -this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12847,7 +12834,7 @@ var UIMixin = {
*/ */
_moveLeft : function(event) { _moveLeft : function(event) {
this._highlightUIElement("UI_left"); this._highlightUIElement("UI_left");
this.xIncrement = this.constants.keyboardNavigation.xMovementSpeed;
this.xIncrement = this.constants.keyboardNavigation.speed.x;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12859,7 +12846,7 @@ var UIMixin = {
*/ */
_moveRight : function(event) { _moveRight : function(event) {
this._highlightUIElement("UI_right"); this._highlightUIElement("UI_right");
this.xIncrement = -this.constants.keyboardNavigation.xMovementSpeed;
this.xIncrement = -this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12871,7 +12858,7 @@ var UIMixin = {
*/ */
_zoomIn : function(event) { _zoomIn : function(event) {
this._highlightUIElement("UI_plus"); this._highlightUIElement("UI_plus");
this.zoomIncrement = this.constants.keyboardNavigation.zoomMovementSpeed;
this.zoomIncrement = this.constants.keyboardNavigation.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12883,7 +12870,7 @@ var UIMixin = {
*/ */
_zoomOut : function() { _zoomOut : function() {
this._highlightUIElement("UI_min"); this._highlightUIElement("UI_min");
this.zoomIncrement = -this.constants.keyboardNavigation.zoomMovementSpeed;
this.zoomIncrement = -this.constants.keyboardNavigation.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -12998,9 +12985,9 @@ function Graph (container, data, options) {
}, },
clustering: { // Per Node in Cluster = PNiC clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering. enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNumberOfNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
absoluteMaxNumberOfNodes:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToMaxNumberOfNodes
reduceToMaxNumberOfNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than absoluteMaxNumberOfNodes. If it is, cluster until reduced to this
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
sectorThreshold: 50, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. sectorThreshold: 50, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
@ -13009,23 +12996,19 @@ function Graph (container, data, options) {
forceAmplification: 0.6, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). forceAmplification: 0.6, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
distanceAmplification: 0.2, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). distanceAmplification: 0.2, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
edgeGrowth: 11, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. edgeGrowth: 11, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
clusterSizeWidthFactor: 10, // (px PNiC) | growth of the width per node in cluster.
clusterSizeHeightFactor: 10, // (px PNiC) | growth of the height per node in cluster.
clusterSizeRadiusFactor: 10, // (px PNiC) | growth of the radius per node in cluster.
nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster.
height: 10, // (px PNiC) | growth of the height per node in cluster.
radius: 10}, // (px PNiC) | growth of the radius per node in cluster.
activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open. activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open.
massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass
}, },
navigationUI: { navigationUI: {
enabled: false, enabled: false,
initiallyVisible: true,
enableToggling: true,
iconPath: this._getIconURL() iconPath: this._getIconURL()
}, },
keyboardNavigation: { keyboardNavigation: {
enabled: false, enabled: false,
xMovementSpeed: 10,
yMovementSpeed: 10,
zoomMovementSpeed: 0.02
speed: {x: 10, y: 10, zoom: 0.02}
}, },
minVelocity: 2, // px/s minVelocity: 2, // px/s
maxIterations: 1000 // maximum number of iteration to stabilize maxIterations: 1000 // maximum number of iteration to stabilize
@ -13039,7 +13022,6 @@ function Graph (container, data, options) {
}); });
// navigationUI variables // navigationUI variables
this.UIvisible = this.constants.navigationUI.initiallyVisible;
this.xIncrement = 0; this.xIncrement = 0;
this.yIncrement = 0; this.yIncrement = 0;
this.zoomIncrement = 0; this.zoomIncrement = 0;
@ -13047,24 +13029,18 @@ function Graph (container, data, options) {
// create a frame and canvas // create a frame and canvas
this._create(); this._create();
// load the sector system. (mandatory, fully integrated with Graph)
this._loadSectorSystem();
// apply options // apply options
this.setOptions(options); this.setOptions(options);
// load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it)
this._loadClusterSystem(); this._loadClusterSystem();
// load the sector system. (mandatory, fully integrated with Graph)
this._loadSectorSystem();
// load the selection system. (mandatory, required by Graph) // load the selection system. (mandatory, required by Graph)
this._loadSelectionSystem(); this._loadSelectionSystem();
// load the navigationUI system. (mandatory, few function calls even when navigationUI is disabled (in this.setSize)
this._loadUISystem();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
// other vars // other vars
var graph = this; var graph = this;
this.freezeSimulation = false;// freeze the simulation this.freezeSimulation = false;// freeze the simulation
@ -13218,7 +13194,7 @@ Graph.prototype.zoomToFit = function(initialZoom) {
if (initialZoom == true) { if (initialZoom == true) {
if (this.constants.clustering.enabled == true && if (this.constants.clustering.enabled == true &&
numberOfNodes >= this.constants.clustering.initialMaxNumberOfNodes) {
numberOfNodes >= this.constants.clustering.initialMaxNodes) {
var zoomLevel = 38.8467 / (numberOfNodes - 14.50184) + 0.0116; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. var zoomLevel = 38.8467 / (numberOfNodes - 14.50184) + 0.0116; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
} }
else { else {
@ -13322,28 +13298,40 @@ Graph.prototype.setOptions = function (options) {
if (options.selectable !== undefined) {this.selectable = options.selectable;} if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.clustering) { if (options.clustering) {
this.constants.clustering.enabled = true;
for (var prop in options.clustering) { for (var prop in options.clustering) {
if (options.clustering.hasOwnProperty(prop)) { if (options.clustering.hasOwnProperty(prop)) {
this.constants.clustering[prop] = options.clustering[prop]; this.constants.clustering[prop] = options.clustering[prop];
} }
} }
} }
else if (options.clustering !== undefined) {
this.constants.clustering.enabled = false;
}
if (options.navigationUI) { if (options.navigationUI) {
this.constants.navigationUI.enabled = true;
for (var prop in options.navigationUI) { for (var prop in options.navigationUI) {
if (options.navigationUI.hasOwnProperty(prop)) { if (options.navigationUI.hasOwnProperty(prop)) {
this.constants.navigationUI[prop] = options.navigationUI[prop]; this.constants.navigationUI[prop] = options.navigationUI[prop];
} }
} }
} }
else if (options.navigationUI !== undefined) {
this.constants.navigationUI.enabled = false;
}
if (options.keyboardNavigation) { if (options.keyboardNavigation) {
this.constants.keyboardNavigation.enabled = true;
for (var prop in options.keyboardNavigation) { for (var prop in options.keyboardNavigation) {
if (options.keyboardNavigation.hasOwnProperty(prop)) { if (options.keyboardNavigation.hasOwnProperty(prop)) {
this.constants.keyboardNavigation[prop] = options.keyboardNavigation[prop]; this.constants.keyboardNavigation[prop] = options.keyboardNavigation[prop];
} }
} }
} }
else if (options.keyboardNavigation !== undefined) {
this.constants.keyboardNavigation.enabled = false;
}
// TODO: work out these options and document them // TODO: work out these options and document them
@ -13409,6 +13397,14 @@ Graph.prototype.setOptions = function (options) {
this.setSize(this.width, this.height); this.setSize(this.width, this.height);
this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
this._setScale(1); this._setScale(1);
// load the navigationUI system.
this._loadUISystem();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
this._redraw();
}; };
/** /**
@ -13485,10 +13481,7 @@ Graph.prototype._createKeyBinds = function() {
var me = this; var me = this;
this.mousetrap = mousetrap; this.mousetrap = mousetrap;
if (this.constants.navigationUI.enabled == true &&
this.constants.navigationUI.enableToggling == true) {
this.mousetrap.bind("u",this._toggleUI.bind(me) , "keydown");
}
this.mousetrap.reset();
if (this.constants.keyboardNavigation.enabled == true) { if (this.constants.keyboardNavigation.enabled == true) {
this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown"); this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown");
@ -14365,7 +14358,7 @@ Graph.prototype._redraw = function() {
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); ctx.restore();
if (this.UIvisible == true) {
if (this.constants.navigationUI.enabled == true) {
this._doInUISector("_drawNodes",ctx,true); this._doInUISector("_drawNodes",ctx,true);
} }
}; };
@ -14557,8 +14550,8 @@ Graph.prototype._initializeForceCalculation = function() {
} }
else { else {
// if there are too many nodes on screen, we cluster without repositioning // if there are too many nodes on screen, we cluster without repositioning
if (this.nodeIndices.length > this.constants.clustering.absoluteMaxNumberOfNodes && this.constants.clustering.enabled == true) {
this.clusterToFit(this.constants.clustering.reduceToMaxNumberOfNodes, false);
if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) {
this.clusterToFit(this.constants.clustering.reduceToNodes, false);
} }
// we now start the force calculation // we now start the force calculation

+ 30
- 36
docs/graph.html View File

@ -1146,9 +1146,9 @@ var nodes = [
var options = { var options = {
clustering: { clustering: {
enabled: false, enabled: false,
initialMaxNumberOfNodes: 100,
absoluteMaxNumberOfNodes:500,
reduceToMaxNumberOfNodes:300,
initialMaxNodes: 100,
clusterThreshold:500,
reduceToNodes:300,
chainThreshold: 0.4, chainThreshold: 0.4,
clusterEdgeThreshold: 20, clusterEdgeThreshold: 20,
sectorThreshold: 50, sectorThreshold: 50,
@ -1157,12 +1157,16 @@ var options = {
forceAmplification: 0.6, forceAmplification: 0.6,
distanceAmplification: 0.2, distanceAmplification: 0.2,
edgeGrowth: 11, edgeGrowth: 11,
clusterSizeWidthFactor: 10,
clusterSizeHeightFactor: 10,
clusterSizeRadiusFactor: 10,
nodeScaling: {width: 10,
height: 10,
radius: 10},
activeAreaBoxSize: 100 activeAreaBoxSize: 100
} }
} }
// OR to just load the module with default values:
var options: {
clustering: true
}
</pre> </pre>
<table> <table>
@ -1180,20 +1184,20 @@ var options = {
<td>On/off switch for clustering. It is assumed clustering is enabled in the descriptions below.</td> <td>On/off switch for clustering. It is assumed clustering is enabled in the descriptions below.</td>
</tr> </tr>
<tr> <tr>
<td>initialMaxNumberOfNodes</td>
<td>initialMaxNodes</td>
<td>Number</td> <td>Number</td>
<td>100</td> <td>100</td>
<td>If the initial amount of nodes is larger than this value, clustering starts until the total number of nodes is less than this value.</td> <td>If the initial amount of nodes is larger than this value, clustering starts until the total number of nodes is less than this value.</td>
</tr> </tr>
<tr> <tr>
<td>absoluteMaxNumberOfNodes</td>
<td>clusterThreshold</td>
<td>Number</td> <td>Number</td>
<td>500</td> <td>500</td>
<td>While zooming in and out, clusters can open up. Once there are more than <code>absoluteMaxNumberOfNodes</code> nodes, <td>While zooming in and out, clusters can open up. Once there are more than <code>absoluteMaxNumberOfNodes</code> nodes,
clustering starts until <code>reduceToMaxNumberOfNodes</code> nodes are left. This is done to ensure performance is continuously fluid.</td> clustering starts until <code>reduceToMaxNumberOfNodes</code> nodes are left. This is done to ensure performance is continuously fluid.</td>
</tr> </tr>
<tr> <tr>
<td>reduceToMaxNumberOfNodes</td>
<td>reduceToNodes</td>
<td>Number</td> <td>Number</td>
<td>300</td> <td>300</td>
<td>While zooming in and out, clusters can open up. Once there are more than <code>absoluteMaxNumberOfNodes</code> nodes, <td>While zooming in and out, clusters can open up. Once there are more than <code>absoluteMaxNumberOfNodes</code> nodes,
@ -1258,19 +1262,19 @@ var options = {
<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>
<td>clusterSizeWidthFactor</td>
<td>nodeScaling.width</td>
<td>Number</td> <td>Number</td>
<td>10</td> <td>10</td>
<td>This factor determines how much the width of a cluster increases in pixels per added node.</td> <td>This factor determines how much the width of a cluster increases in pixels per added node.</td>
</tr> </tr>
<tr> <tr>
<td>clusterSizeHeightFactor</td>
<td>nodeScaling.height</td>
<td>Number</td> <td>Number</td>
<td>10</td> <td>10</td>
<td>This factor determines how much the height of a cluster increases in pixels per added node.</td> <td>This factor determines how much the height of a cluster increases in pixels per added node.</td>
</tr> </tr>
<tr> <tr>
<td>clusterSizeRadiusFactor</td>
<td>nodeScaling.radius</td>
<td>Number</td> <td>Number</td>
<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>
@ -1297,11 +1301,13 @@ var options = {
var options: { var options: {
navigationUI: { navigationUI: {
enabled: false, enabled: false,
initiallyVisible: true,
enableToggling: true,
iconPath: this._getIconURL() // automatic loading of the default icons iconPath: this._getIconURL() // automatic loading of the default icons
} }
} }
// OR to just load the module with default values:
var options: {
navigationUI: true
}
</pre> </pre>
<table> <table>
@ -1318,20 +1324,6 @@ var options: {
<td>false</td> <td>false</td>
<td>On/off switch for the navigation UI elements.</td> <td>On/off switch for the navigation UI elements.</td>
</tr> </tr>
<tr>
<td>initiallyVisible</td>
<td>Boolean</td>
<td>true</td>
<td>The UI elements can be toggled by pressing the "u" key. If <code>initiallyVisible</code> is false, the UI is hidden
until "u" is pressed on the keyboard.
</td>
</tr>
<tr>
<td>enableToggling</td>
<td>Boolean</td>
<td>true</td>
<td>Enable or disable the option to press "u" to toggle the UI elements. If the UI is initially hidden and the toggling is off, the UI cannot be used!</td>
</tr>
<tr> <tr>
<td>iconPath</td> <td>iconPath</td>
<td>string</td> <td>string</td>
@ -1353,11 +1345,13 @@ var options: {
var options: { var options: {
keyboardNavigation: { keyboardNavigation: {
enabled: false, enabled: false,
xMovementSpeed: 10,
yMovementSpeed: 10,
zoomMovementSpeed: 0.02
speed: {x: 10, y: 10, zoom: 0.02}
} }
} }
// OR to just load the module with default values:
var options: {
keyboardNavigation: true
}
</pre> </pre>
<table> <table>
@ -1375,21 +1369,21 @@ var options: {
<td>On/off switch for the keyboard navigation.</td> <td>On/off switch for the keyboard navigation.</td>
</tr> </tr>
<tr> <tr>
<td>xMovementSpeed</td>
<td>speed.x</td>
<td>Number</td> <td>Number</td>
<td>10</td> <td>10</td>
<td>This defines the speed of the camera movement in the x direction when using the keyboard navigation. <td>This defines the speed of the camera movement in the x direction when using the keyboard navigation.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>yMovementSpeed</td>
<td>Boolean</td>
<td>speed.y</td>
<td>Number</td>
<td>10</td> <td>10</td>
<td>This defines the speed of the camera movement in the y direction when using the keyboard navigation.</td> <td>This defines the speed of the camera movement in the y direction when using the keyboard navigation.</td>
</tr> </tr>
<tr> <tr>
<td>zoomMovementSpeed</td>
<td>string</td>
<td>speed.zoom</td>
<td>Number</td>
<td>0.02</td> <td>0.02</td>
<td>This defines the zoomspeed when using the keyboard navigation.</td> <td>This defines the zoomspeed when using the keyboard navigation.</td>
</tr> </tr>

+ 1
- 2
examples/graph/20_UI_example.html View File

@ -165,8 +165,7 @@
</table> </table>
<br /> <br />
Apart from clicking the icons, you can also navigate using the keyboard. The buttons are in table above. 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. The UI buttons can be toggled on or off
by pressing the U button on the keyboard.
Zoom Extends changes the zoom and position of the camera to encompass all visible nodes.
</div> </div>

+ 3
- 3
src/graph/ClusterMixin.js View File

@ -13,7 +13,7 @@ var ClusterMixin = {
* */ * */
startWithClustering : function() { startWithClustering : function() {
// cluster if the data set is big // cluster if the data set is big
this.clusterToFit(this.constants.clustering.initialMaxNumberOfNodes, true);
this.clusterToFit(this.constants.clustering.initialMaxNodes, true);
// updates the lables after clustering // updates the lables after clustering
this.updateLabels(); this.updateLabels();
@ -27,7 +27,7 @@ var ClusterMixin = {
}, },
/** /**
* This function clusters until the initialMaxNumberOfNodes has been reached
* This function clusters until the initialMaxNodes has been reached
* *
* @param {Number} maxNumberOfNodes * @param {Number} maxNumberOfNodes
* @param {Boolean} reposition * @param {Boolean} reposition
@ -68,7 +68,7 @@ var ClusterMixin = {
!(this._sector() == "default" && this.nodeIndices.length == 1)) { !(this._sector() == "default" && this.nodeIndices.length == 1)) {
this._addSector(node); this._addSector(node);
var level = 0; var level = 0;
while ((this.nodeIndices.length < this.constants.clustering.initialMaxNumberOfNodes) && (level < 10)) {
while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) {
this.decreaseClusterLevel(); this.decreaseClusterLevel();
level += 1; level += 1;
} }

+ 35
- 29
src/graph/Graph.js View File

@ -69,9 +69,9 @@ function Graph (container, data, options) {
}, },
clustering: { // Per Node in Cluster = PNiC clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering. enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNumberOfNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
absoluteMaxNumberOfNodes:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToMaxNumberOfNodes
reduceToMaxNumberOfNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than absoluteMaxNumberOfNodes. If it is, cluster until reduced to this
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
sectorThreshold: 50, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. sectorThreshold: 50, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
@ -80,23 +80,19 @@ function Graph (container, data, options) {
forceAmplification: 0.6, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). forceAmplification: 0.6, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
distanceAmplification: 0.2, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). distanceAmplification: 0.2, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
edgeGrowth: 11, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. edgeGrowth: 11, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
clusterSizeWidthFactor: 10, // (px PNiC) | growth of the width per node in cluster.
clusterSizeHeightFactor: 10, // (px PNiC) | growth of the height per node in cluster.
clusterSizeRadiusFactor: 10, // (px PNiC) | growth of the radius per node in cluster.
nodeScaling: {width: 10, // (px PNiC) | growth of the width per node in cluster.
height: 10, // (px PNiC) | growth of the height per node in cluster.
radius: 10}, // (px PNiC) | growth of the radius per node in cluster.
activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open. activeAreaBoxSize: 100, // (px) | box area around the curser where clusters are popped open.
massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass massTransferCoefficient: 1 // (multiplier) | parent.mass += massTransferCoefficient * child.mass
}, },
navigationUI: { navigationUI: {
enabled: false, enabled: false,
initiallyVisible: true,
enableToggling: true,
iconPath: this._getIconURL() iconPath: this._getIconURL()
}, },
keyboardNavigation: { keyboardNavigation: {
enabled: false, enabled: false,
xMovementSpeed: 10,
yMovementSpeed: 10,
zoomMovementSpeed: 0.02
speed: {x: 10, y: 10, zoom: 0.02}
}, },
minVelocity: 2, // px/s minVelocity: 2, // px/s
maxIterations: 1000 // maximum number of iteration to stabilize maxIterations: 1000 // maximum number of iteration to stabilize
@ -110,7 +106,6 @@ function Graph (container, data, options) {
}); });
// navigationUI variables // navigationUI variables
this.UIvisible = this.constants.navigationUI.initiallyVisible;
this.xIncrement = 0; this.xIncrement = 0;
this.yIncrement = 0; this.yIncrement = 0;
this.zoomIncrement = 0; this.zoomIncrement = 0;
@ -118,24 +113,18 @@ function Graph (container, data, options) {
// create a frame and canvas // create a frame and canvas
this._create(); this._create();
// load the sector system. (mandatory, fully integrated with Graph)
this._loadSectorSystem();
// apply options // apply options
this.setOptions(options); this.setOptions(options);
// load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it)
this._loadClusterSystem(); this._loadClusterSystem();
// load the sector system. (mandatory, fully integrated with Graph)
this._loadSectorSystem();
// load the selection system. (mandatory, required by Graph) // load the selection system. (mandatory, required by Graph)
this._loadSelectionSystem(); this._loadSelectionSystem();
// load the navigationUI system. (mandatory, few function calls even when navigationUI is disabled (in this.setSize)
this._loadUISystem();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
// other vars // other vars
var graph = this; var graph = this;
this.freezeSimulation = false;// freeze the simulation this.freezeSimulation = false;// freeze the simulation
@ -289,7 +278,7 @@ Graph.prototype.zoomToFit = function(initialZoom) {
if (initialZoom == true) { if (initialZoom == true) {
if (this.constants.clustering.enabled == true && if (this.constants.clustering.enabled == true &&
numberOfNodes >= this.constants.clustering.initialMaxNumberOfNodes) {
numberOfNodes >= this.constants.clustering.initialMaxNodes) {
var zoomLevel = 38.8467 / (numberOfNodes - 14.50184) + 0.0116; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. var zoomLevel = 38.8467 / (numberOfNodes - 14.50184) + 0.0116; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
} }
else { else {
@ -393,28 +382,40 @@ Graph.prototype.setOptions = function (options) {
if (options.selectable !== undefined) {this.selectable = options.selectable;} if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.clustering) { if (options.clustering) {
this.constants.clustering.enabled = true;
for (var prop in options.clustering) { for (var prop in options.clustering) {
if (options.clustering.hasOwnProperty(prop)) { if (options.clustering.hasOwnProperty(prop)) {
this.constants.clustering[prop] = options.clustering[prop]; this.constants.clustering[prop] = options.clustering[prop];
} }
} }
} }
else if (options.clustering !== undefined) {
this.constants.clustering.enabled = false;
}
if (options.navigationUI) { if (options.navigationUI) {
this.constants.navigationUI.enabled = true;
for (var prop in options.navigationUI) { for (var prop in options.navigationUI) {
if (options.navigationUI.hasOwnProperty(prop)) { if (options.navigationUI.hasOwnProperty(prop)) {
this.constants.navigationUI[prop] = options.navigationUI[prop]; this.constants.navigationUI[prop] = options.navigationUI[prop];
} }
} }
} }
else if (options.navigationUI !== undefined) {
this.constants.navigationUI.enabled = false;
}
if (options.keyboardNavigation) { if (options.keyboardNavigation) {
this.constants.keyboardNavigation.enabled = true;
for (var prop in options.keyboardNavigation) { for (var prop in options.keyboardNavigation) {
if (options.keyboardNavigation.hasOwnProperty(prop)) { if (options.keyboardNavigation.hasOwnProperty(prop)) {
this.constants.keyboardNavigation[prop] = options.keyboardNavigation[prop]; this.constants.keyboardNavigation[prop] = options.keyboardNavigation[prop];
} }
} }
} }
else if (options.keyboardNavigation !== undefined) {
this.constants.keyboardNavigation.enabled = false;
}
// TODO: work out these options and document them // TODO: work out these options and document them
@ -480,6 +481,14 @@ Graph.prototype.setOptions = function (options) {
this.setSize(this.width, this.height); this.setSize(this.width, this.height);
this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
this._setScale(1); this._setScale(1);
// load the navigationUI system.
this._loadUISystem();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
this._redraw();
}; };
/** /**
@ -556,10 +565,7 @@ Graph.prototype._createKeyBinds = function() {
var me = this; var me = this;
this.mousetrap = mousetrap; this.mousetrap = mousetrap;
if (this.constants.navigationUI.enabled == true &&
this.constants.navigationUI.enableToggling == true) {
this.mousetrap.bind("u",this._toggleUI.bind(me) , "keydown");
}
this.mousetrap.reset();
if (this.constants.keyboardNavigation.enabled == true) { if (this.constants.keyboardNavigation.enabled == true) {
this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown"); this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown");
@ -1436,7 +1442,7 @@ Graph.prototype._redraw = function() {
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); ctx.restore();
if (this.UIvisible == true) {
if (this.constants.navigationUI.enabled == true) {
this._doInUISector("_drawNodes",ctx,true); this._doInUISector("_drawNodes",ctx,true);
} }
}; };
@ -1628,8 +1634,8 @@ Graph.prototype._initializeForceCalculation = function() {
} }
else { else {
// if there are too many nodes on screen, we cluster without repositioning // if there are too many nodes on screen, we cluster without repositioning
if (this.nodeIndices.length > this.constants.clustering.absoluteMaxNumberOfNodes && this.constants.clustering.enabled == true) {
this.clusterToFit(this.constants.clustering.reduceToMaxNumberOfNodes, false);
if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) {
this.clusterToFit(this.constants.clustering.reduceToNodes, false);
} }
// we now start the force calculation // we now start the force calculation

+ 3
- 4
src/graph/Node.js View File

@ -62,10 +62,9 @@ function Node(properties, imagelist, grouplist, constants) {
this.resetCluster(); this.resetCluster();
this.dynamicEdgesLength = 0; this.dynamicEdgesLength = 0;
this.clusterSession = 0; this.clusterSession = 0;
this.clusterSizeWidthFactor = constants.clustering.clusterSizeWidthFactor;
this.clusterSizeHeightFactor = constants.clustering.clusterSizeHeightFactor;
this.clusterSizeRadiusFactor = constants.clustering.clusterSizeRadiusFactor;
this.clusterSizeWidthFactor = constants.clustering.nodeScaling.width;
this.clusterSizeHeightFactor = constants.clustering.nodeScaling.height;
this.clusterSizeRadiusFactor = constants.clustering.nodeScaling.radius;
// mass, force, velocity // mass, force, velocity
this.mass = 1; // kg (mass is adjusted for the number of connected edges) this.mass = 1; // kg (mass is adjusted for the number of connected edges)

+ 7
- 5
src/graph/SelectionMixin.js View File

@ -89,7 +89,7 @@ var SelectionMixin = {
_getUINodeAt : function (pointer) { _getUINodeAt : function (pointer) {
var screenPositionObject = this._pointerToScreenPositionObject(pointer); var screenPositionObject = this._pointerToScreenPositionObject(pointer);
var overlappingNodes = this._getAllUINodesOverlappingWith(screenPositionObject); var overlappingNodes = this._getAllUINodesOverlappingWith(screenPositionObject);
if (this.UIvisible && overlappingNodes.length > 0) {
if (overlappingNodes.length > 0) {
return this.sectors["navigationUI"]["nodes"][overlappingNodes[overlappingNodes.length - 1]]; return this.sectors["navigationUI"]["nodes"][overlappingNodes[overlappingNodes.length - 1]];
} }
else { else {
@ -246,10 +246,12 @@ var SelectionMixin = {
* @private * @private
*/ */
_handleTouch : function(pointer) { _handleTouch : function(pointer) {
var node = this._getUINodeAt(pointer);
if (node != null) {
if (this[node.triggerFunction] !== undefined) {
this[node.triggerFunction]();
if (this.constants.navigationUI.enabled == true) {
var node = this._getUINodeAt(pointer);
if (node != null) {
if (this[node.triggerFunction] !== undefined) {
this[node.triggerFunction]();
}
} }
} }
}, },

+ 6
- 20
src/graph/UIMixin.js View File

@ -107,20 +107,6 @@ var UIMixin = {
}, },
/**
* toggle the visibility of the navigationUI
*
* @private
*/
_toggleUI : function() {
if (this.UIvisible === undefined) {
this.UIvisible = false;
}
this.UIvisible = !this.UIvisible;
this._redraw();
},
/** /**
* un-highlight (for lack of a better term) all navigationUI elements * un-highlight (for lack of a better term) all navigationUI elements
* @private * @private
@ -153,7 +139,7 @@ var UIMixin = {
*/ */
_moveUp : function(event) { _moveUp : function(event) {
this._highlightUIElement("UI_up"); this._highlightUIElement("UI_up");
this.yIncrement = this.constants.keyboardNavigation.yMovementSpeed;
this.yIncrement = this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -165,7 +151,7 @@ var UIMixin = {
*/ */
_moveDown : function(event) { _moveDown : function(event) {
this._highlightUIElement("UI_down"); this._highlightUIElement("UI_down");
this.yIncrement = -this.constants.keyboardNavigation.yMovementSpeed;
this.yIncrement = -this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -177,7 +163,7 @@ var UIMixin = {
*/ */
_moveLeft : function(event) { _moveLeft : function(event) {
this._highlightUIElement("UI_left"); this._highlightUIElement("UI_left");
this.xIncrement = this.constants.keyboardNavigation.xMovementSpeed;
this.xIncrement = this.constants.keyboardNavigation.speed.x;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -189,7 +175,7 @@ var UIMixin = {
*/ */
_moveRight : function(event) { _moveRight : function(event) {
this._highlightUIElement("UI_right"); this._highlightUIElement("UI_right");
this.xIncrement = -this.constants.keyboardNavigation.xMovementSpeed;
this.xIncrement = -this.constants.keyboardNavigation.speed.y;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -201,7 +187,7 @@ var UIMixin = {
*/ */
_zoomIn : function(event) { _zoomIn : function(event) {
this._highlightUIElement("UI_plus"); this._highlightUIElement("UI_plus");
this.zoomIncrement = this.constants.keyboardNavigation.zoomMovementSpeed;
this.zoomIncrement = this.constants.keyboardNavigation.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },
@ -213,7 +199,7 @@ var UIMixin = {
*/ */
_zoomOut : function() { _zoomOut : function() {
this._highlightUIElement("UI_min"); this._highlightUIElement("UI_min");
this.zoomIncrement = -this.constants.keyboardNavigation.zoomMovementSpeed;
this.zoomIncrement = -this.constants.keyboardNavigation.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event); this._preventDefault(event);
}, },

Loading…
Cancel
Save