Browse Source

legend WIP

css_transitions
Alex de Mulder 10 years ago
parent
commit
c8cc074db1
16 changed files with 910 additions and 627 deletions
  1. +1
    -0
      Jakefile.js
  2. +92
    -10
      dist/vis.css
  3. +343
    -121
      dist/vis.js
  4. +1
    -1
      dist/vis.min.css
  5. +12
    -12
      dist/vis.min.js
  6. +22
    -6
      examples/Graph2d/01_basic.html
  7. +1
    -2
      src/DataSet.js
  8. +1
    -1
      src/graph/graphMixins/physics/BarnesHut.js
  9. +36
    -35
      src/timeline/DataStep.js
  10. +1
    -1
      src/timeline/Graph2d.js
  11. +1
    -1
      src/timeline/Timeline.js
  12. +7
    -1
      src/timeline/component/DataAxis.js
  13. +294
    -78
      src/timeline/component/Linegraph.js
  14. +0
    -340
      src/timeline/component/Linegraph2.js
  15. +0
    -18
      src/timeline/component/css/dataaxis.css
  16. +98
    -0
      src/timeline/component/css/pathStyles.css

+ 1
- 0
Jakefile.js View File

@ -46,6 +46,7 @@ task('build', {async: true}, function () {
'./src/timeline/component/css/customtime.css', './src/timeline/component/css/customtime.css',
'./src/timeline/component/css/dataaxis.css', './src/timeline/component/css/dataaxis.css',
'./src/timeline/component/css/pathStyles.css',
'./src/graph/css/graph-manipulation.css', './src/graph/css/graph-manipulation.css',
'./src/graph/css/graph-navigation.css' './src/graph/css/graph-navigation.css'

+ 92
- 10
dist/vis.css View File

@ -357,23 +357,105 @@
white-space: nowrap; white-space: nowrap;
} }
.vis.timeline .default {
fill:none;
stroke-width:1px;
stroke: #5388ff;
.vis.timeline .group0 {
fill:#4f81bd;
fill-opacity:0;
stroke-width:2px;
stroke: #4f81bd;
}
.vis.timeline .group1 {
fill:#f79646;
fill-opacity:0;
stroke-width:2px;
stroke: #f79646;
}
.vis.timeline .group2 {
fill: #8c51cf;
fill-opacity:0;
stroke-width:2px;
stroke: #8c51cf;
}
.vis.timeline .group3 {
fill: #75c841;
fill-opacity:0;
stroke-width:2px;
stroke: #75c841;
}
.vis.timeline .group4 {
fill: #ff0100;
fill-opacity:0;
stroke-width:2px;
stroke: #ff0100;
}
.vis.timeline .group5 {
fill: #37d8e6;
fill-opacity:0;
stroke-width:2px;
stroke: #37d8e6;
}
.vis.timeline .group6 {
fill: #042662;
fill-opacity:0;
stroke-width:2px;
stroke: #042662;
}
.vis.timeline .group7 {
fill:#00ff26;
fill-opacity:0;
stroke-width:2px;
stroke: #00ff26;
} }
.vis.timeline .redd {
fill:none;
.vis.timeline .group8 {
fill:#ff00ff;
fill-opacity:0;
stroke-width:2px;
stroke: #ff00ff;
}
.vis.timeline .group9 {
fill: #8f3938;
fill-opacity:0;
stroke-width:2px;
stroke: #8f3938;
}
.vis.timeline .fill {
fill-opacity:0.1;
stroke: none;
}
.vis.timeline .point {
stroke-width:2px;
fill-opacity:1.0;
}
.vis.timeline .legendBackground {
stroke-width:1px; stroke-width:1px;
stroke: #ff0000;
fill-opacity:0.9;
fill: #ffffff;
stroke: #c2c2c2;
} }
.vis.timeline .greenn {
fill:none;
.vis.timeline .outline {
stroke-width:1px; stroke-width:1px;
stroke: #00ff00;
fill-opacity:1;
fill: #ffffff;
stroke: #e5e5e5;
} }
div.graph-manipulationDiv { div.graph-manipulationDiv {
border-width:0px; border-width:0px;
border-bottom: 1px; border-bottom: 1px;

+ 343
- 121
dist/vis.js View File

@ -4,8 +4,8 @@
* *
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 1.1.0
* @date 2014-06-16
* @version @@version
* @date @@date
* *
* @license * @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -2096,13 +2096,12 @@ DataSet.prototype.min = function (field) {
DataSet.prototype.distinct = function (field) { DataSet.prototype.distinct = function (field) {
var data = this.data, var data = this.data,
values = [], values = [],
value,
count = 0; count = 0;
for (var prop in data) { for (var prop in data) {
if (data.hasOwnProperty(prop)) { if (data.hasOwnProperty(prop)) {
var item = data[prop]; var item = data[prop];
value = util.convert(item[field], this.convert[field]);
var value = util.convert(item[field], this.convert[field]);
var exists = false; var exists = false;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
if (values[i] == value) { if (values[i] == value) {
@ -2603,13 +2602,19 @@ function DataAxis (body, options) {
this.options = util.extend({}, this.defaultOptions); this.options = util.extend({}, this.defaultOptions);
this.conversionFactor = 1; this.conversionFactor = 1;
this.setOptions(options);
this.width = Number(this.options.width.replace("px","")); this.width = Number(this.options.width.replace("px",""));
this.height = Number(this.options.height.replace("px",""));
// create the HTML DOM // create the HTML DOM
this._create(); this._create();
} }
DataAxis.prototype = new Component(); DataAxis.prototype = new Component();
DataAxis.prototype.setOptions = Component.prototype.setOptions;
//DataAxis.prototype.setOptions = function(options) {
//
//}
/** /**
@ -2996,9 +3001,22 @@ function Linegraph(body, options) {
this.body = body; this.body = body;
this.defaultOptions = { this.defaultOptions = {
shaded: {
enabled: true,
orientation: 'top' // top, bottom
},
barGraph: {
enabled: true,
binSize: 'auto'
},
drawPoints: {
enabled: true,
size: 6,
style: 'square' // square, circle
},
catmullRom: { catmullRom: {
enabled: true, enabled: true,
parametrization: 'centripetal', // uniform (0,0), chordal (1.0), centripetal (0.5)
parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
alpha: 0.5 alpha: 0.5
} }
}; };
@ -3030,8 +3048,9 @@ function Linegraph(body, options) {
this.selection = []; // list with the ids of all selected nodes this.selection = []; // list with the ids of all selected nodes
this.lastStart = this.body.range.start; this.lastStart = this.body.range.start;
this.touchParams = {}; // stores properties while dragging this.touchParams = {}; // stores properties while dragging
this.lines = [];
this.redundantLines = [];
this.svgElements = {};
this.svgLegendElements = {};
// create the HTML DOM // create the HTML DOM
this._create(); this._create();
@ -3069,31 +3088,28 @@ Linegraph.prototype._create = function(){
frame['linegraph'] = this; frame['linegraph'] = this;
this.dom.frame = frame; this.dom.frame = frame;
// create background panel
var background = document.createElement('div');
background.className = 'background';
frame.appendChild(background);
this.dom.background = background;
// create foreground panel
var foreground = document.createElement('div');
foreground.className = 'foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground;
// create svg element for graph drawing. // create svg element for graph drawing.
this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg");
this.svg.style.position = "relative"
this.svg.style.position = "relative";
this.svg.style.height = "300px"; this.svg.style.height = "300px";
this.svg.style.display = "block"; this.svg.style.display = "block";
frame.appendChild(this.svg); frame.appendChild(this.svg);
this.svgLegend = document.createElementNS('http://www.w3.org/2000/svg',"svg");
this.svgLegend.style.position = "absolute";
this.svgLegend.style.top = "10px";
this.svgLegend.style.height = "300px";
this.svgLegend.style.width = "300px";
this.svgLegend.style.display = "block";
frame.appendChild(this.svgLegend);
// panel with time axis // panel with time axis
this.yAxis = new DataAxis(this.body, { this.yAxis = new DataAxis(this.body, {
orientation: 'left', orientation: 'left',
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
width: '90px',
width: '50px',
height: this.svg.style.height height: this.svg.style.height
}); });
@ -3103,22 +3119,20 @@ Linegraph.prototype._create = function(){
Linegraph.prototype.setOptions = function(options) { Linegraph.prototype.setOptions = function(options) {
if (options) { if (options) {
var fields = ['catmullRom'];
var fields = ['barGraph','catmullRom','shaded','drawPoints'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
console.log(this.options);
if (options.catmullRom) { if (options.catmullRom) {
if (typeof options.catmullRom == 'boolean') { if (typeof options.catmullRom == 'boolean') {
this.options.catmullRom.enabled = options.catmullRom; this.options.catmullRom.enabled = options.catmullRom;
} }
else { else {
if (options.catmullRom.enabled) {
this.options.catmullRom.enabled = options.catmullRom.enabled;
}
else {
this.options.catmullRom.enabled = true;
this.options.catmullRom.enabled = true;
for (var prop in options.catmullRom) {
if (options.catmullRom.hasOwnProperty(prop)) {
this.options.catmullRom[prop] = options.catmullRom[prop];
}
} }
if (options.catmullRom.parametrization) { if (options.catmullRom.parametrization) {
this.options.catmullRom.parametrization = options.catmullRom.parametrization;
if (options.catmullRom.parametrization == 'uniform') { if (options.catmullRom.parametrization == 'uniform') {
this.options.catmullRom.alpha = 0; this.options.catmullRom.alpha = 0;
} }
@ -3126,12 +3140,37 @@ Linegraph.prototype.setOptions = function(options) {
this.options.catmullRom.alpha = 1.0; this.options.catmullRom.alpha = 1.0;
} }
else { else {
this.options.catmullRom.parametrization = 'centripetal'
this.options.catmullRom.parametrization = 'centripetal';
this.options.catmullRom.alpha = 0.5; this.options.catmullRom.alpha = 0.5;
} }
} }
if (options.catmullRom.alpha) {
this.options.catmullRom.alpha = options.catmullRom.alpha;
}
}
if (options.drawPoints) {
if (typeof options.catmullRom == 'boolean') {
this.options.drawPoints.enabled = options.drawPoints;
}
else {
this.options.drawPoints.enabled = true;
for (prop in options.drawPoints) {
if (options.drawPoints.hasOwnProperty(prop)) {
this.options.drawPoints[prop] = options.drawPoints[prop];
}
}
}
}
if (options.shaded) {
if (typeof options.shaded == 'boolean') {
this.options.shaded.enabled = options.shaded;
}
else {
this.options.shaded.enabled = true;
for (prop in options.shaded) {
if (options.shaded.hasOwnProperty(prop)) {
this.options.shaded[prop] = options.shaded.drawPoints[prop];
}
} }
} }
} }
@ -3220,7 +3259,10 @@ Linegraph.prototype.setItems = function(items) {
* @param {Number[]} ids * @param {Number[]} ids
* @protected * @protected
*/ */
Linegraph.prototype._onUpdate = function(ids) {};
Linegraph.prototype._onUpdate = function(ids) {
this.updateGraph();
this.redraw();
};
Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate; Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate;
Linegraph.prototype._onRemove = function(ids) {}; Linegraph.prototype._onRemove = function(ids) {};
@ -3244,7 +3286,7 @@ Linegraph.prototype.redraw = function() {
// calculate actual size and position // calculate actual size and position
this.width = this.dom.frame.offsetWidth; this.width = this.dom.frame.offsetWidth;
this.svgLegend.style.left = (this.width - this.svgLegend.offsetWidth - 10) + "px";
// check if this component is resized // check if this component is resized
resized = this._isResized() || resized; resized = this._isResized() || resized;
if (resized) { if (resized) {
@ -3257,85 +3299,203 @@ Linegraph.prototype.redraw = function() {
}; };
Linegraph.prototype.updateGraph = function() {
// reset the lines
this.redundantLines = this.lines;
this.lines = [];
Linegraph.prototype.updateGraph = function () {
// reset the svg elements
this._prepareSVGElements(this.svgElements);
if (this.width != 0 && this.itemsData != null) { if (this.width != 0 && this.itemsData != null) {
// get the range for the y Axis and draw it // get the range for the y Axis and draw it
var yRange = {start:this.itemsData.min('y').y,end:this.itemsData.max('y').y};
var yRange = {start: this.itemsData.min('y').y, end: this.itemsData.max('y').y};
this.yAxis.setRange(yRange); this.yAxis.setRange(yRange);
this.yAxis.redraw(); this.yAxis.redraw();
console.log(this.itemsData);
// look at different lines // look at different lines
var classes = this.itemsData.distinct('className'); var classes = this.itemsData.distinct('className');
var datapoints;
if (classes.length == 0) {
datapoints = this.itemsData.get();
this.drawGraph(datapoints, 'default');
}
else {
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) { for (var i = 0; i < classes.length; i++) {
datapoints = this.itemsData.get({filter: function(item) {return item.className == classes[i];}});
this.drawGraph(datapoints, classes[i]);
this.drawGraph(classes[i], classes.length);
} }
} }
else {
this.drawGraph('group0', 1);
}
this.drawLegend(classes);
} }
// cleanup the redundant lines;
for (var i = 0; i < this.redundantLines.length; i++) {
this.redundantLines[i].parentNode.removeChild(this.redundantLines[i]);
// cleanup unused svg elements
this._cleanupSVGElements(this.svgElements);
};
Linegraph.prototype.drawGraph = function (className, amountOfGraphs) {
var datapoints = this.itemsData.get({filter: function (item) {
return item.className == className || !item.className;
}});
if (this.options.style == 'bar') {
this.drawBarGraph(datapoints, className, amountOfGraphs);
} }
this.redundantLines = [];
}
else {
this.drawLineGraph(datapoints, className);
}
};
Linegraph.prototype.drawGraph = function(datapoints, className) {
Linegraph.prototype.drawBarGraph = function (datapoints, className, amountOfGraphs) {
if (datapoints != null) { if (datapoints != null) {
if (datapoints.length > 0) { if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints); var dataset = this._prepareData(datapoints);
var path, d;
if (this.redundantLines.length != 0) {
path = this.redundantLines[this.redundantLines.length-1];
this.redundantLines.pop()
}
else {
path = document.createElementNS('http://www.w3.org/2000/svg',"path");
this.svg.appendChild(path);
// draw points
for (var i = 0; i < dataset.length; i++) {
this.drawBar(dataset[i].x, dataset[i].y, className);
} }
path.setAttributeNS(null, "class",className);
this.lines.push(path);
}
}
};
Linegraph.prototype.drawBar = function (x, y, className) {
var width = 10;
rect = this._getSVGElement('rect',this.svgElements, this.svg);
rect.setAttributeNS(null, "x", x - 0.5 * width);
rect.setAttributeNS(null, "y", y);
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", this.svg.offsetHeight - y);
rect.setAttributeNS(null, "class", className + " point");
};
Linegraph.prototype.drawLineGraph = function (datapoints, className) {
if (datapoints != null) {
if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints);
var path, d;
path = this._getSVGElement('path', this.svgElements, this.svg);
path.setAttributeNS(null, "class", className);
// construct path from dataset
if (this.options.catmullRom.enabled == true) { if (this.options.catmullRom.enabled == true) {
d = this._catmullRom(dataset,this.options.catmullRom.alpha);
d = this._catmullRom(dataset);
} }
else { else {
d = this._linear(dataset); d = this._linear(dataset);
} }
path.setAttributeNS(null, "d",d);
// append with points for fill and finalize the path
if (this.options.shaded.enabled == true) {
var fillPath = this._getSVGElement('path',this.svgElements, this.svg);
if (this.options.shaded.orientation == 'top') {
var dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
}
else {
var dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
}
fillPath.setAttributeNS(null, "class", className + " fill");
fillPath.setAttributeNS(null, "d", dFill);
}
// copy properties to path for drawing.
path.setAttributeNS(null, "d", "M" + d);
// draw points
if (this.options.drawPoints.enabled == true) {
this.drawPoints(dataset, className, this.svgElements, this.svg);
}
} }
} }
};
Linegraph.prototype.drawPoints = function (dataset, className, container, svg) {
for (var i = 0; i < dataset.length; i++) {
this.drawPoint(dataset[i].x, dataset[i].y, className, container, svg);
}
};
Linegraph.prototype.drawPoint = function(x, y, className, container, svg) {
var point;
if (this.options.drawPoints.style == 'circle') {
point = this._getSVGElement('circle',container,svg);
point.setAttributeNS(null, "cx", x);
point.setAttributeNS(null, "cy", y);
point.setAttributeNS(null, "r", 0.5 * this.options.drawPoints.size);
point.setAttributeNS(null, "class", className + " point");
}
else {
point = this._getSVGElement('rect',container,svg);
point.setAttributeNS(null, "x", x - 0.5*this.options.drawPoints.size);
point.setAttributeNS(null, "y", y - 0.5*this.options.drawPoints.size);
point.setAttributeNS(null, "width", this.options.drawPoints.size);
point.setAttributeNS(null, "height", this.options.drawPoints.size);
point.setAttributeNS(null, "class", className + " point");
}
return point;
} }
Linegraph.prototype._getSVGElement = function (elementType, container, svg) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (container.hasOwnProperty(elementType)) { // this element has been created before
// check if there is an redundant element
if (container[elementType].redundant.length > 0) {
element = container[elementType].redundant[0];
container[elementType].redundant.shift()
}
else {
// create a new element and add it to the SVG
element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
svg.appendChild(element);
}
}
else {
// create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
container[elementType] = {used: [], redundant: []};
svg.appendChild(element);
}
container[elementType].used.push(element);
return element;
};
Linegraph.prototype._prepareData = function(dataset) {
Linegraph.prototype._cleanupSVGElements = function(container) {
// cleanup the redundant svgElements;
for (var elementType in container) {
if (container.hasOwnProperty(elementType)) {
for (var i = 0; i < container[elementType].redundant.length; i++) {
container[elementType].redundant[i].parentNode.removeChild(container[elementType].redundant[i]);
}
container[elementType].redundant = [];
}
}
};
Linegraph.prototype._prepareSVGElements = function(container) {
// cleanup the redundant svgElements;
for (var elementType in container) {
if (container.hasOwnProperty(elementType)) {
container[elementType].redundant = container[elementType].used;
container[elementType].used = [];
}
}
};
Linegraph.prototype._prepareData = function (dataset) {
var extractedData = []; var extractedData = [];
var xValue, yValue;
for (var i = 0; i < dataset.length; i++) { for (var i = 0; i < dataset.length; i++) {
var xValue = this.body.util.toScreen(new Date(dataset[i].x)) + this.width;
var yValue = this.yAxis.convertValue(dataset[i].y);
extractedData.push({x:xValue, y:yValue});
xValue = this.body.util.toScreen(new Date(dataset[i].x)) + this.width;
yValue = this.yAxis.convertValue(dataset[i].y);
extractedData.push({x: xValue, y: yValue});
} }
// extractedData.sort(function (a,b) {return a.x - b.x;});
// extractedData.sort(function (a,b) {return a.x - b.x;});
return extractedData; return extractedData;
}
};
Linegraph.prototype._catmullRomUniform = function(data) { Linegraph.prototype._catmullRomUniform = function(data) {
// catmull rom // catmull rom
var p0, p1, p2, p3, bp1, bp2
var p0, p1, p2, p3, bp1, bp2;
var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var normalization = 1/6; var normalization = 1/6;
var length = data.length; var length = data.length;
@ -3369,16 +3529,15 @@ Linegraph.prototype._catmullRomUniform = function(data) {
return d; return d;
}; };
Linegraph.prototype._catmullRom = function(data, alpha) {
Linegraph.prototype._catmullRom = function(data) {
var alpha = this.options.catmullRom.alpha;
if (alpha == 0 || alpha === undefined) { if (alpha == 0 || alpha === undefined) {
return this._catmullRomUniform(data); return this._catmullRomUniform(data);
} }
else { else {
var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M;
var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var d = "" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var length = data.length; var length = data.length;
for (var i = 0; i < length - 1; i++) { for (var i = 0; i < length - 1; i++) {
@ -3440,8 +3599,6 @@ Linegraph.prototype._catmullRom = function(data, alpha) {
return d; return d;
} }
}; };
Linegraph.prototype._linear = function(data) { Linegraph.prototype._linear = function(data) {
// linear // linear
var d = ""; var d = "";
@ -3454,8 +3611,72 @@ Linegraph.prototype._linear = function(data) {
} }
} }
return d; return d;
};
Linegraph.prototype.drawLegend = function(classes) {
this._prepareSVGElements(this.svgLegendElements);
var x = 0;
var y = 0;
var lineLength = 30;
var fillHeight = 10;
var spacing = 25;
var path, fillPath, outline;
var legendWidth = 298;
var padding = 5;
var border = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
border.setAttributeNS(null, "x", x);
border.setAttributeNS(null, "y", y);
border.setAttributeNS(null, "width", legendWidth);
border.setAttributeNS(null, "height", y + padding + classes.length * spacing);
border.setAttributeNS(null, "class", "legendBackground");
x += 5;
y += fillHeight + padding;
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) {
outline = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", lineLength);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
path.setAttributeNS(null, "class", classes[i]);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + lineLength) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + lineLength) + ","+y+" L"+ (x + lineLength) + "," + (y - fillHeight));
}
else {
fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " +
"L"+x+"," + (y + fillHeight) + " " +
"L"+ (x + lineLength) + "," + (y + fillHeight) +
"L"+ (x + lineLength) + ","+y);
}
fillPath.setAttributeNS(null, "class", classes[i] + " fill");
}
if (this.options.drawPoints.enabled == true) {
this.drawPoint(x + 0.5 * lineLength,y,classes[i], this.svgLegendElements, this.svgLegend);
}
y += spacing;
}
}
else {
//TODO: bars
}
this._cleanupSVGElements(this.svgLegendElements);
} }
/** /**
* @constructor DataStep * @constructor DataStep
* The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an
@ -3486,7 +3707,7 @@ function DataStep(start, end, minimumStep, containerHeight) {
// variables // variables
this.current = 0; this.current = 0;
this.autoScale = true;
this.autoScale = true;
this.stepIndex = 0; this.stepIndex = 0;
this.step = 1; this.step = 1;
this.scale = 1; this.scale = 1;
@ -3521,6 +3742,41 @@ DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight)
} }
}; };
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.1;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var solutionFound = false;
for (var i = 0; i <= orderOfMagnitude; i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
if (solutionFound == true) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/** /**
* Set the range iterator to the start date. * Set the range iterator to the start date.
*/ */
@ -3587,40 +3843,6 @@ DataStep.prototype.getCurrent = function() {
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.1;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var solutionFound = false;
for (var i = 0; i <= orderOfMagnitude; i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
if (solutionFound == true) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/** /**
* Snap a date to a rounded value. * Snap a date to a rounded value.
* The snap intervals are dependent on the current scale and step. * The snap intervals are dependent on the current scale and step.
@ -8727,7 +8949,7 @@ Timeline.prototype.redraw = function() {
} }
else { // orientation == 'bottom' else { // orientation == 'bottom'
// keep the items aligned to the axis at the bottom // keep the items aligned to the axis at the bottom
offset = props.centerContainer.height - props.center.height;
offset = 0;// props.centerContainer.height - props.center.height;
} }
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset+ 'px'; dom.center.style.top = offset+ 'px';
@ -9387,7 +9609,7 @@ Graph2d.prototype.redraw = function() {
} }
else { // orientation == 'bottom' else { // orientation == 'bottom'
// keep the items aligned to the axis at the bottom // keep the items aligned to the axis at the bottom
offset = props.centerContainer.height - props.center.height;
offset = 0; //props.centerContainer.height - props.center.height;
} }
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset+ 'px'; dom.center.style.top = offset+ 'px';
@ -13587,7 +13809,7 @@ var barnesHutMixin = {
* @private * @private
*/ */
_splitBranch : function(parentBranch) { _splitBranch : function(parentBranch) {
// if the branch is filled with a node, replace the node in the new subset.
// if the branch is shaded with a node, replace the node in the new subset.
var containedNode = null; var containedNode = null;
if (parentBranch.childrenCount == 1) { if (parentBranch.childrenCount == 1) {
containedNode = parentBranch.children.data; containedNode = parentBranch.children.data;

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


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


+ 22
- 6
examples/Graph2d/01_basic.html View File

@ -18,17 +18,33 @@
<script type="text/javascript"> <script type="text/javascript">
var container = document.getElementById('visualization'); var container = document.getElementById('visualization');
var items = [ var items = [
{x: '2014-06-11', y: 10, className: 'redd'},
{x: '2014-06-12', y: 25, className: 'redd'},
{x: '2014-06-13', y: 30, className: 'redd'},
{x: '2014-06-14', y: 10, className: 'greenn'},
{x: '2014-06-15', y: 15, className: 'greenn'},
{x: '2014-06-16', y: 30, className: 'greenn'}
{x: '2014-06-11', y: 10, className: 'group0', label: 'red'},
{x: '2014-06-12', y: 25, className: 'group0', label: 'red'},
{x: '2014-06-13', y: 30, className: 'group0', label: 'red'},
{x: '2014-06-14', y: 10, className: 'group1', label: 'green'},
{x: '2014-06-15', y: 15, className: 'group1', label: 'green'},
{x: '2014-06-16', y: 30, className: 'group1', label: 'green'},
{x: '2014-06-17', y: 10, className: 'group2', label: 'green'},
{x: '2014-06-18', y: 15, className: 'group2', label: 'green'},
{x: '2014-06-19', y: 50, className: 'group2', label: 'green'},
{x: '2014-06-20', y: 10, className: 'group3', label: 'green'},
{x: '2014-06-21', y: 19, className: 'group3', label: 'green'},
{x: '2014-06-22', y: 60, className: 'group3', label: 'green'},
{x: '2014-06-23', y: 10, className: 'group4', label: 'red'},
{x: '2014-06-24', y: 25, className: 'group4', label: 'red'},
{x: '2014-06-25', y: 30, className: 'group4', label: 'red'}
// {x: '2014-06-11', y: 10},
// {x: '2014-06-12', y: 25},
// {x: '2014-06-13', y: 30},
// {x: '2014-06-14', y: 10},
// {x: '2014-06-15', y: 15},
// {x: '2014-06-16', y: 30}
]; ];
var dataset = new vis.DataSet(items); var dataset = new vis.DataSet(items);
var options = {}; var options = {};
var graph2d = new vis.Graph2d(container, dataset, options); var graph2d = new vis.Graph2d(container, dataset, options);
</script> </script>
</body> </body>
</html> </html>

+ 1
- 2
src/DataSet.js View File

@ -794,13 +794,12 @@ DataSet.prototype.min = function (field) {
DataSet.prototype.distinct = function (field) { DataSet.prototype.distinct = function (field) {
var data = this.data, var data = this.data,
values = [], values = [],
value,
count = 0; count = 0;
for (var prop in data) { for (var prop in data) {
if (data.hasOwnProperty(prop)) { if (data.hasOwnProperty(prop)) {
var item = data[prop]; var item = data[prop];
value = util.convert(item[field], this.convert[field]);
var value = util.convert(item[field], this.convert[field]);
var exists = false; var exists = false;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
if (values[i] == value) { if (values[i] == value) {

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

@ -256,7 +256,7 @@ var barnesHutMixin = {
* @private * @private
*/ */
_splitBranch : function(parentBranch) { _splitBranch : function(parentBranch) {
// if the branch is filled with a node, replace the node in the new subset.
// if the branch is shaded with a node, replace the node in the new subset.
var containedNode = null; var containedNode = null;
if (parentBranch.childrenCount == 1) { if (parentBranch.childrenCount == 1) {
containedNode = parentBranch.children.data; containedNode = parentBranch.children.data;

+ 36
- 35
src/timeline/DataStep.js View File

@ -28,7 +28,7 @@ function DataStep(start, end, minimumStep, containerHeight) {
// variables // variables
this.current = 0; this.current = 0;
this.autoScale = true;
this.autoScale = true;
this.stepIndex = 0; this.stepIndex = 0;
this.step = 1; this.step = 1;
this.scale = 1; this.scale = 1;
@ -63,6 +63,41 @@ DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight)
} }
}; };
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.1;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var solutionFound = false;
for (var i = 0; i <= orderOfMagnitude; i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
if (solutionFound == true) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/** /**
* Set the range iterator to the start date. * Set the range iterator to the start date.
*/ */
@ -129,40 +164,6 @@ DataStep.prototype.getCurrent = function() {
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.1;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var solutionFound = false;
for (var i = 0; i <= orderOfMagnitude; i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
if (solutionFound == true) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/** /**
* Snap a date to a rounded value. * Snap a date to a rounded value.
* The snap intervals are dependent on the current scale and step. * The snap intervals are dependent on the current scale and step.

+ 1
- 1
src/timeline/Graph2d.js View File

@ -548,7 +548,7 @@ Graph2d.prototype.redraw = function() {
} }
else { // orientation == 'bottom' else { // orientation == 'bottom'
// keep the items aligned to the axis at the bottom // keep the items aligned to the axis at the bottom
offset = props.centerContainer.height - props.center.height;
offset = 0; //props.centerContainer.height - props.center.height;
} }
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset+ 'px'; dom.center.style.top = offset+ 'px';

+ 1
- 1
src/timeline/Timeline.js View File

@ -568,7 +568,7 @@ Timeline.prototype.redraw = function() {
} }
else { // orientation == 'bottom' else { // orientation == 'bottom'
// keep the items aligned to the axis at the bottom // keep the items aligned to the axis at the bottom
offset = props.centerContainer.height - props.center.height;
offset = 0;// props.centerContainer.height - props.center.height;
} }
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset+ 'px'; dom.center.style.top = offset+ 'px';

+ 7
- 1
src/timeline/component/DataAxis.js View File

@ -43,13 +43,19 @@ function DataAxis (body, options) {
this.options = util.extend({}, this.defaultOptions); this.options = util.extend({}, this.defaultOptions);
this.conversionFactor = 1; this.conversionFactor = 1;
this.setOptions(options);
this.width = Number(this.options.width.replace("px","")); this.width = Number(this.options.width.replace("px",""));
this.height = Number(this.options.height.replace("px",""));
// create the HTML DOM // create the HTML DOM
this._create(); this._create();
} }
DataAxis.prototype = new Component(); DataAxis.prototype = new Component();
DataAxis.prototype.setOptions = Component.prototype.setOptions;
//DataAxis.prototype.setOptions = function(options) {
//
//}
/** /**

+ 294
- 78
src/timeline/component/Linegraph.js View File

@ -5,9 +5,22 @@ function Linegraph(body, options) {
this.body = body; this.body = body;
this.defaultOptions = { this.defaultOptions = {
shaded: {
enabled: true,
orientation: 'top' // top, bottom
},
barGraph: {
enabled: true,
binSize: 'auto'
},
drawPoints: {
enabled: true,
size: 6,
style: 'square' // square, circle
},
catmullRom: { catmullRom: {
enabled: true, enabled: true,
parametrization: 'centripetal', // uniform (0,0), chordal (1.0), centripetal (0.5)
parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
alpha: 0.5 alpha: 0.5
} }
}; };
@ -39,8 +52,9 @@ function Linegraph(body, options) {
this.selection = []; // list with the ids of all selected nodes this.selection = []; // list with the ids of all selected nodes
this.lastStart = this.body.range.start; this.lastStart = this.body.range.start;
this.touchParams = {}; // stores properties while dragging this.touchParams = {}; // stores properties while dragging
this.lines = [];
this.redundantLines = [];
this.svgElements = {};
this.svgLegendElements = {};
// create the HTML DOM // create the HTML DOM
this._create(); this._create();
@ -78,31 +92,28 @@ Linegraph.prototype._create = function(){
frame['linegraph'] = this; frame['linegraph'] = this;
this.dom.frame = frame; this.dom.frame = frame;
// create background panel
var background = document.createElement('div');
background.className = 'background';
frame.appendChild(background);
this.dom.background = background;
// create foreground panel
var foreground = document.createElement('div');
foreground.className = 'foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground;
// create svg element for graph drawing. // create svg element for graph drawing.
this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg");
this.svg.style.position = "relative"
this.svg.style.position = "relative";
this.svg.style.height = "300px"; this.svg.style.height = "300px";
this.svg.style.display = "block"; this.svg.style.display = "block";
frame.appendChild(this.svg); frame.appendChild(this.svg);
this.svgLegend = document.createElementNS('http://www.w3.org/2000/svg',"svg");
this.svgLegend.style.position = "absolute";
this.svgLegend.style.top = "10px";
this.svgLegend.style.height = "300px";
this.svgLegend.style.width = "300px";
this.svgLegend.style.display = "block";
frame.appendChild(this.svgLegend);
// panel with time axis // panel with time axis
this.yAxis = new DataAxis(this.body, { this.yAxis = new DataAxis(this.body, {
orientation: 'left', orientation: 'left',
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
width: '90px',
width: '50px',
height: this.svg.style.height height: this.svg.style.height
}); });
@ -112,22 +123,20 @@ Linegraph.prototype._create = function(){
Linegraph.prototype.setOptions = function(options) { Linegraph.prototype.setOptions = function(options) {
if (options) { if (options) {
var fields = ['catmullRom'];
var fields = ['barGraph','catmullRom','shaded','drawPoints'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
console.log(this.options);
if (options.catmullRom) { if (options.catmullRom) {
if (typeof options.catmullRom == 'boolean') { if (typeof options.catmullRom == 'boolean') {
this.options.catmullRom.enabled = options.catmullRom; this.options.catmullRom.enabled = options.catmullRom;
} }
else { else {
if (options.catmullRom.enabled) {
this.options.catmullRom.enabled = options.catmullRom.enabled;
}
else {
this.options.catmullRom.enabled = true;
this.options.catmullRom.enabled = true;
for (var prop in options.catmullRom) {
if (options.catmullRom.hasOwnProperty(prop)) {
this.options.catmullRom[prop] = options.catmullRom[prop];
}
} }
if (options.catmullRom.parametrization) { if (options.catmullRom.parametrization) {
this.options.catmullRom.parametrization = options.catmullRom.parametrization;
if (options.catmullRom.parametrization == 'uniform') { if (options.catmullRom.parametrization == 'uniform') {
this.options.catmullRom.alpha = 0; this.options.catmullRom.alpha = 0;
} }
@ -135,12 +144,37 @@ Linegraph.prototype.setOptions = function(options) {
this.options.catmullRom.alpha = 1.0; this.options.catmullRom.alpha = 1.0;
} }
else { else {
this.options.catmullRom.parametrization = 'centripetal'
this.options.catmullRom.parametrization = 'centripetal';
this.options.catmullRom.alpha = 0.5; this.options.catmullRom.alpha = 0.5;
} }
} }
if (options.catmullRom.alpha) {
this.options.catmullRom.alpha = options.catmullRom.alpha;
}
}
if (options.drawPoints) {
if (typeof options.catmullRom == 'boolean') {
this.options.drawPoints.enabled = options.drawPoints;
}
else {
this.options.drawPoints.enabled = true;
for (prop in options.drawPoints) {
if (options.drawPoints.hasOwnProperty(prop)) {
this.options.drawPoints[prop] = options.drawPoints[prop];
}
}
}
}
if (options.shaded) {
if (typeof options.shaded == 'boolean') {
this.options.shaded.enabled = options.shaded;
}
else {
this.options.shaded.enabled = true;
for (prop in options.shaded) {
if (options.shaded.hasOwnProperty(prop)) {
this.options.shaded[prop] = options.shaded.drawPoints[prop];
}
} }
} }
} }
@ -229,7 +263,10 @@ Linegraph.prototype.setItems = function(items) {
* @param {Number[]} ids * @param {Number[]} ids
* @protected * @protected
*/ */
Linegraph.prototype._onUpdate = function(ids) {};
Linegraph.prototype._onUpdate = function(ids) {
this.updateGraph();
this.redraw();
};
Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate; Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate;
Linegraph.prototype._onRemove = function(ids) {}; Linegraph.prototype._onRemove = function(ids) {};
@ -253,7 +290,7 @@ Linegraph.prototype.redraw = function() {
// calculate actual size and position // calculate actual size and position
this.width = this.dom.frame.offsetWidth; this.width = this.dom.frame.offsetWidth;
this.svgLegend.style.left = (this.width - this.svgLegend.offsetWidth - 10) + "px";
// check if this component is resized // check if this component is resized
resized = this._isResized() || resized; resized = this._isResized() || resized;
if (resized) { if (resized) {
@ -266,85 +303,203 @@ Linegraph.prototype.redraw = function() {
}; };
Linegraph.prototype.updateGraph = function() {
// reset the lines
this.redundantLines = this.lines;
this.lines = [];
Linegraph.prototype.updateGraph = function () {
// reset the svg elements
this._prepareSVGElements(this.svgElements);
if (this.width != 0 && this.itemsData != null) { if (this.width != 0 && this.itemsData != null) {
// get the range for the y Axis and draw it // get the range for the y Axis and draw it
var yRange = {start:this.itemsData.min('y').y,end:this.itemsData.max('y').y};
var yRange = {start: this.itemsData.min('y').y, end: this.itemsData.max('y').y};
this.yAxis.setRange(yRange); this.yAxis.setRange(yRange);
this.yAxis.redraw(); this.yAxis.redraw();
console.log(this.itemsData);
// look at different lines // look at different lines
var classes = this.itemsData.distinct('className'); var classes = this.itemsData.distinct('className');
var datapoints;
if (classes.length == 0) {
datapoints = this.itemsData.get();
this.drawGraph(datapoints, 'default');
}
else {
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) { for (var i = 0; i < classes.length; i++) {
datapoints = this.itemsData.get({filter: function(item) {return item.className == classes[i];}});
this.drawGraph(datapoints, classes[i]);
this.drawGraph(classes[i], classes.length);
} }
} }
else {
this.drawGraph('group0', 1);
}
this.drawLegend(classes);
} }
// cleanup the redundant lines;
for (var i = 0; i < this.redundantLines.length; i++) {
this.redundantLines[i].parentNode.removeChild(this.redundantLines[i]);
// cleanup unused svg elements
this._cleanupSVGElements(this.svgElements);
};
Linegraph.prototype.drawGraph = function (className, amountOfGraphs) {
var datapoints = this.itemsData.get({filter: function (item) {
return item.className == className || !item.className;
}});
if (this.options.style == 'bar') {
this.drawBarGraph(datapoints, className, amountOfGraphs);
} }
this.redundantLines = [];
}
else {
this.drawLineGraph(datapoints, className);
}
};
Linegraph.prototype.drawGraph = function(datapoints, className) {
Linegraph.prototype.drawBarGraph = function (datapoints, className, amountOfGraphs) {
if (datapoints != null) { if (datapoints != null) {
if (datapoints.length > 0) { if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints); var dataset = this._prepareData(datapoints);
var path, d;
if (this.redundantLines.length != 0) {
path = this.redundantLines[this.redundantLines.length-1];
this.redundantLines.pop()
}
else {
path = document.createElementNS('http://www.w3.org/2000/svg',"path");
this.svg.appendChild(path);
// draw points
for (var i = 0; i < dataset.length; i++) {
this.drawBar(dataset[i].x, dataset[i].y, className);
} }
path.setAttributeNS(null, "class",className);
this.lines.push(path);
}
}
};
Linegraph.prototype.drawBar = function (x, y, className) {
var width = 10;
rect = this._getSVGElement('rect',this.svgElements, this.svg);
rect.setAttributeNS(null, "x", x - 0.5 * width);
rect.setAttributeNS(null, "y", y);
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", this.svg.offsetHeight - y);
rect.setAttributeNS(null, "class", className + " point");
};
Linegraph.prototype.drawLineGraph = function (datapoints, className) {
if (datapoints != null) {
if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints);
var path, d;
path = this._getSVGElement('path', this.svgElements, this.svg);
path.setAttributeNS(null, "class", className);
// construct path from dataset
if (this.options.catmullRom.enabled == true) { if (this.options.catmullRom.enabled == true) {
d = this._catmullRom(dataset,this.options.catmullRom.alpha);
d = this._catmullRom(dataset);
} }
else { else {
d = this._linear(dataset); d = this._linear(dataset);
} }
path.setAttributeNS(null, "d",d);
// append with points for fill and finalize the path
if (this.options.shaded.enabled == true) {
var fillPath = this._getSVGElement('path',this.svgElements, this.svg);
if (this.options.shaded.orientation == 'top') {
var dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
}
else {
var dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
}
fillPath.setAttributeNS(null, "class", className + " fill");
fillPath.setAttributeNS(null, "d", dFill);
}
// copy properties to path for drawing.
path.setAttributeNS(null, "d", "M" + d);
// draw points
if (this.options.drawPoints.enabled == true) {
this.drawPoints(dataset, className, this.svgElements, this.svg);
}
} }
} }
};
Linegraph.prototype.drawPoints = function (dataset, className, container, svg) {
for (var i = 0; i < dataset.length; i++) {
this.drawPoint(dataset[i].x, dataset[i].y, className, container, svg);
}
};
Linegraph.prototype.drawPoint = function(x, y, className, container, svg) {
var point;
if (this.options.drawPoints.style == 'circle') {
point = this._getSVGElement('circle',container,svg);
point.setAttributeNS(null, "cx", x);
point.setAttributeNS(null, "cy", y);
point.setAttributeNS(null, "r", 0.5 * this.options.drawPoints.size);
point.setAttributeNS(null, "class", className + " point");
}
else {
point = this._getSVGElement('rect',container,svg);
point.setAttributeNS(null, "x", x - 0.5*this.options.drawPoints.size);
point.setAttributeNS(null, "y", y - 0.5*this.options.drawPoints.size);
point.setAttributeNS(null, "width", this.options.drawPoints.size);
point.setAttributeNS(null, "height", this.options.drawPoints.size);
point.setAttributeNS(null, "class", className + " point");
}
return point;
} }
Linegraph.prototype._getSVGElement = function (elementType, container, svg) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (container.hasOwnProperty(elementType)) { // this element has been created before
// check if there is an redundant element
if (container[elementType].redundant.length > 0) {
element = container[elementType].redundant[0];
container[elementType].redundant.shift()
}
else {
// create a new element and add it to the SVG
element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
svg.appendChild(element);
}
}
else {
// create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
container[elementType] = {used: [], redundant: []};
svg.appendChild(element);
}
container[elementType].used.push(element);
return element;
};
Linegraph.prototype._cleanupSVGElements = function(container) {
// cleanup the redundant svgElements;
for (var elementType in container) {
if (container.hasOwnProperty(elementType)) {
for (var i = 0; i < container[elementType].redundant.length; i++) {
container[elementType].redundant[i].parentNode.removeChild(container[elementType].redundant[i]);
}
container[elementType].redundant = [];
}
}
};
Linegraph.prototype._prepareSVGElements = function(container) {
// cleanup the redundant svgElements;
for (var elementType in container) {
if (container.hasOwnProperty(elementType)) {
container[elementType].redundant = container[elementType].used;
container[elementType].used = [];
}
}
};
Linegraph.prototype._prepareData = function(dataset) {
Linegraph.prototype._prepareData = function (dataset) {
var extractedData = []; var extractedData = [];
var xValue, yValue;
for (var i = 0; i < dataset.length; i++) { for (var i = 0; i < dataset.length; i++) {
var xValue = this.body.util.toScreen(new Date(dataset[i].x)) + this.width;
var yValue = this.yAxis.convertValue(dataset[i].y);
extractedData.push({x:xValue, y:yValue});
xValue = this.body.util.toScreen(new Date(dataset[i].x)) + this.width;
yValue = this.yAxis.convertValue(dataset[i].y);
extractedData.push({x: xValue, y: yValue});
} }
// extractedData.sort(function (a,b) {return a.x - b.x;});
// extractedData.sort(function (a,b) {return a.x - b.x;});
return extractedData; return extractedData;
}
};
Linegraph.prototype._catmullRomUniform = function(data) { Linegraph.prototype._catmullRomUniform = function(data) {
// catmull rom // catmull rom
var p0, p1, p2, p3, bp1, bp2
var p0, p1, p2, p3, bp1, bp2;
var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var normalization = 1/6; var normalization = 1/6;
var length = data.length; var length = data.length;
@ -378,16 +533,15 @@ Linegraph.prototype._catmullRomUniform = function(data) {
return d; return d;
}; };
Linegraph.prototype._catmullRom = function(data, alpha) {
Linegraph.prototype._catmullRom = function(data) {
var alpha = this.options.catmullRom.alpha;
if (alpha == 0 || alpha === undefined) { if (alpha == 0 || alpha === undefined) {
return this._catmullRomUniform(data); return this._catmullRomUniform(data);
} }
else { else {
var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M;
var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var d = "" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var length = data.length; var length = data.length;
for (var i = 0; i < length - 1; i++) { for (var i = 0; i < length - 1; i++) {
@ -449,8 +603,6 @@ Linegraph.prototype._catmullRom = function(data, alpha) {
return d; return d;
} }
}; };
Linegraph.prototype._linear = function(data) { Linegraph.prototype._linear = function(data) {
// linear // linear
var d = ""; var d = "";
@ -463,4 +615,68 @@ Linegraph.prototype._linear = function(data) {
} }
} }
return d; return d;
};
Linegraph.prototype.drawLegend = function(classes) {
this._prepareSVGElements(this.svgLegendElements);
var x = 0;
var y = 0;
var lineLength = 30;
var fillHeight = 10;
var spacing = 25;
var path, fillPath, outline;
var legendWidth = 298;
var padding = 5;
var border = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
border.setAttributeNS(null, "x", x);
border.setAttributeNS(null, "y", y);
border.setAttributeNS(null, "width", legendWidth);
border.setAttributeNS(null, "height", y + padding + classes.length * spacing);
border.setAttributeNS(null, "class", "legendBackground");
x += 5;
y += fillHeight + padding;
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) {
outline = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", lineLength);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
path.setAttributeNS(null, "class", classes[i]);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + lineLength) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + lineLength) + ","+y+" L"+ (x + lineLength) + "," + (y - fillHeight));
}
else {
fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " +
"L"+x+"," + (y + fillHeight) + " " +
"L"+ (x + lineLength) + "," + (y + fillHeight) +
"L"+ (x + lineLength) + ","+y);
}
fillPath.setAttributeNS(null, "class", classes[i] + " fill");
}
if (this.options.drawPoints.enabled == true) {
this.drawPoint(x + 0.5 * lineLength,y,classes[i], this.svgLegendElements, this.svgLegend);
}
y += spacing;
}
}
else {
//TODO: bars
}
this._cleanupSVGElements(this.svgLegendElements);
} }

+ 0
- 340
src/timeline/component/Linegraph2.js View File

@ -1,340 +0,0 @@
/**
* Created by Alex on 5/6/14.
*/
var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
/**
* An ItemSet holds a set of items and ranges which can be displayed in a
* range. The width is determined by the parent of the ItemSet, and the height
* is determined by the size of the items.
* @param {Panel} backgroundPanel Panel which can be used to display the
* vertical lines of box items.
* @param {Panel} axisPanel Panel on the axis where the dots of box-items
* can be displayed.
* @param {Panel} sidePanel Left side panel holding labels
* @param {Object} [options] See ItemSet.setOptions for the available options.
* @constructor ItemSet
* @extends Panel
*/
function Linegraph(backgroundPanel, axisPanel, sidePanel, options, timeline, sidePanelParent) {
this.id = util.randomUUID();
this.timeline = timeline;
// one options object is shared by this itemset and all its items
this.options = options || {};
this.backgroundPanel = backgroundPanel;
this.axisPanel = axisPanel;
this.sidePanel = sidePanel;
this.sidePanelParent = sidePanelParent;
this.itemOptions = Object.create(this.options);
this.dom = {};
this.hammer = null;
this.itemsData = null; // DataSet
this.groupsData = null; // DataSet
this.range = null; // Range or Object {start: number, end: number}
// listeners for the DataSet of the items
// this.itemListeners = {
// 'add': function(event, params, senderId) {
// if (senderId != me.id) me._onAdd(params.items);
// },
// 'update': function(event, params, senderId) {
// if (senderId != me.id) me._onUpdate(params.items);
// },
// 'remove': function(event, params, senderId) {
// if (senderId != me.id) me._onRemove(params.items);
// }
// };
//
// // listeners for the DataSet of the groups
// this.groupListeners = {
// 'add': function(event, params, senderId) {
// if (senderId != me.id) me._onAddGroups(params.items);
// },
// 'update': function(event, params, senderId) {
// if (senderId != me.id) me._onUpdateGroups(params.items);
// },
// 'remove': function(event, params, senderId) {
// if (senderId != me.id) me._onRemoveGroups(params.items);
// }
// };
this.items = {}; // object with an Item for every data item
this.groups = {}; // Group object for every group
this.groupIds = [];
this.selection = []; // list with the ids of all selected nodes
this.stackDirty = true; // if true, all items will be restacked on next repaint
this.touchParams = {}; // stores properties while dragging
// create the HTML DOM
this.lastStart = 0;
this._create();
var me = this;
this.timeline.on("rangechange", function() {
if (me.lastStart != 0) {
var offset = me.range.start - me.lastStart;
var range = me.range.end - me.range.start;
if (me.width != 0) {
var rangePerPixelInv = me.width/range;
var xOffset = offset * rangePerPixelInv;
me.svg.style.left = util.option.asSize(-me.width - xOffset);
}
}
})
this.timeline.on("rangechanged", function() {
me.lastStart = me.range.start;
me.svg.style.left = util.option.asSize(-me.width);
me.setData.apply(me);
});
// this.data = new DataView(this.items)
}
//Linegraph.prototype = new Panel();
/**
* Create the HTML DOM for the ItemSet
*/
Linegraph.prototype._create = function(){
var frame = document.createElement('div');
frame['timeline-linegraph'] = this;
this.frame = frame;
this.frame.className = 'itemset';
// create background panel
var background = document.createElement('div');
background.className = 'background';
this.backgroundPanel.frame.appendChild(background);
this.dom.background = background;
// create foreground panel
var foreground = document.createElement('div');
foreground.className = 'foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground;
// // create axis panel
// var axis = document.createElement('div');
// axis.className = 'axis';
// this.dom.axis = axis;
// this.axisPanel.frame.appendChild(axis);
//
// // create labelset
// var labelSet = document.createElement('div');
// labelSet.className = 'labelset';
// this.dom.labelSet = labelSet;
// this.sidePanel.frame.appendChild(labelSet);
// this.yAxisDiv = document.createElement('div');
// this.yAxisDiv.style.backgroundColor = 'rgb(220,220,220)';
// this.yAxisDiv.style.width = '100px';
// this.yAxisDiv.style.height = this.svg.style.height;
this._createAxis();
// this.dom.yAxisDiv = this.yAxisDiv;
// this.sidePanel.frame.appendChild(this.yAxisDiv);
this.sidePanel.showPanel.apply(this.sidePanel);
this.sidePanelParent.showPanel();
};
Linegraph.prototype._createAxis = function() {
}
Linegraph.prototype.setData = function() {
if (this.width != 0) {
var dataview = new DataView(this.timeline.itemsData,
{filter: function (item) {return (item.value);}})
var datapoints = dataview.get();
if (datapoints != null) {
if (datapoints.length > 0) {
var dataset = this._extractData(datapoints);
var data = dataset.data;
console.log("height",data,datapoints, dataset);
this.yAxis.setRange({start:dataset.range.low,end:dataset.range.high});
this.yAxis.repaint();
data = this.yAxis.convertValues(data);
var d, d2, d3;
d = this._catmullRom(data,0.5);
d3 = this._catmullRom(data,0);
d2 = this._catmullRom(data,1);
// var data2 = [];
// this.startTime = this.range.start;
// var min = Date.now() - 3600000 * 24 * 30;
// var max = Date.now() + 3600000 * 24 * 10;
// var count = 60;
// var step = (max-min) / count;
//
// var range = this.range.end - this.range.start;
// var rangePerPixel = range/this.width;
// var rangePerPixelInv = this.width/range;
// var xOffset = -this.range.start + this.width*rangePerPixel;
//
// for (var i = 0; i < count; i++) {
// data2.push({x:(min + i*step + xOffset) * rangePerPixelInv, y: 250*(i%2) + 25})
// }
//
// var d2 = this._catmullRom(data2);
this.path.setAttributeNS(null, "d",d);
this.path2.setAttributeNS(null, "d",d2);
this.path3.setAttributeNS(null, "d",d3);
}
}
}
}
/**
* Set options for the Linegraph. Existing options will be extended/overwritten.
* @param {Object} [options] The following options are available:
* {String | function} [className]
* class name for the itemset
* {String} [type]
* Default type for the items. Choose from 'box'
* (default), 'point', or 'range'. The default
* Style can be overwritten by individual items.
* {String} align
* Alignment for the items, only applicable for
* ItemBox. Choose 'center' (default), 'left', or
* 'right'.
* {String} orientation
* Orientation of the item set. Choose 'top' or
* 'bottom' (default).
* {Number} margin.axis
* Margin between the axis and the items in pixels.
* Default is 20.
* {Number} margin.item
* Margin between items in pixels. Default is 10.
* {Number} padding
* Padding of the contents of an item in pixels.
* Must correspond with the items css. Default is 5.
* {Function} snap
* Function to let items snap to nice dates when
* dragging items.
*/
Linegraph.prototype.setOptions = function(options) {
Component.prototype.setOptions.call(this, options);
};
Linegraph.prototype._extractData = function(dataset) {
var extractedData = [];
var low = dataset[0].value;
var high = dataset[0].value;
var range = this.range.end - this.range.start;
var rangePerPixel = range/this.width;
var rangePerPixelInv = this.width/range;
var xOffset = -this.range.start + this.width*rangePerPixel;
for (var i = 0; i < dataset.length; i++) {
var val = new Date(dataset[i].start).getTime();
val += xOffset;
val *= rangePerPixelInv;
extractedData.push({x:val, y:dataset[i].value});
if (low > dataset[i].value) {
low = dataset[i].value;
}
if (high < dataset[i].value) {
high = dataset[i].value;
}
}
//extractedData.sort(function (a,b) {return a.x - b.x;})
return {range:{low:low,high:high},data:extractedData};
}
/**
* Set range (start and end).
* @param {Range | Object} range A Range or an object containing start and end.
*/
Linegraph.prototype.setRange = function(range) {
if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
throw new TypeError('Range must be an instance of Range, ' +
'or an object containing start and end.');
}
this.range = range;
};
Linegraph.prototype.repaint = function() {
var margin = this.options.margin,
range = this.range,
asSize = util.option.asSize,
asString = util.option.asString,
options = this.options,
orientation = this.getOption('orientation'),
resized = false,
frame = this.frame;
// TODO: document this feature to specify one margin for both item and axis distance
if (typeof margin === 'number') {
margin = {
item: margin,
axis: margin
};
}
// update className
this.frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : '');
// check whether zoomed (in that case we need to re-stack everything)
// TODO: would be nicer to get this as a trigger from Range
var visibleInterval = this.range.end - this.range.start;
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth);
if (zoomed) this.stackDirty = true;
this.lastVisibleInterval = visibleInterval;
this.lastWidth = this.width;
// reposition frame
this.frame.style.left = asSize(options.left, '');
this.frame.style.right = asSize(options.right, '');
this.frame.style.top = asSize((orientation == 'top') ? '0' : '');
this.frame.style.bottom = asSize((orientation == 'top') ? '' : '0');
this.frame.style.width = asSize(options.width, '100%');
// frame.style.height = asSize(height);
//frame.style.height = asSize('height' in options ? options.height : height); // TODO: reckon with height
// calculate actual size and position
this.top = this.frame.offsetTop;
this.left = this.frame.offsetLeft;
this.width = this.frame.offsetWidth;
// this.height = height;
// check if this component is resized
resized = this._isResized() || resized;
if (resized) {
this.svg.style.width = asSize(3*this.width);
this.svg.style.left = asSize(-this.width);
}
if (zoomed) {
this.setData();
}
}

+ 0
- 18
src/timeline/component/css/dataaxis.css View File

@ -32,21 +32,3 @@
color: #979797; color: #979797;
white-space: nowrap; white-space: nowrap;
} }
.vis.timeline .default {
fill:none;
stroke-width:1px;
stroke: #5388ff;
}
.vis.timeline .redd {
fill:none;
stroke-width:1px;
stroke: #ff0000;
}
.vis.timeline .greenn {
fill:none;
stroke-width:1px;
stroke: #00ff00;
}

+ 98
- 0
src/timeline/component/css/pathStyles.css View File

@ -0,0 +1,98 @@
.vis.timeline .group0 {
fill:#4f81bd;
fill-opacity:0;
stroke-width:2px;
stroke: #4f81bd;
}
.vis.timeline .group1 {
fill:#f79646;
fill-opacity:0;
stroke-width:2px;
stroke: #f79646;
}
.vis.timeline .group2 {
fill: #8c51cf;
fill-opacity:0;
stroke-width:2px;
stroke: #8c51cf;
}
.vis.timeline .group3 {
fill: #75c841;
fill-opacity:0;
stroke-width:2px;
stroke: #75c841;
}
.vis.timeline .group4 {
fill: #ff0100;
fill-opacity:0;
stroke-width:2px;
stroke: #ff0100;
}
.vis.timeline .group5 {
fill: #37d8e6;
fill-opacity:0;
stroke-width:2px;
stroke: #37d8e6;
}
.vis.timeline .group6 {
fill: #042662;
fill-opacity:0;
stroke-width:2px;
stroke: #042662;
}
.vis.timeline .group7 {
fill:#00ff26;
fill-opacity:0;
stroke-width:2px;
stroke: #00ff26;
}
.vis.timeline .group8 {
fill:#ff00ff;
fill-opacity:0;
stroke-width:2px;
stroke: #ff00ff;
}
.vis.timeline .group9 {
fill: #8f3938;
fill-opacity:0;
stroke-width:2px;
stroke: #8f3938;
}
.vis.timeline .fill {
fill-opacity:0.1;
stroke: none;
}
.vis.timeline .point {
stroke-width:2px;
fill-opacity:1.0;
}
.vis.timeline .legendBackground {
stroke-width:1px;
fill-opacity:0.9;
fill: #ffffff;
stroke: #c2c2c2;
}
.vis.timeline .outline {
stroke-width:1px;
fill-opacity:1;
fill: #ffffff;
stroke: #e5e5e5;
}

Loading…
Cancel
Save