Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 Kevin 1
/**
2
 * Copyright (c) 2009 Chris Leonello
3
 * jqPlot is currently available for use in all personal or commercial projects 
4
 * under both the MIT and GPL version 2.0 licenses. This means that you can 
5
 * choose the license that best suits your project and use it accordingly. 
6
 *
7
 * The author would appreciate an email letting him know of any substantial
8
 * use of jqPlot.  You can reach the author at: chris dot leonello at gmail 
9
 * dot com or see http://www.jqplot.com/info.php .  This is, of course, 
10
 * not required.
11
 *
12
 * If you are feeling kind and generous, consider supporting the project by
13
 * making a donation at: http://www.jqplot.com/donate.php .
14
 *
15
 * Thanks for using jqPlot!
16
 * 
17
 */
18
(function($) {
19
 
20
    // Class: $.jqplot.BarRenderer
21
    // A plugin renderer for jqPlot to draw a bar plot.
22
    // Draws series as a line.
23
 
24
    $.jqplot.BarRenderer = function(){
25
        $.jqplot.LineRenderer.call(this);
26
    };
27
 
28
    $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer();
29
    $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer;
30
 
31
    // called with scope of series.
32
    $.jqplot.BarRenderer.prototype.init = function(options) {
33
        // Group: Properties
34
        //
35
        // prop: barPadding
36
        // Number of pixels between adjacent bars at the same axis value.
37
        this.barPadding = 8;
38
        // prop: barMargin
39
        // Number of pixels between groups of bars at adjacent axis values.
40
        this.barMargin = 10;
41
        // prop: barDirection
42
        // 'vertical' = up and down bars, 'horizontal' = side to side bars
43
        this.barDirection = 'vertical';
44
        // prop: barWidth
45
        // Width of the bar in pixels (auto by devaul).  null = calculated automatically.
46
        this.barWidth = null;
47
        // prop: shadowOffset
48
        // offset of the shadow from the slice and offset of 
49
        // each succesive stroke of the shadow from the last.
50
        this.shadowOffset = 2;
51
        // prop: shadowDepth
52
        // number of strokes to apply to the shadow, 
53
        // each stroke offset shadowOffset from the last.
54
        this.shadowDepth = 5;
55
        // prop: shadowAlpha
56
        // transparency of the shadow (0 = transparent, 1 = opaque)
57
        this.shadowAlpha = 0.08;
58
        $.extend(true, this, options);
59
        // fill is still needed to properly draw the legend.
60
        // bars have to be filled.
61
        this.fill = true;
62
        if (this.barDirection == 'vertical' ) {
63
            this._primaryAxis = '_xaxis';
64
            this._stackAxis = 'y';
65
            this.fillAxis = 'y';
66
        }
67
        else {
68
            this._primaryAxis = '_yaxis';
69
            this._stackAxis = 'x';
70
            this.fillAxis = 'x';
71
        }
72
        // set the shape renderer options
73
        var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill};
74
        this.renderer.shapeRenderer.init(opts);
75
        // set the shadow renderer options
76
        var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill};
77
        this.renderer.shadowRenderer.init(sopts);
78
    };
79
 
80
    // called with scope of series
81
    function barPreInit(target, data, seriesDefaults, options) {
82
        if (this.rendererOptions.barDirection == 'horizontal') {
83
            this._stackAxis = 'x';
84
            this._primaryAxis = '_yaxis';
85
        }
86
    }
87
 
88
    $.jqplot.preSeriesInitHooks.push(barPreInit);
89
 
90
    // needs to be called with scope of series, not renderer.
91
    $.jqplot.BarRenderer.prototype.calcSeriesNumbers = function() {
92
        var nvals = 0;
93
        var nseries = 0;
94
        var paxis = this[this._primaryAxis];
95
        var s, series, pos;
96
        // loop through all series on this axis
97
        for (var i=0; i < paxis._series.length; i++) {
98
            series = paxis._series[i];
99
            if (series === this) {
100
                pos = i;
101
            }
102
            // is the series rendered as a bar?
103
            if (series.renderer.constructor == $.jqplot.BarRenderer) {
104
                // gridData may not be computed yet, use data length insted
105
                nvals += series.data.length;
106
                nseries += 1;
107
            }
108
        }
109
        return [nvals, nseries, pos];
110
    };
