X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=be76320d3b46e1dee9c56fc8ce1e17152eee63c5;hb=01a14b85eeef23febb25b82966349bf76ee00481;hp=50ff4865b6e9ddf85711c913f7f547091c781a5d;hpb=cbe41be103cc129ff2477a872cb802538aafb3d3;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 50ff486..be76320 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -145,14 +145,6 @@ DygraphCanvasRenderer.isSupported = function(canvasName) { }; /** - * @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 @@ -160,19 +152,6 @@ DygraphCanvasRenderer.prototype.setColors = function(colors) { * @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(); }; @@ -360,7 +339,7 @@ DygraphCanvasRenderer.prototype._drawSeries = function( strategy.init(); - while(iter.hasNext()) { + while(iter.hasNext) { point = iter.next(); if (point.canvasy === null || point.canvasy != point.canvasy) { if (stepPlot && prevCanvasX !== null) { @@ -371,7 +350,7 @@ DygraphCanvasRenderer.prototype._drawSeries = function( } 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; @@ -380,7 +359,7 @@ DygraphCanvasRenderer.prototype._drawSeries = function( // 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; } } @@ -440,20 +419,13 @@ DygraphCanvasRenderer.prototype._drawLine = function(ctx, i) { /** * 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; @@ -466,177 +438,219 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { // 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(); } };