Blame | Last modification | View Log | RSS feed
/*Flot plugin for stacking data sets, i.e. putting them on top of eachother, for accumulative graphs.The plugin assumes the data is sorted on x (or y if stackinghorizontally). For line charts, it is assumed that if a line has anundefined gap (from a null point), then the line above it should havethe same gap - insert zeros instead of "null" if you want anotherbehaviour. This also holds for the start and end of the chart. Notethat stacking a mix of positive and negative values in most instancesdoesn't make sense (so it looks weird).Two or more series are stacked when their "stack" attribute is set tothe same key (which can be any number or string or just "true"). Tospecify the default stack, you can setseries: {stack: null or true or key (number/string)}or specify it for a specific series$.plot($("#placeholder"), [{ data: [ ... ], stack: true }])The stacking order is determined by the order of the data series inthe array (later series end up on top of the previous).Internally, the plugin modifies the datapoints in each series, addingan offset to the y value. For line series, extra data points areinserted through interpolation. If there's a second y value, it's alsoadjusted (e.g for bar charts or filled areas).*/(function ($) {var options = {series: { stack: null } // or number/string};function init(plot) {function findMatchingSeries(s, allseries) {var res = nullfor (var i = 0; i < allseries.length; ++i) {if (s == allseries[i])break;if (allseries[i].stack == s.stack)res = allseries[i];}return res;}function stackData(plot, s, datapoints) {if (s.stack == null)return;var other = findMatchingSeries(s, plot.getData());if (!other)return;var ps = datapoints.pointsize,points = datapoints.points,otherps = other.datapoints.pointsize,otherpoints = other.datapoints.points,newpoints = [],px, py, intery, qx, qy, bottom,withlines = s.lines.show,horizontal = s.bars.horizontal,withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y),withsteps = withlines && s.lines.steps,fromgap = true,keyOffset = horizontal ? 1 : 0,accumulateOffset = horizontal ? 0 : 1,i = 0, j = 0, l;while (true) {if (i >= points.length)break;l = newpoints.length;if (points[i] == null) {// copy gapsfor (m = 0; m < ps; ++m)newpoints.push(points[i + m]);i += ps;}else if (j >= otherpoints.length) {// for lines, we can't use the rest of the pointsif (!withlines) {for (m = 0; m < ps; ++m)newpoints.push(points[i + m]);}i += ps;}else if (otherpoints[j] == null) {// oops, got a gapfor (m = 0; m < ps; ++m)newpoints.push(null);fromgap = true;j += otherps;}else {// cases where we actually got two pointspx = points[i + keyOffset];py = points[i + accumulateOffset];qx = otherpoints[j + keyOffset];qy = otherpoints[j + accumulateOffset];bottom = 0;if (px == qx) {for (m = 0; m < ps; ++m)newpoints.push(points[i + m]);newpoints[l + accumulateOffset] += qy;bottom = qy;i += ps;j += otherps;}else if (px > qx) {// we got past point below, might need to// insert interpolated extra pointif (withlines && i > 0 && points[i - ps] != null) {intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px);newpoints.push(qx);newpoints.push(intery + qy);for (m = 2; m < ps; ++m)newpoints.push(points[i + m]);bottom = qy;}j += otherps;}else { // px < qxif (fromgap && withlines) {// if we come from a gap, we just skip this pointi += ps;continue;}for (m = 0; m < ps; ++m)newpoints.push(points[i + m]);// we might be able to interpolate a point below,// this can give us a better yif (withlines && j > 0 && otherpoints[j - otherps] != null)bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx);newpoints[l + accumulateOffset] += bottom;i += ps;}fromgap = false;if (l != newpoints.length && withbottom)newpoints[l + 2] += bottom;}// maintain the line steps invariantif (withsteps && l != newpoints.length && l > 0&& newpoints[l] != null&& newpoints[l] != newpoints[l - ps]&& newpoints[l + 1] != newpoints[l - ps + 1]) {for (m = 0; m < ps; ++m)newpoints[l + ps + m] = newpoints[l + m];newpoints[l + 1] = newpoints[l - ps + 1];}}datapoints.points = newpoints;}plot.hooks.processDatapoints.push(stackData);}$.plot.plugins.push({init: init,options: options,name: 'stack',version: '1.2'});})(jQuery);