├── codeFlower.html
├── codeFlower.js
├── d3.geom.js
├── d3.js
├── d3.layout.js
└── dataConverter.js
/codeFlower.html:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/codeFlower.js:
--------------------------------------------------------------------------------
1 | var CodeFlower = function(selector, w, h) {
2 | this.w = w;
3 | this.h = h;
4 |
5 | d3.select(selector).selectAll("svg").remove();
6 |
7 | this.svg = d3.select(selector).append("svg:svg")
8 | .attr('width', w)
9 | .attr('height', h);
10 |
11 | this.svg.append("svg:rect")
12 | .style("stroke", "#999")
13 | .style("fill", "#fff")
14 | .attr('width', w)
15 | .attr('height', h);
16 |
17 | this.force = d3.layout.force()
18 | .on("tick", this.tick.bind(this))
19 | .charge(function(d) { return d._children ? -d.size / 100 : -40; })
20 | .linkDistance(function(d) { return d.target._children ? 80 : 25; })
21 | .size([h, w]);
22 | };
23 |
24 | CodeFlower.prototype.update = function(json) {
25 | if (json) this.json = json;
26 |
27 | this.json.fixed = true;
28 | this.json.x = this.w / 2;
29 | this.json.y = this.h / 2;
30 |
31 | var nodes = this.flatten(this.json);
32 | var links = d3.layout.tree().links(nodes);
33 | var total = nodes.length || 1;
34 |
35 | // remove existing text (will readd it afterwards to be sure it's on top)
36 | this.svg.selectAll("text").remove();
37 |
38 | // Restart the force layout
39 | this.force
40 | .gravity(Math.atan(total / 50) / Math.PI * 0.4)
41 | .nodes(nodes)
42 | .links(links)
43 | .start();
44 |
45 | // Update the links
46 | this.link = this.svg.selectAll("line.link")
47 | .data(links, function(d) { return d.target.name; });
48 |
49 | // Enter any new links
50 | this.link.enter().insert("svg:line", ".node")
51 | .attr("class", "link")
52 | .attr("x1", function(d) { return d.source.x; })
53 | .attr("y1", function(d) { return d.source.y; })
54 | .attr("x2", function(d) { return d.target.x; })
55 | .attr("y2", function(d) { return d.target.y; });
56 |
57 | // Exit any old links.
58 | this.link.exit().remove();
59 |
60 | // Update the nodes
61 | this.node = this.svg.selectAll("circle.node")
62 | .data(nodes, function(d) { return d.name; })
63 | .classed("collapsed", function(d) { return d._children ? 1 : 0; });
64 |
65 | this.node.transition()
66 | .attr("r", function(d) { return d.children ? 3.5 : Math.pow(d.size, 2/5) || 1; });
67 |
68 | // Enter any new nodes
69 | this.node.enter().append('svg:circle')
70 | .attr("class", "node")
71 | .classed('directory', function(d) { return (d._children || d.children) ? 1 : 0; })
72 | .attr("r", function(d) { return d.children ? 3.5 : Math.pow(d.size, 2/5) || 1; })
73 | .style("fill", function color(d) {
74 | return "hsl(" + parseInt(360 / total * d.id, 10) + ",90%,70%)";
75 | })
76 | .call(this.force.drag)
77 | .on("click", this.click.bind(this))
78 | .on("mouseover", this.mouseover.bind(this))
79 | .on("mouseout", this.mouseout.bind(this));
80 |
81 | // Exit any old nodes
82 | this.node.exit().remove();
83 |
84 | this.text = this.svg.append('svg:text')
85 | .attr('class', 'nodetext')
86 | .attr('dy', 0)
87 | .attr('dx', 0)
88 | .attr('text-anchor', 'middle');
89 |
90 | return this;
91 | };
92 |
93 | CodeFlower.prototype.flatten = function(root) {
94 | var nodes = [], i = 0;
95 |
96 | function recurse(node) {
97 | if (node.children) {
98 | node.size = node.children.reduce(function(p, v) {
99 | return p + recurse(v);
100 | }, 0);
101 | }
102 | if (!node.id) node.id = ++i;
103 | nodes.push(node);
104 | return node.size;
105 | }
106 |
107 | root.size = recurse(root);
108 | return nodes;
109 | };
110 |
111 | CodeFlower.prototype.click = function(d) {
112 | // Toggle children on click.
113 | if (d.children) {
114 | d._children = d.children;
115 | d.children = null;
116 | } else {
117 | d.children = d._children;
118 | d._children = null;
119 | }
120 | this.update();
121 | };
122 |
123 | CodeFlower.prototype.mouseover = function(d) {
124 | this.text.attr('transform', 'translate(' + d.x + ',' + (d.y - 5 - (d.children ? 3.5 : Math.sqrt(d.size) / 2)) + ')')
125 | .text(d.name + ": " + d.size + " loc")
126 | .style('display', null);
127 | };
128 |
129 | CodeFlower.prototype.mouseout = function(d) {
130 | this.text.style('display', 'none');
131 | };
132 |
133 | CodeFlower.prototype.tick = function() {
134 | var h = this.h;
135 | var w = this.w;
136 | this.link.attr("x1", function(d) { return d.source.x; })
137 | .attr("y1", function(d) { return d.source.y; })
138 | .attr("x2", function(d) { return d.target.x; })
139 | .attr("y2", function(d) { return d.target.y; });
140 |
141 | this.node.attr("transform", function(d) {
142 | return "translate(" + Math.max(5, Math.min(w - 5, d.x)) + "," + Math.max(5, Math.min(h - 5, d.y)) + ")";
143 | });
144 | };
145 |
146 | CodeFlower.prototype.cleanup = function() {
147 | this.update([]);
148 | this.force.stop();
149 | };
--------------------------------------------------------------------------------
/d3.geom.js:
--------------------------------------------------------------------------------
1 | (function(){d3.geom = {};
2 | /**
3 | * Computes a contour for a given input grid function using the marching
5 | * squares algorithm. Returns the contour polygon as an array of points.
6 | *
7 | * @param grid a two-input function(x, y) that returns true for values
8 | * inside the contour and false for values outside the contour.
9 | * @param start an optional starting point [x, y] on the grid.
10 | * @returns polygon [[x1, y1], [x2, y2], …]
11 | */
12 | d3.geom.contour = function(grid, start) {
13 | var s = start || d3_geom_contourStart(grid), // starting point
14 | c = [], // contour polygon
15 | x = s[0], // current x position
16 | y = s[1], // current y position
17 | dx = 0, // next x direction
18 | dy = 0, // next y direction
19 | pdx = NaN, // previous x direction
20 | pdy = NaN, // previous y direction
21 | i = 0;
22 |
23 | do {
24 | // determine marching squares index
25 | i = 0;
26 | if (grid(x-1, y-1)) i += 1;
27 | if (grid(x, y-1)) i += 2;
28 | if (grid(x-1, y )) i += 4;
29 | if (grid(x, y )) i += 8;
30 |
31 | // determine next direction
32 | if (i === 6) {
33 | dx = pdy === -1 ? -1 : 1;
34 | dy = 0;
35 | } else if (i === 9) {
36 | dx = 0;
37 | dy = pdx === 1 ? -1 : 1;
38 | } else {
39 | dx = d3_geom_contourDx[i];
40 | dy = d3_geom_contourDy[i];
41 | }
42 |
43 | // update contour polygon
44 | if (dx != pdx && dy != pdy) {
45 | c.push([x, y]);
46 | pdx = dx;
47 | pdy = dy;
48 | }
49 |
50 | x += dx;
51 | y += dy;
52 | } while (s[0] != x || s[1] != y);
53 |
54 | return c;
55 | };
56 |
57 | // lookup tables for marching directions
58 | var d3_geom_contourDx = [1, 0, 1, 1,-1, 0,-1, 1,0, 0,0,0,-1, 0,-1,NaN],
59 | d3_geom_contourDy = [0,-1, 0, 0, 0,-1, 0, 0,1,-1,1,1, 0,-1, 0,NaN];
60 |
61 | function d3_geom_contourStart(grid) {
62 | var x = 0,
63 | y = 0;
64 |
65 | // search for a starting point; begin at origin
66 | // and proceed along outward-expanding diagonals
67 | while (true) {
68 | if (grid(x,y)) {
69 | return [x,y];
70 | }
71 | if (x === 0) {
72 | x = y + 1;
73 | y = 0;
74 | } else {
75 | x = x - 1;
76 | y = y + 1;
77 | }
78 | }
79 | }
80 | /**
81 | * Computes the 2D convex hull of a set of points using Graham's scanning
82 | * algorithm. The algorithm has been implemented as described in Cormen,
83 | * Leiserson, and Rivest's Introduction to Algorithms. The running time of
84 | * this algorithm is O(n log n), where n is the number of input points.
85 | *
86 | * @param vertices [[x1, y1], [x2, y2], …]
87 | * @returns polygon [[x1, y1], [x2, y2], …]
88 | */
89 | d3.geom.hull = function(vertices) {
90 | if (vertices.length < 3) return [];
91 |
92 | var len = vertices.length,
93 | plen = len - 1,
94 | points = [],
95 | stack = [],
96 | i, j, h = 0, x1, y1, x2, y2, u, v, a, sp;
97 |
98 | // find the starting ref point: leftmost point with the minimum y coord
99 | for (i=1; i= (x2*x2 + y2*y2)) {
129 | points[i].index = -1;
130 | } else {
131 | points[u].index = -1;
132 | a = points[i].angle;
133 | u = i;
134 | v = j;
135 | }
136 | } else {
137 | a = points[i].angle;
138 | u = i;
139 | v = j;
140 | }
141 | }
142 |
143 | // initialize the stack
144 | stack.push(h);
145 | for (i=0, j=0; i<2; ++j) {
146 | if (points[j].index !== -1) {
147 | stack.push(points[j].index);
148 | i++;
149 | }
150 | }
151 | sp = stack.length;
152 |
153 | // do graham's scan
154 | for (; j 0;
177 | }
178 | // Note: requires coordinates to be counterclockwise and convex!
179 | d3.geom.polygon = function(coordinates) {
180 |
181 | coordinates.area = function() {
182 | var i = 0,
183 | n = coordinates.length,
184 | a = coordinates[n - 1][0] * coordinates[0][1],
185 | b = coordinates[n - 1][1] * coordinates[0][0];
186 | while (++i < n) {
187 | a += coordinates[i - 1][0] * coordinates[i][1];
188 | b += coordinates[i - 1][1] * coordinates[i][0];
189 | }
190 | return (b - a) * .5;
191 | };
192 |
193 | coordinates.centroid = function(k) {
194 | var i = -1,
195 | n = coordinates.length - 1,
196 | x = 0,
197 | y = 0,
198 | a,
199 | b,
200 | c;
201 | if (!arguments.length) k = -1 / (6 * coordinates.area());
202 | while (++i < n) {
203 | a = coordinates[i];
204 | b = coordinates[i + 1];
205 | c = a[0] * b[1] - b[0] * a[1];
206 | x += (a[0] + b[0]) * c;
207 | y += (a[1] + b[1]) * c;
208 | }
209 | return [x * k, y * k];
210 | };
211 |
212 | // The Sutherland-Hodgman clipping algorithm.
213 | coordinates.clip = function(subject) {
214 | var input,
215 | i = -1,
216 | n = coordinates.length,
217 | j,
218 | m,
219 | a = coordinates[n - 1],
220 | b,
221 | c,
222 | d;
223 | while (++i < n) {
224 | input = subject.slice();
225 | subject.length = 0;
226 | b = coordinates[i];
227 | c = input[(m = input.length) - 1];
228 | j = -1;
229 | while (++j < m) {
230 | d = input[j];
231 | if (d3_geom_polygonInside(d, a, b)) {
232 | if (!d3_geom_polygonInside(c, a, b)) {
233 | subject.push(d3_geom_polygonIntersect(c, d, a, b));
234 | }
235 | subject.push(d);
236 | } else if (d3_geom_polygonInside(c, a, b)) {
237 | subject.push(d3_geom_polygonIntersect(c, d, a, b));
238 | }
239 | c = d;
240 | }
241 | a = b;
242 | }
243 | return subject;
244 | };
245 |
246 | return coordinates;
247 | };
248 |
249 | function d3_geom_polygonInside(p, a, b) {
250 | return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
251 | }
252 |
253 | // Intersect two infinite lines cd and ab.
254 | function d3_geom_polygonIntersect(c, d, a, b) {
255 | var x1 = c[0], x2 = d[0], x3 = a[0], x4 = b[0],
256 | y1 = c[1], y2 = d[1], y3 = a[1], y4 = b[1],
257 | x13 = x1 - x3,
258 | x21 = x2 - x1,
259 | x43 = x4 - x3,
260 | y13 = y1 - y3,
261 | y21 = y2 - y1,
262 | y43 = y4 - y3,
263 | ua = (x43 * y13 - y43 * x13) / (y43 * x21 - x43 * y21);
264 | return [x1 + ua * x21, y1 + ua * y21];
265 | }
266 | // Adapted from Nicolas Garcia Belmonte's JIT implementation:
267 | // http://blog.thejit.org/2010/02/12/voronoi-tessellation/
268 | // http://blog.thejit.org/assets/voronoijs/voronoi.js
269 | // See lib/jit/LICENSE for details.
270 |
271 | // Notes:
272 | //
273 | // This implementation does not clip the returned polygons, so if you want to
274 | // clip them to a particular shape you will need to do that either in SVG or by
275 | // post-processing with d3.geom.polygon's clip method.
276 | //
277 | // If any vertices are coincident or have NaN positions, the behavior of this
278 | // method is undefined. Most likely invalid polygons will be returned. You
279 | // should filter invalid points, and consolidate coincident points, before
280 | // computing the tessellation.
281 |
282 | /**
283 | * @param vertices [[x1, y1], [x2, y2], …]
284 | * @returns polygons [[[x1, y1], [x2, y2], …], …]
285 | */
286 | d3.geom.voronoi = function(vertices) {
287 | var polygons = vertices.map(function() { return []; });
288 |
289 | d3_voronoi_tessellate(vertices, function(e) {
290 | var s1,
291 | s2,
292 | x1,
293 | x2,
294 | y1,
295 | y2;
296 | if (e.a === 1 && e.b >= 0) {
297 | s1 = e.ep.r;
298 | s2 = e.ep.l;
299 | } else {
300 | s1 = e.ep.l;
301 | s2 = e.ep.r;
302 | }
303 | if (e.a === 1) {
304 | y1 = s1 ? s1.y : -1e6;
305 | x1 = e.c - e.b * y1;
306 | y2 = s2 ? s2.y : 1e6;
307 | x2 = e.c - e.b * y2;
308 | } else {
309 | x1 = s1 ? s1.x : -1e6;
310 | y1 = e.c - e.a * x1;
311 | x2 = s2 ? s2.x : 1e6;
312 | y2 = e.c - e.a * x2;
313 | }
314 | var v1 = [x1, y1],
315 | v2 = [x2, y2];
316 | polygons[e.region.l.index].push(v1, v2);
317 | polygons[e.region.r.index].push(v1, v2);
318 | });
319 |
320 | // Reconnect the polygon segments into counterclockwise loops.
321 | return polygons.map(function(polygon, i) {
322 | var cx = vertices[i][0],
323 | cy = vertices[i][1];
324 | polygon.forEach(function(v) {
325 | v.angle = Math.atan2(v[0] - cx, v[1] - cy);
326 | });
327 | return polygon.sort(function(a, b) {
328 | return a.angle - b.angle;
329 | }).filter(function(d, i) {
330 | return !i || (d.angle - polygon[i - 1].angle > 1e-10);
331 | });
332 | });
333 | };
334 |
335 | var d3_voronoi_opposite = {"l": "r", "r": "l"};
336 |
337 | function d3_voronoi_tessellate(vertices, callback) {
338 |
339 | var Sites = {
340 | list: vertices
341 | .map(function(v, i) {
342 | return {
343 | index: i,
344 | x: v[0],
345 | y: v[1]
346 | };
347 | })
348 | .sort(function(a, b) {
349 | return a.y < b.y ? -1
350 | : a.y > b.y ? 1
351 | : a.x < b.x ? -1
352 | : a.x > b.x ? 1
353 | : 0;
354 | }),
355 | bottomSite: null
356 | };
357 |
358 | var EdgeList = {
359 | list: [],
360 | leftEnd: null,
361 | rightEnd: null,
362 |
363 | init: function() {
364 | EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l");
365 | EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l");
366 | EdgeList.leftEnd.r = EdgeList.rightEnd;
367 | EdgeList.rightEnd.l = EdgeList.leftEnd;
368 | EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd);
369 | },
370 |
371 | createHalfEdge: function(edge, side) {
372 | return {
373 | edge: edge,
374 | side: side,
375 | vertex: null,
376 | "l": null,
377 | "r": null
378 | };
379 | },
380 |
381 | insert: function(lb, he) {
382 | he.l = lb;
383 | he.r = lb.r;
384 | lb.r.l = he;
385 | lb.r = he;
386 | },
387 |
388 | leftBound: function(p) {
389 | var he = EdgeList.leftEnd;
390 | do {
391 | he = he.r;
392 | } while (he != EdgeList.rightEnd && Geom.rightOf(he, p));
393 | he = he.l;
394 | return he;
395 | },
396 |
397 | del: function(he) {
398 | he.l.r = he.r;
399 | he.r.l = he.l;
400 | he.edge = null;
401 | },
402 |
403 | right: function(he) {
404 | return he.r;
405 | },
406 |
407 | left: function(he) {
408 | return he.l;
409 | },
410 |
411 | leftRegion: function(he) {
412 | return he.edge == null
413 | ? Sites.bottomSite
414 | : he.edge.region[he.side];
415 | },
416 |
417 | rightRegion: function(he) {
418 | return he.edge == null
419 | ? Sites.bottomSite
420 | : he.edge.region[d3_voronoi_opposite[he.side]];
421 | }
422 | };
423 |
424 | var Geom = {
425 |
426 | bisect: function(s1, s2) {
427 | var newEdge = {
428 | region: {"l": s1, "r": s2},
429 | ep: {"l": null, "r": null}
430 | };
431 |
432 | var dx = s2.x - s1.x,
433 | dy = s2.y - s1.y,
434 | adx = dx > 0 ? dx : -dx,
435 | ady = dy > 0 ? dy : -dy;
436 |
437 | newEdge.c = s1.x * dx + s1.y * dy
438 | + (dx * dx + dy * dy) * .5;
439 |
440 | if (adx > ady) {
441 | newEdge.a = 1;
442 | newEdge.b = dy / dx;
443 | newEdge.c /= dx;
444 | } else {
445 | newEdge.b = 1;
446 | newEdge.a = dx / dy;
447 | newEdge.c /= dy;
448 | }
449 |
450 | return newEdge;
451 | },
452 |
453 | intersect: function(el1, el2) {
454 | var e1 = el1.edge,
455 | e2 = el2.edge;
456 | if (!e1 || !e2 || (e1.region.r == e2.region.r)) {
457 | return null;
458 | }
459 | var d = (e1.a * e2.b) - (e1.b * e2.a);
460 | if (Math.abs(d) < 1e-10) {
461 | return null;
462 | }
463 | var xint = (e1.c * e2.b - e2.c * e1.b) / d,
464 | yint = (e2.c * e1.a - e1.c * e2.a) / d,
465 | e1r = e1.region.r,
466 | e2r = e2.region.r,
467 | el,
468 | e;
469 | if ((e1r.y < e2r.y) ||
470 | (e1r.y == e2r.y && e1r.x < e2r.x)) {
471 | el = el1;
472 | e = e1;
473 | } else {
474 | el = el2;
475 | e = e2;
476 | }
477 | var rightOfSite = (xint >= e.region.r.x);
478 | if ((rightOfSite && (el.side === "l")) ||
479 | (!rightOfSite && (el.side === "r"))) {
480 | return null;
481 | }
482 | return {
483 | x: xint,
484 | y: yint
485 | };
486 | },
487 |
488 | rightOf: function(he, p) {
489 | var e = he.edge,
490 | topsite = e.region.r,
491 | rightOfSite = (p.x > topsite.x);
492 |
493 | if (rightOfSite && (he.side === "l")) {
494 | return 1;
495 | }
496 | if (!rightOfSite && (he.side === "r")) {
497 | return 0;
498 | }
499 | if (e.a === 1) {
500 | var dyp = p.y - topsite.y,
501 | dxp = p.x - topsite.x,
502 | fast = 0,
503 | above = 0;
504 |
505 | if ((!rightOfSite && (e.b < 0)) ||
506 | (rightOfSite && (e.b >= 0))) {
507 | above = fast = (dyp >= e.b * dxp);
508 | } else {
509 | above = ((p.x + p.y * e.b) > e.c);
510 | if (e.b < 0) {
511 | above = !above;
512 | }
513 | if (!above) {
514 | fast = 1;
515 | }
516 | }
517 | if (!fast) {
518 | var dxs = topsite.x - e.region.l.x;
519 | above = (e.b * (dxp * dxp - dyp * dyp)) <
520 | (dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b));
521 |
522 | if (e.b < 0) {
523 | above = !above;
524 | }
525 | }
526 | } else /* e.b == 1 */ {
527 | var yl = e.c - e.a * p.x,
528 | t1 = p.y - yl,
529 | t2 = p.x - topsite.x,
530 | t3 = yl - topsite.y;
531 |
532 | above = (t1 * t1) > (t2 * t2 + t3 * t3);
533 | }
534 | return he.side === "l" ? above : !above;
535 | },
536 |
537 | endPoint: function(edge, side, site) {
538 | edge.ep[side] = site;
539 | if (!edge.ep[d3_voronoi_opposite[side]]) return;
540 | callback(edge);
541 | },
542 |
543 | distance: function(s, t) {
544 | var dx = s.x - t.x,
545 | dy = s.y - t.y;
546 | return Math.sqrt(dx * dx + dy * dy);
547 | }
548 | };
549 |
550 | var EventQueue = {
551 | list: [],
552 |
553 | insert: function(he, site, offset) {
554 | he.vertex = site;
555 | he.ystar = site.y + offset;
556 | for (var i=0, list=EventQueue.list, l=list.length; i next.ystar ||
559 | (he.ystar == next.ystar &&
560 | site.x > next.vertex.x)) {
561 | continue;
562 | } else {
563 | break;
564 | }
565 | }
566 | list.splice(i, 0, he);
567 | },
568 |
569 | del: function(he) {
570 | for (var i=0, ls=EventQueue.list, l=ls.length; i top.y) {
646 | temp = bot;
647 | bot = top;
648 | top = temp;
649 | pm = "r";
650 | }
651 | e = Geom.bisect(bot, top);
652 | bisector = EdgeList.createHalfEdge(e, pm);
653 | EdgeList.insert(llbnd, bisector);
654 | Geom.endPoint(e, d3_voronoi_opposite[pm], v);
655 | p = Geom.intersect(llbnd, bisector);
656 | if (p) {
657 | EventQueue.del(llbnd);
658 | EventQueue.insert(llbnd, p, Geom.distance(p, bot));
659 | }
660 | p = Geom.intersect(bisector, rrbnd);
661 | if (p) {
662 | EventQueue.insert(bisector, p, Geom.distance(p, bot));
663 | }
664 | } else {
665 | break;
666 | }
667 | }//end while
668 |
669 | for (lbnd = EdgeList.right(EdgeList.leftEnd);
670 | lbnd != EdgeList.rightEnd;
671 | lbnd = EdgeList.right(lbnd)) {
672 | callback(lbnd.edge);
673 | }
674 | }
675 | /**
676 | * @param vertices [[x1, y1], [x2, y2], …]
677 | * @returns triangles [[[x1, y1], [x2, y2], [x3, y3]], …]
678 | */
679 | d3.geom.delaunay = function(vertices) {
680 | var edges = vertices.map(function() { return []; }),
681 | triangles = [];
682 |
683 | // Use the Voronoi tessellation to determine Delaunay edges.
684 | d3_voronoi_tessellate(vertices, function(e) {
685 | edges[e.region.l.index].push(vertices[e.region.r.index]);
686 | });
687 |
688 | // Reconnect the edges into counterclockwise triangles.
689 | edges.forEach(function(edge, i) {
690 | var v = vertices[i],
691 | cx = v[0],
692 | cy = v[1];
693 | edge.forEach(function(v) {
694 | v.angle = Math.atan2(v[0] - cx, v[1] - cy);
695 | });
696 | edge.sort(function(a, b) {
697 | return a.angle - b.angle;
698 | });
699 | for (var j = 0, m = edge.length - 1; j < m; j++) {
700 | triangles.push([v, edge[j], edge[j + 1]]);
701 | }
702 | });
703 |
704 | return triangles;
705 | };
706 | // Constructs a new quadtree for the specified array of points. A quadtree is a
707 | // two-dimensional recursive spatial subdivision. This implementation uses
708 | // square partitions, dividing each square into four equally-sized squares. Each
709 | // point exists in a unique node; if multiple points are in the same position,
710 | // some points may be stored on internal nodes rather than leaf nodes. Quadtrees
711 | // can be used to accelerate various spatial operations, such as the Barnes-Hut
712 | // approximation for computing n-body forces, or collision detection.
713 | d3.geom.quadtree = function(points, x1, y1, x2, y2) {
714 | var p,
715 | i = -1,
716 | n = points.length;
717 |
718 | // Type conversion for deprecated API.
719 | if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint);
720 |
721 | // Allow bounds to be specified explicitly.
722 | if (arguments.length < 5) {
723 | if (arguments.length === 3) {
724 | y2 = x2 = y1;
725 | y1 = x1;
726 | } else {
727 | x1 = y1 = Infinity;
728 | x2 = y2 = -Infinity;
729 |
730 | // Compute bounds.
731 | while (++i < n) {
732 | p = points[i];
733 | if (p.x < x1) x1 = p.x;
734 | if (p.y < y1) y1 = p.y;
735 | if (p.x > x2) x2 = p.x;
736 | if (p.y > y2) y2 = p.y;
737 | }
738 |
739 | // Squarify the bounds.
740 | var dx = x2 - x1,
741 | dy = y2 - y1;
742 | if (dx > dy) y2 = y1 + dx;
743 | else x2 = x1 + dy;
744 | }
745 | }
746 |
747 | // Recursively inserts the specified point p at the node n or one of its
748 | // descendants. The bounds are defined by [x1, x2] and [y1, y2].
749 | function insert(n, p, x1, y1, x2, y2) {
750 | if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid points
751 | if (n.leaf) {
752 | var v = n.point;
753 | if (v) {
754 | // If the point at this leaf node is at the same position as the new
755 | // point we are adding, we leave the point associated with the
756 | // internal node while adding the new point to a child node. This
757 | // avoids infinite recursion.
758 | if ((Math.abs(v.x - p.x) + Math.abs(v.y - p.y)) < .01) {
759 | insertChild(n, p, x1, y1, x2, y2);
760 | } else {
761 | n.point = null;
762 | insertChild(n, v, x1, y1, x2, y2);
763 | insertChild(n, p, x1, y1, x2, y2);
764 | }
765 | } else {
766 | n.point = p;
767 | }
768 | } else {
769 | insertChild(n, p, x1, y1, x2, y2);
770 | }
771 | }
772 |
773 | // Recursively inserts the specified point p into a descendant of node n. The
774 | // bounds are defined by [x1, x2] and [y1, y2].
775 | function insertChild(n, p, x1, y1, x2, y2) {
776 | // Compute the split point, and the quadrant in which to insert p.
777 | var sx = (x1 + x2) * .5,
778 | sy = (y1 + y2) * .5,
779 | right = p.x >= sx,
780 | bottom = p.y >= sy,
781 | i = (bottom << 1) + right;
782 |
783 | // Recursively insert into the child node.
784 | n.leaf = false;
785 | n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
786 |
787 | // Update the bounds as we recurse.
788 | if (right) x1 = sx; else x2 = sx;
789 | if (bottom) y1 = sy; else y2 = sy;
790 | insert(n, p, x1, y1, x2, y2);
791 | }
792 |
793 | // Create the root node.
794 | var root = d3_geom_quadtreeNode();
795 |
796 | root.add = function(p) {
797 | insert(root, p, x1, y1, x2, y2);
798 | };
799 |
800 | root.visit = function(f) {
801 | d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2);
802 | };
803 |
804 | // Insert all points.
805 | points.forEach(root.add);
806 | return root;
807 | };
808 |
809 | function d3_geom_quadtreeNode() {
810 | return {
811 | leaf: true,
812 | nodes: [],
813 | point: null
814 | };
815 | }
816 |
817 | function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
818 | if (!f(node, x1, y1, x2, y2)) {
819 | var sx = (x1 + x2) * .5,
820 | sy = (y1 + y2) * .5,
821 | children = node.nodes;
822 | if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
823 | if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
824 | if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
825 | if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
826 | }
827 | }
828 |
829 | function d3_geom_quadtreePoint(p) {
830 | return {
831 | x: p[0],
832 | y: p[1]
833 | };
834 | }
835 | })();
836 |
--------------------------------------------------------------------------------
/d3.layout.js:
--------------------------------------------------------------------------------
1 | (function(){d3.layout = {};
2 | // Implements hierarchical edge bundling using Holten's algorithm. For each
3 | // input link, a path is computed that travels through the tree, up the parent
4 | // hierarchy to the least common ancestor, and then back down to the destination
5 | // node. Each path is simply an array of nodes.
6 | d3.layout.bundle = function() {
7 | return function(links) {
8 | var paths = [],
9 | i = -1,
10 | n = links.length;
11 | while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
12 | return paths;
13 | };
14 | };
15 |
16 | function d3_layout_bundlePath(link) {
17 | var start = link.source,
18 | end = link.target,
19 | lca = d3_layout_bundleLeastCommonAncestor(start, end),
20 | points = [start];
21 | while (start !== lca) {
22 | start = start.parent;
23 | points.push(start);
24 | }
25 | var k = points.length;
26 | while (end !== lca) {
27 | points.splice(k, 0, end);
28 | end = end.parent;
29 | }
30 | return points;
31 | }
32 |
33 | function d3_layout_bundleAncestors(node) {
34 | var ancestors = [],
35 | parent = node.parent;
36 | while (parent != null) {
37 | ancestors.push(node);
38 | node = parent;
39 | parent = parent.parent;
40 | }
41 | ancestors.push(node);
42 | return ancestors;
43 | }
44 |
45 | function d3_layout_bundleLeastCommonAncestor(a, b) {
46 | if (a === b) return a;
47 | var aNodes = d3_layout_bundleAncestors(a),
48 | bNodes = d3_layout_bundleAncestors(b),
49 | aNode = aNodes.pop(),
50 | bNode = bNodes.pop(),
51 | sharedNode = null;
52 | while (aNode === bNode) {
53 | sharedNode = aNode;
54 | aNode = aNodes.pop();
55 | bNode = bNodes.pop();
56 | }
57 | return sharedNode;
58 | }
59 | d3.layout.chord = function() {
60 | var chord = {},
61 | chords,
62 | groups,
63 | matrix,
64 | n,
65 | padding = 0,
66 | sortGroups,
67 | sortSubgroups,
68 | sortChords;
69 |
70 | function relayout() {
71 | var subgroups = {},
72 | groupSums = [],
73 | groupIndex = d3.range(n),
74 | subgroupIndex = [],
75 | k,
76 | x,
77 | x0,
78 | i,
79 | j;
80 |
81 | chords = [];
82 | groups = [];
83 |
84 | // Compute the sum.
85 | k = 0, i = -1; while (++i < n) {
86 | x = 0, j = -1; while (++j < n) {
87 | x += matrix[i][j];
88 | }
89 | groupSums.push(x);
90 | subgroupIndex.push(d3.range(n));
91 | k += x;
92 | }
93 |
94 | // Sort groups…
95 | if (sortGroups) {
96 | groupIndex.sort(function(a, b) {
97 | return sortGroups(groupSums[a], groupSums[b]);
98 | });
99 | }
100 |
101 | // Sort subgroups…
102 | if (sortSubgroups) {
103 | subgroupIndex.forEach(function(d, i) {
104 | d.sort(function(a, b) {
105 | return sortSubgroups(matrix[i][a], matrix[i][b]);
106 | });
107 | });
108 | }
109 |
110 | // Convert the sum to scaling factor for [0, 2pi].
111 | // TODO Allow start and end angle to be specified.
112 | // TODO Allow padding to be specified as percentage?
113 | k = (2 * Math.PI - padding * n) / k;
114 |
115 | // Compute the start and end angle for each group and subgroup.
116 | // Note: Opera has a bug reordering object literal properties!
117 | x = 0, i = -1; while (++i < n) {
118 | x0 = x, j = -1; while (++j < n) {
119 | var di = groupIndex[i],
120 | dj = subgroupIndex[di][j],
121 | v = matrix[di][dj],
122 | a0 = x,
123 | a1 = x += v * k;
124 | subgroups[di + "-" + dj] = {
125 | index: di,
126 | subindex: dj,
127 | startAngle: a0,
128 | endAngle: a1,
129 | value: v
130 | };
131 | }
132 | groups.push({
133 | index: di,
134 | startAngle: x0,
135 | endAngle: x,
136 | value: (x - x0) / k
137 | });
138 | x += padding;
139 | }
140 |
141 | // Generate chords for each (non-empty) subgroup-subgroup link.
142 | i = -1; while (++i < n) {
143 | j = i - 1; while (++j < n) {
144 | var source = subgroups[i + "-" + j],
145 | target = subgroups[j + "-" + i];
146 | if (source.value || target.value) {
147 | chords.push(source.value < target.value
148 | ? {source: target, target: source}
149 | : {source: source, target: target});
150 | }
151 | }
152 | }
153 |
154 | if (sortChords) resort();
155 | }
156 |
157 | function resort() {
158 | chords.sort(function(a, b) {
159 | return sortChords(
160 | (a.source.value + a.target.value) / 2,
161 | (b.source.value + b.target.value) / 2);
162 | });
163 | }
164 |
165 | chord.matrix = function(x) {
166 | if (!arguments.length) return matrix;
167 | n = (matrix = x) && matrix.length;
168 | chords = groups = null;
169 | return chord;
170 | };
171 |
172 | chord.padding = function(x) {
173 | if (!arguments.length) return padding;
174 | padding = x;
175 | chords = groups = null;
176 | return chord;
177 | };
178 |
179 | chord.sortGroups = function(x) {
180 | if (!arguments.length) return sortGroups;
181 | sortGroups = x;
182 | chords = groups = null;
183 | return chord;
184 | };
185 |
186 | chord.sortSubgroups = function(x) {
187 | if (!arguments.length) return sortSubgroups;
188 | sortSubgroups = x;
189 | chords = null;
190 | return chord;
191 | };
192 |
193 | chord.sortChords = function(x) {
194 | if (!arguments.length) return sortChords;
195 | sortChords = x;
196 | if (chords) resort();
197 | return chord;
198 | };
199 |
200 | chord.chords = function() {
201 | if (!chords) relayout();
202 | return chords;
203 | };
204 |
205 | chord.groups = function() {
206 | if (!groups) relayout();
207 | return groups;
208 | };
209 |
210 | return chord;
211 | };
212 | // A rudimentary force layout using Gauss-Seidel.
213 | d3.layout.force = function() {
214 | var force = {},
215 | event = d3.dispatch("tick"),
216 | size = [1, 1],
217 | drag,
218 | alpha,
219 | friction = .9,
220 | linkDistance = d3_layout_forceLinkDistance,
221 | linkStrength = d3_layout_forceLinkStrength,
222 | charge = -30,
223 | gravity = .1,
224 | theta = .8,
225 | interval,
226 | nodes = [],
227 | links = [],
228 | distances,
229 | strengths,
230 | charges;
231 |
232 | function repulse(node) {
233 | return function(quad, x1, y1, x2, y2) {
234 | if (quad.point !== node) {
235 | var dx = quad.cx - node.x,
236 | dy = quad.cy - node.y,
237 | dn = 1 / Math.sqrt(dx * dx + dy * dy);
238 |
239 | /* Barnes-Hut criterion. */
240 | if ((x2 - x1) * dn < theta) {
241 | var k = quad.charge * dn * dn;
242 | node.px -= dx * k;
243 | node.py -= dy * k;
244 | return true;
245 | }
246 |
247 | if (quad.point && isFinite(dn)) {
248 | var k = quad.pointCharge * dn * dn;
249 | node.px -= dx * k;
250 | node.py -= dy * k;
251 | }
252 | }
253 | return !quad.charge;
254 | };
255 | }
256 |
257 | function tick() {
258 | var n = nodes.length,
259 | m = links.length,
260 | q,
261 | i, // current index
262 | o, // current object
263 | s, // current source
264 | t, // current target
265 | l, // current distance
266 | k, // current force
267 | x, // x-distance
268 | y; // y-distance
269 |
270 | // gauss-seidel relaxation for links
271 | for (i = 0; i < m; ++i) {
272 | o = links[i];
273 | s = o.source;
274 | t = o.target;
275 | x = t.x - s.x;
276 | y = t.y - s.y;
277 | if (l = (x * x + y * y)) {
278 | l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
279 | x *= l;
280 | y *= l;
281 | t.x -= x * (k = s.weight / (t.weight + s.weight));
282 | t.y -= y * k;
283 | s.x += x * (k = 1 - k);
284 | s.y += y * k;
285 | }
286 | }
287 |
288 | // apply gravity forces
289 | if (k = alpha * gravity) {
290 | x = size[0] / 2;
291 | y = size[1] / 2;
292 | i = -1; if (k) while (++i < n) {
293 | o = nodes[i];
294 | o.x += (x - o.x) * k;
295 | o.y += (y - o.y) * k;
296 | }
297 | }
298 |
299 | // compute quadtree center of mass and apply charge forces
300 | if (charge) {
301 | d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
302 | i = -1; while (++i < n) {
303 | if (!(o = nodes[i]).fixed) {
304 | q.visit(repulse(o));
305 | }
306 | }
307 | }
308 |
309 | // position verlet integration
310 | i = -1; while (++i < n) {
311 | o = nodes[i];
312 | if (o.fixed) {
313 | o.x = o.px;
314 | o.y = o.py;
315 | } else {
316 | o.x -= (o.px - (o.px = o.x)) * friction;
317 | o.y -= (o.py - (o.py = o.y)) * friction;
318 | }
319 | }
320 |
321 | event.tick({type: "tick", alpha: alpha});
322 |
323 | // simulated annealing, basically
324 | return (alpha *= .99) < .005;
325 | }
326 |
327 | force.on = function(type, listener) {
328 | event.on(type, listener);
329 | return force;
330 | };
331 |
332 | force.nodes = function(x) {
333 | if (!arguments.length) return nodes;
334 | nodes = x;
335 | return force;
336 | };
337 |
338 | force.links = function(x) {
339 | if (!arguments.length) return links;
340 | links = x;
341 | return force;
342 | };
343 |
344 | force.size = function(x) {
345 | if (!arguments.length) return size;
346 | size = x;
347 | return force;
348 | };
349 |
350 | force.linkDistance = function(x) {
351 | if (!arguments.length) return linkDistance;
352 | linkDistance = d3.functor(x);
353 | return force;
354 | };
355 |
356 | // For backwards-compatibility.
357 | force.distance = force.linkDistance;
358 |
359 | force.linkStrength = function(x) {
360 | if (!arguments.length) return linkStrength;
361 | linkStrength = d3.functor(x);
362 | return force;
363 | };
364 |
365 | force.friction = function(x) {
366 | if (!arguments.length) return friction;
367 | friction = x;
368 | return force;
369 | };
370 |
371 | force.charge = function(x) {
372 | if (!arguments.length) return charge;
373 | charge = typeof x === "function" ? x : +x;
374 | return force;
375 | };
376 |
377 | force.gravity = function(x) {
378 | if (!arguments.length) return gravity;
379 | gravity = x;
380 | return force;
381 | };
382 |
383 | force.theta = function(x) {
384 | if (!arguments.length) return theta;
385 | theta = x;
386 | return force;
387 | };
388 |
389 | force.start = function() {
390 | var i,
391 | j,
392 | n = nodes.length,
393 | m = links.length,
394 | w = size[0],
395 | h = size[1],
396 | neighbors,
397 | o;
398 |
399 | for (i = 0; i < n; ++i) {
400 | (o = nodes[i]).index = i;
401 | o.weight = 0;
402 | }
403 |
404 | distances = [];
405 | strengths = [];
406 | for (i = 0; i < m; ++i) {
407 | o = links[i];
408 | if (typeof o.source == "number") o.source = nodes[o.source];
409 | if (typeof o.target == "number") o.target = nodes[o.target];
410 | distances[i] = linkDistance.call(this, o, i);
411 | strengths[i] = linkStrength.call(this, o, i);
412 | ++o.source.weight;
413 | ++o.target.weight;
414 | }
415 |
416 | for (i = 0; i < n; ++i) {
417 | o = nodes[i];
418 | if (isNaN(o.x)) o.x = position("x", w);
419 | if (isNaN(o.y)) o.y = position("y", h);
420 | if (isNaN(o.px)) o.px = o.x;
421 | if (isNaN(o.py)) o.py = o.y;
422 | }
423 |
424 | charges = [];
425 | if (typeof charge === "function") {
426 | for (i = 0; i < n; ++i) {
427 | charges[i] = +charge.call(this, nodes[i], i);
428 | }
429 | } else {
430 | for (i = 0; i < n; ++i) {
431 | charges[i] = charge;
432 | }
433 | }
434 |
435 | // initialize node position based on first neighbor
436 | function position(dimension, size) {
437 | var neighbors = neighbor(i),
438 | j = -1,
439 | m = neighbors.length,
440 | x;
441 | while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x;
442 | return Math.random() * size;
443 | }
444 |
445 | // initialize neighbors lazily
446 | function neighbor() {
447 | if (!neighbors) {
448 | neighbors = [];
449 | for (j = 0; j < n; ++j) {
450 | neighbors[j] = [];
451 | }
452 | for (j = 0; j < m; ++j) {
453 | var o = links[j];
454 | neighbors[o.source.index].push(o.target);
455 | neighbors[o.target.index].push(o.source);
456 | }
457 | }
458 | return neighbors[i];
459 | }
460 |
461 | return force.resume();
462 | };
463 |
464 | force.resume = function() {
465 | alpha = .1;
466 | d3.timer(tick);
467 | return force;
468 | };
469 |
470 | force.stop = function() {
471 | alpha = 0;
472 | return force;
473 | };
474 |
475 | // use `node.call(force.drag)` to make nodes draggable
476 | force.drag = function() {
477 | if (!drag) drag = d3.behavior.drag()
478 | .origin(Object)
479 | .on("dragstart", dragstart)
480 | .on("drag", d3_layout_forceDrag)
481 | .on("dragend", d3_layout_forceDragEnd);
482 |
483 | this.on("mouseover.force", d3_layout_forceDragOver)
484 | .on("mouseout.force", d3_layout_forceDragOut)
485 | .call(drag);
486 | };
487 |
488 | function dragstart(d) {
489 | d3_layout_forceDragOver(d3_layout_forceDragNode = d);
490 | d3_layout_forceDragForce = force;
491 | }
492 |
493 | return force;
494 | };
495 |
496 | var d3_layout_forceDragForce,
497 | d3_layout_forceDragNode;
498 |
499 | function d3_layout_forceDragOver(d) {
500 | d.fixed |= 2;
501 | }
502 |
503 | function d3_layout_forceDragOut(d) {
504 | if (d !== d3_layout_forceDragNode) d.fixed &= 1;
505 | }
506 |
507 | function d3_layout_forceDragEnd() {
508 | d3_layout_forceDrag();
509 | d3_layout_forceDragNode.fixed &= 1;
510 | d3_layout_forceDragForce = d3_layout_forceDragNode = null;
511 | }
512 |
513 | function d3_layout_forceDrag() {
514 | d3_layout_forceDragNode.px = d3.event.x;
515 | d3_layout_forceDragNode.py = d3.event.y;
516 | d3_layout_forceDragForce.resume(); // restart annealing
517 | }
518 |
519 | function d3_layout_forceAccumulate(quad, alpha, charges) {
520 | var cx = 0,
521 | cy = 0;
522 | quad.charge = 0;
523 | if (!quad.leaf) {
524 | var nodes = quad.nodes,
525 | n = nodes.length,
526 | i = -1,
527 | c;
528 | while (++i < n) {
529 | c = nodes[i];
530 | if (c == null) continue;
531 | d3_layout_forceAccumulate(c, alpha, charges);
532 | quad.charge += c.charge;
533 | cx += c.charge * c.cx;
534 | cy += c.charge * c.cy;
535 | }
536 | }
537 | if (quad.point) {
538 | // jitter internal nodes that are coincident
539 | if (!quad.leaf) {
540 | quad.point.x += Math.random() - .5;
541 | quad.point.y += Math.random() - .5;
542 | }
543 | var k = alpha * charges[quad.point.index];
544 | quad.charge += quad.pointCharge = k;
545 | cx += k * quad.point.x;
546 | cy += k * quad.point.y;
547 | }
548 | quad.cx = cx / quad.charge;
549 | quad.cy = cy / quad.charge;
550 | }
551 |
552 | function d3_layout_forceLinkDistance(link) {
553 | return 20;
554 | }
555 |
556 | function d3_layout_forceLinkStrength(link) {
557 | return 1;
558 | }
559 | d3.layout.partition = function() {
560 | var hierarchy = d3.layout.hierarchy(),
561 | size = [1, 1]; // width, height
562 |
563 | function position(node, x, dx, dy) {
564 | var children = node.children;
565 | node.x = x;
566 | node.y = node.depth * dy;
567 | node.dx = dx;
568 | node.dy = dy;
569 | if (children && (n = children.length)) {
570 | var i = -1,
571 | n,
572 | c,
573 | d;
574 | dx = node.value ? dx / node.value : 0;
575 | while (++i < n) {
576 | position(c = children[i], x, d = c.value * dx, dy);
577 | x += d;
578 | }
579 | }
580 | }
581 |
582 | function depth(node) {
583 | var children = node.children,
584 | d = 0;
585 | if (children && (n = children.length)) {
586 | var i = -1,
587 | n;
588 | while (++i < n) d = Math.max(d, depth(children[i]));
589 | }
590 | return 1 + d;
591 | }
592 |
593 | function partition(d, i) {
594 | var nodes = hierarchy.call(this, d, i);
595 | position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
596 | return nodes;
597 | }
598 |
599 | partition.size = function(x) {
600 | if (!arguments.length) return size;
601 | size = x;
602 | return partition;
603 | };
604 |
605 | return d3_layout_hierarchyRebind(partition, hierarchy);
606 | };
607 | d3.layout.pie = function() {
608 | var value = Number,
609 | sort = d3_layout_pieSortByValue,
610 | startAngle = 0,
611 | endAngle = 2 * Math.PI;
612 |
613 | function pie(data, i) {
614 |
615 | // Compute the numeric values for each data element.
616 | var values = data.map(function(d, i) { return +value.call(pie, d, i); });
617 |
618 | // Compute the start angle.
619 | var a = +(typeof startAngle === "function"
620 | ? startAngle.apply(this, arguments)
621 | : startAngle);
622 |
623 | // Compute the angular scale factor: from value to radians.
624 | var k = ((typeof endAngle === "function"
625 | ? endAngle.apply(this, arguments)
626 | : endAngle) - startAngle)
627 | / d3.sum(values);
628 |
629 | // Optionally sort the data.
630 | var index = d3.range(data.length);
631 | if (sort != null) index.sort(sort === d3_layout_pieSortByValue
632 | ? function(i, j) { return values[j] - values[i]; }
633 | : function(i, j) { return sort(data[i], data[j]); });
634 |
635 | // Compute the arcs!
636 | var arcs = index.map(function(i) {
637 | return {
638 | data: data[i],
639 | value: d = values[i],
640 | startAngle: a,
641 | endAngle: a += d * k
642 | };
643 | });
644 |
645 | // Return the arcs in the original data's order.
646 | return data.map(function(d, i) {
647 | return arcs[index[i]];
648 | });
649 | }
650 |
651 | /**
652 | * Specifies the value function *x*, which returns a nonnegative numeric value
653 | * for each datum. The default value function is `Number`. The value function
654 | * is passed two arguments: the current datum and the current index.
655 | */
656 | pie.value = function(x) {
657 | if (!arguments.length) return value;
658 | value = x;
659 | return pie;
660 | };
661 |
662 | /**
663 | * Specifies a sort comparison operator *x*. The comparator is passed two data
664 | * elements from the data array, a and b; it returns a negative value if a is
665 | * less than b, a positive value if a is greater than b, and zero if a equals
666 | * b.
667 | */
668 | pie.sort = function(x) {
669 | if (!arguments.length) return sort;
670 | sort = x;
671 | return pie;
672 | };
673 |
674 | /**
675 | * Specifies the overall start angle of the pie chart. Defaults to 0. The
676 | * start angle can be specified either as a constant or as a function; in the
677 | * case of a function, it is evaluated once per array (as opposed to per
678 | * element).
679 | */
680 | pie.startAngle = function(x) {
681 | if (!arguments.length) return startAngle;
682 | startAngle = x;
683 | return pie;
684 | };
685 |
686 | /**
687 | * Specifies the overall end angle of the pie chart. Defaults to 2π. The
688 | * end angle can be specified either as a constant or as a function; in the
689 | * case of a function, it is evaluated once per array (as opposed to per
690 | * element).
691 | */
692 | pie.endAngle = function(x) {
693 | if (!arguments.length) return endAngle;
694 | endAngle = x;
695 | return pie;
696 | };
697 |
698 | return pie;
699 | };
700 |
701 | var d3_layout_pieSortByValue = {};
702 | // data is two-dimensional array of x,y; we populate y0
703 | d3.layout.stack = function() {
704 | var values = Object,
705 | order = d3_layout_stackOrders["default"],
706 | offset = d3_layout_stackOffsets["zero"],
707 | out = d3_layout_stackOut,
708 | x = d3_layout_stackX,
709 | y = d3_layout_stackY;
710 |
711 | function stack(data, index) {
712 |
713 | // Convert series to canonical two-dimensional representation.
714 | var series = data.map(function(d, i) {
715 | return values.call(stack, d, i);
716 | });
717 |
718 | // Convert each series to canonical [[x,y]] representation.
719 | var points = series.map(function(d, i) {
720 | return d.map(function(v, i) {
721 | return [x.call(stack, v, i), y.call(stack, v, i)];
722 | });
723 | });
724 |
725 | // Compute the order of series, and permute them.
726 | var orders = order.call(stack, points, index);
727 | series = d3.permute(series, orders);
728 | points = d3.permute(points, orders);
729 |
730 | // Compute the baseline…
731 | var offsets = offset.call(stack, points, index);
732 |
733 | // And propagate it to other series.
734 | var n = series.length,
735 | m = series[0].length,
736 | i,
737 | j,
738 | o;
739 | for (j = 0; j < m; ++j) {
740 | out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
741 | for (i = 1; i < n; ++i) {
742 | out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
743 | }
744 | }
745 |
746 | return data;
747 | }
748 |
749 | stack.values = function(x) {
750 | if (!arguments.length) return values;
751 | values = x;
752 | return stack;
753 | };
754 |
755 | stack.order = function(x) {
756 | if (!arguments.length) return order;
757 | order = typeof x === "function" ? x : d3_layout_stackOrders[x];
758 | return stack;
759 | };
760 |
761 | stack.offset = function(x) {
762 | if (!arguments.length) return offset;
763 | offset = typeof x === "function" ? x : d3_layout_stackOffsets[x];
764 | return stack;
765 | };
766 |
767 | stack.x = function(z) {
768 | if (!arguments.length) return x;
769 | x = z;
770 | return stack;
771 | };
772 |
773 | stack.y = function(z) {
774 | if (!arguments.length) return y;
775 | y = z;
776 | return stack;
777 | };
778 |
779 | stack.out = function(z) {
780 | if (!arguments.length) return out;
781 | out = z;
782 | return stack;
783 | };
784 |
785 | return stack;
786 | }
787 |
788 | function d3_layout_stackX(d) {
789 | return d.x;
790 | }
791 |
792 | function d3_layout_stackY(d) {
793 | return d.y;
794 | }
795 |
796 | function d3_layout_stackOut(d, y0, y) {
797 | d.y0 = y0;
798 | d.y = y;
799 | }
800 |
801 | var d3_layout_stackOrders = {
802 |
803 | "inside-out": function(data) {
804 | var n = data.length,
805 | i,
806 | j,
807 | max = data.map(d3_layout_stackMaxIndex),
808 | sums = data.map(d3_layout_stackReduceSum),
809 | index = d3.range(n).sort(function(a, b) { return max[a] - max[b]; }),
810 | top = 0,
811 | bottom = 0,
812 | tops = [],
813 | bottoms = [];
814 | for (i = 0; i < n; ++i) {
815 | j = index[i];
816 | if (top < bottom) {
817 | top += sums[j];
818 | tops.push(j);
819 | } else {
820 | bottom += sums[j];
821 | bottoms.push(j);
822 | }
823 | }
824 | return bottoms.reverse().concat(tops);
825 | },
826 |
827 | "reverse": function(data) {
828 | return d3.range(data.length).reverse();
829 | },
830 |
831 | "default": function(data) {
832 | return d3.range(data.length);
833 | }
834 |
835 | };
836 |
837 | var d3_layout_stackOffsets = {
838 |
839 | "silhouette": function(data) {
840 | var n = data.length,
841 | m = data[0].length,
842 | sums = [],
843 | max = 0,
844 | i,
845 | j,
846 | o,
847 | y0 = [];
848 | for (j = 0; j < m; ++j) {
849 | for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
850 | if (o > max) max = o;
851 | sums.push(o);
852 | }
853 | for (j = 0; j < m; ++j) {
854 | y0[j] = (max - sums[j]) / 2;
855 | }
856 | return y0;
857 | },
858 |
859 | "wiggle": function(data) {
860 | var n = data.length,
861 | x = data[0],
862 | m = x.length,
863 | max = 0,
864 | i,
865 | j,
866 | k,
867 | s1,
868 | s2,
869 | s3,
870 | dx,
871 | o,
872 | o0,
873 | y0 = [];
874 | y0[0] = o = o0 = 0;
875 | for (j = 1; j < m; ++j) {
876 | for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
877 | for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
878 | for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
879 | s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
880 | }
881 | s2 += s3 * data[i][j][1];
882 | }
883 | y0[j] = o -= s1 ? s2 / s1 * dx : 0;
884 | if (o < o0) o0 = o;
885 | }
886 | for (j = 0; j < m; ++j) y0[j] -= o0;
887 | return y0;
888 | },
889 |
890 | "expand": function(data) {
891 | var n = data.length,
892 | m = data[0].length,
893 | k = 1 / n,
894 | i,
895 | j,
896 | o,
897 | y0 = [];
898 | for (j = 0; j < m; ++j) {
899 | for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
900 | if (o) for (i = 0; i < n; i++) data[i][j][1] /= o;
901 | else for (i = 0; i < n; i++) data[i][j][1] = k;
902 | }
903 | for (j = 0; j < m; ++j) y0[j] = 0;
904 | return y0;
905 | },
906 |
907 | "zero": function(data) {
908 | var j = -1,
909 | m = data[0].length,
910 | y0 = [];
911 | while (++j < m) y0[j] = 0;
912 | return y0;
913 | }
914 |
915 | };
916 |
917 | function d3_layout_stackMaxIndex(array) {
918 | var i = 1,
919 | j = 0,
920 | v = array[0][1],
921 | k,
922 | n = array.length;
923 | for (; i < n; ++i) {
924 | if ((k = array[i][1]) > v) {
925 | j = i;
926 | v = k;
927 | }
928 | }
929 | return j;
930 | }
931 |
932 | function d3_layout_stackReduceSum(d) {
933 | return d.reduce(d3_layout_stackSum, 0);
934 | }
935 |
936 | function d3_layout_stackSum(p, d) {
937 | return p + d[1];
938 | }
939 | d3.layout.histogram = function() {
940 | var frequency = true,
941 | valuer = Number,
942 | ranger = d3_layout_histogramRange,
943 | binner = d3_layout_histogramBinSturges;
944 |
945 | function histogram(data, i) {
946 | var bins = [],
947 | values = data.map(valuer, this),
948 | range = ranger.call(this, values, i),
949 | thresholds = binner.call(this, range, values, i),
950 | bin,
951 | i = -1,
952 | n = values.length,
953 | m = thresholds.length - 1,
954 | k = frequency ? 1 : 1 / n,
955 | x;
956 |
957 | // Initialize the bins.
958 | while (++i < m) {
959 | bin = bins[i] = [];
960 | bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
961 | bin.y = 0;
962 | }
963 |
964 | // Fill the bins, ignoring values outside the range.
965 | i = -1; while(++i < n) {
966 | x = values[i];
967 | if ((x >= range[0]) && (x <= range[1])) {
968 | bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
969 | bin.y += k;
970 | bin.push(data[i]);
971 | }
972 | }
973 |
974 | return bins;
975 | }
976 |
977 | // Specifies how to extract a value from the associated data. The default
978 | // value function is `Number`, which is equivalent to the identity function.
979 | histogram.value = function(x) {
980 | if (!arguments.length) return valuer;
981 | valuer = x;
982 | return histogram;
983 | };
984 |
985 | // Specifies the range of the histogram. Values outside the specified range
986 | // will be ignored. The argument `x` may be specified either as a two-element
987 | // array representing the minimum and maximum value of the range, or as a
988 | // function that returns the range given the array of values and the current
989 | // index `i`. The default range is the extent (minimum and maximum) of the
990 | // values.
991 | histogram.range = function(x) {
992 | if (!arguments.length) return ranger;
993 | ranger = d3.functor(x);
994 | return histogram;
995 | };
996 |
997 | // Specifies how to bin values in the histogram. The argument `x` may be
998 | // specified as a number, in which case the range of values will be split
999 | // uniformly into the given number of bins. Or, `x` may be an array of
1000 | // threshold values, defining the bins; the specified array must contain the
1001 | // rightmost (upper) value, thus specifying n + 1 values for n bins. Or, `x`
1002 | // may be a function which is evaluated, being passed the range, the array of
1003 | // values, and the current index `i`, returning an array of thresholds. The
1004 | // default bin function will divide the values into uniform bins using
1005 | // Sturges' formula.
1006 | histogram.bins = function(x) {
1007 | if (!arguments.length) return binner;
1008 | binner = typeof x === "number"
1009 | ? function(range) { return d3_layout_histogramBinFixed(range, x); }
1010 | : d3.functor(x);
1011 | return histogram;
1012 | };
1013 |
1014 | // Specifies whether the histogram's `y` value is a count (frequency) or a
1015 | // probability (density). The default value is true.
1016 | histogram.frequency = function(x) {
1017 | if (!arguments.length) return frequency;
1018 | frequency = !!x;
1019 | return histogram;
1020 | };
1021 |
1022 | return histogram;
1023 | };
1024 |
1025 | function d3_layout_histogramBinSturges(range, values) {
1026 | return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
1027 | }
1028 |
1029 | function d3_layout_histogramBinFixed(range, n) {
1030 | var x = -1,
1031 | b = +range[0],
1032 | m = (range[1] - b) / n,
1033 | f = [];
1034 | while (++x <= n) f[x] = m * x + b;
1035 | return f;
1036 | }
1037 |
1038 | function d3_layout_histogramRange(values) {
1039 | return [d3.min(values), d3.max(values)];
1040 | }
1041 | d3.layout.hierarchy = function() {
1042 | var sort = d3_layout_hierarchySort,
1043 | children = d3_layout_hierarchyChildren,
1044 | value = d3_layout_hierarchyValue;
1045 |
1046 | // Recursively compute the node depth and value.
1047 | // Also converts the data representation into a standard hierarchy structure.
1048 | function recurse(data, depth, nodes) {
1049 | var childs = children.call(hierarchy, data, depth),
1050 | node = d3_layout_hierarchyInline ? data : {data: data};
1051 | node.depth = depth;
1052 | nodes.push(node);
1053 | if (childs && (n = childs.length)) {
1054 | var i = -1,
1055 | n,
1056 | c = node.children = [],
1057 | v = 0,
1058 | j = depth + 1;
1059 | while (++i < n) {
1060 | d = recurse(childs[i], j, nodes);
1061 | d.parent = node;
1062 | c.push(d);
1063 | v += d.value;
1064 | }
1065 | if (sort) c.sort(sort);
1066 | if (value) node.value = v;
1067 | } else if (value) {
1068 | node.value = +value.call(hierarchy, data, depth) || 0;
1069 | }
1070 | return node;
1071 | }
1072 |
1073 | // Recursively re-evaluates the node value.
1074 | function revalue(node, depth) {
1075 | var children = node.children,
1076 | v = 0;
1077 | if (children && (n = children.length)) {
1078 | var i = -1,
1079 | n,
1080 | j = depth + 1;
1081 | while (++i < n) v += revalue(children[i], j);
1082 | } else if (value) {
1083 | v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0;
1084 | }
1085 | if (value) node.value = v;
1086 | return v;
1087 | }
1088 |
1089 | function hierarchy(d) {
1090 | var nodes = [];
1091 | recurse(d, 0, nodes);
1092 | return nodes;
1093 | }
1094 |
1095 | hierarchy.sort = function(x) {
1096 | if (!arguments.length) return sort;
1097 | sort = x;
1098 | return hierarchy;
1099 | };
1100 |
1101 | hierarchy.children = function(x) {
1102 | if (!arguments.length) return children;
1103 | children = x;
1104 | return hierarchy;
1105 | };
1106 |
1107 | hierarchy.value = function(x) {
1108 | if (!arguments.length) return value;
1109 | value = x;
1110 | return hierarchy;
1111 | };
1112 |
1113 | // Re-evaluates the `value` property for the specified hierarchy.
1114 | hierarchy.revalue = function(root) {
1115 | revalue(root, 0);
1116 | return root;
1117 | };
1118 |
1119 | return hierarchy;
1120 | };
1121 |
1122 | // A method assignment helper for hierarchy subclasses.
1123 | function d3_layout_hierarchyRebind(object, hierarchy) {
1124 | object.sort = d3.rebind(object, hierarchy.sort);
1125 | object.children = d3.rebind(object, hierarchy.children);
1126 | object.links = d3_layout_hierarchyLinks;
1127 | object.value = d3.rebind(object, hierarchy.value);
1128 |
1129 | // If the new API is used, enabling inlining.
1130 | object.nodes = function(d) {
1131 | d3_layout_hierarchyInline = true;
1132 | return (object.nodes = object)(d);
1133 | };
1134 |
1135 | return object;
1136 | }
1137 |
1138 | function d3_layout_hierarchyChildren(d) {
1139 | return d.children;
1140 | }
1141 |
1142 | function d3_layout_hierarchyValue(d) {
1143 | return d.value;
1144 | }
1145 |
1146 | function d3_layout_hierarchySort(a, b) {
1147 | return b.value - a.value;
1148 | }
1149 |
1150 | // Returns an array source+target objects for the specified nodes.
1151 | function d3_layout_hierarchyLinks(nodes) {
1152 | return d3.merge(nodes.map(function(parent) {
1153 | return (parent.children || []).map(function(child) {
1154 | return {source: parent, target: child};
1155 | });
1156 | }));
1157 | }
1158 |
1159 | // For backwards-compatibility, don't enable inlining by default.
1160 | var d3_layout_hierarchyInline = false;
1161 | d3.layout.pack = function() {
1162 | var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort),
1163 | size = [1, 1];
1164 |
1165 | function pack(d, i) {
1166 | var nodes = hierarchy.call(this, d, i),
1167 | root = nodes[0];
1168 |
1169 | // Recursively compute the layout.
1170 | root.x = 0;
1171 | root.y = 0;
1172 | d3_layout_packTree(root);
1173 |
1174 | // Scale the layout to fit the requested size.
1175 | var w = size[0],
1176 | h = size[1],
1177 | k = 1 / Math.max(2 * root.r / w, 2 * root.r / h);
1178 | d3_layout_packTransform(root, w / 2, h / 2, k);
1179 |
1180 | return nodes;
1181 | }
1182 |
1183 | pack.size = function(x) {
1184 | if (!arguments.length) return size;
1185 | size = x;
1186 | return pack;
1187 | };
1188 |
1189 | return d3_layout_hierarchyRebind(pack, hierarchy);
1190 | };
1191 |
1192 | function d3_layout_packSort(a, b) {
1193 | return a.value - b.value;
1194 | }
1195 |
1196 | function d3_layout_packInsert(a, b) {
1197 | var c = a._pack_next;
1198 | a._pack_next = b;
1199 | b._pack_prev = a;
1200 | b._pack_next = c;
1201 | c._pack_prev = b;
1202 | }
1203 |
1204 | function d3_layout_packSplice(a, b) {
1205 | a._pack_next = b;
1206 | b._pack_prev = a;
1207 | }
1208 |
1209 | function d3_layout_packIntersects(a, b) {
1210 | var dx = b.x - a.x,
1211 | dy = b.y - a.y,
1212 | dr = a.r + b.r;
1213 | return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon
1214 | }
1215 |
1216 | function d3_layout_packCircle(nodes) {
1217 | var xMin = Infinity,
1218 | xMax = -Infinity,
1219 | yMin = Infinity,
1220 | yMax = -Infinity,
1221 | n = nodes.length,
1222 | a, b, c, j, k;
1223 |
1224 | function bound(node) {
1225 | xMin = Math.min(node.x - node.r, xMin);
1226 | xMax = Math.max(node.x + node.r, xMax);
1227 | yMin = Math.min(node.y - node.r, yMin);
1228 | yMax = Math.max(node.y + node.r, yMax);
1229 | }
1230 |
1231 | // Create node links.
1232 | nodes.forEach(d3_layout_packLink);
1233 |
1234 | // Create first node.
1235 | a = nodes[0];
1236 | a.x = -a.r;
1237 | a.y = 0;
1238 | bound(a);
1239 |
1240 | // Create second node.
1241 | if (n > 1) {
1242 | b = nodes[1];
1243 | b.x = b.r;
1244 | b.y = 0;
1245 | bound(b);
1246 |
1247 | // Create third node and build chain.
1248 | if (n > 2) {
1249 | c = nodes[2];
1250 | d3_layout_packPlace(a, b, c);
1251 | bound(c);
1252 | d3_layout_packInsert(a, c);
1253 | a._pack_prev = c;
1254 | d3_layout_packInsert(c, b);
1255 | b = a._pack_next;
1256 |
1257 | // Now iterate through the rest.
1258 | for (var i = 3; i < n; i++) {
1259 | d3_layout_packPlace(a, b, c = nodes[i]);
1260 |
1261 | // Search for the closest intersection.
1262 | var isect = 0, s1 = 1, s2 = 1;
1263 | for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
1264 | if (d3_layout_packIntersects(j, c)) {
1265 | isect = 1;
1266 | break;
1267 | }
1268 | }
1269 | if (isect == 1) {
1270 | for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
1271 | if (d3_layout_packIntersects(k, c)) {
1272 | if (s2 < s1) {
1273 | isect = -1;
1274 | j = k;
1275 | }
1276 | break;
1277 | }
1278 | }
1279 | }
1280 |
1281 | // Update node chain.
1282 | if (isect == 0) {
1283 | d3_layout_packInsert(a, c);
1284 | b = c;
1285 | bound(c);
1286 | } else if (isect > 0) {
1287 | d3_layout_packSplice(a, j);
1288 | b = j;
1289 | i--;
1290 | } else { // isect < 0
1291 | d3_layout_packSplice(j, b);
1292 | a = j;
1293 | i--;
1294 | }
1295 | }
1296 | }
1297 | }
1298 |
1299 | // Re-center the circles and return the encompassing radius.
1300 | var cx = (xMin + xMax) / 2,
1301 | cy = (yMin + yMax) / 2,
1302 | cr = 0;
1303 | for (var i = 0; i < n; i++) {
1304 | var node = nodes[i];
1305 | node.x -= cx;
1306 | node.y -= cy;
1307 | cr = Math.max(cr, node.r + Math.sqrt(node.x * node.x + node.y * node.y));
1308 | }
1309 |
1310 | // Remove node links.
1311 | nodes.forEach(d3_layout_packUnlink);
1312 |
1313 | return cr;
1314 | }
1315 |
1316 | function d3_layout_packLink(node) {
1317 | node._pack_next = node._pack_prev = node;
1318 | }
1319 |
1320 | function d3_layout_packUnlink(node) {
1321 | delete node._pack_next;
1322 | delete node._pack_prev;
1323 | }
1324 |
1325 | function d3_layout_packTree(node) {
1326 | var children = node.children;
1327 | if (children && children.length) {
1328 | children.forEach(d3_layout_packTree);
1329 | node.r = d3_layout_packCircle(children);
1330 | } else {
1331 | node.r = Math.sqrt(node.value);
1332 | }
1333 | }
1334 |
1335 | function d3_layout_packTransform(node, x, y, k) {
1336 | var children = node.children;
1337 | node.x = (x += k * node.x);
1338 | node.y = (y += k * node.y);
1339 | node.r *= k;
1340 | if (children) {
1341 | var i = -1, n = children.length;
1342 | while (++i < n) d3_layout_packTransform(children[i], x, y, k);
1343 | }
1344 | }
1345 |
1346 | function d3_layout_packPlace(a, b, c) {
1347 | var db = a.r + c.r,
1348 | dx = b.x - a.x,
1349 | dy = b.y - a.y;
1350 | if (db && (dx || dy)) {
1351 | var da = b.r + c.r,
1352 | dc = Math.sqrt(dx * dx + dy * dy),
1353 | cos = Math.max(-1, Math.min(1, (db * db + dc * dc - da * da) / (2 * db * dc))),
1354 | theta = Math.acos(cos),
1355 | x = cos * (db /= dc),
1356 | y = Math.sin(theta) * db;
1357 | c.x = a.x + x * dx + y * dy;
1358 | c.y = a.y + x * dy - y * dx;
1359 | } else {
1360 | c.x = a.x + db;
1361 | c.y = a.y;
1362 | }
1363 | }
1364 | // Implements a hierarchical layout using the cluster (or dendogram) algorithm.
1365 | d3.layout.cluster = function() {
1366 | var hierarchy = d3.layout.hierarchy().sort(null).value(null),
1367 | separation = d3_layout_treeSeparation,
1368 | size = [1, 1]; // width, height
1369 |
1370 | function cluster(d, i) {
1371 | var nodes = hierarchy.call(this, d, i),
1372 | root = nodes[0],
1373 | previousNode,
1374 | x = 0,
1375 | kx,
1376 | ky;
1377 |
1378 | // First walk, computing the initial x & y values.
1379 | d3_layout_treeVisitAfter(root, function(node) {
1380 | var children = node.children;
1381 | if (children && children.length) {
1382 | node.x = d3_layout_clusterX(children);
1383 | node.y = d3_layout_clusterY(children);
1384 | } else {
1385 | node.x = previousNode ? x += separation(node, previousNode) : 0;
1386 | node.y = 0;
1387 | previousNode = node;
1388 | }
1389 | });
1390 |
1391 | // Compute the left-most, right-most, and depth-most nodes for extents.
1392 | var left = d3_layout_clusterLeft(root),
1393 | right = d3_layout_clusterRight(root),
1394 | x0 = left.x - separation(left, right) / 2,
1395 | x1 = right.x + separation(right, left) / 2;
1396 |
1397 | // Second walk, normalizing x & y to the desired size.
1398 | d3_layout_treeVisitAfter(root, function(node) {
1399 | node.x = (node.x - x0) / (x1 - x0) * size[0];
1400 | node.y = (1 - node.y / root.y) * size[1];
1401 | });
1402 |
1403 | return nodes;
1404 | }
1405 |
1406 | cluster.separation = function(x) {
1407 | if (!arguments.length) return separation;
1408 | separation = x;
1409 | return cluster;
1410 | };
1411 |
1412 | cluster.size = function(x) {
1413 | if (!arguments.length) return size;
1414 | size = x;
1415 | return cluster;
1416 | };
1417 |
1418 | return d3_layout_hierarchyRebind(cluster, hierarchy);
1419 | };
1420 |
1421 | function d3_layout_clusterY(children) {
1422 | return 1 + d3.max(children, function(child) {
1423 | return child.y;
1424 | });
1425 | }
1426 |
1427 | function d3_layout_clusterX(children) {
1428 | return children.reduce(function(x, child) {
1429 | return x + child.x;
1430 | }, 0) / children.length;
1431 | }
1432 |
1433 | function d3_layout_clusterLeft(node) {
1434 | var children = node.children;
1435 | return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
1436 | }
1437 |
1438 | function d3_layout_clusterRight(node) {
1439 | var children = node.children, n;
1440 | return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
1441 | }
1442 | // Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
1443 | d3.layout.tree = function() {
1444 | var hierarchy = d3.layout.hierarchy().sort(null).value(null),
1445 | separation = d3_layout_treeSeparation,
1446 | size = [1, 1]; // width, height
1447 |
1448 | function tree(d, i) {
1449 | var nodes = hierarchy.call(this, d, i),
1450 | root = nodes[0];
1451 |
1452 | function firstWalk(node, previousSibling) {
1453 | var children = node.children,
1454 | layout = node._tree;
1455 | if (children && (n = children.length)) {
1456 | var n,
1457 | firstChild = children[0],
1458 | previousChild,
1459 | ancestor = firstChild,
1460 | child,
1461 | i = -1;
1462 | while (++i < n) {
1463 | child = children[i];
1464 | firstWalk(child, previousChild);
1465 | ancestor = apportion(child, previousChild, ancestor);
1466 | previousChild = child;
1467 | }
1468 | d3_layout_treeShift(node);
1469 | var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
1470 | if (previousSibling) {
1471 | layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
1472 | layout.mod = layout.prelim - midpoint;
1473 | } else {
1474 | layout.prelim = midpoint;
1475 | }
1476 | } else {
1477 | if (previousSibling) {
1478 | layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
1479 | }
1480 | }
1481 | }
1482 |
1483 | function secondWalk(node, x) {
1484 | node.x = node._tree.prelim + x;
1485 | var children = node.children;
1486 | if (children && (n = children.length)) {
1487 | var i = -1,
1488 | n;
1489 | x += node._tree.mod;
1490 | while (++i < n) {
1491 | secondWalk(children[i], x);
1492 | }
1493 | }
1494 | }
1495 |
1496 | function apportion(node, previousSibling, ancestor) {
1497 | if (previousSibling) {
1498 | var vip = node,
1499 | vop = node,
1500 | vim = previousSibling,
1501 | vom = node.parent.children[0],
1502 | sip = vip._tree.mod,
1503 | sop = vop._tree.mod,
1504 | sim = vim._tree.mod,
1505 | som = vom._tree.mod,
1506 | shift;
1507 | while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
1508 | vom = d3_layout_treeLeft(vom);
1509 | vop = d3_layout_treeRight(vop);
1510 | vop._tree.ancestor = node;
1511 | shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
1512 | if (shift > 0) {
1513 | d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
1514 | sip += shift;
1515 | sop += shift;
1516 | }
1517 | sim += vim._tree.mod;
1518 | sip += vip._tree.mod;
1519 | som += vom._tree.mod;
1520 | sop += vop._tree.mod;
1521 | }
1522 | if (vim && !d3_layout_treeRight(vop)) {
1523 | vop._tree.thread = vim;
1524 | vop._tree.mod += sim - sop;
1525 | }
1526 | if (vip && !d3_layout_treeLeft(vom)) {
1527 | vom._tree.thread = vip;
1528 | vom._tree.mod += sip - som;
1529 | ancestor = node;
1530 | }
1531 | }
1532 | return ancestor;
1533 | }
1534 |
1535 | // Initialize temporary layout variables.
1536 | d3_layout_treeVisitAfter(root, function(node, previousSibling) {
1537 | node._tree = {
1538 | ancestor: node,
1539 | prelim: 0,
1540 | mod: 0,
1541 | change: 0,
1542 | shift: 0,
1543 | number: previousSibling ? previousSibling._tree.number + 1 : 0
1544 | };
1545 | });
1546 |
1547 | // Compute the layout using Buchheim et al.'s algorithm.
1548 | firstWalk(root);
1549 | secondWalk(root, -root._tree.prelim);
1550 |
1551 | // Compute the left-most, right-most, and depth-most nodes for extents.
1552 | var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost),
1553 | right = d3_layout_treeSearch(root, d3_layout_treeRightmost),
1554 | deep = d3_layout_treeSearch(root, d3_layout_treeDeepest),
1555 | x0 = left.x - separation(left, right) / 2,
1556 | x1 = right.x + separation(right, left) / 2,
1557 | y1 = deep.depth || 1;
1558 |
1559 | // Clear temporary layout variables; transform x and y.
1560 | d3_layout_treeVisitAfter(root, function(node) {
1561 | node.x = (node.x - x0) / (x1 - x0) * size[0];
1562 | node.y = node.depth / y1 * size[1];
1563 | delete node._tree;
1564 | });
1565 |
1566 | return nodes;
1567 | }
1568 |
1569 | tree.separation = function(x) {
1570 | if (!arguments.length) return separation;
1571 | separation = x;
1572 | return tree;
1573 | };
1574 |
1575 | tree.size = function(x) {
1576 | if (!arguments.length) return size;
1577 | size = x;
1578 | return tree;
1579 | };
1580 |
1581 | return d3_layout_hierarchyRebind(tree, hierarchy);
1582 | };
1583 |
1584 | function d3_layout_treeSeparation(a, b) {
1585 | return a.parent == b.parent ? 1 : 2;
1586 | }
1587 |
1588 | // function d3_layout_treeSeparationRadial(a, b) {
1589 | // return (a.parent == b.parent ? 1 : 2) / a.depth;
1590 | // }
1591 |
1592 | function d3_layout_treeLeft(node) {
1593 | var children = node.children;
1594 | return children && children.length ? children[0] : node._tree.thread;
1595 | }
1596 |
1597 | function d3_layout_treeRight(node) {
1598 | var children = node.children,
1599 | n;
1600 | return children && (n = children.length) ? children[n - 1] : node._tree.thread;
1601 | }
1602 |
1603 | function d3_layout_treeSearch(node, compare) {
1604 | var children = node.children;
1605 | if (children && (n = children.length)) {
1606 | var child,
1607 | n,
1608 | i = -1;
1609 | while (++i < n) {
1610 | if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
1611 | node = child;
1612 | }
1613 | }
1614 | }
1615 | return node;
1616 | }
1617 |
1618 | function d3_layout_treeRightmost(a, b) {
1619 | return a.x - b.x;
1620 | }
1621 |
1622 | function d3_layout_treeLeftmost(a, b) {
1623 | return b.x - a.x;
1624 | }
1625 |
1626 | function d3_layout_treeDeepest(a, b) {
1627 | return a.depth - b.depth;
1628 | }
1629 |
1630 | function d3_layout_treeVisitAfter(node, callback) {
1631 | function visit(node, previousSibling) {
1632 | var children = node.children;
1633 | if (children && (n = children.length)) {
1634 | var child,
1635 | previousChild = null,
1636 | i = -1,
1637 | n;
1638 | while (++i < n) {
1639 | child = children[i];
1640 | visit(child, previousChild);
1641 | previousChild = child;
1642 | }
1643 | }
1644 | callback(node, previousSibling);
1645 | }
1646 | visit(node, null);
1647 | }
1648 |
1649 | function d3_layout_treeShift(node) {
1650 | var shift = 0,
1651 | change = 0,
1652 | children = node.children,
1653 | i = children.length,
1654 | child;
1655 | while (--i >= 0) {
1656 | child = children[i]._tree;
1657 | child.prelim += shift;
1658 | child.mod += shift;
1659 | shift += child.shift + (change += child.change);
1660 | }
1661 | }
1662 |
1663 | function d3_layout_treeMove(ancestor, node, shift) {
1664 | ancestor = ancestor._tree;
1665 | node = node._tree;
1666 | var change = shift / (node.number - ancestor.number);
1667 | ancestor.change += change;
1668 | node.change -= change;
1669 | node.shift += shift;
1670 | node.prelim += shift;
1671 | node.mod += shift;
1672 | }
1673 |
1674 | function d3_layout_treeAncestor(vim, node, ancestor) {
1675 | return vim._tree.ancestor.parent == node.parent
1676 | ? vim._tree.ancestor
1677 | : ancestor;
1678 | }
1679 | // Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk
1680 | // Modified to support a target aspect ratio by Jeff Heer
1681 | d3.layout.treemap = function() {
1682 | var hierarchy = d3.layout.hierarchy(),
1683 | round = Math.round,
1684 | size = [1, 1], // width, height
1685 | padding = null,
1686 | pad = d3_layout_treemapPadNull,
1687 | sticky = false,
1688 | stickies,
1689 | ratio = 0.5 * (1 + Math.sqrt(5)); // golden ratio
1690 |
1691 | // Compute the area for each child based on value & scale.
1692 | function scale(children, k) {
1693 | var i = -1,
1694 | n = children.length,
1695 | child,
1696 | area;
1697 | while (++i < n) {
1698 | area = (child = children[i]).value * (k < 0 ? 0 : k);
1699 | child.area = isNaN(area) || area <= 0 ? 0 : area;
1700 | }
1701 | }
1702 |
1703 | // Recursively arranges the specified node's children into squarified rows.
1704 | function squarify(node) {
1705 | var children = node.children;
1706 | if (children && children.length) {
1707 | var rect = pad(node),
1708 | row = [],
1709 | remaining = children.slice(), // copy-on-write
1710 | child,
1711 | best = Infinity, // the best row score so far
1712 | score, // the current row score
1713 | u = Math.min(rect.dx, rect.dy), // initial orientation
1714 | n;
1715 | scale(remaining, rect.dx * rect.dy / node.value);
1716 | row.area = 0;
1717 | while ((n = remaining.length) > 0) {
1718 | row.push(child = remaining[n - 1]);
1719 | row.area += child.area;
1720 | if ((score = worst(row, u)) <= best) { // continue with this orientation
1721 | remaining.pop();
1722 | best = score;
1723 | } else { // abort, and try a different orientation
1724 | row.area -= row.pop().area;
1725 | position(row, u, rect, false);
1726 | u = Math.min(rect.dx, rect.dy);
1727 | row.length = row.area = 0;
1728 | best = Infinity;
1729 | }
1730 | }
1731 | if (row.length) {
1732 | position(row, u, rect, true);
1733 | row.length = row.area = 0;
1734 | }
1735 | children.forEach(squarify);
1736 | }
1737 | }
1738 |
1739 | // Recursively resizes the specified node's children into existing rows.
1740 | // Preserves the existing layout!
1741 | function stickify(node) {
1742 | var children = node.children;
1743 | if (children && children.length) {
1744 | var rect = pad(node),
1745 | remaining = children.slice(), // copy-on-write
1746 | child,
1747 | row = [];
1748 | scale(remaining, rect.dx * rect.dy / node.value);
1749 | row.area = 0;
1750 | while (child = remaining.pop()) {
1751 | row.push(child);
1752 | row.area += child.area;
1753 | if (child.z != null) {
1754 | position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
1755 | row.length = row.area = 0;
1756 | }
1757 | }
1758 | children.forEach(stickify);
1759 | }
1760 | }
1761 |
1762 | // Computes the score for the specified row, as the worst aspect ratio.
1763 | function worst(row, u) {
1764 | var s = row.area,
1765 | r,
1766 | rmax = 0,
1767 | rmin = Infinity,
1768 | i = -1,
1769 | n = row.length;
1770 | while (++i < n) {
1771 | if (!(r = row[i].area)) continue;
1772 | if (r < rmin) rmin = r;
1773 | if (r > rmax) rmax = r;
1774 | }
1775 | s *= s;
1776 | u *= u;
1777 | return s
1778 | ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio))
1779 | : Infinity;
1780 | }
1781 |
1782 | // Positions the specified row of nodes. Modifies `rect`.
1783 | function position(row, u, rect, flush) {
1784 | var i = -1,
1785 | n = row.length,
1786 | x = rect.x,
1787 | y = rect.y,
1788 | v = u ? round(row.area / u) : 0,
1789 | o;
1790 | if (u == rect.dx) { // horizontal subdivision
1791 | if (flush || v > rect.dy) v = v ? rect.dy : 0; // over+underflow
1792 | while (++i < n) {
1793 | o = row[i];
1794 | o.x = x;
1795 | o.y = y;
1796 | o.dy = v;
1797 | x += o.dx = v ? round(o.area / v) : 0;
1798 | }
1799 | o.z = true;
1800 | o.dx += rect.x + rect.dx - x; // rounding error
1801 | rect.y += v;
1802 | rect.dy -= v;
1803 | } else { // vertical subdivision
1804 | if (flush || v > rect.dx) v = v ? rect.dx : 0; // over+underflow
1805 | while (++i < n) {
1806 | o = row[i];
1807 | o.x = x;
1808 | o.y = y;
1809 | o.dx = v;
1810 | y += o.dy = v ? round(o.area / v) : 0;
1811 | }
1812 | o.z = false;
1813 | o.dy += rect.y + rect.dy - y; // rounding error
1814 | rect.x += v;
1815 | rect.dx -= v;
1816 | }
1817 | }
1818 |
1819 | function treemap(d) {
1820 | var nodes = stickies || hierarchy(d),
1821 | root = nodes[0];
1822 | root.x = 0;
1823 | root.y = 0;
1824 | root.dx = size[0];
1825 | root.dy = size[1];
1826 | if (stickies) hierarchy.revalue(root);
1827 | scale([root], root.dx * root.dy / root.value);
1828 | (stickies ? stickify : squarify)(root);
1829 | if (sticky) stickies = nodes;
1830 | return nodes;
1831 | }
1832 |
1833 | treemap.size = function(x) {
1834 | if (!arguments.length) return size;
1835 | size = x;
1836 | return treemap;
1837 | };
1838 |
1839 | treemap.padding = function(x) {
1840 | if (!arguments.length) return padding;
1841 |
1842 | function padFunction(node) {
1843 | var p = x.call(treemap, node, node.depth);
1844 | return p == null
1845 | ? d3_layout_treemapPadNull(node)
1846 | : d3_layout_treemapPad(node, typeof p === "number" ? [p, p, p, p] : p);
1847 | }
1848 |
1849 | function padConstant(node) {
1850 | return d3_layout_treemapPad(node, x);
1851 | }
1852 |
1853 | var type;
1854 | pad = (padding = x) == null ? d3_layout_treemapPadNull
1855 | : (type = typeof x) === "function" ? padFunction
1856 | : type === "number" ? (x = [x, x, x, x], padConstant)
1857 | : padConstant;
1858 | return treemap;
1859 | };
1860 |
1861 | treemap.round = function(x) {
1862 | if (!arguments.length) return round != Number;
1863 | round = x ? Math.round : Number;
1864 | return treemap;
1865 | };
1866 |
1867 | treemap.sticky = function(x) {
1868 | if (!arguments.length) return sticky;
1869 | sticky = x;
1870 | stickies = null;
1871 | return treemap;
1872 | };
1873 |
1874 | treemap.ratio = function(x) {
1875 | if (!arguments.length) return ratio;
1876 | ratio = x;
1877 | return treemap;
1878 | };
1879 |
1880 | return d3_layout_hierarchyRebind(treemap, hierarchy);
1881 | };
1882 |
1883 | function d3_layout_treemapPadNull(node) {
1884 | return {x: node.x, y: node.y, dx: node.dx, dy: node.dy};
1885 | }
1886 |
1887 | function d3_layout_treemapPad(node, padding) {
1888 | var x = node.x + padding[3],
1889 | y = node.y + padding[0],
1890 | dx = node.dx - padding[1] - padding[3],
1891 | dy = node.dy - padding[0] - padding[2];
1892 | if (dx < 0) { x += dx / 2; dx = 0; }
1893 | if (dy < 0) { y += dy / 2; dy = 0; }
1894 | return {x: x, y: y, dx: dx, dy: dy};
1895 | }
1896 | })();
1897 |
--------------------------------------------------------------------------------
/dataConverter.js:
--------------------------------------------------------------------------------
1 | var convertToJSON = function(data, origin) {
2 | return (origin == 'cloc') ? convertFromClocToJSON(data) : convertFromWcToJSON(data);
3 | };
4 |
5 | /**
6 | * Convert the output of cloc in csv to JSON format
7 | *
8 | * > cloc . --csv --exclude-dir=vendor,tmp --by-file --report-file=data.cloc
9 | */
10 | var convertFromClocToJSON = function(data) {
11 | var lines = data.split("\n");
12 | lines.shift(); // drop the header line
13 |
14 | var json = {};
15 | lines.forEach(function(line) {
16 | var cols = line.split(',');
17 | var filename = cols[1];
18 | if (!filename) return;
19 | var elements = filename.split(/[\/\\]/);
20 | var current = json;
21 | elements.forEach(function(element) {
22 | if (!current[element]) {
23 | current[element] = {};
24 | }
25 | current = current[element];
26 | });
27 | current.language = cols[0];
28 | current.size = parseInt(cols[4], 10);
29 | });
30 |
31 | json = getChildren(json)[0];
32 | json.name = 'root';
33 |
34 | return json;
35 | };
36 |
37 | /**
38 | * Convert the output of wc to JSON format
39 | *
40 | * > git ls-files | xargs wc -l
41 | */
42 | var convertFromWcToJSON = function(data) {
43 | var lines = data.split("\n");
44 |
45 | var json = {};
46 | var filename, size, cols, elements, current;
47 | lines.forEach(function(line) {
48 | cols = line.trim().split(' ');
49 | size = parseInt(cols[0], 10);
50 | if (!size) return;
51 | filename = cols[1];
52 | if (filename === "total") return;
53 | if (!filename) return;
54 | elements = filename.split(/[\/\\]/);
55 | current = json;
56 | elements.forEach(function(element) {
57 | if (!current[element]) {
58 | current[element] = {};
59 | }
60 | current = current[element];
61 | });
62 | current.size = size;
63 | });
64 |
65 | json.children = getChildren(json);
66 | json.name = 'root';
67 |
68 | return json;
69 | };
70 |
71 | /**
72 | * Convert a simple json object into another specifying children as an array
73 | * Works recursively
74 | *
75 | * example input:
76 | * { a: { b: { c: { size: 12 }, d: { size: 34 } }, e: { size: 56 } } }
77 | * example output
78 | * { name: a, children: [
79 | * { name: b, children: [
80 | * { name: c, size: 12 },
81 | * { name: d, size: 34 }
82 | * ] },
83 | * { name: e, size: 56 }
84 | * ] } }
85 | */
86 | var getChildren = function(json) {
87 | var children = [];
88 | if (json.language) return children;
89 | for (var key in json) {
90 | var child = { name: key };
91 | if (json[key].size) {
92 | // value node
93 | child.size = json[key].size;
94 | child.language = json[key].language;
95 | } else {
96 | // children node
97 | var childChildren = getChildren(json[key]);
98 | if (childChildren) child.children = childChildren;
99 | }
100 | children.push(child);
101 | delete json[key];
102 | }
103 | return children;
104 | };
105 |
106 | // Recursively count all elements in a tree
107 | var countElements = function(node) {
108 | var nbElements = 1;
109 | if (node.children) {
110 | nbElements += node.children.reduce(function(p, v) { return p + countElements(v); }, 0);
111 | }
112 | return nbElements;
113 | };
114 |
--------------------------------------------------------------------------------