|
1 // data is two-dimensional array of x,y; we populate y0 |
|
2 d3.layout.stack = function() { |
|
3 var values = Object, |
|
4 order = d3_layout_stackOrders["default"], |
|
5 offset = d3_layout_stackOffsets["zero"], |
|
6 out = d3_layout_stackOut, |
|
7 x = d3_layout_stackX, |
|
8 y = d3_layout_stackY; |
|
9 |
|
10 function stack(data, index) { |
|
11 |
|
12 // Convert series to canonical two-dimensional representation. |
|
13 var series = data.map(function(d, i) { |
|
14 return values.call(stack, d, i); |
|
15 }); |
|
16 |
|
17 // Convert each series to canonical [[x,y]] representation. |
|
18 var points = series.map(function(d, i) { |
|
19 return d.map(function(v, i) { |
|
20 return [x.call(stack, v, i), y.call(stack, v, i)]; |
|
21 }); |
|
22 }); |
|
23 |
|
24 // Compute the order of series, and permute them. |
|
25 var orders = order.call(stack, points, index); |
|
26 series = d3.permute(series, orders); |
|
27 points = d3.permute(points, orders); |
|
28 |
|
29 // Compute the baseline… |
|
30 var offsets = offset.call(stack, points, index); |
|
31 |
|
32 // And propagate it to other series. |
|
33 var n = series.length, |
|
34 m = series[0].length, |
|
35 i, |
|
36 j, |
|
37 o; |
|
38 for (j = 0; j < m; ++j) { |
|
39 out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); |
|
40 for (i = 1; i < n; ++i) { |
|
41 out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); |
|
42 } |
|
43 } |
|
44 |
|
45 return data; |
|
46 } |
|
47 |
|
48 stack.values = function(x) { |
|
49 if (!arguments.length) return values; |
|
50 values = x; |
|
51 return stack; |
|
52 }; |
|
53 |
|
54 stack.order = function(x) { |
|
55 if (!arguments.length) return order; |
|
56 order = typeof x === "function" ? x : d3_layout_stackOrders[x]; |
|
57 return stack; |
|
58 }; |
|
59 |
|
60 stack.offset = function(x) { |
|
61 if (!arguments.length) return offset; |
|
62 offset = typeof x === "function" ? x : d3_layout_stackOffsets[x]; |
|
63 return stack; |
|
64 }; |
|
65 |
|
66 stack.x = function(z) { |
|
67 if (!arguments.length) return x; |
|
68 x = z; |
|
69 return stack; |
|
70 }; |
|
71 |
|
72 stack.y = function(z) { |
|
73 if (!arguments.length) return y; |
|
74 y = z; |
|
75 return stack; |
|
76 }; |
|
77 |
|
78 stack.out = function(z) { |
|
79 if (!arguments.length) return out; |
|
80 out = z; |
|
81 return stack; |
|
82 }; |
|
83 |
|
84 return stack; |
|
85 } |
|
86 |
|
87 function d3_layout_stackX(d) { |
|
88 return d.x; |
|
89 } |
|
90 |
|
91 function d3_layout_stackY(d) { |
|
92 return d.y; |
|
93 } |
|
94 |
|
95 function d3_layout_stackOut(d, y0, y) { |
|
96 d.y0 = y0; |
|
97 d.y = y; |
|
98 } |
|
99 |
|
100 var d3_layout_stackOrders = { |
|
101 |
|
102 "inside-out": function(data) { |
|
103 var n = data.length, |
|
104 i, |
|
105 j, |
|
106 max = data.map(d3_layout_stackMaxIndex), |
|
107 sums = data.map(d3_layout_stackReduceSum), |
|
108 index = d3.range(n).sort(function(a, b) { return max[a] - max[b]; }), |
|
109 top = 0, |
|
110 bottom = 0, |
|
111 tops = [], |
|
112 bottoms = []; |
|
113 for (i = 0; i < n; ++i) { |
|
114 j = index[i]; |
|
115 if (top < bottom) { |
|
116 top += sums[j]; |
|
117 tops.push(j); |
|
118 } else { |
|
119 bottom += sums[j]; |
|
120 bottoms.push(j); |
|
121 } |
|
122 } |
|
123 return bottoms.reverse().concat(tops); |
|
124 }, |
|
125 |
|
126 "reverse": function(data) { |
|
127 return d3.range(data.length).reverse(); |
|
128 }, |
|
129 |
|
130 "default": function(data) { |
|
131 return d3.range(data.length); |
|
132 } |
|
133 |
|
134 }; |
|
135 |
|
136 var d3_layout_stackOffsets = { |
|
137 |
|
138 "silhouette": function(data) { |
|
139 var n = data.length, |
|
140 m = data[0].length, |
|
141 sums = [], |
|
142 max = 0, |
|
143 i, |
|
144 j, |
|
145 o, |
|
146 y0 = []; |
|
147 for (j = 0; j < m; ++j) { |
|
148 for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; |
|
149 if (o > max) max = o; |
|
150 sums.push(o); |
|
151 } |
|
152 for (j = 0; j < m; ++j) { |
|
153 y0[j] = (max - sums[j]) / 2; |
|
154 } |
|
155 return y0; |
|
156 }, |
|
157 |
|
158 "wiggle": function(data) { |
|
159 var n = data.length, |
|
160 x = data[0], |
|
161 m = x.length, |
|
162 max = 0, |
|
163 i, |
|
164 j, |
|
165 k, |
|
166 s1, |
|
167 s2, |
|
168 s3, |
|
169 dx, |
|
170 o, |
|
171 o0, |
|
172 y0 = []; |
|
173 y0[0] = o = o0 = 0; |
|
174 for (j = 1; j < m; ++j) { |
|
175 for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; |
|
176 for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { |
|
177 for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { |
|
178 s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; |
|
179 } |
|
180 s2 += s3 * data[i][j][1]; |
|
181 } |
|
182 y0[j] = o -= s1 ? s2 / s1 * dx : 0; |
|
183 if (o < o0) o0 = o; |
|
184 } |
|
185 for (j = 0; j < m; ++j) y0[j] -= o0; |
|
186 return y0; |
|
187 }, |
|
188 |
|
189 "expand": function(data) { |
|
190 var n = data.length, |
|
191 m = data[0].length, |
|
192 k = 1 / n, |
|
193 i, |
|
194 j, |
|
195 o, |
|
196 y0 = []; |
|
197 for (j = 0; j < m; ++j) { |
|
198 for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; |
|
199 if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; |
|
200 else for (i = 0; i < n; i++) data[i][j][1] = k; |
|
201 } |
|
202 for (j = 0; j < m; ++j) y0[j] = 0; |
|
203 return y0; |
|
204 }, |
|
205 |
|
206 "zero": function(data) { |
|
207 var j = -1, |
|
208 m = data[0].length, |
|
209 y0 = []; |
|
210 while (++j < m) y0[j] = 0; |
|
211 return y0; |
|
212 } |
|
213 |
|
214 }; |
|
215 |
|
216 function d3_layout_stackMaxIndex(array) { |
|
217 var i = 1, |
|
218 j = 0, |
|
219 v = array[0][1], |
|
220 k, |
|
221 n = array.length; |
|
222 for (; i < n; ++i) { |
|
223 if ((k = array[i][1]) > v) { |
|
224 j = i; |
|
225 v = k; |
|
226 } |
|
227 } |
|
228 return j; |
|
229 } |
|
230 |
|
231 function d3_layout_stackReduceSum(d) { |
|
232 return d.reduce(d3_layout_stackSum, 0); |
|
233 } |
|
234 |
|
235 function d3_layout_stackSum(p, d) { |
|
236 return p + d[1]; |
|
237 } |