Browse Source

Added physics configuration, removed svgTimeline from this branch

css_transitions
Alex de Mulder 10 years ago
parent
commit
cdb939c45f
18 changed files with 843 additions and 1539 deletions
  1. +444
    -59
      dist/vis.js
  2. +12
    -2
      docs/graph.html
  3. +3
    -75
      examples/graph/02_random_nodes.html
  4. +118
    -0
      examples/graph/25_physics_configuration.html
  5. +0
    -42
      examples/svgTimeline/01_basic.html
  6. +0
    -65
      examples/svgTimeline/03_much_data.html
  7. +3
    -1
      src/graph/Edge.js
  8. +2
    -0
      src/graph/Graph.js
  9. +17
    -0
      src/graph/graphMixins/HierarchicalLayoutMixin.js
  10. +3
    -0
      src/graph/graphMixins/MixinLoader.js
  11. +1
    -1
      src/graph/graphMixins/physics/HierarchialRepulsion.js
  12. +240
    -3
      src/graph/graphMixins/physics/PhysicsMixin.js
  13. +0
    -84
      src/svgTimeline/Item.js
  14. +0
    -5
      src/svgTimeline/d3.v3.js
  15. +0
    -7
      src/svgTimeline/hammer.min.js
  16. +0
    -9
      src/svgTimeline/moment-with-langs.min.js
  17. +0
    -374
      src/svgTimeline/svgTimeline.js
  18. +0
    -812
      src/svgTimeline/util.js

+ 444
- 59
dist/vis.js View File

