Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3284 vikas 1
/*
2
Flot plugin for stacking data sets, i.e. putting them on top of each
3
other, for accumulative graphs.
4
 
5
The plugin assumes the data is sorted on x (or y if stacking
6
horizontally). For line charts, it is assumed that if a line has an
7
undefined gap (from a null point), then the line above it should have
8
the same gap - insert zeros instead of "null" if you want another
9
behaviour. This also holds for the start and end of the chart. Note
10
that stacking a mix of positive and negative values in most instances
11
doesn't make sense (so it looks weird).
12
 
13
Two or more series are stacked when their "stack" attribute is set to
14
the same key (which can be any number or string or just "true"). To
15
specify the default stack, you can set
16
 
17
  series: {
18
    stack: null or true or key (number/string)
19
  }
20
 
21
or specify it for a specific series
22
 
23
  $.plot($("#placeholder"), [{ data: [ ... ], stack: true }])
24
 
25
The stacking order is determined by the order of the data series in
26
the array (later series end up on top of the previous).
27
 
28
Internally, the plugin modifies the datapoints in each series, adding
29
an offset to the y value. For line series, extra data points are
30
inserted through interpolation. If there's a second y value, it's also
31
adjusted (e.g for bar charts or filled areas).
32
*/
33
 
34
(function ($) {
35
    var options = {
36
        series: { stack: null } // or number/string
37
    };
38
 
39
    function init(plot) {
40
        function findMatchingSeries(s, allseries) {
41
            var res = null
42
            for (var i = 0; i < allseries.length; ++i) {
43
                if (s == allseries[i])
44
                    break;
45
 
46
                if (allseries[i].stack == s.stack)
47
                    res = allseries[i];
48
            }
49
 
50
            return res;
51
        }
52
 
53
        function stackData(plot, s, datapoints) {
54
            if (s.stack == null)
55
                return;
56
 
57
            var other = findMatchingSeries(s, plot.getData());
58
            if (!other)
59
                return;
60
 
61
            var ps = datapoints.pointsize,
62
                points = datapoints.points,
63
                otherps = other.datapoints.pointsize,
64
                otherpoints = other.datapoints.points,
65
                newpoints = [],
66
                px, py, intery, qx, qy, bottom,
67
                withlines = s.lines.show,
68
                horizontal = s.bars.horizontal,
69
                withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y),
70
                withsteps = withlines && s.lines.steps,
71
                fromgap = true,
72
                keyOffset = horizontal ? 1 : 0,
73
                accumulateOffset = horizontal ? 0 : 1,
74
                i = 0, j = 0, l;
75
 
76
            while (true) {
77
                if (i >= points.length)
78
                    break;
79
 
80
                l = newpoints.length;
81
 
82
                if (points[i] == null) {
83
                    // copy gaps
84
                    for (m = 0; m < ps; ++m)
85
                        newpoints.push(points[i + m]);
86
                    i += ps;
87
                }
88
                else if (j >= otherpoints.length) {
89
                    // for lines, we can't use the rest of the points
90
                    if (!withlines) {
91
                        for (m = 0; m < ps; ++m)
92
                            newpoints.push(points[i + m]);
93
                    }
94
                    i += ps;
95
                }
96
                else if (otherpoints[j] == null) {
97
                    // oops, got a gap
98
                    for (m = 0; m < ps; ++m)
99
                        newpoints.push(null);
100
                    fromgap = true;
101
                    j += otherps;
102
                }
103
                else {
104
                    // cases where we actually got two points
105
                    px = points[i + keyOffset];
106
                    py = points[i + accumulateOffset];
107
                    qx = otherpoints[j + keyOffset];
108
                    qy = otherpoints[j + accumulateOffset];
109
                    bottom = 0;
110
 
111
                    if (px == qx) {
112
                        for (m = 0; m < ps; ++m)
113
                            newpoints.push(points[i + m]);
114
 
115
                        newpoints[l + accumulateOffset] += qy;
116
                        bottom = qy;
117
 
118
                        i += ps;
119
                        j += otherps;
120
                    }
121
                    else if (px > qx) {
122
                        // we got past point below, might need to
123
                        // insert interpolated extra point
124
                        if (withlines && i > 0 && points[i - ps] != null) {
125
                            intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px);
126
                            newpoints.push(qx);
127
                            newpoints.push(intery + qy);
128
                            for (m = 2; m < ps; ++m)
129
                                newpoints.push(points[i + m]);
130
                            bottom = qy; 
131
                        }
132
 
133
                        j += otherps;
134
                    }
135
                    else { // px < qx
136
                        if (fromgap && withlines) {
137
                            // if we come from a gap, we just skip this point
138
                            i += ps;
139
                            continue;
140
                        }
141
 
142
                        for (m = 0; m < ps; ++m)
143
                            newpoints.push(points[i + m]);
144
 
145
                        // we might be able to interpolate a point below,
146
                        // this can give us a better y
147
                        if (withlines && j > 0 && otherpoints[j - otherps] != null)
148
                            bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx);
149
 
150
                        newpoints[l + accumulateOffset] += bottom;
151
 
152
                        i += ps;
153
                    }
154
 
155
                    fromgap = false;
156
 
157
                    if (l != newpoints.length && withbottom)
158
                        newpoints[l + 2] += bottom;
159
                }
160
 
161
                // maintain the line steps invariant
162
                if (withsteps && l != newpoints.length && l > 0
163
                    && newpoints[l] != null
164
                    && newpoints[l] != newpoints[l - ps]
165
                    && newpoints[l + 1] != newpoints[l - ps + 1]) {
166
                    for (m = 0; m < ps; ++m)
167
                        newpoints[l + ps + m] = newpoints[l + m];
168
                    newpoints[l + 1] = newpoints[l - ps + 1];
169
                }
170
            }
171
 
172
            datapoints.points = newpoints;
173
        }
174
 
175
        plot.hooks.processDatapoints.push(stackData);
176
    }
177
 
178
    $.plot.plugins.push({
179
        init: init,
180
        options: options,
181
        name: 'stack',
182
        version: '1.2'
183
    });
184
})(jQuery);