};
/**
- * @param { [String] } colors Array of color strings. Should have one entry for
- * each series to be rendered.
- */
-DygraphCanvasRenderer.prototype.setColors = function(colors) {
- this.colorScheme_ = colors;
-};
-
-/**
* This method is responsible for drawing everything on the chart, including
* lines, error bars, fills and axes.
* It is called immediately after clear() on every frame, including during pans
* @private
*/
DygraphCanvasRenderer.prototype.render = function() {
- // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to
- // half-integers. This prevents them from drawing in two rows/cols.
- var ctx = this.elementContext;
- function halfUp(x) { return Math.round(x) + 0.5; }
- function halfDown(y){ return Math.round(y) - 0.5; }
-
- if (this.attr_('underlayCallback')) {
- // NOTE: we pass the dygraph object to this callback twice to avoid breaking
- // users who expect a deprecated form of this callback.
- this.attr_('underlayCallback')(ctx, this.area, this.dygraph_, this.dygraph_);
- }
-
- // Do the ordinary rendering, as before
this._renderLineChart();
};
strategy.init();
- while(iter.hasNext()) {
+ while(iter.hasNext) {
point = iter.next();
if (point.canvasy === null || point.canvasy != point.canvasy) {
if (stepPlot && prevCanvasX !== null) {
}
prevCanvasX = prevCanvasY = null;
} else {
- nextCanvasY = iter.hasNext() ? iter.peek().canvasy : null;
+ nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
// TODO: we calculate isNullOrNaN for this point, and the next, and then, when
// we iterate, test for isNullOrNaN again. Why bother?
var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY;
// Also consider a point to be "isolated" if it's adjacent to a
// null point, excluding the graph edges.
if ((!first && !prevCanvasX) ||
- (iter.hasNext() && isNextCanvasYNullOrNaN)) {
+ (iter.hasNext && isNextCanvasYNullOrNaN)) {
isIsolated = true;
}
}
/**
* Actually draw the lines chart, including error bars.
- * TODO(danvk): split this into several smaller functions.
* @private
*/
DygraphCanvasRenderer.prototype._renderLineChart = function() {
- // TODO(danvk): use this.attr_ for many of these.
var ctx = this.elementContext;
- var fillAlpha = this.attr_('fillAlpha');
var errorBars = this.attr_("errorBars") || this.attr_("customBars");
var fillGraph = this.attr_("fillGraph");
- var stackedGraph = this.attr_("stackedGraph");
- var stepPlot = this.attr_("stepPlot");
- var points = this.layout.points;
- var pointsLength = points.length;
- var point, i, prevX, prevY, prevYs, color, setName, newYs, err_color, rgb, yscale, axis;
+ var i;
var setNames = this.layout.setNames;
var setCount = setNames.length;
// TODO(bhs): this loop is a hot-spot for high-point-count charts. These
// transformations can be pushed into the canvas via linear transformation
// matrices.
- for (i = pointsLength; i--;) {
- point = points[i];
+ var points = this.layout.points;
+ for (i = points.length; i--;) {
+ var point = points[i];
point.canvasx = this.area.w * point.x + this.area.x;
point.canvasy = this.area.h * point.y + this.area.y;
}
- // create paths
+ // Draw any "fills", i.e. error bars or the filled area under a series.
+ // These must all be drawn before any lines, so that the main lines of a
+ // series are drawn on top.
if (errorBars) {
- ctx.save();
if (fillGraph) {
this.dygraph_.warn("Can't use fillGraph option with error bars");
}
- for (i = 0; i < setCount; i++) {
- setName = setNames[i];
- axis = this.dygraph_.axisPropertiesForSeries(setName);
- color = this.colors[setName];
-
- var firstIndexInSet = this.layout.setPointsOffsets[i];
- var setLength = this.layout.setPointsLengths[i];
-
- var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
- DygraphCanvasRenderer._getIteratorPredicate(this.attr_("connectSeparatedPoints")));
-
- // setup graphics context
- prevX = NaN;
- prevY = NaN;
- prevYs = [-1, -1];
- yscale = axis.yscale;
- // should be same color as the lines but only 15% opaque.
- rgb = new RGBColor(color);
- err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' +
- fillAlpha + ')';
- ctx.fillStyle = err_color;
- ctx.beginPath();
- while (iter.hasNext()) {
- point = iter.next();
- if (point.name == setName) { // TODO(klausw): this is always true
- if (!Dygraph.isOK(point.y)) {
- prevX = NaN;
- continue;
- }
+ ctx.save();
+ this.drawErrorBars_(points);
+ ctx.restore();
+ } else if (fillGraph) {
+ ctx.save();
+ this.drawFillBars_(points);
+ ctx.restore();
+ }
+
+ // Drawing the lines.
+ for (i = 0; i < setCount; i += 1) {
+ this._drawLine(ctx, i);
+ }
+};
- // TODO(danvk): here
+/**
+ * Draws the shaded error bars/confidence intervals for each series.
+ * This happens before the center lines are drawn, since the center lines
+ * need to be drawn on top of the error bars for all series.
+ *
+ * @private
+ */
+DygraphCanvasRenderer.prototype.drawErrorBars_ = function(points) {
+ var ctx = this.elementContext;
+ var setNames = this.layout.setNames;
+ var setCount = setNames.length;
+ var fillAlpha = this.attr_('fillAlpha');
+ var stepPlot = this.attr_('stepPlot');
+
+ var newYs;
+
+ for (var i = 0; i < setCount; i++) {
+ var setName = setNames[i];
+ var axis = this.dygraph_.axisPropertiesForSeries(setName);
+ var color = this.colors[setName];
+
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+
+ var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ DygraphCanvasRenderer._getIteratorPredicate(
+ this.attr_("connectSeparatedPoints")));
+
+ // setup graphics context
+ var prevX = NaN;
+ var prevY = NaN;
+ var prevYs = [-1, -1];
+ var yscale = axis.yscale;
+ // should be same color as the lines but only 15% opaque.
+ var rgb = new RGBColor(color);
+ var err_color =
+ 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
+ ctx.fillStyle = err_color;
+ ctx.beginPath();
+ while (iter.hasNext) {
+ var point = iter.next();
+ if (point.name == setName) { // TODO(klausw): this is always true
+ if (!Dygraph.isOK(point.y)) {
+ prevX = NaN;
+ continue;
+ }
+
+ // TODO(danvk): here
+ if (stepPlot) {
+ newYs = [ point.y_bottom, point.y_top ];
+ prevY = point.y;
+ } else {
+ newYs = [ point.y_bottom, point.y_top ];
+ }
+ newYs[0] = this.area.h * newYs[0] + this.area.y;
+ newYs[1] = this.area.h * newYs[1] + this.area.y;
+ if (!isNaN(prevX)) {
if (stepPlot) {
- newYs = [ point.y_bottom, point.y_top ];
- prevY = point.y;
+ ctx.moveTo(prevX, newYs[0]);
} else {
- newYs = [ point.y_bottom, point.y_top ];
+ ctx.moveTo(prevX, prevYs[0]);
}
- newYs[0] = this.area.h * newYs[0] + this.area.y;
- newYs[1] = this.area.h * newYs[1] + this.area.y;
- if (!isNaN(prevX)) {
- if (stepPlot) {
- ctx.moveTo(prevX, newYs[0]);
- } else {
- ctx.moveTo(prevX, prevYs[0]);
- }
- ctx.lineTo(point.canvasx, newYs[0]);
- ctx.lineTo(point.canvasx, newYs[1]);
- if (stepPlot) {
- ctx.lineTo(prevX, newYs[1]);
- } else {
- ctx.lineTo(prevX, prevYs[1]);
- }
- ctx.closePath();
+ ctx.lineTo(point.canvasx, newYs[0]);
+ ctx.lineTo(point.canvasx, newYs[1]);
+ if (stepPlot) {
+ ctx.lineTo(prevX, newYs[1]);
+ } else {
+ ctx.lineTo(prevX, prevYs[1]);
}
- prevYs = newYs;
- prevX = point.canvasx;
+ ctx.closePath();
}
+ prevYs = newYs;
+ prevX = point.canvasx;
}
- ctx.fill();
}
- ctx.restore();
- } else if (fillGraph) {
- ctx.save();
- var baseline = {}; // for stacked graphs: baseline for filling
- var currBaseline;
-
- // process sets in reverse order (needed for stacked graphs)
- for (i = setCount - 1; i >= 0; i--) {
- setName = setNames[i];
- color = this.colors[setName];
- axis = this.dygraph_.axisPropertiesForSeries(setName);
- var axisY = 1.0 + axis.minyval * axis.yscale;
- if (axisY < 0.0) axisY = 0.0;
- else if (axisY > 1.0) axisY = 1.0;
- axisY = this.area.h * axisY + this.area.y;
- var firstIndexInSet = this.layout.setPointsOffsets[i];
- var setLength = this.layout.setPointsLengths[i];
-
- var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
- DygraphCanvasRenderer._getIteratorPredicate(this.attr_("connectSeparatedPoints")));
-
- // setup graphics context
- prevX = NaN;
- prevYs = [-1, -1];
- yscale = axis.yscale;
- // should be same color as the lines but only 15% opaque.
- rgb = new RGBColor(color);
- err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' +
- fillAlpha + ')';
- ctx.fillStyle = err_color;
- ctx.beginPath();
- while(iter.hasNext()) {
- point = iter.next();
- if (point.name == setName) { // TODO(klausw): this is always true
- if (!Dygraph.isOK(point.y)) {
- prevX = NaN;
- continue;
- }
- if (stackedGraph) {
- currBaseline = baseline[point.canvasx];
- var lastY;
- if (currBaseline === undefined) {
- lastY = axisY;
- } else {
- if(stepPlot) {
- lastY = currBaseline[0];
- } else {
- lastY = currBaseline;
- }
- }
- newYs = [ point.canvasy, lastY ];
+ ctx.fill();
+ }
+};
+
+/**
+ * Draws the shaded regions when "fillGraph" is set. Not to be confused with
+ * error bars.
+ *
+ * @private
+ */
+DygraphCanvasRenderer.prototype.drawFillBars_ = function(points) {
+ var ctx = this.elementContext;
+ var setNames = this.layout.setNames;
+ var setCount = setNames.length;
+ var fillAlpha = this.attr_('fillAlpha');
+ var stepPlot = this.attr_('stepPlot');
+ var stackedGraph = this.attr_("stackedGraph");
+ var baseline = {}; // for stacked graphs: baseline for filling
+ var currBaseline;
+
+ // process sets in reverse order (needed for stacked graphs)
+ for (var i = setCount - 1; i >= 0; i--) {
+ var setName = setNames[i];
+ var color = this.colors[setName];
+ var axis = this.dygraph_.axisPropertiesForSeries(setName);
+ var axisY = 1.0 + axis.minyval * axis.yscale;
+ if (axisY < 0.0) axisY = 0.0;
+ else if (axisY > 1.0) axisY = 1.0;
+ axisY = this.area.h * axisY + this.area.y;
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+
+ var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ DygraphCanvasRenderer._getIteratorPredicate(
+ this.attr_("connectSeparatedPoints")));
+
+ // setup graphics context
+ var prevX = NaN;
+ var prevYs = [-1, -1];
+ var newYs;
+ var yscale = axis.yscale;
+ // should be same color as the lines but only 15% opaque.
+ var rgb = new RGBColor(color);
+ var err_color =
+ 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
+ ctx.fillStyle = err_color;
+ ctx.beginPath();
+ while(iter.hasNext) {
+ var point = iter.next();
+ if (point.name == setName) { // TODO(klausw): this is always true
+ if (!Dygraph.isOK(point.y)) {
+ prevX = NaN;
+ continue;
+ }
+ if (stackedGraph) {
+ currBaseline = baseline[point.canvasx];
+ var lastY;
+ if (currBaseline === undefined) {
+ lastY = axisY;
+ } else {
if(stepPlot) {
- // Step plots must keep track of the top and bottom of
- // the baseline at each point.
- if(prevYs[0] === -1) {
- baseline[point.canvasx] = [ point.canvasy, axisY ];
- } else {
- baseline[point.canvasx] = [ point.canvasy, prevYs[0] ];
- }
+ lastY = currBaseline[0];
} else {
- baseline[point.canvasx] = point.canvasy;
+ lastY = currBaseline;
}
+ }
+ newYs = [ point.canvasy, lastY ];
+ if(stepPlot) {
+ // Step plots must keep track of the top and bottom of
+ // the baseline at each point.
+ if(prevYs[0] === -1) {
+ baseline[point.canvasx] = [ point.canvasy, axisY ];
+ } else {
+ baseline[point.canvasx] = [ point.canvasy, prevYs[0] ];
+ }
} else {
- newYs = [ point.canvasy, axisY ];
+ baseline[point.canvasx] = point.canvasy;
}
- if (!isNaN(prevX)) {
- ctx.moveTo(prevX, prevYs[0]);
- if (stepPlot) {
- ctx.lineTo(point.canvasx, prevYs[0]);
- if(currBaseline) {
- // Draw to the bottom of the baseline
- ctx.lineTo(point.canvasx, currBaseline[1]);
- } else {
- ctx.lineTo(point.canvasx, newYs[1]);
- }
+ } else {
+ newYs = [ point.canvasy, axisY ];
+ }
+ if (!isNaN(prevX)) {
+ ctx.moveTo(prevX, prevYs[0]);
+
+ if (stepPlot) {
+ ctx.lineTo(point.canvasx, prevYs[0]);
+ if(currBaseline) {
+ // Draw to the bottom of the baseline
+ ctx.lineTo(point.canvasx, currBaseline[1]);
} else {
- ctx.lineTo(point.canvasx, newYs[0]);
ctx.lineTo(point.canvasx, newYs[1]);
}
-
- ctx.lineTo(prevX, prevYs[1]);
- ctx.closePath();
+ } else {
+ ctx.lineTo(point.canvasx, newYs[0]);
+ ctx.lineTo(point.canvasx, newYs[1]);
}
- prevYs = newYs;
- prevX = point.canvasx;
+
+ ctx.lineTo(prevX, prevYs[1]);
+ ctx.closePath();
}
+ prevYs = newYs;
+ prevX = point.canvasx;
}
- ctx.fill();
}
- ctx.restore();
- }
-
- // Drawing the lines.
- for (i = 0; i < setCount; i += 1) {
- this._drawLine(ctx, i);
+ ctx.fill();
}
};