toolkit/javascript/d3/src/layout/stack.js
changeset 47 c0b4a8b5a012
equal deleted inserted replaced
46:efd9c589177a 47:c0b4a8b5a012
       
     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 }