X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=29341e1b83bd5b0b883ad4d3d2ca30a078816275;hb=79b3ee420f46d964b796adc06b4cd64e74a44cb8;hp=edce7a751170c83e6ca35eb94a3ee86a7f973dcc;hpb=9012dd21921ff553d61acbdfc41dad298c1ec089;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index edce7a7..29341e1 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -4,7 +4,7 @@ /** * @fileoverview Based on PlotKit, but modified to meet the needs of dygraphs. * In particular, support for: - * - grid overlays + * - grid overlays * - error bars * - dygraphs attribute system */ @@ -19,7 +19,7 @@ DygraphLayout = function(dygraph, options) { this.options = {}; // TODO(danvk): remove, use attr_ instead. Dygraph.update(this.options, options ? options : {}); this.datasets = new Array(); - this.annotations = new Array() + this.annotations = new Array(); }; DygraphLayout.prototype.attr_ = function(name) { @@ -33,6 +33,7 @@ DygraphLayout.prototype.addDataset = function(setname, set_xy) { DygraphLayout.prototype.setAnnotations = function(ann) { // The Dygraph object's annotations aren't parsed. We parse them here and // save a copy. + this.annotations = []; var parse = this.attr_('xValueParser'); for (var i = 0; i < ann.length; i++) { var a = {}; @@ -69,11 +70,13 @@ DygraphLayout.prototype._evaluateLimits = function() { for (var name in this.datasets) { if (!this.datasets.hasOwnProperty(name)) continue; var series = this.datasets[name]; - var x1 = series[0][0]; - if (!this.minxval || x1 < this.minxval) this.minxval = x1; - - var x2 = series[series.length - 1][0]; - if (!this.maxxval || x2 > this.maxxval) this.maxxval = x2; + if (series.length > 1) { + var x1 = series[0][0]; + if (!this.minxval || x1 < this.minxval) this.minxval = x1; + + var x2 = series[series.length - 1][0]; + if (!this.maxxval || x2 > this.maxxval) this.maxxval = x2; + } } } this.xrange = this.maxxval - this.minxval; @@ -81,8 +84,8 @@ DygraphLayout.prototype._evaluateLimits = function() { for (var i = 0; i < this.options.yAxes.length; i++) { var axis = this.options.yAxes[i]; - axis.minyval = axis.valueRange[0]; - axis.maxyval = axis.valueRange[1]; + axis.minyval = axis.computedValueRange[0]; + axis.maxyval = axis.computedValueRange[1]; axis.yrange = axis.maxyval - axis.minyval; axis.yscale = (axis.yrange != 0 ? 1.0 / axis.yrange : 1.0); } @@ -108,13 +111,6 @@ DygraphLayout.prototype._evaluateLineCharts = function() { name: setName }; - // limit the x, y values so they do not overdraw - if (point.y <= 0.0) { - point.y = 0.0; - } - if (point.y >= 1.0) { - point.y = 1.0; - } this.points.push(point); } } @@ -210,6 +206,35 @@ DygraphLayout.prototype.updateOptions = function(new_options) { Dygraph.update(this.options, new_options ? new_options : {}); }; +/** + * Return a copy of the point at the indicated index, with its yval unstacked. + * @param int index of point in layout_.points + */ +DygraphLayout.prototype.unstackPointAtIndex = function(idx) { + var point = this.points[idx]; + + // Clone the point since we modify it + var unstackedPoint = {}; + for (var i in point) { + unstackedPoint[i] = point[i]; + } + + if (!this.attr_("stackedGraph")) { + return unstackedPoint; + } + + // The unstacked yval is equal to the current yval minus the yval of the + // next point at the same xval. + for (var i = idx+1; i < this.points.length; i++) { + if (this.points[i].xval == point.xval) { + unstackedPoint.yval -= this.points[i].yval; + break; + } + } + + return unstackedPoint; +} + // Subclass PlotKit.CanvasRenderer to add: // 1. X/Y grid overlay // 2. Ability to draw error bars (if required) @@ -270,8 +295,33 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { this.area.h = this.height - this.options.axisLabelFontSize - 2 * this.options.axisTickSize; + // Shrink the drawing area to accomodate additional y-axes. + if (this.dygraph_.numAxes() == 2) { + // TODO(danvk): per-axis setting. + this.area.w -= (this.options.yAxisLabelWidth + 2 * this.options.axisTickSize); + } else if (this.dygraph_.numAxes() > 2) { + this.dygraph_.error("Only two y-axes are supported at this time. (Trying " + + "to use " + this.dygraph_.numAxes() + ")"); + } + this.container.style.position = "relative"; this.container.style.width = this.width + "px"; + + // Set up a clipping area for the canvas (and the interaction canvas). + // This ensures that we don't overdraw. + var ctx = this.dygraph_.canvas_.getContext("2d"); + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); + + ctx = this.dygraph_.hidden_.getContext("2d"); + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); +}; + +DygraphCanvasRenderer.prototype.attr_ = function(x) { + return this.dygraph_.attr_(x); }; DygraphCanvasRenderer.prototype.clear = function() { @@ -297,15 +347,15 @@ DygraphCanvasRenderer.prototype.clear = function() { for (var i = 0; i < this.xlabels.length; i++) { var el = this.xlabels[i]; - el.parentNode.removeChild(el); + if (el.parentNode) el.parentNode.removeChild(el); } for (var i = 0; i < this.ylabels.length; i++) { var el = this.ylabels[i]; - el.parentNode.removeChild(el); + if (el.parentNode) el.parentNode.removeChild(el); } for (var i = 0; i < this.annotations.length; i++) { var el = this.annotations[i]; - el.parentNode.removeChild(el); + if (el.parentNode) el.parentNode.removeChild(el); } this.xlabels = new Array(); this.ylabels = new Array(); @@ -336,17 +386,11 @@ DygraphCanvasRenderer.isSupported = function(canvasName) { * Draw an X/Y grid on top of the existing plot */ DygraphCanvasRenderer.prototype.render = function() { - // Shrink the drawing area to accomodate additional y-axes. - if (this.layout.options.yAxes.length == 2) { - // TODO(danvk): per-axis setting. - this.area.w -= (this.options.yAxisLabelWidth + 2 * this.options.axisTickSize); - } else if (this.layout.options.yAxes.length > 2) { - this.dygraph_.error("Only two y-axes are supported at this time. (Trying " + - "to use " + this.layout.yAxes.length + ")"); - } - - // Draw the new X/Y grid + // 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.element.getContext("2d"); + function halfUp(x){return Math.round(x)+0.5}; + function halfDown(y){return Math.round(y)-0.5}; if (this.options.underlayCallback) { this.options.underlayCallback(ctx, this.area, this.layout, this.dygraph_); @@ -358,9 +402,10 @@ DygraphCanvasRenderer.prototype.render = function() { ctx.strokeStyle = this.options.gridLineColor; ctx.lineWidth = this.options.axisLineWidth; for (var i = 0; i < ticks.length; i++) { - if (ticks[i][0] != 0) continue; // TODO(danvk): per-axis property - var x = this.area.x; - var y = this.area.y + ticks[i][1] * this.area.h; + // TODO(danvk): allow secondary axes to draw a grid, too. + if (ticks[i][0] != 0) continue; + var x = halfUp(this.area.x); + var y = halfDown(this.area.y + ticks[i][1] * this.area.h); ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + this.area.w, y); @@ -375,8 +420,8 @@ DygraphCanvasRenderer.prototype.render = function() { ctx.strokeStyle = this.options.gridLineColor; ctx.lineWidth = this.options.axisLineWidth; for (var i=0; i