111
 
112
    $.jqplot.BarRenderer.prototype.setBarWidth = function() {
113
        // need to know how many data values we have on the approprate axis and figure it out.
114
        var i;
115
        var nvals = 0;
116
        var nseries = 0;
117
        var paxis = this[this._primaryAxis];
118
        var s, series, pos;
119
        var temp = this.renderer.calcSeriesNumbers.call(this);
120
        nvals = temp[0];
121
        nseries = temp[1];
122
        var nticks = paxis.numberTicks;
123
        var nbins = (nticks-1)/2;
124
        // so, now we have total number of axis values.
125
        if (paxis.name == 'xaxis' || paxis.name == 'x2axis') {
126
            if (this._stack) {
127
                this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin;
128
            }
129
            else {
130
                this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
131
                // this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries;
132
            }
133
        }
134
        else {
135
            if (this._stack) {
136
                this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin;
137
            }
138
            else {
139
                this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
140
                // this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries;
141
            }
142
        }
143
        return [nvals, nseries];
144
    };
145
 
146
    $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) {
147
        var i;
148
        var opts = (options != undefined) ? options : {};
149
        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
150
        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
151
        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
152
        var xaxis = this.xaxis;
153
        var yaxis = this.yaxis;
154
        var xp = this._xaxis.series_u2p;
155
        var yp = this._yaxis.series_u2p;
156
        var pointx, pointy, nvals, nseries, pos;
157
 
158
        if (this.barWidth == null) {
159
            this.renderer.setBarWidth.call(this);
160
        }
161
 
162
        var temp = this.renderer.calcSeriesNumbers.call(this);
163
        nvals = temp[0];
164
        nseries = temp[1];
165
        pos = temp[2];
166
 
167
        if (this._stack) {
168
            this._barNudge = 0;
169
        }
170
        else {
171
            this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
172
        }
173
        if (showLine) {
174
            var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
175
            var negativeColor = negativeColors.get(this.index);
176
            var isnegative = false;
177
            var posfs = opts.fillStyle;
178
            var tempfs;
179
 
180
            if (this.barDirection == 'vertical') {
181
                for (var i=0; i<gridData.length; i++) {
182
                    points = [];
183
                    var base = gridData[i][0] + this._barNudge;
184
                    var ystart;
185
 
186
                    // stacked
187
                    if (this._stack && this._prevGridData.length) {
188
                        ystart = this._prevGridData[i][1];
189
                    }
190
                    // not stacked and first series in stack
191
                    else {
192
                        if (this.fillToZero) {
193
                            ystart = this._yaxis.series_u2p(0);
194
                        }
195
                        else {
196
                            ystart = ctx.canvas.height;
197
                        }
198
                    }
199
                    if (this.fillToZero && this._plotData[i][1] < 0) {
200
                        isnegative = true;
201
                        opts.fillStyle = negativeColor;
202
                    }
203
                    else {
204
                        opts.fillStyle = posfs;
205
                        isnegative = false;
206
                    }
207
 
208
                    points.push([base-this.barWidth/2, ystart]);
209
                    points.push([base-this.barWidth/2, gridData[i][1]]);
210
                    points.push([base+this.barWidth/2, gridData[i][1]]);
211
                    points.push([base+this.barWidth/2, ystart]);
212
                    // now draw the shadows if not stacked.
213
                    // for stacked plots, they are predrawn by drawShadow
214
                    if (shadow && !this._stack) {
215
                        this.renderer.shadowRenderer.draw(ctx, points, opts);
216
                    }
217
                    this.renderer.shapeRenderer.draw(ctx, points, opts); 
218
                }
219
            }
220
 
221
            else if (this.barDirection == 'horizontal'){
222
                for (var i=0; i<gridData.length; i++) {
223
                    points = [];
224
                    var base = gridData[i][1] - this._barNudge;
225
                    var xstart;
226
 
227
                    if (this._stack && this._prevGridData.length) {
228
                        xstart = this._prevGridData[i][0];
229
                    }
230
                    else {
231
                        xstart = 0;
232
                    }
233
 
234
                    points.push([xstart, base+this.barWidth/2]);
235
                    points.push([gridData[i][0], base+this.barWidth/2]);
236
                    points.push([gridData[i][0], base-this.barWidth/2]);
237
                    points.push([xstart, base-this.barWidth/2]);
238
                    // now draw the shadows if not stacked.
239
                    // for stacked plots, they are predrawn by drawShadow
240
                    if (shadow && !this._stack) {
241
                        this.renderer.shadowRenderer.draw(ctx, points, opts);
242
                    }
243
                    this.renderer.shapeRenderer.draw(ctx, points, opts); 
244
                }  
245
            }
246
        }                
247
 
248
    };
