X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=830ed2aa5a05787c02264580b6acf7ed563df0a6;hb=0abfbd7e563fc0e53015ec875c324890761071ba;hp=fb724e1e8bef01d23543dc6df0753058c6475c54;hpb=b03b9d235686d105e46a4263bcaddf8eee2b9553;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index fb724e1..830ed2a 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 */ @@ -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,20 +70,25 @@ 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; this.xscale = (this.xrange != 0 ? 1/this.xrange : 1.0); - this.minyval = this.options.yAxis[0]; - this.maxyval = this.options.yAxis[1]; - this.yrange = this.maxyval - this.minyval; - this.yscale = (this.yrange != 0 ? 1/this.yrange : 1.0); + for (var i = 0; i < this.options.yAxes.length; i++) { + var axis = this.options.yAxes[i]; + 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); + } }; DygraphLayout.prototype._evaluateLineCharts = function() { @@ -92,12 +98,14 @@ DygraphLayout.prototype._evaluateLineCharts = function() { if (!this.datasets.hasOwnProperty(setName)) continue; var dataset = this.datasets[setName]; + var axis = this.options.yAxes[this.options.seriesToAxisMap[setName]]; + for (var j = 0; j < dataset.length; j++) { var item = dataset[j]; var point = { // TODO(danvk): here x: ((parseFloat(item[0]) - this.minxval) * this.xscale), - y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale), + y: 1.0 - ((parseFloat(item[1]) - axis.minyval) * axis.yscale), xval: parseFloat(item[0]), yval: parseFloat(item[1]), name: setName @@ -127,12 +135,15 @@ DygraphLayout.prototype._evaluateLineTicks = function() { } this.yticks = new Array(); - for (var i = 0; i < this.options.yTicks.length; i++) { - var tick = this.options.yTicks[i]; - var label = tick.label; - var pos = 1.0 - (this.yscale * (tick.v - this.minyval)); - if ((pos >= 0.0) && (pos <= 1.0)) { - this.yticks.push([pos, label]); + for (var i = 0; i < this.options.yAxes.length; i++ ) { + var axis = this.options.yAxes[i]; + for (var j = 0; j < axis.ticks.length; j++) { + var tick = axis.ticks[j]; + var label = tick.label; + var pos = 1.0 - (axis.yscale * (tick.v - axis.minyval)); + if ((pos >= 0.0) && (pos <= 1.0)) { + this.yticks.push([i, pos, label]); + } } } }; @@ -281,7 +292,9 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { this.ylabels = new Array(); this.annotations = new Array(); + // TODO(danvk): consider all axes in this computation. this.area = { + // TODO(danvk): per-axis setting. x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize, y: 0 }; @@ -289,8 +302,29 @@ 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.element.getContext("2d"); + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); + + var 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.clear = function() { @@ -368,8 +402,10 @@ DygraphCanvasRenderer.prototype.render = function() { ctx.strokeStyle = this.options.gridLineColor; ctx.lineWidth = this.options.axisLineWidth; for (var i = 0; i < ticks.length; i++) { + // TODO(danvk): allow secondary axes to draw a grid, too. + if (ticks[i][0] != 0) continue; var x = this.area.x; - var y = this.area.y + ticks[i][0] * this.area.h; + var y = this.area.y + ticks[i][1] * this.area.h; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + this.area.w, y); @@ -437,14 +473,19 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { var tick = this.layout.yticks[i]; if (typeof(tick) == "function") return; var x = this.area.x; - var y = this.area.y + tick[0] * this.area.h; + var sgn = 1; + if (tick[0] == 1) { // right-side y-axis + x = this.area.x + this.area.w; + sgn = -1; + } + var y = this.area.y + tick[1] * this.area.h; context.beginPath(); context.moveTo(x, y); - context.lineTo(x - this.options.axisTickSize, y); + context.lineTo(x - sgn * this.options.axisTickSize, y); context.closePath(); context.stroke(); - var label = makeDiv(tick[1]); + var label = makeDiv(tick[2]); var top = (y - this.options.axisLabelFontSize / 2); if (top < 0) top = 0; @@ -453,8 +494,14 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { } else { label.style.top = top + "px"; } - label.style.left = "0px"; - label.style.textAlign = "right"; + if (tick[0] == 0) { + label.style.left = "0px"; + label.style.textAlign = "right"; + } else if (tick[0] == 1) { + label.style.left = (this.area.x + this.area.w + + this.options.axisTickSize) + "px"; + label.style.textAlign = "left"; + } label.style.width = this.options.yAxisLabelWidth + "px"; this.container.appendChild(label); this.ylabels.push(label); @@ -477,6 +524,14 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { context.lineTo(this.area.x, this.area.y + this.area.h); context.closePath(); context.stroke(); + + if (this.dygraph_.numAxes() == 2) { + context.beginPath(); + context.moveTo(this.area.x + this.area.w, this.area.y); + context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h); + context.closePath(); + context.stroke(); + } } if (this.options.drawXAxis) { @@ -668,6 +723,8 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { for (var i = 0; i < setCount; i++) { var setName = setNames[i]; + var axis = this.layout.options.yAxes[ + this.layout.options.seriesToAxisMap[setName]]; var color = this.colors[setName]; // setup graphics context @@ -675,7 +732,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { var prevX = NaN; var prevY = NaN; var prevYs = [-1, -1]; - var yscale = this.layout.yscale; + 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 + ',' + @@ -723,23 +780,24 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { ctx.fill(); } } else if (fillGraph) { - var axisY = 1.0 + this.layout.minyval * this.layout.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 baseline = [] // for stacked graphs: baseline for filling // 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.layout.options.yAxes[ + this.layout.options.seriesToAxisMap[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; // setup graphics context ctx.save(); var prevX = NaN; var prevYs = [-1, -1]; - var yscale = this.layout.yscale; + 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 + ',' +