@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 0.6.0-SNAPSHOT * @version 0.6.0-SNAPSHOT
* @date 2014-02-27
* @date 2014-03-03
* *
* @license * @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -2429,7 +2429,7 @@ SvgAxis.prototype._update = function() {
var yearScale = [1,2,3,4,5,6,7,8,9,10,15,20,25,50,75,100,150,250,500,1000]; 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 multipliers = [1,1000,60000,3600000,24*3600000,30*24*3600000,365*24*3600000];
var scales = [milliSecondScale,secondScale,minuteScale,hourScale,dayScale,monthScale,yearScale] var scales = [milliSecondScale,secondScale,minuteScale,hourScale,dayScale,monthScale,yearScale]
var formats = ["SSS","mm:ss","hh:mm:ss","DD HH:mm","MM-DD","MM","YYYY"]
var formats = ["SSS","mm:ss","hh:mm:ss","DD HH:mm","DD-MM","MM-YYYY","YYYY"]
var indices = this._getAppropriateScale(scales,multipliers); var indices = this._getAppropriateScale(scales,multipliers);
var scale = scales[indices[0]][indices[1]] * multipliers[indices[0]]; var scale = scales[indices[0]][indices[1]] * multipliers[indices[0]];
@ -2487,16 +2487,25 @@ SvgAxis.prototype._getAppropriateScale = function(scales,multipliers) {
* @param {Object} options Options * @param {Object} options Options
*/ */
function SvgTimeline (container, items, options) { function SvgTimeline (container, items, options) {
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.constants = { this.constants = {
width:1400, width:1400,
height:400, height:400,
barHeight: 60 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.container = container;
this._createSVG(); this._createSVG();
@ -2521,6 +2530,8 @@ function SvgTimeline (container, items, options) {
this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
//this._drawLines(); //this._drawLines();
this._update();
} }
SvgTimeline.prototype._createSVG = function() { SvgTimeline.prototype._createSVG = function() {
@ -2531,14 +2542,18 @@ SvgTimeline.prototype._createSVG = function() {
.attr("style","border:1px solid black") .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});
}
/**
* Get the pointer location from a touch location
* @param {{pageX: Number, pageY: Number}} touch
* @return {{x: Number, y: Number}} pointer
* @private
*/
SvgTimeline.prototype._getPointer = function (touch) { SvgTimeline.prototype._getPointer = function (touch) {
return { return {
x: touch.pageX, x: touch.pageX,
@ -2599,9 +2614,115 @@ SvgTimeline.prototype._onMouseWheel = function(event) {
}; };
SvgTimeline.prototype._onMouseMoveTitle = function() {}; SvgTimeline.prototype._onMouseMoveTitle = function() {};
SvgTimeline.prototype._update = function() { SvgTimeline.prototype._update = function() {
this.axis._update(); 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 * @constructor TimeStep
@ -10698,6 +10819,7 @@ function Edge (properties, graph, constants) {
this.width = constants.edges.width; this.width = constants.edges.width;
this.value = undefined; this.value = undefined;
this.length = constants.physics.springLength; this.length = constants.physics.springLength;
this.customLength = false;
this.selected = false; this.selected = false;
this.smooth = constants.smoothCurves; this.smooth = constants.smoothCurves;
@ -10753,7 +10875,8 @@ Edge.prototype.setProperties = function(properties, constants) {
if (properties.title !== undefined) {this.title = properties.title;} if (properties.title !== undefined) {this.title = properties.title;}
if (properties.width !== undefined) {this.width = properties.width;} if (properties.width !== undefined) {this.width = properties.width;}
if (properties.value !== undefined) {this.value = properties.value;} 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 // Added to support dashed lines
// David Jordan // David Jordan
@ -11641,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 * 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. * if there is more than one node. If it is just one node, we dont calculate anything.
@ -11779,7 +11946,7 @@ var physicsMixin = {
if (edge.connected) { if (edge.connected) {
// only calculate forces if nodes are in the same sector // only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { 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 // this implies that the edges between big clusters are longer
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
@ -11828,7 +11995,7 @@ var physicsMixin = {
var node2 = edge.via; var node2 = edge.via;
var node3 = edge.from; var node3 = edge.from;
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
@ -11861,6 +12028,10 @@ var physicsMixin = {
springForce = this.constants.physics.springConstant * (edgeLength - length) / length; springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
if (length == 0) {
length = 0.01;
}
fx = dx * springForce; fx = dx * springForce;
fy = dy * springForce; fy = dy * springForce;
@ -11868,8 +12039,242 @@ var physicsMixin = {
node1.fy += fy; node1.fy += fy;
node2.fx -= fx; node2.fx -= fx;
node2.fy -= fy; 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. * Created by Alex on 2/10/14.
*/ */
@ -11896,7 +12301,7 @@ var hierarchalRepulsionMixin = {
// repulsing forces between nodes // repulsing forces between nodes
var nodeDistance = this.constants.physics.repulsion.nodeDistance;
var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance;
var minimumDistance = nodeDistance; var minimumDistance = nodeDistance;
// we loop from i over all but the last entree in the array // we loop from i over all but the last entree in the array
@ -12650,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. * Created by Alex on 2/4/14.
@ -15557,51 +15979,12 @@ var graphMixinLoaders = {
_loadPhysicsSystem : function() { _loadPhysicsSystem : function() {
this._loadMixin(physicsMixin); this._loadMixin(physicsMixin);
this._loadSelectedForceSolver(); 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);
}
},
/** /**
@ -15828,6 +16211,7 @@ function Graph (container, data, options) {
altLength: undefined altLength: undefined
} }
}, },
configurePhysics:false,
physics: { physics: {
barnesHut: { barnesHut: {
enabled: true, enabled: true,
@ -16226,6 +16610,7 @@ Graph.prototype.setOptions = function (options) {
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;} if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;} if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;} if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.onAdd) { if (options.onAdd) {
this.triggerFunctions.add = options.onAdd; this.triggerFunctions.add = options.onAdd;

+ 12
- 2
docs/graph.html View File

@ -669,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. Barnes-Hut nBody simulation is used by default. See section <a href="#Physics">Physics</a> for an overview of the available options.
</td> </td>
</tr> </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> <tr>
<td><a href="#Data_manipulation">dataManipulation</a></td> <td><a href="#Data_manipulation">dataManipulation</a></td>
@ -1175,7 +1184,7 @@ var nodes = [
The original simulation method was based on particel physics with a repulsion field (potential) around each node, 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. 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. 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> </p>
<pre class="prettyprint"> <pre class="prettyprint">
// These variables must be defined in an options object named physics. // These variables must be defined in an options object named physics.
@ -1708,7 +1717,8 @@ var options: {
<td>nodeSpacing</td> <td>nodeSpacing</td>
<td>Number</td> <td>Number</td>
<td>100</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>
<tr> <tr>
<td>direction</td> <td>direction</td>

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

@ -75,21 +75,11 @@
nodes: nodes, nodes: nodes,
edges: edges edges: edges
}; };
/*
var options = {
nodes: {
shape: 'circle'
},
edges: {
length: 50
},
stabilize: false
};
*/
var options = { var options = {
edges: { edges: {
}, },
stabilize: false
stabilize: false,
}; };
graph = new vis.Graph(container, data, options); graph = new vis.Graph(container, data, options);
@ -107,69 +97,7 @@
<input id="nodeCount" type="text" value="25" style="width: 50px;"> <input id="nodeCount" type="text" value="25" style="width: 50px;">
<input type="submit" value="Go"> <input type="submit" value="Go">
</form> </form>
<input type="radio" name="physicsMethod" value="Barnes Hut" checked="checked">Barnes Hut <br />
<input type="radio" name="physicsMethod" value="Repulsion">Repulsion <br />
<input type="radio" name="physicsMethod" value="Hierarchical Repulsion">Hierarchical Repulsion <br />
<table class="graphPhysicsTable">
<tr>
<td><b>Barnes Hut</b></td>
</tr>
<tr>
<td>gravitationalConstant</td><td>-500</td><td><input type="range" min="500" max="20000" value="2000" step="50" style="width:300px"></td><td>-20000</td><td><input value="2000"></td>
</tr>
<tr>
<td>centralGravity</td><td>0.0</td><td><input type="range" min="0" max="3" value="0.3" step="0.1" style="width:300px"></td><td>3</td><td><input value="0.03"></td>
</tr>
<tr>
<td>springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px"></td><td>500</td><td><input value="100"></td>
</tr>
<tr>
<td>springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.01" style="width:300px"></td><td>0.5</td><td><input value="0.05"></td>
</tr>
<tr>
<td>damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.01" style="width:300px"></td><td>0.3</td><td><input value="0.09"></td>
</tr>
</table>
<table class="graphPhysicsTable">
<tr>
<td><b>Repulsion</b></td>
</tr>
<tr>
<td>nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="100" step="50" style="width:300px"></td><td>300</td><td><input value="100"></td>
</tr>
<tr>
<td>centralGravity</td><td>0.0</td><td><input type="range" min="0" max="3" value="0.1" step="0.1" style="width:300px"></td><td>3</td><td><input value="0.01"></td>
</tr>
<tr>
<td>springLength</td><td>0</td><td><input type="range" min="0" max="500" value="200" step="1" style="width:300px"></td><td>500</td><td><input value="200"></td>
</tr>
<tr>
<td>springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.05" step="0.01" style="width:300px"></td><td>0.5</td><td><input value="0.05"></td>
</tr>
<tr>
<td>damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.01" style="width:300px"></td><td>0.3</td><td><input value="0.09"></td>
</tr>
</table>
<table class="graphPhysicsTable">
<tr>
<td><b>Hierarchical Repulsion</b></td>
</tr>
<tr>
<td>nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="60" step="50" style="width:300px"></td><td>300</td><td><input value="60"></td>
</tr>
<tr>
<td>centralGravity</td><td>0.0</td><td><input type="range" min="0" max="3" value="0" step="0.1" style="width:300px"></td><td>3</td><td><input value="0"></td>
</tr>
<tr>
<td>springLength</td><td>0</td><td><input type="range" min="0" max="500" value="100" step="1" style="width:300px"></td><td>500</td><td><input value="100"></td>
</tr>
<tr>
<td>springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="0.01" step="0.01" style="width:300px"></td><td>0.5</td><td><input value="0.01"></td>
</tr>
<tr>
<td>damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="0.09" step="0.01" style="width:300px"></td><td>0.3</td><td><input value="0.09"></td>
</tr>
</table>
<br> <br>
<div id="mygraph"></div> <div id="mygraph"></div>

+ 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
- 42
examples/svgTimeline/01_basic.html View File

@ -1,42 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Basic demo</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
#bars {
fill: #d2e2ff;
}
</style>
<script src="../../src/svgTimeline/util.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/d3.v3.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/hammer.min.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/moment-with-langs.min.js"></script>
<script src="../../src/svgTimeline/Item.js"></script>
<script src="../../src/svgTimeline/svgTimeline.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = [
{id: 1, content: 'item 1', start: '2014-02-20'},
{id: 2, content: 'item 2', start: '2014-02-14'},
{id: 3, content: 'item 3', start: '2014-02-18'},
{id: 4, content: 'item 4', start: '2014-02-16', end: '2014-02-19'},
{id: 5, content: 'item 5', start: '2014-02-25'},
{id: 6, content: 'item 6', start: '2014-02-27'}
];
var options = {};
var timeline = new SvgTimeline(container, items, options);
</script>
</body>
</html>

+ 0
- 65
examples/svgTimeline/03_much_data.html View File

@ -1,65 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | a lot of data</title>
<style>
body, html {
font-family: arial, sans-serif;
font-size: 11pt;
}
#bars {
fill: #d2e2ff;
}
</style>
<script src="../../src/svgTimeline/util.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/d3.v3.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/hammer.min.js" charset="utf-8"></script>
<script src="../../src/svgTimeline/moment-with-langs.min.js"></script>
<script src="../../src/svgTimeline/Item.js"></script>
<script src="../../src/svgTimeline/svgTimeline.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>
Test with a lot of data
</h1>
<p>
<label for="count">Number of items</label>
<input id="count" value="100">
<input id="draw" type="button" value="draw" onclick="draw()">
</p>
<div id="visualization"></div>
<script>
function draw() {
// create a dataset with items
var now = moment().minutes(0).seconds(0).milliseconds(0);
// create data
var count = parseInt(document.getElementById('count').value) || 100;
var newData = [];
for (var i = 0; i < count; i++) {
newData.push({id: i, content: 'item ' + i, start: now.clone().add('days', i)});
}
var container = document.getElementById('visualization');
container.innerHTML = ""
var options = {
start: now.clone().add('days', -3),
end: now.clone().add('days', 11),
zoomMin: 1000 * 60 * 60 * 24, // a day
zoomMax: 1000 * 60 * 60 * 24 * 30 * 3 // three months
//maxHeight: 300,
//height: '300px',
//orientation: 'top'
};
var timeline = new SvgTimeline(container, newData, options);
}
</script>
</body>
</html>

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

@ -32,6 +32,7 @@ function Edge (properties, graph, constants) {
this.width = constants.edges.width; this.width = constants.edges.width;
this.value = undefined; this.value = undefined;
this.length = constants.physics.springLength; this.length = constants.physics.springLength;
this.customLength = false;
this.selected = false; this.selected = false;
this.smooth = constants.smoothCurves; this.smooth = constants.smoothCurves;
@ -87,7 +88,8 @@ Edge.prototype.setProperties = function(properties, constants) {
if (properties.title !== undefined) {this.title = properties.title;} if (properties.title !== undefined) {this.title = properties.title;}
if (properties.width !== undefined) {this.width = properties.width;} if (properties.width !== undefined) {this.width = properties.width;}
if (properties.value !== undefined) {this.value = properties.value;} 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 // Added to support dashed lines
// David Jordan // David Jordan

+ 2
- 0
src/graph/Graph.js View File

@ -73,6 +73,7 @@ function Graph (container, data, options) {
altLength: undefined altLength: undefined
} }
}, },
configurePhysics:false,
physics: { physics: {
barnesHut: { barnesHut: {
enabled: true, enabled: true,
@ -471,6 +472,7 @@ Graph.prototype.setOptions = function (options) {
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;} if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;} if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;} if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.onAdd) { if (options.onAdd) {
this.triggerFunctions.add = options.onAdd; this.triggerFunctions.add = options.onAdd;

+ 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
- 0
src/graph/graphMixins/MixinLoader.js View File

@ -43,6 +43,9 @@ var graphMixinLoaders = {
_loadPhysicsSystem : function() { _loadPhysicsSystem : function() {
this._loadMixin(physicsMixin); this._loadMixin(physicsMixin);
this._loadSelectedForceSolver(); this._loadSelectedForceSolver();
if (this.constants.configurePhysics == true) {
this._loadPhysicsConfiguration();
}
}, },

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

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

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

@ -200,7 +200,7 @@ var physicsMixin = {
if (edge.connected) { if (edge.connected) {
// only calculate forces if nodes are in the same sector // only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { 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 // this implies that the edges between big clusters are longer
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
@ -249,7 +249,7 @@ var physicsMixin = {
var node2 = edge.via; var node2 = edge.via;
var node3 = edge.from; var node3 = edge.from;
edgeLength = edge.length;
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
@ -282,6 +282,10 @@ var physicsMixin = {
springForce = this.constants.physics.springConstant * (edgeLength - length) / length; springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
if (length == 0) {
length = 0.01;
}
fx = dx * springForce; fx = dx * springForce;
fy = dy * springForce; fy = dy * springForce;
@ -289,5 +293,238 @@ var physicsMixin = {
node1.fy += fy; node1.fy += fy;
node2.fx -= fx; node2.fx -= fx;
node2.fy -= fy; 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();
};

+ 0
- 84
src/svgTimeline/Item.js View File

@ -1,84 +0,0 @@
/**
* @class Item
* A node. A node can be connected to other nodes via one or multiple edges.
* @param {object} properties An object containing properties for the node. All
* properties are optional, except for the id.
* {number} id Id of the node. Required
* {string} label Text label for the node
* {number} x Horizontal position of the node
* {number} y Vertical position of the node
* {string} shape Node shape, available:
* "database", "circle", "ellipse",
* "box", "image", "text", "dot",
* "star", "triangle", "triangleDown",
* "square"
* {string} image An image url
* {string} title An title text, can be HTML
* {anytype} group A group name or number
* @param {Object} constants An object with default values for
* example for the color
*
*/
function Item(properties, constants) {
this.id = null;
this.start = null;
this.end = 0;
this.content = "no content";
this.class = "";
this.level = 0;
this.active = false;
this.setProperties(properties, constants);
this.timeX = 0;
this.drawX = 0;
this.y = 0;
this.width = 40;
this.convertDatesToUNIX();
this.duration = 0;
if (this.end != 0) {
this.duration = this.end - this.start;
}
this.svg = null;
this.svgLine = null;
}
/**
* Set or overwrite properties for the node
* @param {Object} properties an object with properties
* @param {Object} constants and object with default, global properties
*/
Item.prototype.setProperties = function(properties, constants) {
if (!properties) {
return;
}
// basic properties
if (properties.id !== undefined) {this.id = properties.id;}
else {throw("An ID is required.")}
if (properties.start !== undefined) {this.start = properties.start;}
else {throw("A start property is required. -->" + this.id)}
if (properties.end !== undefined) {this.end = properties.end;}
if (properties.class !== undefined) {this.class = properties.class;}
if (properties.content !== undefined) {this.content = properties.content;}
};
Item.prototype.convertDatesToUNIX = function() {
this.start = moment(this.start,"YYYY-MM-DD").valueOf();
if (this.end != 0) {
this.end = moment(this.end,"YYYY-MM-DD").valueOf();
}
}
Item.prototype.getLength = function(msPerPixel) {
this.width = Math.max(40,this.duration/msPerPixel);
return this.width;
}

+ 0
- 5
src/svgTimeline/d3.v3.js
File diff suppressed because it is too large
View File


+ 0
- 7
src/svgTimeline/hammer.min.js
File diff suppressed because it is too large
View File


+ 0
- 9
src/svgTimeline/moment-with-langs.min.js
File diff suppressed because it is too large
View File


+ 0
- 374
src/svgTimeline/svgTimeline.js View File

@ -1,374 +0,0 @@
/**
* 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;
}

+ 0
- 812
src/svgTimeline/util.js View File

@ -1,812 +0,0 @@
/**
* utility functions
*/
var util = {};
/**
* Test whether given object is a number
* @param {*} object
* @return {Boolean} isNumber
*/
util.isNumber = function isNumber(object) {
return (object instanceof Number || typeof object == 'number');
};
/**
* Test whether given object is a string
* @param {*} object
* @return {Boolean} isString
*/
util.isString = function isString(object) {
return (object instanceof String || typeof object == 'string');
};
/**
* Test whether given object is a Date, or a String containing a Date
* @param {Date | String} object
* @return {Boolean} isDate
*/
util.isDate = function isDate(object) {
if (object instanceof Date) {
return true;
}
else if (util.isString(object)) {
// test whether this string contains a date
var match = ASPDateRegex.exec(object);
if (match) {
return true;
}
else if (!isNaN(Date.parse(object))) {
return true;
}
}
return false;
};
/**
* Test whether given object is an instance of google.visualization.DataTable
* @param {*} object
* @return {Boolean} isDataTable
*/
util.isDataTable = function isDataTable(object) {
return (typeof (google) !== 'undefined') &&
(google.visualization) &&
(google.visualization.DataTable) &&
(object instanceof google.visualization.DataTable);
};
/**
* Create a semi UUID
* source: http://stackoverflow.com/a/105074/1262753
* @return {String} uuid
*/
util.randomUUID = function randomUUID () {
var S4 = function () {
return Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16);
};
return (
S4() + S4() + '-' +
S4() + '-' +
S4() + '-' +
S4() + '-' +
S4() + S4() + S4()
);
};
/**
* Extend object a with the properties of object b or a series of objects
* Only properties with defined values are copied
* @param {Object} a
* @param {... Object} b
* @return {Object} a
*/
util.extend = function (a, b) {
for (var i = 1, len = arguments.length; i < len; i++) {
var other = arguments[i];
for (var prop in other) {
if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
a[prop] = other[prop];
}
}
}
return a;
};
/**
* Convert an object to another type
* @param {Boolean | Number | String | Date | Moment | Null | undefined} object
* @param {String | undefined} type Name of the type. Available types:
* 'Boolean', 'Number', 'String',
* 'Date', 'Moment', ISODate', 'ASPDate'.
* @return {*} object
* @throws Error
*/
util.convert = function convert(object, type) {
var match;
if (object === undefined) {
return undefined;
}
if (object === null) {
return null;
}
if (!type) {
return object;
}
if (!(typeof type === 'string') && !(type instanceof String)) {
throw new Error('Type must be a string');
}
//noinspection FallthroughInSwitchStatementJS
switch (type) {
case 'boolean':
case 'Boolean':
return Boolean(object);
case 'number':
case 'Number':
return Number(object.valueOf());
case 'string':
case 'String':
return String(object);
case 'Date':
if (util.isNumber(object)) {
return new Date(object);
}
if (object instanceof Date) {
return new Date(object.valueOf());
}
else if (moment.isMoment(object)) {
return new Date(object.valueOf());
}
if (util.isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return new Date(Number(match[1])); // parse number
}
else {
return moment(object).toDate(); // parse string
}
}
else {
throw new Error(
'Cannot convert object of type ' + util.getType(object) +
' to type Date');
}
case 'Moment':
if (util.isNumber(object)) {
return moment(object);
}
if (object instanceof Date) {
return moment(object.valueOf());
}
else if (moment.isMoment(object)) {
return moment(object);
}
if (util.isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return moment(Number(match[1])); // parse number
}
else {
return moment(object); // parse string
}
}
else {
throw new Error(
'Cannot convert object of type ' + util.getType(object) +
' to type Date');
}
case 'ISODate':
if (util.isNumber(object)) {
return new Date(object);
}
else if (object instanceof Date) {
return object.toISOString();
}
else if (moment.isMoment(object)) {
return object.toDate().toISOString();
}
else if (util.isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return new Date(Number(match[1])).toISOString(); // parse number
}
else {
return new Date(object).toISOString(); // parse string
}
}
else {
throw new Error(
'Cannot convert object of type ' + util.getType(object) +
' to type ISODate');
}
case 'ASPDate':
if (util.isNumber(object)) {
return '/Date(' + object + ')/';
}
else if (object instanceof Date) {
return '/Date(' + object.valueOf() + ')/';
}
else if (util.isString(object)) {
match = ASPDateRegex.exec(object);
var value;
if (match) {
// object is an ASP date
value = new Date(Number(match[1])).valueOf(); // parse number
}
else {
value = new Date(object).valueOf(); // parse string
}
return '/Date(' + value + ')/';
}
else {
throw new Error(
'Cannot convert object of type ' + util.getType(object) +
' to type ASPDate');
}
default:
throw new Error('Cannot convert object of type ' + util.getType(object) +
' to type "' + type + '"');
}
};
// parse ASP.Net Date pattern,
// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
// code from http://momentjs.com/
var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
/**
* Get the type of an object, for example util.getType([]) returns 'Array'
* @param {*} object
* @return {String} type
*/
util.getType = function getType(object) {
var type = typeof object;
if (type == 'object') {
if (object == null) {
return 'null';
}
if (object instanceof Boolean) {
return 'Boolean';
}
if (object instanceof Number) {
return 'Number';
}
if (object instanceof String) {
return 'String';
}
if (object instanceof Array) {
return 'Array';
}
if (object instanceof Date) {
return 'Date';
}
return 'Object';
}
else if (type == 'number') {
return 'Number';
}
else if (type == 'boolean') {
return 'Boolean';
}
else if (type == 'string') {
return 'String';
}
return type;
};
/**
* Retrieve the absolute left value of a DOM element
* @param {Element} elem A dom element, for example a div
* @return {number} left The absolute left position of this element
* in the browser page.
*/
util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
var doc = document.documentElement;
var body = document.body;
var left = elem.offsetLeft;
var e = elem.offsetParent;
while (e != null && e != body && e != doc) {
left += e.offsetLeft;
left -= e.scrollLeft;
e = e.offsetParent;
}
return left;
};
/**
* Retrieve the absolute top value of a DOM element
* @param {Element} elem A dom element, for example a div
* @return {number} top The absolute top position of this element
* in the browser page.
*/
util.getAbsoluteTop = function getAbsoluteTop (elem) {
var doc = document.documentElement;
var body = document.body;
var top = elem.offsetTop;
var e = elem.offsetParent;
while (e != null && e != body && e != doc) {
top += e.offsetTop;
top -= e.scrollTop;
e = e.offsetParent;
}
return top;
};
/**
* Get the absolute, vertical mouse position from an event.
* @param {Event} event
* @return {Number} pageY
*/
util.getPageY = function getPageY (event) {
if ('pageY' in event) {
return event.pageY;
}
else {
var clientY;
if (('targetTouches' in event) && event.targetTouches.length) {
clientY = event.targetTouches[0].clientY;
}
else {
clientY = event.clientY;
}
var doc = document.documentElement;
var body = document.body;
return clientY +
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
( doc && doc.clientTop || body && body.clientTop || 0 );
}
};
/**
* Get the absolute, horizontal mouse position from an event.
* @param {Event} event
* @return {Number} pageX
*/
util.getPageX = function getPageX (event) {
if ('pageY' in event) {
return event.pageX;
}
else {
var clientX;
if (('targetTouches' in event) && event.targetTouches.length) {
clientX = event.targetTouches[0].clientX;
}
else {
clientX = event.clientX;
}
var doc = document.documentElement;
var body = document.body;
return clientX +
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
( doc && doc.clientLeft || body && body.clientLeft || 0 );
}
};
/**
* add a className to the given elements style
* @param {Element} elem
* @param {String} className
*/
util.addClassName = function addClassName(elem, className) {
var classes = elem.className.split(' ');
if (classes.indexOf(className) == -1) {
classes.push(className); // add the class to the array
elem.className = classes.join(' ');
}
};
/**
* add a className to the given elements style
* @param {Element} elem
* @param {String} className
*/
util.removeClassName = function removeClassname(elem, className) {
var classes = elem.className.split(' ');
var index = classes.indexOf(className);
if (index != -1) {
classes.splice(index, 1); // remove the class from the array
elem.className = classes.join(' ');
}
};
/**
* For each method for both arrays and objects.
* In case of an array, the built-in Array.forEach() is applied.
* In case of an Object, the method loops over all properties of the object.
* @param {Object | Array} object An Object or Array
* @param {function} callback Callback method, called for each item in
* the object or array with three parameters:
* callback(value, index, object)
*/
util.forEach = function forEach (object, callback) {
var i,
len;
if (object instanceof Array) {
// array
for (i = 0, len = object.length; i < len; i++) {
callback(object[i], i, object);
}
}
else {
// object
for (i in object) {
if (object.hasOwnProperty(i)) {
callback(object[i], i, object);
}
}
}
};
/**
* Update a property in an object
* @param {Object} object
* @param {String} key
* @param {*} value
* @return {Boolean} changed
*/
util.updateProperty = function updateProp (object, key, value) {
if (object[key] !== value) {
object[key] = value;
return true;
}
else {
return false;
}
};
/**
* Add and event listener. Works for all browsers
* @param {Element} element An html element
* @param {string} action The action, for example "click",
* without the prefix "on"
* @param {function} listener The callback function to be executed
* @param {boolean} [useCapture]
*/
util.addEventListener = function addEventListener(element, action, listener, useCapture) {
if (element.addEventListener) {
if (useCapture === undefined)
useCapture = false;
if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
action = "DOMMouseScroll"; // For Firefox
}
element.addEventListener(action, listener, useCapture);
} else {
element.attachEvent("on" + action, listener); // IE browsers
}
};
/**
* Remove an event listener from an element
* @param {Element} element An html dom element
* @param {string} action The name of the event, for example "mousedown"
* @param {function} listener The listener function
* @param {boolean} [useCapture]
*/
util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
if (element.removeEventListener) {
// non-IE browsers
if (useCapture === undefined)
useCapture = false;
if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
action = "DOMMouseScroll"; // For Firefox
}
element.removeEventListener(action, listener, useCapture);
} else {
// IE browsers
element.detachEvent("on" + action, listener);
}
};
/**
* Get HTML element which is the target of the event
* @param {Event} event
* @return {Element} target element
*/
util.getTarget = function getTarget(event) {
// code from http://www.quirksmode.org/js/events_properties.html
if (!event) {
event = window.event;
}
var target;
if (event.target) {
target = event.target;
}
else if (event.srcElement) {
target = event.srcElement;
}
if (target.nodeType != undefined && target.nodeType == 3) {
// defeat Safari bug
target = target.parentNode;
}
return target;
};
/**
* Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent
* @param {Element} element
* @param {Event} event
*/
util.fakeGesture = function fakeGesture (element, event) {
var eventType = null;
// for hammer.js 1.0.5
var gesture = Hammer.event.collectEventData(this, eventType, event);
// for hammer.js 1.0.6
//var touches = Hammer.event.getTouchList(event, eventType);
// var gesture = Hammer.event.collectEventData(this, eventType, touches, event);
// on IE in standards mode, no touches are recognized by hammer.js,
// resulting in NaN values for center.pageX and center.pageY
if (isNaN(gesture.center.pageX)) {
gesture.center.pageX = event.pageX;
}
if (isNaN(gesture.center.pageY)) {
gesture.center.pageY = event.pageY;
}
return gesture;
};
util.option = {};
/**
* Convert a value into a boolean
* @param {Boolean | function | undefined} value
* @param {Boolean} [defaultValue]
* @returns {Boolean} bool
*/
util.option.asBoolean = function (value, defaultValue) {
if (typeof value == 'function') {
value = value();
}
if (value != null) {
return (value != false);
}
return defaultValue || null;
};
/**
* Convert a value into a number
* @param {Boolean | function | undefined} value
* @param {Number} [defaultValue]
* @returns {Number} number
*/
util.option.asNumber = function (value, defaultValue) {
if (typeof value == 'function') {
value = value();
}
if (value != null) {
return Number(value) || defaultValue || null;
}
return defaultValue || null;
};
/**
* Convert a value into a string
* @param {String | function | undefined} value
* @param {String} [defaultValue]
* @returns {String} str
*/
util.option.asString = function (value, defaultValue) {
if (typeof value == 'function') {
value = value();
}
if (value != null) {
return String(value);
}
return defaultValue || null;
};
/**
* Convert a size or location into a string with pixels or a percentage
* @param {String | Number | function | undefined} value
* @param {String} [defaultValue]
* @returns {String} size
*/
util.option.asSize = function (value, defaultValue) {
if (typeof value == 'function') {
value = value();
}
if (util.isString(value)) {
return value;
}
else if (util.isNumber(value)) {
return value + 'px';
}
else {
return defaultValue || null;
}
};
/**
* Convert a value into a DOM element
* @param {HTMLElement | function | undefined} value
* @param {HTMLElement} [defaultValue]
* @returns {HTMLElement | null} dom
*/
util.option.asElement = function (value, defaultValue) {
if (typeof value == 'function') {
value = value();
}
return value || defaultValue || null;
};
util.GiveDec = function GiveDec(Hex)
{
if(Hex == "A")
Value = 10;
else
if(Hex == "B")
Value = 11;
else
if(Hex == "C")
Value = 12;
else
if(Hex == "D")
Value = 13;
else
if(Hex == "E")
Value = 14;
else
if(Hex == "F")
Value = 15;
else
Value = eval(Hex)
return Value;
}
util.GiveHex = function GiveHex(Dec)
{
if(Dec == 10)
Value = "A";
else
if(Dec == 11)
Value = "B";
else
if(Dec == 12)
Value = "C";
else
if(Dec == 13)
Value = "D";
else
if(Dec == 14)
Value = "E";
else
if(Dec == 15)
Value = "F";
else
Value = "" + Dec;
return Value;
}
/**
* http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
*
* @param {String} hex
* @returns {{r: *, g: *, b: *}}
*/
util.hexToRGB = function hexToRGB(hex) {
hex = hex.replace("#","").toUpperCase();
var a = util.GiveDec(hex.substring(0, 1));
var b = util.GiveDec(hex.substring(1, 2));
var c = util.GiveDec(hex.substring(2, 3));
var d = util.GiveDec(hex.substring(3, 4));
var e = util.GiveDec(hex.substring(4, 5));
var f = util.GiveDec(hex.substring(5, 6));
var r = (a * 16) + b;
var g = (c * 16) + d;
var b = (e * 16) + f;
return {r:r,g:g,b:b};
};
util.RGBToHex = function RGBToHex(red,green,blue) {
var a = util.GiveHex(Math.floor(red / 16));
var b = util.GiveHex(red % 16);
var c = util.GiveHex(Math.floor(green / 16));
var d = util.GiveHex(green % 16);
var e = util.GiveHex(Math.floor(blue / 16));
var f = util.GiveHex(blue % 16);
var hex = a + b + c + d + e + f;
return "#" + hex;
};
/**
* http://www.javascripter.net/faq/rgb2hsv.htm
*
* @param red
* @param green
* @param blue
* @returns {*}
* @constructor
*/
util.RGBToHSV = function RGBToHSV (red,green,blue) {
red=red/255; green=green/255; blue=blue/255;
var minRGB = Math.min(red,Math.min(green,blue));
var maxRGB = Math.max(red,Math.max(green,blue));
// Black-gray-white
if (minRGB == maxRGB) {
return {h:0,s:0,v:minRGB};
}
// Colors other than black-gray-white:
var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red);
var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5);
var hue = 60*(h - d/(maxRGB - minRGB))/360;
var saturation = (maxRGB - minRGB)/maxRGB;
var value = maxRGB;
return {h:hue,s:saturation,v:value};
};
/**
* https://gist.github.com/mjijackson/5311256
* @param hue
* @param saturation
* @param value
* @returns {{r: number, g: number, b: number}}
* @constructor
*/
util.HSVToRGB = function HSVToRGB(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
};
util.HSVToHex = function HSVToHex(h,s,v) {
var rgb = util.HSVToRGB(h,s,v);
return util.RGBToHex(rgb.r,rgb.g,rgb.b);
}
util.hexToHSV = function hexToHSV(hex) {
var rgb = util.hexToRGB(hex);
return util.RGBToHSV(rgb.r,rgb.g,rgb.b);
}
util.isValidHex = function isValidHex(hex) {
var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
return isOk;
}

Loading…
Cancel
Save