249
 
250
 
251
    // for stacked plots, shadows will be pre drawn by drawShadow.
252
    $.jqplot.BarRenderer.prototype.drawShadow = function(ctx, gridData, options) {
253
        var i;
254
        var opts = (options != undefined) ? options : {};
255
        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
256
        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
257
        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
258
        var xaxis = this.xaxis;
259
        var yaxis = this.yaxis;
260
        var xp = this._xaxis.series_u2p;
261
        var yp = this._yaxis.series_u2p;
262
        var pointx, pointy, nvals, nseries, pos;
263
 
264
        if (this._stack && this.shadow) {
265
            if (this.barWidth == null) {
266
                this.renderer.setBarWidth.call(this);
267
            }
268
 
269
            var temp = this.renderer.calcSeriesNumbers.call(this);
270
            nvals = temp[0];
271
            nseries = temp[1];
272
            pos = temp[2];
273
 
274
            if (this._stack) {
275
                this._barNudge = 0;
276
            }
277
            else {
278
                this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
279
            }
280
            if (showLine) {
281
 
282
                if (this.barDirection == 'vertical') {
283
                    for (var i=0; i<gridData.length; i++) {
284
                        points = [];
285
                        var base = gridData[i][0] + this._barNudge;
286
                        var ystart;
287
 
288
                        if (this._stack && this._prevGridData.length) {
289
                            ystart = this._prevGridData[i][1];
290
                        }
291
                        else {
292
                            if (this.fillToZero) {
293
                                ystart = this._yaxis.series_u2p(0);
294
                            }
295
                            else {
296
                                ystart = ctx.canvas.height;
297
                            }
298
                        }
299
 
300
                        points.push([base-this.barWidth/2, ystart]);
301
                        points.push([base-this.barWidth/2, gridData[i][1]]);
302
                        points.push([base+this.barWidth/2, gridData[i][1]]);
303
                        points.push([base+this.barWidth/2, ystart]);
304
                        this.renderer.shadowRenderer.draw(ctx, points, opts);
305
                    }
306
                }
307
 
308
                else if (this.barDirection == 'horizontal'){
309
                    for (var i=0; i<gridData.length; i++) {
310
                        points = [];
311
                        var base = gridData[i][1] - this._barNudge;
312
                        var xstart;
313
 
314
                        if (this._stack && this._prevGridData.length) {
315
                            xstart = this._prevGridData[i][0];
316
                        }
317
                        else {
318
                            xstart = 0;
319
                        }
320
 
321
                        points.push([xstart, base+this.barWidth/2]);
322
                        points.push([gridData[i][0], base+this.barWidth/2]);
323
                        points.push([gridData[i][0], base-this.barWidth/2]);
324
                        points.push([xstart, base-this.barWidth/2]);
325
                        this.renderer.shadowRenderer.draw(ctx, points, opts);
326
                    }  
327
                }
328
            }   
329
 
330
        }
331
 
332
 
333
    };
334
})(jQuerySysStatWidget || jQuery);