|
var DOMutil = require('../../../DOMutil');
|
|
var Points = require('./points');
|
|
|
|
/**
|
|
*
|
|
* @param {vis.GraphGroup.id} groupId
|
|
* @param {Object} options // TODO: Describe options
|
|
* @constructor Bargraph
|
|
*/
|
|
function Bargraph(groupId, options) { // eslint-disable-line no-unused-vars
|
|
}
|
|
|
|
Bargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
|
|
var fillHeight = iconHeight * 0.5;
|
|
var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
|
|
outline.setAttributeNS(null, "x", x);
|
|
outline.setAttributeNS(null, "y", y - fillHeight);
|
|
outline.setAttributeNS(null, "width", iconWidth);
|
|
outline.setAttributeNS(null, "height", 2 * fillHeight);
|
|
outline.setAttributeNS(null, "class", "vis-outline");
|
|
|
|
var barWidth = Math.round(0.3 * iconWidth);
|
|
var originalWidth = group.options.barChart.width;
|
|
var scale = originalWidth / barWidth;
|
|
var bar1Height = Math.round(0.4 * iconHeight);
|
|
var bar2Height = Math.round(0.75 * iconHeight);
|
|
|
|
var offset = Math.round((iconWidth - (2 * barWidth)) / 3);
|
|
|
|
DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
|
|
DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
|
|
|
|
if (group.options.drawPoints.enabled == true) {
|
|
var groupTemplate = {
|
|
style: group.options.drawPoints.style,
|
|
styles: group.options.drawPoints.styles,
|
|
size: (group.options.drawPoints.size / scale),
|
|
className: group.className
|
|
};
|
|
DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg);
|
|
DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* draw a bar graph
|
|
*
|
|
* @param {Array<vis.GraphGroup.id>} groupIds
|
|
* @param {Object} processedGroupData
|
|
* @param {{svg: Object, svgElements: Array<Object>, options: Object, groups: Array<vis.Group>}} framework
|
|
*/
|
|
Bargraph.draw = function (groupIds, processedGroupData, framework) {
|
|
var combinedData = [];
|
|
var intersections = {};
|
|
var coreDistance;
|
|
var key, drawData;
|
|
var group;
|
|
var i, j;
|
|
var barPoints = 0;
|
|
|
|
// combine all barchart data
|
|
for (i = 0; i < groupIds.length; i++) {
|
|
group = framework.groups[groupIds[i]];
|
|
if (group.options.style === 'bar') {
|
|
if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) {
|
|
for (j = 0; j < processedGroupData[groupIds[i]].length; j++) {
|
|
combinedData.push({
|
|
screen_x: processedGroupData[groupIds[i]][j].screen_x,
|
|
screen_end: processedGroupData[groupIds[i]][j].screen_end,
|
|
screen_y: processedGroupData[groupIds[i]][j].screen_y,
|
|
x: processedGroupData[groupIds[i]][j].x,
|
|
end: processedGroupData[groupIds[i]][j].end,
|
|
y: processedGroupData[groupIds[i]][j].y,
|
|
groupId: groupIds[i],
|
|
label: processedGroupData[groupIds[i]][j].label
|
|
});
|
|
barPoints += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (barPoints === 0) {
|
|
return;
|
|
}
|
|
|
|
// sort by time and by group
|
|
combinedData.sort(function (a, b) {
|
|
if (a.screen_x === b.screen_x) {
|
|
return a.groupId < b.groupId ? -1 : 1;
|
|
}
|
|
else {
|
|
return a.screen_x - b.screen_x;
|
|
}
|
|
});
|
|
|
|
// get intersections
|
|
Bargraph._getDataIntersections(intersections, combinedData);
|
|
|
|
// plot barchart
|
|
for (i = 0; i < combinedData.length; i++) {
|
|
group = framework.groups[combinedData[i].groupId];
|
|
var minWidth = group.options.barChart.minWidth != undefined ? group.options.barChart.minWidth : 0.1 * group.options.barChart.width;
|
|
|
|
key = combinedData[i].screen_x;
|
|
var heightOffset = 0;
|
|
if (intersections[key] === undefined) {
|
|
if (i + 1 < combinedData.length) {
|
|
coreDistance = Math.abs(combinedData[i + 1].screen_x - key);
|
|
}
|
|
drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
|
|
}
|
|
else {
|
|
var nextKey = i + (intersections[key].amount - intersections[key].resolved);
|
|
if (nextKey < combinedData.length) {
|
|
coreDistance = Math.abs(combinedData[nextKey].screen_x - key);
|
|
}
|
|
drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
|
|
intersections[key].resolved += 1;
|
|
|
|
if (group.options.stack === true && group.options.excludeFromStacking !== true) {
|
|
if (combinedData[i].screen_y < group.zeroPosition) {
|
|
heightOffset = intersections[key].accumulatedNegative;
|
|
intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].screen_y;
|
|
}
|
|
else {
|
|
heightOffset = intersections[key].accumulatedPositive;
|
|
intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].screen_y;
|
|
}
|
|
}
|
|
else if (group.options.barChart.sideBySide === true) {
|
|
drawData.width = drawData.width / intersections[key].amount;
|
|
drawData.offset += (intersections[key].resolved) * drawData.width - (0.5 * drawData.width * (intersections[key].amount + 1));
|
|
}
|
|
}
|
|
|
|
let dataWidth = drawData.width;
|
|
let start = combinedData[i].screen_x;
|
|
|
|
// are we drawing explicit boxes? (we supplied an end value)
|
|
if (combinedData[i].screen_end != undefined){
|
|
dataWidth = combinedData[i].screen_end - combinedData[i].screen_x;
|
|
start += (dataWidth * 0.5);
|
|
}
|
|
else {
|
|
start += drawData.offset
|
|
}
|
|
|
|
DOMutil.drawBar(start, combinedData[i].screen_y - heightOffset, dataWidth, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
|
|
|
|
// draw points
|
|
if (group.options.drawPoints.enabled === true) {
|
|
let pointData = {
|
|
screen_x: combinedData[i].screen_x,
|
|
screen_y: combinedData[i].screen_y - heightOffset,
|
|
x: combinedData[i].x,
|
|
y: combinedData[i].y,
|
|
groupId: combinedData[i].groupId,
|
|
label: combinedData[i].label
|
|
};
|
|
Points.draw([pointData], group, framework, drawData.offset);
|
|
//DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Fill the intersections object with counters of how many datapoints share the same x coordinates
|
|
* @param {Object} intersections
|
|
* @param {Array<Object>} combinedData
|
|
* @private
|
|
*/
|
|
Bargraph._getDataIntersections = function (intersections, combinedData) {
|
|
// get intersections
|
|
var coreDistance;
|
|
for (var i = 0; i < combinedData.length; i++) {
|
|
if (i + 1 < combinedData.length) {
|
|
coreDistance = Math.abs(combinedData[i + 1].screen_x - combinedData[i].screen_x);
|
|
}
|
|
if (i > 0) {
|
|
coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].screen_x - combinedData[i].screen_x));
|
|
}
|
|
if (coreDistance === 0) {
|
|
if (intersections[combinedData[i].screen_x] === undefined) {
|
|
intersections[combinedData[i].screen_x] = {
|
|
amount: 0,
|
|
resolved: 0,
|
|
accumulatedPositive: 0,
|
|
accumulatedNegative: 0
|
|
};
|
|
}
|
|
intersections[combinedData[i].screen_x].amount += 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the width and offset for bargraphs based on the coredistance between datapoints
|
|
*
|
|
* @param {number} coreDistance
|
|
* @param {vis.Group} group
|
|
* @param {number} minWidth
|
|
* @returns {{width: Number, offset: Number}}
|
|
* @private
|
|
*/
|
|
Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) {
|
|
var width, offset;
|
|
if (coreDistance < group.options.barChart.width && coreDistance > 0) {
|
|
width = coreDistance < minWidth ? minWidth : coreDistance
|
|
|
|
offset = 0; // recalculate offset with the new width;
|
|
if (group.options.barChart.align === 'left') {
|
|
offset -= 0.5 * coreDistance;
|
|
}
|
|
else if (group.options.barChart.align === 'right') {
|
|
offset += 0.5 * coreDistance;
|
|
}
|
|
}
|
|
else {
|
|
// default settings
|
|
width = group.options.barChart.width;
|
|
offset = 0;
|
|
if (group.options.barChart.align === 'left') {
|
|
offset -= 0.5 * group.options.barChart.width;
|
|
}
|
|
else if (group.options.barChart.align === 'right') {
|
|
offset += 0.5 * group.options.barChart.width;
|
|
}
|
|
}
|
|
|
|
return {width: width, offset: offset};
|
|
};
|
|
|
|
Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) {
|
|
if (combinedData.length > 0) {
|
|
// sort by time and by group
|
|
combinedData.sort(function (a, b) {
|
|
if (a.screen_x === b.screen_x) {
|
|
return a.groupId < b.groupId ? -1 : 1;
|
|
}
|
|
else {
|
|
return a.screen_x - b.screen_x;
|
|
}
|
|
});
|
|
var intersections = {};
|
|
|
|
Bargraph._getDataIntersections(intersections, combinedData);
|
|
groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData);
|
|
groupRanges[groupLabel].yAxisOrientation = orientation;
|
|
groupIds.push(groupLabel);
|
|
}
|
|
};
|
|
|
|
Bargraph._getStackedYRange = function (intersections, combinedData) {
|
|
var key;
|
|
var yMin = combinedData[0].screen_y;
|
|
var yMax = combinedData[0].screen_y;
|
|
for (var i = 0; i < combinedData.length; i++) {
|
|
key = combinedData[i].screen_x;
|
|
if (intersections[key] === undefined) {
|
|
yMin = yMin > combinedData[i].screen_y ? combinedData[i].screen_y : yMin;
|
|
yMax = yMax < combinedData[i].screen_y ? combinedData[i].screen_y : yMax;
|
|
}
|
|
else {
|
|
if (combinedData[i].screen_y < 0) {
|
|
intersections[key].accumulatedNegative += combinedData[i].screen_y;
|
|
}
|
|
else {
|
|
intersections[key].accumulatedPositive += combinedData[i].screen_y;
|
|
}
|
|
}
|
|
}
|
|
for (var xpos in intersections) {
|
|
if (intersections.hasOwnProperty(xpos)) {
|
|
yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
|
|
yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
|
|
yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
|
|
yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
|
|
}
|
|
}
|
|
|
|
return {min: yMin, max: yMax};
|
|
};
|
|
|
|
module.exports = Bargraph;
|