9bf75125e06daa006700edbce75508282b420835
1 // Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
2 // All Rights Reserved.
5 * @fileoverview Subclasses various parts of PlotKit to meet the additional
6 * needs of Dygraph: grid overlays and error bars
9 // Subclass PlotKit.Layout to add:
10 // 1. Sigma/errorBars properties
11 // 2. Copy error terms for PlotKit.CanvasRenderer._renderLineChart
14 * Creates a new DygraphLayout object. Options are the same as those allowed
15 * by the PlotKit.Layout constructor.
16 * @param {Object} options Options for PlotKit.Layout
17 * @return {Object} The DygraphLayout object
19 DygraphLayout
= function(options
) {
20 PlotKit
.Layout
.call(this, "line", options
);
22 DygraphLayout
.prototype = new PlotKit
.Layout();
25 * Behaves the same way as PlotKit.Layout, but also copies the errors
28 DygraphLayout
.prototype.evaluateWithError
= function() {
30 if (!this.options
.errorBars
) return;
32 // Copy over the error terms
33 var i
= 0; // index in this.points
34 for (var setName
in this.datasets
) {
36 var dataset
= this.datasets
[setName
];
37 if (PlotKit
.Base
.isFuncLike(dataset
)) continue;
38 for (var j
= 0; j
< dataset
.length
; j
++, i
++) {
39 var item
= dataset
[j
];
40 var xv
= parseFloat(item
[0]);
41 var yv
= parseFloat(item
[1]);
43 if (xv
== this.points
[i
].xval
&&
44 yv
== this.points
[i
].yval
) {
45 this.points
[i
].errorMinus
= parseFloat(item
[2]);
46 this.points
[i
].errorPlus
= parseFloat(item
[3]);
53 * Convenience function to remove all the data sets from a graph
55 DygraphLayout
.prototype.removeAllDatasets
= function() {
57 this.datasets
= new Array();
61 * Change the values of various layout options
62 * @param {Object} new_options an associative array of new properties
64 DygraphLayout
.prototype.updateOptions
= function(new_options
) {
65 MochiKit
.Base
.update(this.options
, new_options
? new_options
: {});
68 // Subclass PlotKit.CanvasRenderer to add:
69 // 1. X/Y grid overlay
70 // 2. Ability to draw error bars (if required)
73 * Sets some PlotKit.CanvasRenderer options
74 * @param {Object} element The canvas to attach to
75 * @param {Layout} layout The DygraphLayout object for this graph.
76 * @param {Object} options Options to pass on to CanvasRenderer
78 DygraphCanvasRenderer
= function(element
, layout
, options
) {
79 PlotKit
.CanvasRenderer
.call(this, element
, layout
, options
);
80 this.options
.shouldFill
= false;
81 this.options
.shouldStroke
= true;
82 this.options
.drawYGrid
= true;
83 this.options
.drawXGrid
= true;
84 this.options
.gridLineColor
= MochiKit
.Color
.Color
.grayColor();
85 MochiKit
.Base
.update(this.options
, options
);
87 // TODO(danvk) This shouldn't be necessary: effects should be overlaid
88 this.options
.drawBackground
= false;
90 DygraphCanvasRenderer
.prototype = new PlotKit
.CanvasRenderer();
93 * Draw an X/Y grid on top of the existing plot
95 DygraphCanvasRenderer
.prototype.render
= function() {
96 // Draw the new X/Y grid
97 var ctx
= this.element
.getContext("2d");
98 if (this.options
.drawYGrid
) {
99 var ticks
= this.layout
.yticks
;
101 ctx
.strokeStyle
= this.options
.gridLineColor
.toRGBString();
102 ctx
.lineWidth
= this.options
.axisLineWidth
;
103 for (var i
= 0; i
< ticks
.length
; i
++) {
105 var y
= this.area
.y
+ ticks
[i
][0] * this.area
.h
;
108 ctx
.lineTo(x
+ this.area
.w
, y
);
114 if (this.options
.drawXGrid
) {
115 var ticks
= this.layout
.xticks
;
117 ctx
.strokeStyle
= this.options
.gridLineColor
.toRGBString();
118 ctx
.lineWidth
= this.options
.axisLineWidth
;
119 for (var i
=0; i
<ticks
.length
; i
++) {
120 var x
= this.area
.x
+ ticks
[i
][0] * this.area
.w
;
121 var y
= this.area
.y
+ this.area
.h
;
124 ctx
.lineTo(x
, this.area
.y
);
130 // Do the ordinary rendering, as before
131 // TODO(danvk) Call super.render()
132 this._renderLineChart();
133 this._renderLineAxis();
137 * Overrides the CanvasRenderer method to draw error bars
139 DygraphCanvasRenderer
.prototype._renderLineChart
= function() {
140 var context
= this.element
.getContext("2d");
141 var colorCount
= this.options
.colorScheme
.length
;
142 var colorScheme
= this.options
.colorScheme
;
143 var setNames
= MochiKit
.Base
.keys(this.layout
.datasets
);
144 var errorBars
= this.layout
.options
.errorBars
;
145 var setCount
= setNames
.length
;
146 var bind
= MochiKit
.Base
.bind
;
147 var partial
= MochiKit
.Base
.partial
;
150 var updatePoint
= function(point
) {
151 point
.canvasx
= this.area
.w
* point
.x
+ this.area
.x
;
152 point
.canvasy
= this.area
.h
* point
.y
+ this.area
.y
;
154 MochiKit
.Iter
.forEach(this.layout
.points
, updatePoint
, this);
157 var makePath
= function(ctx
) {
158 for (var i
= 0; i
< setCount
; i
++) {
159 var setName
= setNames
[i
];
160 var color
= colorScheme
[i
%colorCount
];
161 var strokeX
= this.options
.strokeColorTransform
;
163 // setup graphics context
165 context
.strokeStyle
= color
.toRGBString();
166 context
.lineWidth
= this.options
.strokeWidth
;
168 var point
= this.layout
.points
[0];
169 var first_point
= true;
170 var addPoint
= function(ctx_
, point
) {
171 if (point
.name
== setName
) {
172 if (!point
.canvasy
|| isNaN(point
.canvasy
)) {
173 // this will make us move to the next point, not draw a line to it.
177 ctx_
.moveTo(point
.canvasx
, point
.canvasy
);
180 ctx_
.lineTo(point
.canvasx
, point
.canvasy
);
185 MochiKit
.Iter
.forEach(this.layout
.points
, partial(addPoint
, ctx
), this);
190 var makeErrorBars
= function(ctx
) {
191 for (var i
= 0; i
< setCount
; i
++) {
192 var setName
= setNames
[i
];
193 var color
= colorScheme
[i
% colorCount
];
194 var strokeX
= this.options
.strokeColorTransform
;
196 // setup graphics context
198 context
.strokeStyle
= color
.toRGBString();
199 context
.lineWidth
= this.options
.strokeWidth
;
201 var prevYs
= [-1, -1];
203 var yscale
= this.layout
.yscale
;
204 var errorTrapezoid
= function(ctx_
,point
) {
206 if (point
.name
== setName
) {
207 if (!point
.y
|| isNaN(point
.y
)) {
211 var newYs
= [ point
.y
- point
.errorPlus
* yscale
,
212 point
.y
+ point
.errorMinus
* yscale
];
213 newYs
[0] = this.area
.h
* newYs
[0] + this.area
.y
;
214 newYs
[1] = this.area
.h
* newYs
[1] + this.area
.y
;
216 ctx_
.moveTo(prevX
, prevYs
[0]);
217 ctx_
.lineTo(point
.canvasx
, newYs
[0]);
218 ctx_
.lineTo(point
.canvasx
, newYs
[1]);
219 ctx_
.lineTo(prevX
, prevYs
[1]);
222 prevYs
[0] = newYs
[0];
223 prevYs
[1] = newYs
[1];
224 prevX
= point
.canvasx
;
227 // should be same color as the lines
228 var err_color
= color
.colorWithAlpha(0.15);
229 ctx
.fillStyle
= err_color
.toRGBString();
231 MochiKit
.Iter
.forEach(this.layout
.points
, partial(errorTrapezoid
, ctx
), this);
237 bind(makeErrorBars
, this)(context
);
238 bind(makePath
, this)(context
);