Browse Source

Merge remote-tracking branch 'origin/develop' into develop

css_transitions
josdejong 10 years ago
parent
commit
72e71c59ff
15 changed files with 1197 additions and 128 deletions
  1. +2
    -0
      Jakefile.js
  2. +689
    -49
      dist/vis.js
  3. +9
    -9
      dist/vis.min.js
  4. +50
    -7
      docs/graph.html
  5. +3
    -12
      examples/graph/02_random_nodes.html
  6. +12
    -1
      examples/graph/21_data_manipulation.html
  7. +118
    -0
      examples/graph/25_physics_configuration.html
  8. +0
    -1
      examples/timeline/03_much_data.html
  9. +4
    -2
      src/graph/Edge.js
  10. +3
    -1
      src/graph/Graph.js
  11. +17
    -0
      src/graph/graphMixins/HierarchicalLayoutMixin.js
  12. +3
    -42
      src/graph/graphMixins/MixinLoader.js
  13. +2
    -0
      src/graph/graphMixins/SelectionMixin.js
  14. +1
    -1
      src/graph/graphMixins/physics/HierarchialRepulsion.js
  15. +284
    -3
      src/graph/graphMixins/physics/PhysicsMixin.js

+ 2
- 0
Jakefile.js View File

@ -64,6 +64,8 @@ task('build', {async: true}, function () {
'./src/DataSet.js',
'./src/DataView.js',
'./src/svgTimeline/svgTimeline.js',
'./src/timeline/TimeStep.js',
'./src/timeline/Stack.js',
'./src/timeline/Range.js',

+ 689
- 49
dist/vis.js View File

@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
* @version @@version
* @date @@date
* @version 0.6.0-SNAPSHOT
* @date 2014-03-03
*
* @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -2350,6 +2350,380 @@ DataView.prototype._trigger = DataSet.prototype._trigger;
DataView.prototype.subscribe = DataView.prototype.on;
DataView.prototype.unsubscribe = DataView.prototype.off;
/**
* Created by Alex on 2/27/14.
*/
function SvgAxis (range,mainId, constants) {
this.svgId = mainId;
this.range = range;
this.constants = constants;
this.duration = this.range.end - this.range.start; // in milliseconds
this.minColumnWidth = 100;
this._drawElements();
this._update();
}
SvgAxis.prototype._drawElements = function() {
d3.select(this.svgId)
.append("rect")
.attr("id","bars")
.attr("x",0)
.attr("y",0)
.attr("width", this.constants.width)
.attr("height",this.constants.barHeight)
.style("stroke", "rgb(6,120,155)");
this.leftText = d3.select(this.svgId)
.append("text")
.attr("x", 5)
.attr("y", 20)
.attr("font-size", 14)
.text(moment(this.range.start));
this.rightText = d3.select(this.svgId)
.append("text")
.attr("y", 20)
.attr("font-size", 14)
.text(moment(this.range.end))
this.rightText.attr("x", this.constants.width - 5 - this.rightText.node().getBBox().width);
this.dateLabels = {};
this.markerLines = {};
}
SvgAxis.prototype._createMarkerLine = function(index) {
this.markerLines[index] = {svg:d3.select("svg#main").append("line")
.attr('y1',0)
.attr('y2',this.constants.height)
.style("stroke", "rgb(220,220,220)")
}
}
SvgAxis.prototype._createDateLabel = function(index) {
this.dateLabels[index] = {svg:d3.select(this.svgId)
.append("text")
.attr("font-size",12)
, active:false};
}
SvgAxis.prototype._update = function() {
this.duration = this.range.end - this.range.start; // in milliseconds
this.leftText.text(moment(this.range.start).format("DD-MM-YYYY HH:mm:ss"))
this.rightText.text(moment(this.range.end).format("DD-MM-YYYY"))
this.rightText.attr("x", this.constants.width - 5 - this.rightText.node().getBBox().width);
this.msPerPixel = this.duration / this.constants.width;
this.columnDuration = this.minColumnWidth * this.msPerPixel;
var milliSecondScale = [1,10,50,100,250,500];
var secondScale = [1,5,15,30];
var minuteScale = [1,5,15,30];
var hourScale = [1,3,6,12];
var dayScale = [1,2,3,5,10,15];
var monthScale = [1,2,3,4,5,6];
var yearScale = [1,2,3,4,5,6,7,8,9,10,15,20,25,50,75,100,150,250,500,1000];
var multipliers = [1,1000,60000,3600000,24*3600000,30*24*3600000,365*24*3600000];
var scales = [milliSecondScale,secondScale,minuteScale,hourScale,dayScale,monthScale,yearScale]
var formats = ["SSS","mm:ss","hh:mm:ss","DD HH:mm","DD-MM","MM-YYYY","YYYY"]
var indices = this._getAppropriateScale(scales,multipliers);
var scale = scales[indices[0]][indices[1]] * multipliers[indices[0]];
var dateCorrection = (this.range.start.valueOf() % scale) +3600000;
for (var i = 0; i < 30; i++) {
var date = this.range.start + i*scale - dateCorrection;
if (((i+1)*scale - dateCorrection)/this.msPerPixel > this.constants.width + 200) {
if (this.dateLabels.hasOwnProperty(i)) {
this.dateLabels[i].svg.remove();
delete this.dateLabels[i]
}
if (this.markerLines.hasOwnProperty(i)) {
this.markerLines[i].svg.remove();
delete this.markerLines[i]
}
}
else {
if (!this.dateLabels.hasOwnProperty(i)) {
this._createDateLabel(i);
}
if (!this.markerLines.hasOwnProperty(i)) {
this._createMarkerLine(i);
}
this.dateLabels[i].svg.text(moment(date).format(formats[indices[0]]))
.attr("x",(i*scale - dateCorrection)/this.msPerPixel)
.attr("y",50)
this.markerLines[i].svg.attr("x1",(i*scale - dateCorrection)/this.msPerPixel)
.attr("x2",(i*scale - dateCorrection)/this.msPerPixel)
}
}
}
SvgAxis.prototype._getAppropriateScale = function(scales,multipliers) {
for (var i = 0; i < scales.length; i++) {
for (var j = 0; j < scales[i].length; j++) {
if (scales[i][j] * multipliers[i] > this.columnDuration) {
return [i,j]
}
}
}
}
/**
* @constructor SvgTimeline
* Create a graph visualization, displaying nodes and edges.
*
* @param {Element} container The DOM element in which the Graph will
* be created. Normally a div element.
* @param {Object} items An object containing parameters
* {Array} nodes
* {Array} edges
* @param {Object} options Options
*/
function SvgTimeline (container, items, options) {
this.constants = {
width:1400,
height:400,
barHeight: 60
}
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
this.range = {
start:now.clone().add('days', -3).valueOf(),
end: now.clone().add('days', 4).valueOf()
}
this.items = {};
this.sortedItems = [];
this.activeItems = {};
this.sortedActiveItems = [];
this._createItems(items);
this.container = container;
this._createSVG();
this.axis = new SvgAxis(this.range,"svg#main",this.constants);
var me = this;
this.hammer = Hammer(document.getElementById("main"), {
prevent_default: true
});
this.hammer.on('tap', me._onTap.bind(me) );
this.hammer.on('doubletap', me._onDoubleTap.bind(me) );
this.hammer.on('hold', me._onHold.bind(me) );
this.hammer.on('pinch', me._onPinch.bind(me) );
this.hammer.on('touch', me._onTouch.bind(me) );
this.hammer.on('dragstart', me._onDragStart.bind(me) );
this.hammer.on('drag', me._onDrag.bind(me) );
this.hammer.on('dragend', me._onDragEnd.bind(me) );
this.hammer.on('release', me._onRelease.bind(me) );
this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
//this._drawLines();
this._update();
}
SvgTimeline.prototype._createSVG = function() {
d3.select("div#visualization")
.append("svg").attr("id","main")
.attr("width",this.constants.width)
.attr("height",this.constants.height)
.attr("style","border:1px solid black")
};
SvgTimeline.prototype._createItems = function (items) {
for (var i = 0; i < items.length; i++) {
this.items[items[i].id] = new Item(items[i], this.constants);
this.sortedItems.push(this.items[items[i].id]);
}
this._sortItems(this.sortedItems);
}
SvgTimeline.prototype._sortItems = function (items) {
items.sort(function(a,b) {return a.start - b.start});
}
SvgTimeline.prototype._getPointer = function (touch) {
return {
x: touch.pageX,
y: touch.pageY
};
};
SvgTimeline.prototype._onTap = function() {};
SvgTimeline.prototype._onDoubleTap = function() {};
SvgTimeline.prototype._onHold = function() {};
SvgTimeline.prototype._onPinch = function() {};
SvgTimeline.prototype._onTouch = function(event) {};
SvgTimeline.prototype._onDragStart = function(event) {
this.initialDragPos = this._getPointer(event.gesture.center);
};
SvgTimeline.prototype._onDrag = function(event) {
var pointer = this._getPointer(event.gesture.center);
var diffX = pointer.x - this.initialDragPos.x;
// var diffY = pointer.y - this.initialDragPos.y;
this.initialDragPos = pointer;
this.range.start -= diffX * this.axis.msPerPixel;
this.range.end -= diffX * this.axis.msPerPixel;
this._update();
};
SvgTimeline.prototype._onDragEnd = function() {};
SvgTimeline.prototype._onRelease = function() {};
SvgTimeline.prototype._onMouseWheel = function(event) {
var delta = 0;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta/120;
}
else if (event.detail) { /* Mozilla case. */
// In Mozilla, sign of delta is different than in IE.
// Also, delta is multiple of 3.
delta = -event.detail/3;
}
if (delta) {
var pointer = {x:event.x, y:event.y}
var center = this.range.start + this.axis.duration * 0.5;
var zoomSpeed = 0.1;
var scrollSpeed = 0.1;
this.range.start = center - 0.5*(this.axis.duration * (1 - delta*zoomSpeed));
this.range.end = this.range.start + (this.axis.duration * (1 - delta*zoomSpeed));
var diffX = delta*(pointer.x - 0.5*this.constants.width);
// var diffY = pointer.y - this.initialDragPos.y;
this.range.start -= diffX * this.axis.msPerPixel * scrollSpeed;
this.range.end -= diffX * this.axis.msPerPixel * scrollSpeed;
this._update();
}
};
SvgTimeline.prototype._onMouseMoveTitle = function() {};
SvgTimeline.prototype._update = function() {
this.axis._update();
this._getActiveItems();
this._updateItems();
};
SvgTimeline.prototype._getActiveItems = function() {
// reset all currently active items to inactive
for (var itemId in this.activeItems) {
if (this.activeItems.hasOwnProperty(itemId)) {
this.activeItems[itemId].active = false;
}
}
this.sortedActiveItems = []
var rangeStart = this.range.start-200*this.axis.msPerPixel
var rangeEnd = (this.range.end+200*this.axis.msPerPixel)
for (var itemId in this.items) {
if (this.items.hasOwnProperty(itemId)) {
if (this.items[itemId].start >= rangeStart && this.items[itemId].start < rangeEnd ||
this.items[itemId].end >= rangeStart && this.items[itemId].end < rangeEnd) {
if (this.items[itemId].active == false) {
this.activeItems[itemId] = this.items[itemId];
}
this.activeItems[itemId].active = true;
this.sortedActiveItems.push(this.activeItems[itemId]);
}
}
}
this._sortItems(this.sortedActiveItems);
// cleanup
for (var itemId in this.activeItems) {
if (this.activeItems.hasOwnProperty(itemId)) {
if (this.activeItems[itemId].active == false) {
this.activeItems[itemId].svg.remove();
this.activeItems[itemId].svg = null;
this.activeItems[itemId].svgLine.remove();
this.activeItems[itemId].svgLine = null;
delete this.activeItems[itemId];
}
}
}
};
SvgTimeline.prototype._updateItems = function() {
for (var i = 0; i < this.sortedActiveItems.length; i++) {
var item = this.sortedActiveItems[i];
if (item.svg == null) {
// item.svg = d3.select("svg#main")
// .append("rect")
// .attr("class","item")
// .style("stroke", "rgb(6,120,155)")
// .style("fill", "rgb(6,120,155)");
item.svg = d3.select("svg#main")
.append("foreignObject")
item.svgContent = item.svg.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.style("background-color", "#ff00ff")
.html("<h1>An HTML Foreign Object in SVG</h1><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu enim quam. Quisque nisi risus, sagittis quis tempor nec, aliquam eget neque. Nulla bibendum semper lorem non ullamcorper. Nulla non ligula lorem. Praesent porttitor, tellus nec suscipit aliquam, enim elit posuere lorem, at laoreet enim ligula sed tortor. Ut sodales, urna a aliquam semper, nibh diam gravida sapien, sit amet fermentum purus lacus eget massa. Donec ac arcu vel magna consequat pretium et vel ligula. Donec sit amet erat elit. Vivamus eu metus eget est hendrerit rutrum. Curabitur vitae orci et leo interdum egestas ut sit amet dui. In varius enim ut sem posuere in tristique metus ultrices.<p>Integer mollis massa at orci porta vestibulum. Pellentesque dignissim turpis ut tortor ultricies condimentum et quis nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer euismod lorem vulputate dui pharetra luctus. Sed vulputate, nunc quis porttitor scelerisque, dui est varius ipsum, eu blandit mauris nibh pellentesque tortor. Vivamus ultricies ante eget ipsum pulvinar ac tempor turpis mollis. Morbi tortor orci, euismod vel sagittis ac, lobortis nec est. Quisque euismod venenatis felis at dapibus. Vestibulum dignissim nulla ut nisi tristique porttitor. Proin et nunc id arcu cursus dapibus non quis libero. Nunc ligula mi, bibendum non mattis nec, luctus id neque. Suspendisse ut eros lacus. Praesent eget lacus eget risus congue vestibulum. Morbi tincidunt pulvinar lacus sed faucibus. Phasellus sed vestibulum sapien.");
if (item.end == 0) {
item.svgLine = d3.select("svg#main")
.append("line")
.attr("y1",this.constants.barHeight)
.style("stroke", "rgb(200,200,255)")
.style("stroke-width", 3)
}
}
item.svg.attr('width',item.getLength(this.axis.msPerPixel))
.attr("x",this._getXforItem(item))
.attr("y",this._getYforItem(item, i))
.attr('height',25)
if (item.end == 0) {
item.svgLine.attr('y2',item.y)
.attr('x1',item.timeX)
.attr('x2',item.timeX)
}
}
};
SvgTimeline.prototype._getXforItem = function(item) {
item.timeX = (item.start - this.range.start)/this.axis.msPerPixel;
if (item.end == 0) {
item.drawX = item.timeX - item.width * 0.5;
}
else {
item.drawX = item.timeX;
}
return item.drawX;
}
SvgTimeline.prototype._getYforItem = function(item, index) {
var bounds = 10;
var startIndex = Math.max(0,index-bounds);
item.level = 0;
for (var i = startIndex; i < index; i++) {
var item2 = this.sortedActiveItems[i];
if (item.drawX <= (item2.drawX + item2.width + 5) && item2.level == item.level) {
item.level += 1;
}
}
item.y = 100 + 50*item.level;
return item.y;
}
/**
* @constructor TimeStep
* The class TimeStep is an iterator for dates. You provide a start date and an
@ -10445,6 +10819,7 @@ function Edge (properties, graph, constants) {
this.width = constants.edges.width;
this.value = undefined;
this.length = constants.physics.springLength;
this.customLength = false;
this.selected = false;
this.smooth = constants.smoothCurves;
@ -10500,7 +10875,8 @@ Edge.prototype.setProperties = function(properties, constants) {
if (properties.title !== undefined) {this.title = properties.title;}
if (properties.width !== undefined) {this.width = properties.width;}
if (properties.value !== undefined) {this.value = properties.value;}
if (properties.length !== undefined) {this.length = properties.length;}
if (properties.length !== undefined) {this.length = properties.length;
this.customLength = true;}
// Added to support dashed lines
// David Jordan
@ -10644,7 +11020,7 @@ Edge.prototype._drawLine = function(ctx) {
ctx.lineWidth = this._getLineWidth();
var point;
if (this.from != this.to+9) {
if (this.from != this.to) {
// draw line
this._line(ctx);
@ -11388,6 +11764,50 @@ var physicsMixin = {
},
/**
* This loads the node force solver based on the barnes hut or repulsion algorithm
*
* @private
*/
_loadSelectedForceSolver : function() {
// this overloads the this._calculateNodeForces
if (this.constants.physics.barnesHut.enabled == true) {
this._clearMixin(repulsionMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity;
this.constants.physics.springLength = this.constants.physics.barnesHut.springLength;
this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant;
this.constants.physics.damping = this.constants.physics.barnesHut.damping;
this._loadMixin(barnesHutMixin);
}
else if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._clearMixin(barnesHutMixin);
this._clearMixin(repulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant;
this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping;
this._loadMixin(hierarchalRepulsionMixin);
}
else {
this._clearMixin(barnesHutMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.barnesHutTree = undefined;
this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.repulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant;
this.constants.physics.damping = this.constants.physics.repulsion.damping;
this._loadMixin(repulsionMixin);
}
},
/**
* Before calculating the forces, we check if we need to cluster to keep up performance and we check
* if there is more than one node. If it is just one node, we dont calculate anything.
@ -11526,7 +11946,7 @@ var physicsMixin = {
if (edge.connected) {
// only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
// this implies that the edges between big clusters are longer
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
@ -11575,7 +11995,7 @@ var physicsMixin = {
var node2 = edge.via;
var node3 = edge.from;
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
@ -11608,6 +12028,10 @@ var physicsMixin = {
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
if (length == 0) {
length = 0.01;
}
fx = dx * springForce;
fy = dy * springForce;
@ -11615,8 +12039,242 @@ var physicsMixin = {
node1.fy += fy;
node2.fx -= fx;
node2.fy -= fy;
},
/**
* Load the HTML for the physics config and bind it
* @private
*/
_loadPhysicsConfiguration : function() {
if (this.physicsConfiguration === undefined) {
this.physicsConfiguration = document.createElement('div');
this.physicsConfiguration.className = "PhysicsConfiguration";
this.physicsConfiguration.innerHTML = '' +
'<table><tr><td><b>Simulation Mode:</b></td></tr>' +
'<tr>' +
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod1" value="BH" checked="checked">Barnes Hut</td>' +
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod2" value="R">Repulsion</td>'+
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod3" value="H">Hierarchical</td>' +
'</tr>'+
'</table>' +
'<table id="graph_BH_table" style="display:none">'+
'<tr><td><b>Barnes Hut</b></td></tr>'+
'<tr>'+
'<td width="150px">gravitationalConstant</td><td>0</td><td><input type="range" min="500" max="20000" value="2000" step="25" style="width:300px" id="graph_BH_gc"></td><td width="50px">-20000</td><td><input value="-2000" id="graph_BH_gc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0.3" step="0.05" style="width:300px" id="graph_BH_cg"></td><td>3</td><td><input value="0.03" id="graph_BH_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_BH_sl"></td><td>500</td><td><input value="100" id="graph_BH_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.005" style="width:300px" id="graph_BH_sc"></td><td>0.5</td><td><input value="0.05" id="graph_BH_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_BH_damp"></td><td>0.3</td><td><input value="0.09" id="graph_BH_damp_value" style="width:60px"></td>'+
'</tr>'+
'</table>'+
'<table id="graph_R_table" style="display:none">'+
'<tr><td><b>Repulsion</b></td></tr>'+
'<tr>'+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="100" step="1" style="width:300px" id="graph_R_nd"></td><td width="50px">300</td><td><input value="100" id="graph_R_nd_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0.1" step="0.05" style="width:300px" id="graph_R_cg"></td><td>3</td><td><input value="0.01" id="graph_R_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="200" step="1" style="width:300px" id="graph_R_sl"></td><td>500</td><td><input value="200" id="graph_R_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.005" style="width:300px" id="graph_R_sc"></td><td>0.5</td><td><input value="0.05" id="graph_R_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_R_damp"></td><td>0.3</td><td><input value="0.09" id="graph_R_damp_value" style="width:60px"></td>'+
'</tr>'+
'</table>'+
'<table id="graph_H_table" style="display:none">'+
'<tr><td width="150"><b>Hierarchical</b></td></tr>'+
'<tr>'+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="60" step="1" style="width:300px" id="graph_H_nd"></td><td width="50px">300</td><td><input value="60" id="graph_H_nd_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0" step="0.05" style="width:300px" id="graph_H_cg"></td><td>3</td><td><input value="0" id="graph_H_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_H_sl"></td><td>500</td><td><input value="100" id="graph_H_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.01" step="0.005" style="width:300px" id="graph_H_sc"></td><td>0.5</td><td><input value="0.01" id="graph_H_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_H_damp"></td><td>0.3</td><td><input value="0.09" id="graph_H_damp_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">direction</td><td>1</td><td><input type="range" min="0" max="3" value="0" step="1" style="width:300px" id="graph_H_direction"></td><td>4</td><td><input value="LR" id="graph_H_direction_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">levelSeparation</td><td>1</td><td><input type="range" min="0" max="500" value="150" step="1" style="width:300px" id="graph_H_levsep"></td><td>500</td><td><input value="150" id="graph_H_levsep_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">nodeSpacing</td><td>1</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_H_nspac"></td><td>500</td><td><input value="100" id="graph_H_nspac_value" style="width:60px"></td>'+
'</tr>'+
'</table>'
this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement);
var hierarchicalLayoutDirections = ["LR","RL","UD","DU"];
var rangeElement;
rangeElement = document.getElementById('graph_BH_gc');
rangeElement.innerHTML = this.constants.physics.barnesHut.gravitationalConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_gc',-1,"physics_barnesHut_gravitationalConstant");
rangeElement = document.getElementById('graph_BH_cg');
rangeElement.innerHTML = this.constants.physics.barnesHut.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_BH_sc');
rangeElement.innerHTML = this.constants.physics.barnesHut.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_BH_sl');
rangeElement.innerHTML = this.constants.physics.barnesHut.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_BH_damp');
rangeElement.innerHTML = this.constants.physics.barnesHut.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_R_nd');
rangeElement.innerHTML = this.constants.physics.repulsion.nodeDistance;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_nd',1,"physics_repulsion_nodeDistance");
rangeElement = document.getElementById('graph_R_cg');
rangeElement.innerHTML = this.constants.physics.repulsion.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_R_sc');
rangeElement.innerHTML = this.constants.physics.repulsion.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_R_sl');
rangeElement.innerHTML = this.constants.physics.repulsion.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_R_damp');
rangeElement.innerHTML = this.constants.physics.repulsion.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_H_nd');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.nodeDistance;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nd',1,"physics_hierarchicalRepulsion_nodeDistance");
rangeElement = document.getElementById('graph_H_cg');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_H_sc');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_H_sl');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_H_damp');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_H_direction');
rangeElement.innerHTML = hierarchicalLayoutDirections.indexOf(this.constants.hierarchicalLayout.direction);
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_direction',hierarchicalLayoutDirections,"hierarchicalLayout_direction");
rangeElement = document.getElementById('graph_H_levsep');
rangeElement.innerHTML = this.constants.hierarchicalLayout.levelSeparation;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_levsep',1,"hierarchicalLayout_levelSeparation");
rangeElement = document.getElementById('graph_H_nspac');
rangeElement.innerHTML = this.constants.hierarchicalLayout.nodeSpacing;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nspac',1,"hierarchicalLayout_nodeSpacing");
var radioButton1 = document.getElementById("graph_physicsMethod1");
var radioButton2 = document.getElementById("graph_physicsMethod2");
var radioButton3 = document.getElementById("graph_physicsMethod3");
radioButton2.checked = true;
if (this.constants.physics.barnesHut.enabled) {
radioButton1.checked = true;
}
if (this.constants.hierarchicalLayout.enabled) {
radioButton3.checked = true;
}
switchConfigurations.apply(this);
radioButton1.onchange = switchConfigurations.bind(this);
radioButton2.onchange = switchConfigurations.bind(this);
radioButton3.onchange = switchConfigurations.bind(this);
}
},
_overWriteGraphConstants : function(constantsVariableName, value) {
var nameArray = constantsVariableName.split("_");
if (nameArray.length == 1) {
this.constants[nameArray[0]] = value;
}
else if (nameArray.length == 2) {
this.constants[nameArray[0]][nameArray[1]] = value;
}
else if (nameArray.length == 3) {
this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value;
}
}
}
function switchConfigurations () {
var ids = ["graph_BH_table","graph_R_table","graph_H_table"]
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value;
var tableId = "graph_" + radioButton + "_table";
var table = document.getElementById(tableId);
table.style.display = "block";
for (var i = 0; i < ids.length; i++) {
if (ids[i] != tableId) {
table = document.getElementById(ids[i]);
table.style.display = "none";
}
}
this._restoreNodes();
if (radioButton == "R") {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.barnesHut.enabled = false;
}
else if (radioButton == "H") {
this.constants.hierarchicalLayout.enabled = true;
this.constants.physics.hierarchicalRepulsion.enabeled = true;
this.constants.physics.barnesHut.enabled = false;
this._setupHierarchicalLayout();
}
else {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.barnesHut.enabled = true;
}
this._loadSelectedForceSolver();
this.moving = true;
this.start();
}
function showValueOfRange (id,map,constantsVariableName) {
var valueId = id + "_value";
var rangeValue = document.getElementById(id).value;
if (constantsVariableName == "hierarchicalLayout_direction" ||
constantsVariableName == "hierarchicalLayout_levelSeparation" ||
constantsVariableName == "hierarchicalLayout_nodeSpacing") {
this._setupHierarchicalLayout();
}
if (map instanceof Array) {
document.getElementById(valueId).value = map[parseInt(rangeValue)];
this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]);
}
else {
document.getElementById(valueId).value = map * parseFloat(rangeValue);
this._overWriteGraphConstants(constantsVariableName,map * parseFloat(rangeValue));
}
this.moving = true;
this.start();
};
/**
* Created by Alex on 2/10/14.
*/
@ -11643,7 +12301,7 @@ var hierarchalRepulsionMixin = {
// repulsing forces between nodes
var nodeDistance = this.constants.physics.repulsion.nodeDistance;
var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance;
var minimumDistance = nodeDistance;
// we loop from i over all but the last entree in the array
@ -12397,7 +13055,24 @@ var HierarchicalLayoutMixin = {
}
}
}
},
/**
* Unfix nodes
*
* @private
*/
_restoreNodes : function() {
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
this.nodes[nodeId].xFixed = false;
this.nodes[nodeId].yFixed = false;
}
}
}
};
/**
* Created by Alex on 2/4/14.
@ -14930,6 +15605,7 @@ var SelectionMixin = {
this._unselectAll();
}
}
this.emit("click", this.getSelection());
this._redraw();
},
@ -14948,6 +15624,7 @@ var SelectionMixin = {
"y" : this._canvasToY(pointer.y)};
this.openCluster(node);
}
this.emit("doubleClick", this.getSelection());
},
@ -15302,51 +15979,12 @@ var graphMixinLoaders = {
_loadPhysicsSystem : function() {
this._loadMixin(physicsMixin);
this._loadSelectedForceSolver();
},
/**
* This loads the node force solver based on the barnes hut or repulsion algorithm
*
* @private
*/
_loadSelectedForceSolver : function() {
// this overloads the this._calculateNodeForces
if (this.constants.physics.barnesHut.enabled == true) {
this._clearMixin(repulsionMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity;
this.constants.physics.springLength = this.constants.physics.barnesHut.springLength;
this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant;
this.constants.physics.damping = this.constants.physics.barnesHut.damping;
this._loadMixin(barnesHutMixin);
}
else if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._clearMixin(barnesHutMixin);
this._clearMixin(repulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant;
this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping;
this._loadMixin(hierarchalRepulsionMixin);
if (this.constants.configurePhysics == true) {
this._loadPhysicsConfiguration();
}
else {
this._clearMixin(barnesHutMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.barnesHutTree = undefined;
},
this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.repulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant;
this.constants.physics.damping = this.constants.physics.repulsion.damping;
this._loadMixin(repulsionMixin);
}
},
/**
@ -15573,6 +16211,7 @@ function Graph (container, data, options) {
altLength: undefined
}
},
configurePhysics:false,
physics: {
barnesHut: {
enabled: true,
@ -15971,6 +16610,7 @@ Graph.prototype.setOptions = function (options) {
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.onAdd) {
this.triggerFunctions.add = options.onAdd;

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


+ 50
- 7
docs/graph.html View File

@ -55,7 +55,7 @@
<li><a href="#Edges_configuration">Edges</a></li>
<li><a href="#Groups_configuration">Groups</a></li>
<li><a href="#Physics">Physics</a></li>
<li><a href="#Data_manipulation">Data_manipulation</a></li>
<li><a href="#Data_manipulation">Data manipulation</a></li>
<li><a href="#Clustering">Clustering</a></li>
<li><a href="#Navigation_controls">Navigation controls</a></li>
<li><a href="#Keyboard_navigation">Keyboard navigation</a></li>
@ -298,7 +298,8 @@ var nodes = [
<td>true</td>
<td>If allowedToMove is false, then the node will not move from its supplied position.
If only an x position has been supplied, it is only fixed in the x-direction.
The same holds for y. If both x and y have been defined, the node will not move.</td>
The same holds for y. If both x and y have been defined, the node will not move.
If no x or y have been supplied, this argument will not do anything.</td>
</tr>
<tr>
<td>fontColor</td>
@ -668,6 +669,15 @@ var options = {
Barnes-Hut nBody simulation is used by default. See section <a href="#Physics">Physics</a> for an overview of the available options.
</td>
</tr>
<tr>
<td><a href="#Physics">configurePhysics</a></td>
<td>Boolean</td>
<td>false</td>
<td>
Enabling this setting will create a physics configuration div above the graph. You can use this to fine tune the physics system to suit your needs.
Because of the many possible configurations, there is not a one-size-fits-all setting. By using this tool, you can adapt the physics to your dataset.
</td>
</tr>
<tr>
<td><a href="#Data_manipulation">dataManipulation</a></td>
@ -850,7 +860,8 @@ var options = {
<td>false</td>
<td>If allowedToMove is false, then the node will not move from its supplied position.
If only an x position has been supplied, it is only fixed in the x-direction.
The same holds for y. If both x and y have been defined, the node will not move.</td>
The same holds for y. If both x and y have been defined, the node will not move.
If no x or y have been supplied, this argument will not do anything.</td>
</tr>
<tr>
@ -1173,7 +1184,7 @@ var nodes = [
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 <a href="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.
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.
</p>
<pre class="prettyprint">
// These variables must be defined in an options object named physics.
@ -1388,12 +1399,12 @@ var options: {
};
</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.
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 resize.
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.
An code snippet from example 21 is shown below.
</p>
<pre class="prettyprint">
graph.on("frameResize", function(params) {console.log(params.width,params.height)});
graph.on("resize", function(params) {console.log(params.width,params.height)});
</pre>
<h3 id="Clustering">Clustering</h3>
@ -1706,7 +1717,8 @@ var options: {
<td>nodeSpacing</td>
<td>Number</td>
<td>100</td>
<td>This defines the space between nodes in the same level (in the X-direction, considering UP-DOWN direction).</td>
<td>This defines the space between nodes in the same level (in the X-direction, considering UP-DOWN direction).
This is only relevant during the initial placing of nodes.</td>
</tr>
<tr>
<td>direction</td>
@ -1855,9 +1867,40 @@ graph.off('select', onSelect);
<td>
<ul>
<li><code>nodes</code>: an array with the ids of the selected nodes</li>
<li><code>edges</code>: an array with the ids of the selected edges</li>
</ul>
</td>
</tr>
<tr>
<td>click</td>
<td>Fired after the user clicks or taps on a touchscreen.</td>
<td>
<ul>
<li><code>nodes</code>: an array with the ids of the selected nodes</li>
<li><code>edges</code>: an array with the ids of the selected edges</li>
</ul>
</td>
</tr>
<tr>
<td>doubleClick</td>
<td>Fired after the user double clicks or double taps on a touchscreen.</td>
<td>
<ul>
<li><code>nodes</code>: an array with the ids of the selected nodes</li>
<li><code>edges</code>: an array with the ids of the selected edges</li>
</ul>
</td>
</tr>
<tr>
<td>resize</td>
<td>Fired when the size of the canvas has been updated (not neccecarily changed) by the setSize() function or by the setOptions() function.</td>
<td>
<ul>
<li><code>width</code>: the new width of the canvas</li>
<li><code>height</code>: the new height of the canvas</li>
</ul>
</td>
</tr>
</table>

+ 3
- 12
examples/graph/02_random_nodes.html View File

@ -75,21 +75,11 @@
nodes: nodes,
edges: edges
};
/*
var options = {
nodes: {
shape: 'circle'
},
edges: {
length: 50
},
stabilize: false
};
*/
var options = {
edges: {
},
stabilize: false
stabilize: false,
};
graph = new vis.Graph(container, data, options);
@ -107,6 +97,7 @@
<input id="nodeCount" type="text" value="25" style="width: 50px;">
<input type="submit" value="Go">
</form>
<br>
<div id="mygraph"></div>

+ 12
- 1
examples/graph/21_data_manipulation.html View File

@ -146,6 +146,17 @@
saveButton.onclick = saveData.bind(this,data,callback);
cancelButton.onclick = clearPopUp.bind();
div.style.display = 'block';
},
onConnect: function(data,callback) {
if (data.from == data.to) {
var r=confirm("Do you want to connect the node to itself?");
if (r==true) {
callback(data);
}
}
else {
callback(data);
}
}
};
graph = new vis.Graph(container, data, options);
@ -155,7 +166,7 @@
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
graph.on("frameResize", function(params) {console.log(params.width,params.height)});
graph.on("resize", function(params) {console.log(params.width,params.height)});
function clearPopUp() {
var saveButton = document.getElementById('saveButton');

+ 118
- 0
examples/graph/25_physics_configuration.html View File

@ -0,0 +1,118 @@
<!doctype html>
<html>
<head>
<title>Graph | Playing with Physics</title>
<style type="text/css">
body {
font: 10pt sans;
}
#mygraph {
width: 600px;
height: 600px;
border: 1px solid lightgray;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<script type="text/javascript">
var nodes = null;
var edges = null;
var graph = null;
function draw() {
nodes = [];
edges = [];
var connectionCount = [];
// randomly create some nodes and edges
var nodeCount = document.getElementById('nodeCount').value;
for (var i = 0; i < nodeCount; i++) {
nodes.push({
id: i,
label: String(i)
});
connectionCount[i] = 0;
// create edges in a scale-free-graph way
if (i == 1) {
var from = i;
var to = 0;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
else if (i > 1) {
var conn = edges.length * 2;
var rand = Math.floor(Math.random() * conn);
var cum = 0;
var j = 0;
while (j < connectionCount.length && cum < rand) {
cum += connectionCount[j];
j++;
}
var from = i;
var to = j;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
}
// create a graph
var container = document.getElementById('mygraph');
var data = {
nodes: nodes,
edges: edges
};
var options = {
edges: {
},
stabilize: false,
configurePhysics:true
};
graph = new vis.Graph(container, data, options);
// add event listeners
graph.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
}
</script>
</head>
<body onload="draw();">
<h2>Playing with Physics</h2>
<div style="width: 700px; font-size:14px;">
Every dataset is different. Nodes can have different sizes based on content, interconnectivity can be high or low etc. Because of this, graph has a special option
that the user can use to explore which settings may be good for him or her. This is ment to be used during the development phase when you are implementing vis.js. Once you have found
settings you are happy with, you can supply them to graph using the documented physics options.
On start, the default settings will be loaded. Keep in mind that selecting the hierarchical simulation mode <b>disables</b> smooth curves. These will not be enabled again afterwards.
</div>
<br />
<form onsubmit="draw(); return false;">
<label for="nodeCount">Number of nodes:</label>
<input id="nodeCount" type="text" value="25" style="width: 50px;">
<input type="submit" value="Go">
</form>
<br>
<div id="mygraph"></div>
<p id="selection"></p>
</body>
</html>

+ 0
- 1
examples/timeline/03_much_data.html View File

@ -63,7 +63,6 @@
};
var timeline = new vis.Timeline(container, items, options);
</script>
</body>
</html>

+ 4
- 2
src/graph/Edge.js View File

@ -32,6 +32,7 @@ function Edge (properties, graph, constants) {
this.width = constants.edges.width;
this.value = undefined;
this.length = constants.physics.springLength;
this.customLength = false;
this.selected = false;
this.smooth = constants.smoothCurves;
@ -87,7 +88,8 @@ Edge.prototype.setProperties = function(properties, constants) {
if (properties.title !== undefined) {this.title = properties.title;}
if (properties.width !== undefined) {this.width = properties.width;}
if (properties.value !== undefined) {this.value = properties.value;}
if (properties.length !== undefined) {this.length = properties.length;}
if (properties.length !== undefined) {this.length = properties.length;
this.customLength = true;}
// Added to support dashed lines
// David Jordan
@ -231,7 +233,7 @@ Edge.prototype._drawLine = function(ctx) {
ctx.lineWidth = this._getLineWidth();
var point;
if (this.from != this.to+9) {
if (this.from != this.to) {
// draw line
this._line(ctx);

+ 3
- 1
src/graph/Graph.js View File

@ -73,6 +73,7 @@ function Graph (container, data, options) {
altLength: undefined
}
},
configurePhysics:false,
physics: {
barnesHut: {
enabled: true,
@ -471,6 +472,7 @@ Graph.prototype.setOptions = function (options) {
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.onAdd) {
this.triggerFunctions.add = options.onAdd;
@ -1184,7 +1186,7 @@ Graph.prototype.setSize = function(width, height) {
this.manipulationDiv.style.width = this.frame.canvas.clientWidth;
}
this.emit('frameResize', {width:this.frame.canvas.width,height:this.frame.canvas.height});
this.emit('resize', {width:this.frame.canvas.width,height:this.frame.canvas.height});
};
/**

+ 17
- 0
src/graph/graphMixins/HierarchicalLayoutMixin.js View File

@ -275,5 +275,22 @@ var HierarchicalLayoutMixin = {
}
}
}
},
/**
* Unfix nodes
*
* @private
*/
_restoreNodes : function() {
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
this.nodes[nodeId].xFixed = false;
this.nodes[nodeId].yFixed = false;
}
}
}
};

+ 3
- 42
src/graph/graphMixins/MixinLoader.js View File

@ -43,51 +43,12 @@ var graphMixinLoaders = {
_loadPhysicsSystem : function() {
this._loadMixin(physicsMixin);
this._loadSelectedForceSolver();
},
/**
* This loads the node force solver based on the barnes hut or repulsion algorithm
*
* @private
*/
_loadSelectedForceSolver : function() {
// this overloads the this._calculateNodeForces
if (this.constants.physics.barnesHut.enabled == true) {
this._clearMixin(repulsionMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity;
this.constants.physics.springLength = this.constants.physics.barnesHut.springLength;
this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant;
this.constants.physics.damping = this.constants.physics.barnesHut.damping;
this._loadMixin(barnesHutMixin);
if (this.constants.configurePhysics == true) {
this._loadPhysicsConfiguration();
}
else if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._clearMixin(barnesHutMixin);
this._clearMixin(repulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant;
this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping;
this._loadMixin(hierarchalRepulsionMixin);
}
else {
this._clearMixin(barnesHutMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.barnesHutTree = undefined;
},
this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.repulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant;
this.constants.physics.damping = this.constants.physics.repulsion.damping;
this._loadMixin(repulsionMixin);
}
},
/**

+ 2
- 0
src/graph/graphMixins/SelectionMixin.js View File

@ -402,6 +402,7 @@ var SelectionMixin = {
this._unselectAll();
}
}
this.emit("click", this.getSelection());
this._redraw();
},
@ -420,6 +421,7 @@ var SelectionMixin = {
"y" : this._canvasToY(pointer.y)};
this.openCluster(node);
}
this.emit("doubleClick", this.getSelection());
},

+ 1
- 1
src/graph/graphMixins/physics/HierarchialRepulsion.js View File

@ -24,7 +24,7 @@ var hierarchalRepulsionMixin = {
// repulsing forces between nodes
var nodeDistance = this.constants.physics.repulsion.nodeDistance;
var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance;
var minimumDistance = nodeDistance;
// we loop from i over all but the last entree in the array

+ 284
- 3
src/graph/graphMixins/physics/PhysicsMixin.js View File

@ -18,6 +18,50 @@ var physicsMixin = {
},
/**
* This loads the node force solver based on the barnes hut or repulsion algorithm
*
* @private
*/
_loadSelectedForceSolver : function() {
// this overloads the this._calculateNodeForces
if (this.constants.physics.barnesHut.enabled == true) {
this._clearMixin(repulsionMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity;
this.constants.physics.springLength = this.constants.physics.barnesHut.springLength;
this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant;
this.constants.physics.damping = this.constants.physics.barnesHut.damping;
this._loadMixin(barnesHutMixin);
}
else if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._clearMixin(barnesHutMixin);
this._clearMixin(repulsionMixin);
this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant;
this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping;
this._loadMixin(hierarchalRepulsionMixin);
}
else {
this._clearMixin(barnesHutMixin);
this._clearMixin(hierarchalRepulsionMixin);
this.barnesHutTree = undefined;
this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity;
this.constants.physics.springLength = this.constants.physics.repulsion.springLength;
this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant;
this.constants.physics.damping = this.constants.physics.repulsion.damping;
this._loadMixin(repulsionMixin);
}
},
/**
* Before calculating the forces, we check if we need to cluster to keep up performance and we check
* if there is more than one node. If it is just one node, we dont calculate anything.
@ -156,7 +200,7 @@ var physicsMixin = {
if (edge.connected) {
// only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
// this implies that the edges between big clusters are longer
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
@ -205,7 +249,7 @@ var physicsMixin = {
var node2 = edge.via;
var node3 = edge.from;
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
@ -238,6 +282,10 @@ var physicsMixin = {
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
if (length == 0) {
length = 0.01;
}
fx = dx * springForce;
fy = dy * springForce;
@ -245,5 +293,238 @@ var physicsMixin = {
node1.fy += fy;
node2.fx -= fx;
node2.fy -= fy;
},
/**
* Load the HTML for the physics config and bind it
* @private
*/
_loadPhysicsConfiguration : function() {
if (this.physicsConfiguration === undefined) {
this.physicsConfiguration = document.createElement('div');
this.physicsConfiguration.className = "PhysicsConfiguration";
this.physicsConfiguration.innerHTML = '' +
'<table><tr><td><b>Simulation Mode:</b></td></tr>' +
'<tr>' +
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod1" value="BH" checked="checked">Barnes Hut</td>' +
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod2" value="R">Repulsion</td>'+
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod3" value="H">Hierarchical</td>' +
'</tr>'+
'</table>' +
'<table id="graph_BH_table" style="display:none">'+
'<tr><td><b>Barnes Hut</b></td></tr>'+
'<tr>'+
'<td width="150px">gravitationalConstant</td><td>0</td><td><input type="range" min="500" max="20000" value="2000" step="25" style="width:300px" id="graph_BH_gc"></td><td width="50px">-20000</td><td><input value="-2000" id="graph_BH_gc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0.3" step="0.05" style="width:300px" id="graph_BH_cg"></td><td>3</td><td><input value="0.03" id="graph_BH_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_BH_sl"></td><td>500</td><td><input value="100" id="graph_BH_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.005" style="width:300px" id="graph_BH_sc"></td><td>0.5</td><td><input value="0.05" id="graph_BH_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_BH_damp"></td><td>0.3</td><td><input value="0.09" id="graph_BH_damp_value" style="width:60px"></td>'+
'</tr>'+
'</table>'+
'<table id="graph_R_table" style="display:none">'+
'<tr><td><b>Repulsion</b></td></tr>'+
'<tr>'+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="100" step="1" style="width:300px" id="graph_R_nd"></td><td width="50px">300</td><td><input value="100" id="graph_R_nd_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0.1" step="0.05" style="width:300px" id="graph_R_cg"></td><td>3</td><td><input value="0.01" id="graph_R_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="200" step="1" style="width:300px" id="graph_R_sl"></td><td>500</td><td><input value="200" id="graph_R_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.005" style="width:300px" id="graph_R_sc"></td><td>0.5</td><td><input value="0.05" id="graph_R_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_R_damp"></td><td>0.3</td><td><input value="0.09" id="graph_R_damp_value" style="width:60px"></td>'+
'</tr>'+
'</table>'+
'<table id="graph_H_table" style="display:none">'+
'<tr><td width="150"><b>Hierarchical</b></td></tr>'+
'<tr>'+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="60" step="1" style="width:300px" id="graph_H_nd"></td><td width="50px">300</td><td><input value="60" id="graph_H_nd_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="0" step="0.05" style="width:300px" id="graph_H_cg"></td><td>3</td><td><input value="0" id="graph_H_cg_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_H_sl"></td><td>500</td><td><input value="100" id="graph_H_sl_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.01" step="0.005" style="width:300px" id="graph_H_sc"></td><td>0.5</td><td><input value="0.01" id="graph_H_sc_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.005" style="width:300px" id="graph_H_damp"></td><td>0.3</td><td><input value="0.09" id="graph_H_damp_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">direction</td><td>1</td><td><input type="range" min="0" max="3" value="0" step="1" style="width:300px" id="graph_H_direction"></td><td>4</td><td><input value="LR" id="graph_H_direction_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">levelSeparation</td><td>1</td><td><input type="range" min="0" max="500" value="150" step="1" style="width:300px" id="graph_H_levsep"></td><td>500</td><td><input value="150" id="graph_H_levsep_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">nodeSpacing</td><td>1</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px" id="graph_H_nspac"></td><td>500</td><td><input value="100" id="graph_H_nspac_value" style="width:60px"></td>'+
'</tr>'+
'</table>'
this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement);
var hierarchicalLayoutDirections = ["LR","RL","UD","DU"];
var rangeElement;
rangeElement = document.getElementById('graph_BH_gc');
rangeElement.innerHTML = this.constants.physics.barnesHut.gravitationalConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_gc',-1,"physics_barnesHut_gravitationalConstant");
rangeElement = document.getElementById('graph_BH_cg');
rangeElement.innerHTML = this.constants.physics.barnesHut.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_BH_sc');
rangeElement.innerHTML = this.constants.physics.barnesHut.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_BH_sl');
rangeElement.innerHTML = this.constants.physics.barnesHut.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_BH_damp');
rangeElement.innerHTML = this.constants.physics.barnesHut.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_R_nd');
rangeElement.innerHTML = this.constants.physics.repulsion.nodeDistance;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_nd',1,"physics_repulsion_nodeDistance");
rangeElement = document.getElementById('graph_R_cg');
rangeElement.innerHTML = this.constants.physics.repulsion.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_R_sc');
rangeElement.innerHTML = this.constants.physics.repulsion.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_R_sl');
rangeElement.innerHTML = this.constants.physics.repulsion.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_R_damp');
rangeElement.innerHTML = this.constants.physics.repulsion.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_H_nd');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.nodeDistance;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nd',1,"physics_hierarchicalRepulsion_nodeDistance");
rangeElement = document.getElementById('graph_H_cg');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.centralGravity;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_cg',1,"physics_centralGravity");
rangeElement = document.getElementById('graph_H_sc');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.springConstant;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sc',1,"physics_springConstant");
rangeElement = document.getElementById('graph_H_sl');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.springLength;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sl',1,"physics_springLength");
rangeElement = document.getElementById('graph_H_damp');
rangeElement.innerHTML = this.constants.physics.hierarchicalRepulsion.damping;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_H_direction');
rangeElement.innerHTML = hierarchicalLayoutDirections.indexOf(this.constants.hierarchicalLayout.direction);
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_direction',hierarchicalLayoutDirections,"hierarchicalLayout_direction");
rangeElement = document.getElementById('graph_H_levsep');
rangeElement.innerHTML = this.constants.hierarchicalLayout.levelSeparation;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_levsep',1,"hierarchicalLayout_levelSeparation");
rangeElement = document.getElementById('graph_H_nspac');
rangeElement.innerHTML = this.constants.hierarchicalLayout.nodeSpacing;
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nspac',1,"hierarchicalLayout_nodeSpacing");
var radioButton1 = document.getElementById("graph_physicsMethod1");
var radioButton2 = document.getElementById("graph_physicsMethod2");
var radioButton3 = document.getElementById("graph_physicsMethod3");
radioButton2.checked = true;
if (this.constants.physics.barnesHut.enabled) {
radioButton1.checked = true;
}
if (this.constants.hierarchicalLayout.enabled) {
radioButton3.checked = true;
}
switchConfigurations.apply(this);
radioButton1.onchange = switchConfigurations.bind(this);
radioButton2.onchange = switchConfigurations.bind(this);
radioButton3.onchange = switchConfigurations.bind(this);
}
},
_overWriteGraphConstants : function(constantsVariableName, value) {
var nameArray = constantsVariableName.split("_");
if (nameArray.length == 1) {
this.constants[nameArray[0]] = value;
}
else if (nameArray.length == 2) {
this.constants[nameArray[0]][nameArray[1]] = value;
}
else if (nameArray.length == 3) {
this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value;
}
}
}
}
function switchConfigurations () {
var ids = ["graph_BH_table","graph_R_table","graph_H_table"]
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value;
var tableId = "graph_" + radioButton + "_table";
var table = document.getElementById(tableId);
table.style.display = "block";
for (var i = 0; i < ids.length; i++) {
if (ids[i] != tableId) {
table = document.getElementById(ids[i]);
table.style.display = "none";
}
}
this._restoreNodes();
if (radioButton == "R") {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.barnesHut.enabled = false;
}
else if (radioButton == "H") {
this.constants.hierarchicalLayout.enabled = true;
this.constants.physics.hierarchicalRepulsion.enabeled = true;
this.constants.physics.barnesHut.enabled = false;
this._setupHierarchicalLayout();
}
else {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.barnesHut.enabled = true;
}
this._loadSelectedForceSolver();
this.moving = true;
this.start();
}
function showValueOfRange (id,map,constantsVariableName) {
var valueId = id + "_value";
var rangeValue = document.getElementById(id).value;
if (constantsVariableName == "hierarchicalLayout_direction" ||
constantsVariableName == "hierarchicalLayout_levelSeparation" ||
constantsVariableName == "hierarchicalLayout_nodeSpacing") {
this._setupHierarchicalLayout();
}
if (map instanceof Array) {
document.getElementById(valueId).value = map[parseInt(rangeValue)];
this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]);
}
else {
document.getElementById(valueId).value = map * parseFloat(rangeValue);
this._overWriteGraphConstants(constantsVariableName,map * parseFloat(rangeValue));
}
this.moving = true;
this.start();
};

Loading…
Cancel
Save