source: Dev/branches/jQueryUI/client/d3/src/layout/treemap.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 5.8 KB
Line 
1// Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk
2// Modified to support a target aspect ratio by Jeff Heer
3d3.layout.treemap = function() {
4  var hierarchy = d3.layout.hierarchy(),
5      round = Math.round,
6      size = [1, 1], // width, height
7      padding = null,
8      pad = d3_layout_treemapPadNull,
9      sticky = false,
10      stickies,
11      ratio = 0.5 * (1 + Math.sqrt(5)); // golden ratio
12
13  // Compute the area for each child based on value & scale.
14  function scale(children, k) {
15    var i = -1,
16        n = children.length,
17        child,
18        area;
19    while (++i < n) {
20      area = (child = children[i]).value * (k < 0 ? 0 : k);
21      child.area = isNaN(area) || area <= 0 ? 0 : area;
22    }
23  }
24
25  // Recursively arranges the specified node's children into squarified rows.
26  function squarify(node) {
27    if (!node.children) return;
28    var rect = pad(node),
29        row = [],
30        children = node.children.slice(), // copy-on-write
31        child,
32        best = Infinity, // the best row score so far
33        score, // the current row score
34        u = Math.min(rect.dx, rect.dy), // initial orientation
35        n;
36    scale(children, rect.dx * rect.dy / node.value);
37    row.area = 0;
38    while ((n = children.length) > 0) {
39      row.push(child = children[n - 1]);
40      row.area += child.area;
41      if ((score = worst(row, u)) <= best) { // continue with this orientation
42        children.pop();
43        best = score;
44      } else { // abort, and try a different orientation
45        row.area -= row.pop().area;
46        position(row, u, rect, false);
47        u = Math.min(rect.dx, rect.dy);
48        row.length = row.area = 0;
49        best = Infinity;
50      }
51    }
52    if (row.length) {
53      position(row, u, rect, true);
54      row.length = row.area = 0;
55    }
56    node.children.forEach(squarify);
57  }
58
59  // Recursively resizes the specified node's children into existing rows.
60  // Preserves the existing layout!
61  function stickify(node) {
62    if (!node.children) return;
63    var rect = pad(node),
64        children = node.children.slice(), // copy-on-write
65        child,
66        row = [];
67    scale(children, rect.dx * rect.dy / node.value);
68    row.area = 0;
69    while (child = children.pop()) {
70      row.push(child);
71      row.area += child.area;
72      if (child.z != null) {
73        position(row, child.z ? rect.dx : rect.dy, rect, !children.length);
74        row.length = row.area = 0;
75      }
76    }
77    node.children.forEach(stickify);
78  }
79
80  // Computes the score for the specified row, as the worst aspect ratio.
81  function worst(row, u) {
82    var s = row.area,
83        r,
84        rmax = 0,
85        rmin = Infinity,
86        i = -1,
87        n = row.length;
88    while (++i < n) {
89      if (!(r = row[i].area)) continue;
90      if (r < rmin) rmin = r;
91      if (r > rmax) rmax = r;
92    }
93    s *= s;
94    u *= u;
95    return s
96        ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio))
97        : Infinity;
98  }
99
100  // Positions the specified row of nodes. Modifies `rect`.
101  function position(row, u, rect, flush) {
102    var i = -1,
103        n = row.length,
104        x = rect.x,
105        y = rect.y,
106        v = u ? round(row.area / u) : 0,
107        o;
108    if (u == rect.dx) { // horizontal subdivision
109      if (flush || v > rect.dy) v = rect.dy; // over+underflow
110      while (++i < n) {
111        o = row[i];
112        o.x = x;
113        o.y = y;
114        o.dy = v;
115        x += o.dx = v ? round(o.area / v) : 0;
116      }
117      o.z = true;
118      o.dx += rect.x + rect.dx - x; // rounding error
119      rect.y += v;
120      rect.dy -= v;
121    } else { // vertical subdivision
122      if (flush || v > rect.dx) v = rect.dx; // over+underflow
123      while (++i < n) {
124        o = row[i];
125        o.x = x;
126        o.y = y;
127        o.dx = v;
128        y += o.dy = v ? round(o.area / v) : 0;
129      }
130      o.z = false;
131      o.dy += rect.y + rect.dy - y; // rounding error
132      rect.x += v;
133      rect.dx -= v;
134    }
135  }
136
137  function treemap(d) {
138    var nodes = stickies || hierarchy(d),
139        root = nodes[0];
140    root.x = 0;
141    root.y = 0;
142    root.dx = size[0];
143    root.dy = size[1];
144    if (stickies) hierarchy.revalue(root);
145    scale([root], root.dx * root.dy / root.value);
146    (stickies ? stickify : squarify)(root);
147    if (sticky) stickies = nodes;
148    return nodes;
149  }
150
151  treemap.size = function(x) {
152    if (!arguments.length) return size;
153    size = x;
154    return treemap;
155  };
156
157  treemap.padding = function(x) {
158    if (!arguments.length) return padding;
159
160    function padFunction(node) {
161      var p = x.call(treemap, node, node.depth);
162      return p == null
163          ? d3_layout_treemapPadNull(node)
164          : d3_layout_treemapPad(node, typeof p === "number" ? [p, p, p, p] : p);
165    }
166
167    function padConstant(node) {
168      return d3_layout_treemapPad(node, x);
169    }
170
171    var type;
172    pad = (padding = x) == null ? d3_layout_treemapPadNull
173        : (type = typeof x) === "function" ? padFunction
174        : type === "number" ? (x = [x, x, x, x], padConstant)
175        : padConstant;
176    return treemap;
177  };
178
179  treemap.round = function(x) {
180    if (!arguments.length) return round != Number;
181    round = x ? Math.round : Number;
182    return treemap;
183  };
184
185  treemap.sticky = function(x) {
186    if (!arguments.length) return sticky;
187    sticky = x;
188    stickies = null;
189    return treemap;
190  };
191
192  treemap.ratio = function(x) {
193    if (!arguments.length) return ratio;
194    ratio = x;
195    return treemap;
196  };
197
198  return d3_layout_hierarchyRebind(treemap, hierarchy);
199};
200
201function d3_layout_treemapPadNull(node) {
202  return {x: node.x, y: node.y, dx: node.dx, dy: node.dy};
203}
204
205function d3_layout_treemapPad(node, padding) {
206  var x = node.x + padding[3],
207      y = node.y + padding[0],
208      dx = node.dx - padding[1] - padding[3],
209      dy = node.dy - padding[0] - padding[2];
210  if (dx < 0) { x += dx / 2; dx = 0; }
211  if (dy < 0) { y += dy / 2; dy = 0; }
212  return {x: x, y: y, dx: dx, dy: dy};
213}
Note: See TracBrowser for help on using the repository browser.