|
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 if ((alpha *= .99) < .005) { |
|
322 event.tick({type: "tick", alpha: alpha}); |
|
323 return true; |
|
324 } |
|
325 else { |
|
326 return false; |
|
327 } |
|
328 |
|
329 /*event.tick({type: "tick", alpha: alpha}); |
|
330 |
|
331 // simulated annealing, basically |
|
332 return (alpha *= .99) < .005;*/ |
|
333 } |
|
334 |
|
335 force.on = function(type, listener) { |
|
336 event.on(type, listener); |
|
337 return force; |
|
338 }; |
|
339 |
|
340 force.nodes = function(x) { |
|
341 if (!arguments.length) return nodes; |
|
342 nodes = x; |
|
343 return force; |
|
344 }; |
|
345 |
|
346 force.links = function(x) { |
|
347 if (!arguments.length) return links; |
|
348 links = x; |
|
349 return force; |
|
350 }; |
|
351 |
|
352 force.size = function(x) { |
|
353 if (!arguments.length) return size; |
|
354 size = x; |
|
355 return force; |
|
356 }; |
|
357 |
|
358 force.linkDistance = function(x) { |
|
359 if (!arguments.length) return linkDistance; |
|
360 linkDistance = d3.functor(x); |
|
361 return force; |
|
362 }; |
|
363 |
|
364 // For backwards-compatibility. |
|
365 force.distance = force.linkDistance; |
|
366 |
|
367 force.linkStrength = function(x) { |
|
368 if (!arguments.length) return linkStrength; |
|
369 linkStrength = d3.functor(x); |
|
370 return force; |
|
371 }; |
|
372 |
|
373 force.friction = function(x) { |
|
374 if (!arguments.length) return friction; |
|
375 friction = x; |
|
376 return force; |
|
377 }; |
|
378 |
|
379 force.charge = function(x) { |
|
380 if (!arguments.length) return charge; |
|
381 charge = typeof x === "function" ? x : +x; |
|
382 return force; |
|
383 }; |
|
384 |
|
385 force.gravity = function(x) { |
|
386 if (!arguments.length) return gravity; |
|
387 gravity = x; |
|
388 return force; |
|
389 }; |
|
390 |
|
391 force.theta = function(x) { |
|
392 if (!arguments.length) return theta; |
|
393 theta = x; |
|
394 return force; |
|
395 }; |
|
396 |
|
397 force.start = function() { |
|
398 var i, |
|
399 j, |
|
400 n = nodes.length, |
|
401 m = links.length, |
|
402 w = size[0], |
|
403 h = size[1], |
|
404 neighbors, |
|
405 o; |
|
406 |
|
407 for (i = 0; i < n; ++i) { |
|
408 (o = nodes[i]).index = i; |
|
409 o.weight = 0; |
|
410 } |
|
411 |
|
412 distances = []; |
|
413 strengths = []; |
|
414 for (i = 0; i < m; ++i) { |
|
415 o = links[i]; |
|
416 if (typeof o.source == "number") o.source = nodes[o.source]; |
|
417 if (typeof o.target == "number") o.target = nodes[o.target]; |
|
418 distances[i] = linkDistance.call(this, o, i); |
|
419 strengths[i] = linkStrength.call(this, o, i); |
|
420 ++o.source.weight; |
|
421 ++o.target.weight; |
|
422 } |
|
423 |
|
424 for (i = 0; i < n; ++i) { |
|
425 o = nodes[i]; |
|
426 if (isNaN(o.x)) o.x = position("x", w); |
|
427 if (isNaN(o.y)) o.y = position("y", h); |
|
428 if (isNaN(o.px)) o.px = o.x; |
|
429 if (isNaN(o.py)) o.py = o.y; |
|
430 } |
|
431 |
|
432 charges = []; |
|
433 if (typeof charge === "function") { |
|
434 for (i = 0; i < n; ++i) { |
|
435 charges[i] = +charge.call(this, nodes[i], i); |
|
436 } |
|
437 } else { |
|
438 for (i = 0; i < n; ++i) { |
|
439 charges[i] = charge; |
|
440 } |
|
441 } |
|
442 |
|
443 // initialize node position based on first neighbor |
|
444 function position(dimension, size) { |
|
445 var neighbors = neighbor(i), |
|
446 j = -1, |
|
447 m = neighbors.length, |
|
448 x; |
|
449 while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x; |
|
450 return Math.random() * size; |
|
451 } |
|
452 |
|
453 // initialize neighbors lazily |
|
454 function neighbor() { |
|
455 if (!neighbors) { |
|
456 neighbors = []; |
|
457 for (j = 0; j < n; ++j) { |
|
458 neighbors[j] = []; |
|
459 } |
|
460 for (j = 0; j < m; ++j) { |
|
461 var o = links[j]; |
|
462 neighbors[o.source.index].push(o.target); |
|
463 neighbors[o.target.index].push(o.source); |
|
464 } |
|
465 } |
|
466 return neighbors[i]; |
|
467 } |
|
468 |
|
469 return force.resume(); |
|
470 }; |
|
471 |
|
472 force.resume = function() { |
|
473 alpha = .1; |
|
474 d3.timer(tick); |
|
475 return force; |
|
476 }; |
|
477 |
|
478 force.stop = function() { |
|
479 alpha = 0; |
|
480 return force; |
|
481 }; |
|
482 |
|
483 // use `node.call(force.drag)` to make nodes draggable |
|
484 force.drag = function() { |
|
485 if (!drag) drag = d3.behavior.drag() |
|
486 .on("dragstart", dragstart) |
|
487 .on("drag", d3_layout_forceDrag) |
|
488 .on("dragend", d3_layout_forceDragEnd); |
|
489 |
|
490 this.on("mouseover.force", d3_layout_forceDragOver) |
|
491 .on("mouseout.force", d3_layout_forceDragOut) |
|
492 .call(drag); |
|
493 }; |
|
494 |
|
495 function dragstart(d) { |
|
496 d3_layout_forceDragOver(d3_layout_forceDragNode = d); |
|
497 d3_layout_forceDragForce = force; |
|
498 } |
|
499 |
|
500 return force; |
|
501 }; |
|
502 |
|
503 var d3_layout_forceDragForce, |
|
504 d3_layout_forceDragNode; |
|
505 |
|
506 function d3_layout_forceDragOver(d) { |
|
507 d.fixed |= 2; |
|
508 } |
|
509 |
|
510 function d3_layout_forceDragOut(d) { |
|
511 if (d !== d3_layout_forceDragNode) d.fixed &= 1; |
|
512 } |
|
513 |
|
514 function d3_layout_forceDragEnd() { |
|
515 d3_layout_forceDrag(); |
|
516 d3_layout_forceDragNode.fixed &= 1; |
|
517 d3_layout_forceDragForce = d3_layout_forceDragNode = null; |
|
518 } |
|
519 |
|
520 function d3_layout_forceDrag() { |
|
521 d3_layout_forceDragNode.px += d3.event.dx; |
|
522 d3_layout_forceDragNode.py += d3.event.dy; |
|
523 d3_layout_forceDragForce.resume(); // restart annealing |
|
524 } |
|
525 |
|
526 function d3_layout_forceAccumulate(quad, alpha, charges) { |
|
527 var cx = 0, |
|
528 cy = 0; |
|
529 quad.charge = 0; |
|
530 if (!quad.leaf) { |
|
531 var nodes = quad.nodes, |
|
532 n = nodes.length, |
|
533 i = -1, |
|
534 c; |
|
535 while (++i < n) { |
|
536 c = nodes[i]; |
|
537 if (c == null) continue; |
|
538 d3_layout_forceAccumulate(c, alpha, charges); |
|
539 quad.charge += c.charge; |
|
540 cx += c.charge * c.cx; |
|
541 cy += c.charge * c.cy; |
|
542 } |
|
543 } |
|
544 if (quad.point) { |
|
545 // jitter internal nodes that are coincident |
|
546 if (!quad.leaf) { |
|
547 quad.point.x += Math.random() - .5; |
|
548 quad.point.y += Math.random() - .5; |
|
549 } |
|
550 var k = alpha * charges[quad.point.index]; |
|
551 quad.charge += quad.pointCharge = k; |
|
552 cx += k * quad.point.x; |
|
553 cy += k * quad.point.y; |
|
554 } |
|
555 quad.cx = cx / quad.charge; |
|
556 quad.cy = cy / quad.charge; |
|
557 } |
|
558 |
|
559 function d3_layout_forceLinkDistance(link) { |
|
560 return 20; |
|
561 } |
|
562 |
|
563 function d3_layout_forceLinkStrength(link) { |
|
564 return 1; |
|
565 } |
|
566 d3.layout.partition = function() { |
|
567 var hierarchy = d3.layout.hierarchy(), |
|
568 size = [1, 1]; // width, height |
|
569 |
|
570 function position(node, x, dx, dy) { |
|
571 var children = node.children; |
|
572 node.x = x; |
|
573 node.y = node.depth * dy; |
|
574 node.dx = dx; |
|
575 node.dy = dy; |
|
576 if (children && (n = children.length)) { |
|
577 var i = -1, |
|
578 n, |
|
579 c, |
|
580 d; |
|
581 dx = node.value ? dx / node.value : 0; |
|
582 while (++i < n) { |
|
583 position(c = children[i], x, d = c.value * dx, dy); |
|
584 x += d; |
|
585 } |
|
586 } |
|
587 } |
|
588 |
|
589 function depth(node) { |
|
590 var children = node.children, |
|
591 d = 0; |
|
592 if (children && (n = children.length)) { |
|
593 var i = -1, |
|
594 n; |
|
595 while (++i < n) d = Math.max(d, depth(children[i])); |
|
596 } |
|
597 return 1 + d; |
|
598 } |
|
599 |
|
600 function partition(d, i) { |
|
601 var nodes = hierarchy.call(this, d, i); |
|
602 position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); |
|
603 return nodes; |
|
604 } |
|
605 |
|
606 partition.size = function(x) { |
|
607 if (!arguments.length) return size; |
|
608 size = x; |
|
609 return partition; |
|
610 }; |
|
611 |
|
612 return d3_layout_hierarchyRebind(partition, hierarchy); |
|
613 }; |
|
614 d3.layout.pie = function() { |
|
615 var value = Number, |
|
616 sort = d3_layout_pieSortByValue, |
|
617 startAngle = 0, |
|
618 endAngle = 2 * Math.PI; |
|
619 |
|
620 function pie(data, i) { |
|
621 |
|
622 // Compute the numeric values for each data element. |
|
623 var values = data.map(function(d, i) { return +value.call(pie, d, i); }); |
|
624 |
|
625 // Compute the start angle. |
|
626 var a = +(typeof startAngle === "function" |
|
627 ? startAngle.apply(this, arguments) |
|
628 : startAngle); |
|
629 |
|
630 // Compute the angular scale factor: from value to radians. |
|
631 var k = ((typeof endAngle === "function" |
|
632 ? endAngle.apply(this, arguments) |
|
633 : endAngle) - startAngle) |
|
634 / d3.sum(values); |
|
635 |
|
636 // Optionally sort the data. |
|
637 var index = d3.range(data.length); |
|
638 if (sort != null) index.sort(sort === d3_layout_pieSortByValue |
|
639 ? function(i, j) { return values[j] - values[i]; } |
|
640 : function(i, j) { return sort(data[i], data[j]); }); |
|
641 |
|
642 // Compute the arcs! |
|
643 var arcs = index.map(function(i) { |
|
644 return { |
|
645 data: data[i], |
|
646 value: d = values[i], |
|
647 startAngle: a, |
|
648 endAngle: a += d * k |
|
649 }; |
|
650 }); |
|
651 |
|
652 // Return the arcs in the original data's order. |
|
653 return data.map(function(d, i) { |
|
654 return arcs[index[i]]; |
|
655 }); |
|
656 } |
|
657 |
|
658 /** |
|
659 * Specifies the value function *x*, which returns a nonnegative numeric value |
|
660 * for each datum. The default value function is `Number`. The value function |
|
661 * is passed two arguments: the current datum and the current index. |
|
662 */ |
|
663 pie.value = function(x) { |
|
664 if (!arguments.length) return value; |
|
665 value = x; |
|
666 return pie; |
|
667 }; |
|
668 |
|
669 /** |
|
670 * Specifies a sort comparison operator *x*. The comparator is passed two data |
|
671 * elements from the data array, a and b; it returns a negative value if a is |
|
672 * less than b, a positive value if a is greater than b, and zero if a equals |
|
673 * b. |
|
674 */ |
|
675 pie.sort = function(x) { |
|
676 if (!arguments.length) return sort; |
|
677 sort = x; |
|
678 return pie; |
|
679 }; |
|
680 |
|
681 /** |
|
682 * Specifies the overall start angle of the pie chart. Defaults to 0. The |
|
683 * start angle can be specified either as a constant or as a function; in the |
|
684 * case of a function, it is evaluated once per array (as opposed to per |
|
685 * element). |
|
686 */ |
|
687 pie.startAngle = function(x) { |
|
688 if (!arguments.length) return startAngle; |
|
689 startAngle = x; |
|
690 return pie; |
|
691 }; |
|
692 |
|
693 /** |
|
694 * Specifies the overall end angle of the pie chart. Defaults to 2Ď€. The |
|
695 * end angle can be specified either as a constant or as a function; in the |
|
696 * case of a function, it is evaluated once per array (as opposed to per |
|
697 * element). |
|
698 */ |
|
699 pie.endAngle = function(x) { |
|
700 if (!arguments.length) return endAngle; |
|
701 endAngle = x; |
|
702 return pie; |
|
703 }; |
|
704 |
|
705 return pie; |
|
706 }; |
|
707 |
|
708 var d3_layout_pieSortByValue = {}; |
|
709 // data is two-dimensional array of x,y; we populate y0 |
|
710 d3.layout.stack = function() { |
|
711 var values = Object, |
|
712 order = d3_layout_stackOrders["default"], |
|
713 offset = d3_layout_stackOffsets["zero"], |
|
714 out = d3_layout_stackOut, |
|
715 x = d3_layout_stackX, |
|
716 y = d3_layout_stackY; |
|
717 |
|
718 function stack(data, index) { |
|
719 |
|
720 // Convert series to canonical two-dimensional representation. |
|
721 var series = data.map(function(d, i) { |
|
722 return values.call(stack, d, i); |
|
723 }); |
|
724 |
|
725 // Convert each series to canonical [[x,y]] representation. |
|
726 var points = series.map(function(d, i) { |
|
727 return d.map(function(v, i) { |
|
728 return [x.call(stack, v, i), y.call(stack, v, i)]; |
|
729 }); |
|
730 }); |
|
731 |
|
732 // Compute the order of series, and permute them. |
|
733 var orders = order.call(stack, points, index); |
|
734 series = d3.permute(series, orders); |
|
735 points = d3.permute(points, orders); |
|
736 |
|
737 // Compute the baseline… |
|
738 var offsets = offset.call(stack, points, index); |
|
739 |
|
740 // And propagate it to other series. |
|
741 var n = series.length, |
|
742 m = series[0].length, |
|
743 i, |
|
744 j, |
|
745 o; |
|
746 for (j = 0; j < m; ++j) { |
|
747 out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); |
|
748 for (i = 1; i < n; ++i) { |
|
749 out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); |
|
750 } |
|
751 } |
|
752 |
|
753 return data; |
|
754 } |
|
755 |
|
756 stack.values = function(x) { |
|
757 if (!arguments.length) return values; |
|
758 values = x; |
|
759 return stack; |
|
760 }; |
|
761 |
|
762 stack.order = function(x) { |
|
763 if (!arguments.length) return order; |
|
764 order = typeof x === "function" ? x : d3_layout_stackOrders[x]; |
|
765 return stack; |
|
766 }; |
|
767 |
|
768 stack.offset = function(x) { |
|
769 if (!arguments.length) return offset; |
|
770 offset = typeof x === "function" ? x : d3_layout_stackOffsets[x]; |
|
771 return stack; |
|
772 }; |
|
773 |
|
774 stack.x = function(z) { |
|
775 if (!arguments.length) return x; |
|
776 x = z; |
|
777 return stack; |
|
778 }; |
|
779 |
|
780 stack.y = function(z) { |
|
781 if (!arguments.length) return y; |
|
782 y = z; |
|
783 return stack; |
|
784 }; |
|
785 |
|
786 stack.out = function(z) { |
|
787 if (!arguments.length) return out; |
|
788 out = z; |
|
789 return stack; |
|
790 }; |
|
791 |
|
792 return stack; |
|
793 } |
|
794 |
|
795 function d3_layout_stackX(d) { |
|
796 return d.x; |
|
797 } |
|
798 |
|
799 function d3_layout_stackY(d) { |
|
800 return d.y; |
|
801 } |
|
802 |
|
803 function d3_layout_stackOut(d, y0, y) { |
|
804 d.y0 = y0; |
|
805 d.y = y; |
|
806 } |
|
807 |
|
808 var d3_layout_stackOrders = { |
|
809 |
|
810 "inside-out": function(data) { |
|
811 var n = data.length, |
|
812 i, |
|
813 j, |
|
814 max = data.map(d3_layout_stackMaxIndex), |
|
815 sums = data.map(d3_layout_stackReduceSum), |
|
816 index = d3.range(n).sort(function(a, b) { return max[a] - max[b]; }), |
|
817 top = 0, |
|
818 bottom = 0, |
|
819 tops = [], |
|
820 bottoms = []; |
|
821 for (i = 0; i < n; ++i) { |
|
822 j = index[i]; |
|
823 if (top < bottom) { |
|
824 top += sums[j]; |
|
825 tops.push(j); |
|
826 } else { |
|
827 bottom += sums[j]; |
|
828 bottoms.push(j); |
|
829 } |
|
830 } |
|
831 return bottoms.reverse().concat(tops); |
|
832 }, |
|
833 |
|
834 "reverse": function(data) { |
|
835 return d3.range(data.length).reverse(); |
|
836 }, |
|
837 |
|
838 "default": function(data) { |
|
839 return d3.range(data.length); |
|
840 } |
|
841 |
|
842 }; |
|
843 |
|
844 var d3_layout_stackOffsets = { |
|
845 |
|
846 "silhouette": function(data) { |
|
847 var n = data.length, |
|
848 m = data[0].length, |
|
849 sums = [], |
|
850 max = 0, |
|
851 i, |
|
852 j, |
|
853 o, |
|
854 y0 = []; |
|
855 for (j = 0; j < m; ++j) { |
|
856 for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; |
|
857 if (o > max) max = o; |
|
858 sums.push(o); |
|
859 } |
|
860 for (j = 0; j < m; ++j) { |
|
861 y0[j] = (max - sums[j]) / 2; |
|
862 } |
|
863 return y0; |
|
864 }, |
|
865 |
|
866 "wiggle": function(data) { |
|
867 var n = data.length, |
|
868 x = data[0], |
|
869 m = x.length, |
|
870 max = 0, |
|
871 i, |
|
872 j, |
|
873 k, |
|
874 s1, |
|
875 s2, |
|
876 s3, |
|
877 dx, |
|
878 o, |
|
879 o0, |
|
880 y0 = []; |
|
881 y0[0] = o = o0 = 0; |
|
882 for (j = 1; j < m; ++j) { |
|
883 for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; |
|
884 for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { |
|
885 for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { |
|
886 s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; |
|
887 } |
|
888 s2 += s3 * data[i][j][1]; |
|
889 } |
|
890 y0[j] = o -= s1 ? s2 / s1 * dx : 0; |
|
891 if (o < o0) o0 = o; |
|
892 } |
|
893 for (j = 0; j < m; ++j) y0[j] -= o0; |
|
894 return y0; |
|
895 }, |
|
896 |
|
897 "expand": function(data) { |
|
898 var n = data.length, |
|
899 m = data[0].length, |
|
900 k = 1 / n, |
|
901 i, |
|
902 j, |
|
903 o, |
|
904 y0 = []; |
|
905 for (j = 0; j < m; ++j) { |
|
906 for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; |
|
907 if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; |
|
908 else for (i = 0; i < n; i++) data[i][j][1] = k; |
|
909 } |
|
910 for (j = 0; j < m; ++j) y0[j] = 0; |
|
911 return y0; |
|
912 }, |
|
913 |
|
914 "zero": function(data) { |
|
915 var j = -1, |
|
916 m = data[0].length, |
|
917 y0 = []; |
|
918 while (++j < m) y0[j] = 0; |
|
919 return y0; |
|
920 } |
|
921 |
|
922 }; |
|
923 |
|
924 function d3_layout_stackMaxIndex(array) { |
|
925 var i = 1, |
|
926 j = 0, |
|
927 v = array[0][1], |
|
928 k, |
|
929 n = array.length; |
|
930 for (; i < n; ++i) { |
|
931 if ((k = array[i][1]) > v) { |
|
932 j = i; |
|
933 v = k; |
|
934 } |
|
935 } |
|
936 return j; |
|
937 } |
|
938 |
|
939 function d3_layout_stackReduceSum(d) { |
|
940 return d.reduce(d3_layout_stackSum, 0); |
|
941 } |
|
942 |
|
943 function d3_layout_stackSum(p, d) { |
|
944 return p + d[1]; |
|
945 } |
|
946 d3.layout.histogram = function() { |
|
947 var frequency = true, |
|
948 valuer = Number, |
|
949 ranger = d3_layout_histogramRange, |
|
950 binner = d3_layout_histogramBinSturges; |
|
951 |
|
952 function histogram(data, i) { |
|
953 var bins = [], |
|
954 values = data.map(valuer, this), |
|
955 range = ranger.call(this, values, i), |
|
956 thresholds = binner.call(this, range, values, i), |
|
957 bin, |
|
958 i = -1, |
|
959 n = values.length, |
|
960 m = thresholds.length - 1, |
|
961 k = frequency ? 1 : 1 / n, |
|
962 x; |
|
963 |
|
964 // Initialize the bins. |
|
965 while (++i < m) { |
|
966 bin = bins[i] = []; |
|
967 bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); |
|
968 bin.y = 0; |
|
969 } |
|
970 |
|
971 // Fill the bins, ignoring values outside the range. |
|
972 i = -1; while(++i < n) { |
|
973 x = values[i]; |
|
974 if ((x >= range[0]) && (x <= range[1])) { |
|
975 bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; |
|
976 bin.y += k; |
|
977 bin.push(data[i]); |
|
978 } |
|
979 } |
|
980 |
|
981 return bins; |
|
982 } |
|
983 |
|
984 // Specifies how to extract a value from the associated data. The default |
|
985 // value function is `Number`, which is equivalent to the identity function. |
|
986 histogram.value = function(x) { |
|
987 if (!arguments.length) return valuer; |
|
988 valuer = x; |
|
989 return histogram; |
|
990 }; |
|
991 |
|
992 // Specifies the range of the histogram. Values outside the specified range |
|
993 // will be ignored. The argument `x` may be specified either as a two-element |
|
994 // array representing the minimum and maximum value of the range, or as a |
|
995 // function that returns the range given the array of values and the current |
|
996 // index `i`. The default range is the extent (minimum and maximum) of the |
|
997 // values. |
|
998 histogram.range = function(x) { |
|
999 if (!arguments.length) return ranger; |
|
1000 ranger = d3.functor(x); |
|
1001 return histogram; |
|
1002 }; |
|
1003 |
|
1004 // Specifies how to bin values in the histogram. The argument `x` may be |
|
1005 // specified as a number, in which case the range of values will be split |
|
1006 // uniformly into the given number of bins. Or, `x` may be an array of |
|
1007 // threshold values, defining the bins; the specified array must contain the |
|
1008 // rightmost (upper) value, thus specifying n + 1 values for n bins. Or, `x` |
|
1009 // may be a function which is evaluated, being passed the range, the array of |
|
1010 // values, and the current index `i`, returning an array of thresholds. The |
|
1011 // default bin function will divide the values into uniform bins using |
|
1012 // Sturges' formula. |
|
1013 histogram.bins = function(x) { |
|
1014 if (!arguments.length) return binner; |
|
1015 binner = typeof x === "number" |
|
1016 ? function(range) { return d3_layout_histogramBinFixed(range, x); } |
|
1017 : d3.functor(x); |
|
1018 return histogram; |
|
1019 }; |
|
1020 |
|
1021 // Specifies whether the histogram's `y` value is a count (frequency) or a |
|
1022 // probability (density). The default value is true. |
|
1023 histogram.frequency = function(x) { |
|
1024 if (!arguments.length) return frequency; |
|
1025 frequency = !!x; |
|
1026 return histogram; |
|
1027 }; |
|
1028 |
|
1029 return histogram; |
|
1030 }; |
|
1031 |
|
1032 function d3_layout_histogramBinSturges(range, values) { |
|
1033 return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); |
|
1034 } |
|
1035 |
|
1036 function d3_layout_histogramBinFixed(range, n) { |
|
1037 var x = -1, |
|
1038 b = +range[0], |
|
1039 m = (range[1] - b) / n, |
|
1040 f = []; |
|
1041 while (++x <= n) f[x] = m * x + b; |
|
1042 return f; |
|
1043 } |
|
1044 |
|
1045 function d3_layout_histogramRange(values) { |
|
1046 return [d3.min(values), d3.max(values)]; |
|
1047 } |
|
1048 d3.layout.hierarchy = function() { |
|
1049 var sort = d3_layout_hierarchySort, |
|
1050 children = d3_layout_hierarchyChildren, |
|
1051 value = d3_layout_hierarchyValue; |
|
1052 |
|
1053 // Recursively compute the node depth and value. |
|
1054 // Also converts the data representation into a standard hierarchy structure. |
|
1055 function recurse(data, depth, nodes) { |
|
1056 var childs = children.call(hierarchy, data, depth), |
|
1057 node = d3_layout_hierarchyInline ? data : {data: data}; |
|
1058 node.depth = depth; |
|
1059 nodes.push(node); |
|
1060 if (childs && (n = childs.length)) { |
|
1061 var i = -1, |
|
1062 n, |
|
1063 c = node.children = [], |
|
1064 v = 0, |
|
1065 j = depth + 1; |
|
1066 while (++i < n) { |
|
1067 d = recurse(childs[i], j, nodes); |
|
1068 d.parent = node; |
|
1069 c.push(d); |
|
1070 v += d.value; |
|
1071 } |
|
1072 if (sort) c.sort(sort); |
|
1073 if (value) node.value = v; |
|
1074 } else if (value) { |
|
1075 node.value = +value.call(hierarchy, data, depth) || 0; |
|
1076 } |
|
1077 return node; |
|
1078 } |
|
1079 |
|
1080 // Recursively re-evaluates the node value. |
|
1081 function revalue(node, depth) { |
|
1082 var children = node.children, |
|
1083 v = 0; |
|
1084 if (children && (n = children.length)) { |
|
1085 var i = -1, |
|
1086 n, |
|
1087 j = depth + 1; |
|
1088 while (++i < n) v += revalue(children[i], j); |
|
1089 } else if (value) { |
|
1090 v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0; |
|
1091 } |
|
1092 if (value) node.value = v; |
|
1093 return v; |
|
1094 } |
|
1095 |
|
1096 function hierarchy(d) { |
|
1097 var nodes = []; |
|
1098 recurse(d, 0, nodes); |
|
1099 return nodes; |
|
1100 } |
|
1101 |
|
1102 hierarchy.sort = function(x) { |
|
1103 if (!arguments.length) return sort; |
|
1104 sort = x; |
|
1105 return hierarchy; |
|
1106 }; |
|
1107 |
|
1108 hierarchy.children = function(x) { |
|
1109 if (!arguments.length) return children; |
|
1110 children = x; |
|
1111 return hierarchy; |
|
1112 }; |
|
1113 |
|
1114 hierarchy.value = function(x) { |
|
1115 if (!arguments.length) return value; |
|
1116 value = x; |
|
1117 return hierarchy; |
|
1118 }; |
|
1119 |
|
1120 // Re-evaluates the `value` property for the specified hierarchy. |
|
1121 hierarchy.revalue = function(root) { |
|
1122 revalue(root, 0); |
|
1123 return root; |
|
1124 }; |
|
1125 |
|
1126 return hierarchy; |
|
1127 }; |
|
1128 |
|
1129 // A method assignment helper for hierarchy subclasses. |
|
1130 function d3_layout_hierarchyRebind(object, hierarchy) { |
|
1131 object.sort = d3.rebind(object, hierarchy.sort); |
|
1132 object.children = d3.rebind(object, hierarchy.children); |
|
1133 object.links = d3_layout_hierarchyLinks; |
|
1134 object.value = d3.rebind(object, hierarchy.value); |
|
1135 |
|
1136 // If the new API is used, enabling inlining. |
|
1137 object.nodes = function(d) { |
|
1138 d3_layout_hierarchyInline = true; |
|
1139 return (object.nodes = object)(d); |
|
1140 }; |
|
1141 |
|
1142 return object; |
|
1143 } |
|
1144 |
|
1145 function d3_layout_hierarchyChildren(d) { |
|
1146 return d.children; |
|
1147 } |
|
1148 |
|
1149 function d3_layout_hierarchyValue(d) { |
|
1150 return d.value; |
|
1151 } |
|
1152 |
|
1153 function d3_layout_hierarchySort(a, b) { |
|
1154 return b.value - a.value; |
|
1155 } |
|
1156 |
|
1157 // Returns an array source+target objects for the specified nodes. |
|
1158 function d3_layout_hierarchyLinks(nodes) { |
|
1159 return d3.merge(nodes.map(function(parent) { |
|
1160 return (parent.children || []).map(function(child) { |
|
1161 return {source: parent, target: child}; |
|
1162 }); |
|
1163 })); |
|
1164 } |
|
1165 |
|
1166 // For backwards-compatibility, don't enable inlining by default. |
|
1167 var d3_layout_hierarchyInline = false; |
|
1168 d3.layout.pack = function() { |
|
1169 var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), |
|
1170 size = [1, 1]; |
|
1171 |
|
1172 function pack(d, i) { |
|
1173 var nodes = hierarchy.call(this, d, i), |
|
1174 root = nodes[0]; |
|
1175 |
|
1176 // Recursively compute the layout. |
|
1177 root.x = 0; |
|
1178 root.y = 0; |
|
1179 d3_layout_packTree(root); |
|
1180 |
|
1181 // Scale the layout to fit the requested size. |
|
1182 var w = size[0], |
|
1183 h = size[1], |
|
1184 k = 1 / Math.max(2 * root.r / w, 2 * root.r / h); |
|
1185 d3_layout_packTransform(root, w / 2, h / 2, k); |
|
1186 |
|
1187 return nodes; |
|
1188 } |
|
1189 |
|
1190 pack.size = function(x) { |
|
1191 if (!arguments.length) return size; |
|
1192 size = x; |
|
1193 return pack; |
|
1194 }; |
|
1195 |
|
1196 return d3_layout_hierarchyRebind(pack, hierarchy); |
|
1197 }; |
|
1198 |
|
1199 function d3_layout_packSort(a, b) { |
|
1200 return a.value - b.value; |
|
1201 } |
|
1202 |
|
1203 function d3_layout_packInsert(a, b) { |
|
1204 var c = a._pack_next; |
|
1205 a._pack_next = b; |
|
1206 b._pack_prev = a; |
|
1207 b._pack_next = c; |
|
1208 c._pack_prev = b; |
|
1209 } |
|
1210 |
|
1211 function d3_layout_packSplice(a, b) { |
|
1212 a._pack_next = b; |
|
1213 b._pack_prev = a; |
|
1214 } |
|
1215 |
|
1216 function d3_layout_packIntersects(a, b) { |
|
1217 var dx = b.x - a.x, |
|
1218 dy = b.y - a.y, |
|
1219 dr = a.r + b.r; |
|
1220 return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon |
|
1221 } |
|
1222 |
|
1223 function d3_layout_packCircle(nodes) { |
|
1224 var xMin = Infinity, |
|
1225 xMax = -Infinity, |
|
1226 yMin = Infinity, |
|
1227 yMax = -Infinity, |
|
1228 n = nodes.length, |
|
1229 a, b, c, j, k; |
|
1230 |
|
1231 function bound(node) { |
|
1232 xMin = Math.min(node.x - node.r, xMin); |
|
1233 xMax = Math.max(node.x + node.r, xMax); |
|
1234 yMin = Math.min(node.y - node.r, yMin); |
|
1235 yMax = Math.max(node.y + node.r, yMax); |
|
1236 } |
|
1237 |
|
1238 // Create node links. |
|
1239 nodes.forEach(d3_layout_packLink); |
|
1240 |
|
1241 // Create first node. |
|
1242 a = nodes[0]; |
|
1243 a.x = -a.r; |
|
1244 a.y = 0; |
|
1245 bound(a); |
|
1246 |
|
1247 // Create second node. |
|
1248 if (n > 1) { |
|
1249 b = nodes[1]; |
|
1250 b.x = b.r; |
|
1251 b.y = 0; |
|
1252 bound(b); |
|
1253 |
|
1254 // Create third node and build chain. |
|
1255 if (n > 2) { |
|
1256 c = nodes[2]; |
|
1257 d3_layout_packPlace(a, b, c); |
|
1258 bound(c); |
|
1259 d3_layout_packInsert(a, c); |
|
1260 a._pack_prev = c; |
|
1261 d3_layout_packInsert(c, b); |
|
1262 b = a._pack_next; |
|
1263 |
|
1264 // Now iterate through the rest. |
|
1265 for (var i = 3; i < n; i++) { |
|
1266 d3_layout_packPlace(a, b, c = nodes[i]); |
|
1267 |
|
1268 // Search for the closest intersection. |
|
1269 var isect = 0, s1 = 1, s2 = 1; |
|
1270 for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { |
|
1271 if (d3_layout_packIntersects(j, c)) { |
|
1272 isect = 1; |
|
1273 break; |
|
1274 } |
|
1275 } |
|
1276 if (isect == 1) { |
|
1277 for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { |
|
1278 if (d3_layout_packIntersects(k, c)) { |
|
1279 if (s2 < s1) { |
|
1280 isect = -1; |
|
1281 j = k; |
|
1282 } |
|
1283 break; |
|
1284 } |
|
1285 } |
|
1286 } |
|
1287 |
|
1288 // Update node chain. |
|
1289 if (isect == 0) { |
|
1290 d3_layout_packInsert(a, c); |
|
1291 b = c; |
|
1292 bound(c); |
|
1293 } else if (isect > 0) { |
|
1294 d3_layout_packSplice(a, j); |
|
1295 b = j; |
|
1296 i--; |
|
1297 } else { // isect < 0 |
|
1298 d3_layout_packSplice(j, b); |
|
1299 a = j; |
|
1300 i--; |
|
1301 } |
|
1302 } |
|
1303 } |
|
1304 } |
|
1305 |
|
1306 // Re-center the circles and return the encompassing radius. |
|
1307 var cx = (xMin + xMax) / 2, |
|
1308 cy = (yMin + yMax) / 2, |
|
1309 cr = 0; |
|
1310 for (var i = 0; i < n; i++) { |
|
1311 var node = nodes[i]; |
|
1312 node.x -= cx; |
|
1313 node.y -= cy; |
|
1314 cr = Math.max(cr, node.r + Math.sqrt(node.x * node.x + node.y * node.y)); |
|
1315 } |
|
1316 |
|
1317 // Remove node links. |
|
1318 nodes.forEach(d3_layout_packUnlink); |
|
1319 |
|
1320 return cr; |
|
1321 } |
|
1322 |
|
1323 function d3_layout_packLink(node) { |
|
1324 node._pack_next = node._pack_prev = node; |
|
1325 } |
|
1326 |
|
1327 function d3_layout_packUnlink(node) { |
|
1328 delete node._pack_next; |
|
1329 delete node._pack_prev; |
|
1330 } |
|
1331 |
|
1332 function d3_layout_packTree(node) { |
|
1333 var children = node.children; |
|
1334 if (children && children.length) { |
|
1335 children.forEach(d3_layout_packTree); |
|
1336 node.r = d3_layout_packCircle(children); |
|
1337 } else { |
|
1338 node.r = Math.sqrt(node.value); |
|
1339 } |
|
1340 } |
|
1341 |
|
1342 function d3_layout_packTransform(node, x, y, k) { |
|
1343 var children = node.children; |
|
1344 node.x = (x += k * node.x); |
|
1345 node.y = (y += k * node.y); |
|
1346 node.r *= k; |
|
1347 if (children) { |
|
1348 var i = -1, n = children.length; |
|
1349 while (++i < n) d3_layout_packTransform(children[i], x, y, k); |
|
1350 } |
|
1351 } |
|
1352 |
|
1353 function d3_layout_packPlace(a, b, c) { |
|
1354 var db = a.r + c.r, |
|
1355 dx = b.x - a.x, |
|
1356 dy = b.y - a.y; |
|
1357 if (db && (dx || dy)) { |
|
1358 var da = b.r + c.r, |
|
1359 dc = Math.sqrt(dx * dx + dy * dy), |
|
1360 cos = Math.max(-1, Math.min(1, (db * db + dc * dc - da * da) / (2 * db * dc))), |
|
1361 theta = Math.acos(cos), |
|
1362 x = cos * (db /= dc), |
|
1363 y = Math.sin(theta) * db; |
|
1364 c.x = a.x + x * dx + y * dy; |
|
1365 c.y = a.y + x * dy - y * dx; |
|
1366 } else { |
|
1367 c.x = a.x + db; |
|
1368 c.y = a.y; |
|
1369 } |
|
1370 } |
|
1371 // Implements a hierarchical layout using the cluster (or dendogram) algorithm. |
|
1372 d3.layout.cluster = function() { |
|
1373 var hierarchy = d3.layout.hierarchy().sort(null).value(null), |
|
1374 separation = d3_layout_treeSeparation, |
|
1375 size = [1, 1]; // width, height |
|
1376 |
|
1377 function cluster(d, i) { |
|
1378 var nodes = hierarchy.call(this, d, i), |
|
1379 root = nodes[0], |
|
1380 previousNode, |
|
1381 x = 0, |
|
1382 kx, |
|
1383 ky; |
|
1384 |
|
1385 // First walk, computing the initial x & y values. |
|
1386 d3_layout_treeVisitAfter(root, function(node) { |
|
1387 var children = node.children; |
|
1388 if (children && children.length) { |
|
1389 node.x = d3_layout_clusterX(children); |
|
1390 node.y = d3_layout_clusterY(children); |
|
1391 } else { |
|
1392 node.x = previousNode ? x += separation(node, previousNode) : 0; |
|
1393 node.y = 0; |
|
1394 previousNode = node; |
|
1395 } |
|
1396 }); |
|
1397 |
|
1398 // Compute the left-most, right-most, and depth-most nodes for extents. |
|
1399 var left = d3_layout_clusterLeft(root), |
|
1400 right = d3_layout_clusterRight(root), |
|
1401 x0 = left.x - separation(left, right) / 2, |
|
1402 x1 = right.x + separation(right, left) / 2; |
|
1403 |
|
1404 // Second walk, normalizing x & y to the desired size. |
|
1405 d3_layout_treeVisitAfter(root, function(node) { |
|
1406 node.x = (node.x - x0) / (x1 - x0) * size[0]; |
|
1407 node.y = (1 - node.y / root.y) * size[1]; |
|
1408 }); |
|
1409 |
|
1410 return nodes; |
|
1411 } |
|
1412 |
|
1413 cluster.separation = function(x) { |
|
1414 if (!arguments.length) return separation; |
|
1415 separation = x; |
|
1416 return cluster; |
|
1417 }; |
|
1418 |
|
1419 cluster.size = function(x) { |
|
1420 if (!arguments.length) return size; |
|
1421 size = x; |
|
1422 return cluster; |
|
1423 }; |
|
1424 |
|
1425 return d3_layout_hierarchyRebind(cluster, hierarchy); |
|
1426 }; |
|
1427 |
|
1428 function d3_layout_clusterY(children) { |
|
1429 return 1 + d3.max(children, function(child) { |
|
1430 return child.y; |
|
1431 }); |
|
1432 } |
|
1433 |
|
1434 function d3_layout_clusterX(children) { |
|
1435 return children.reduce(function(x, child) { |
|
1436 return x + child.x; |
|
1437 }, 0) / children.length; |
|
1438 } |
|
1439 |
|
1440 function d3_layout_clusterLeft(node) { |
|
1441 var children = node.children; |
|
1442 return children && children.length ? d3_layout_clusterLeft(children[0]) : node; |
|
1443 } |
|
1444 |
|
1445 function d3_layout_clusterRight(node) { |
|
1446 var children = node.children, n; |
|
1447 return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; |
|
1448 } |
|
1449 // Node-link tree diagram using the Reingold-Tilford "tidy" algorithm |
|
1450 d3.layout.tree = function() { |
|
1451 var hierarchy = d3.layout.hierarchy().sort(null).value(null), |
|
1452 separation = d3_layout_treeSeparation, |
|
1453 size = [1, 1]; // width, height |
|
1454 |
|
1455 function tree(d, i) { |
|
1456 var nodes = hierarchy.call(this, d, i), |
|
1457 root = nodes[0]; |
|
1458 |
|
1459 function firstWalk(node, previousSibling) { |
|
1460 var children = node.children, |
|
1461 layout = node._tree; |
|
1462 if (children && (n = children.length)) { |
|
1463 var n, |
|
1464 firstChild = children[0], |
|
1465 previousChild, |
|
1466 ancestor = firstChild, |
|
1467 child, |
|
1468 i = -1; |
|
1469 while (++i < n) { |
|
1470 child = children[i]; |
|
1471 firstWalk(child, previousChild); |
|
1472 ancestor = apportion(child, previousChild, ancestor); |
|
1473 previousChild = child; |
|
1474 } |
|
1475 d3_layout_treeShift(node); |
|
1476 var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); |
|
1477 if (previousSibling) { |
|
1478 layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); |
|
1479 layout.mod = layout.prelim - midpoint; |
|
1480 } else { |
|
1481 layout.prelim = midpoint; |
|
1482 } |
|
1483 } else { |
|
1484 if (previousSibling) { |
|
1485 layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); |
|
1486 } |
|
1487 } |
|
1488 } |
|
1489 |
|
1490 function secondWalk(node, x) { |
|
1491 node.x = node._tree.prelim + x; |
|
1492 var children = node.children; |
|
1493 if (children && (n = children.length)) { |
|
1494 var i = -1, |
|
1495 n; |
|
1496 x += node._tree.mod; |
|
1497 while (++i < n) { |
|
1498 secondWalk(children[i], x); |
|
1499 } |
|
1500 } |
|
1501 } |
|
1502 |
|
1503 function apportion(node, previousSibling, ancestor) { |
|
1504 if (previousSibling) { |
|
1505 var vip = node, |
|
1506 vop = node, |
|
1507 vim = previousSibling, |
|
1508 vom = node.parent.children[0], |
|
1509 sip = vip._tree.mod, |
|
1510 sop = vop._tree.mod, |
|
1511 sim = vim._tree.mod, |
|
1512 som = vom._tree.mod, |
|
1513 shift; |
|
1514 while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { |
|
1515 vom = d3_layout_treeLeft(vom); |
|
1516 vop = d3_layout_treeRight(vop); |
|
1517 vop._tree.ancestor = node; |
|
1518 shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); |
|
1519 if (shift > 0) { |
|
1520 d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); |
|
1521 sip += shift; |
|
1522 sop += shift; |
|
1523 } |
|
1524 sim += vim._tree.mod; |
|
1525 sip += vip._tree.mod; |
|
1526 som += vom._tree.mod; |
|
1527 sop += vop._tree.mod; |
|
1528 } |
|
1529 if (vim && !d3_layout_treeRight(vop)) { |
|
1530 vop._tree.thread = vim; |
|
1531 vop._tree.mod += sim - sop; |
|
1532 } |
|
1533 if (vip && !d3_layout_treeLeft(vom)) { |
|
1534 vom._tree.thread = vip; |
|
1535 vom._tree.mod += sip - som; |
|
1536 ancestor = node; |
|
1537 } |
|
1538 } |
|
1539 return ancestor; |
|
1540 } |
|
1541 |
|
1542 // Initialize temporary layout variables. |
|
1543 d3_layout_treeVisitAfter(root, function(node, previousSibling) { |
|
1544 node._tree = { |
|
1545 ancestor: node, |
|
1546 prelim: 0, |
|
1547 mod: 0, |
|
1548 change: 0, |
|
1549 shift: 0, |
|
1550 number: previousSibling ? previousSibling._tree.number + 1 : 0 |
|
1551 }; |
|
1552 }); |
|
1553 |
|
1554 // Compute the layout using Buchheim et al.'s algorithm. |
|
1555 firstWalk(root); |
|
1556 secondWalk(root, -root._tree.prelim); |
|
1557 |
|
1558 // Compute the left-most, right-most, and depth-most nodes for extents. |
|
1559 var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), |
|
1560 right = d3_layout_treeSearch(root, d3_layout_treeRightmost), |
|
1561 deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), |
|
1562 x0 = left.x - separation(left, right) / 2, |
|
1563 x1 = right.x + separation(right, left) / 2, |
|
1564 y1 = deep.depth || 1; |
|
1565 |
|
1566 // Clear temporary layout variables; transform x and y. |
|
1567 d3_layout_treeVisitAfter(root, function(node) { |
|
1568 node.x = (node.x - x0) / (x1 - x0) * size[0]; |
|
1569 node.y = node.depth / y1 * size[1]; |
|
1570 delete node._tree; |
|
1571 }); |
|
1572 |
|
1573 return nodes; |
|
1574 } |
|
1575 |
|
1576 tree.separation = function(x) { |
|
1577 if (!arguments.length) return separation; |
|
1578 separation = x; |
|
1579 return tree; |
|
1580 }; |
|
1581 |
|
1582 tree.size = function(x) { |
|
1583 if (!arguments.length) return size; |
|
1584 size = x; |
|
1585 return tree; |
|
1586 }; |
|
1587 |
|
1588 return d3_layout_hierarchyRebind(tree, hierarchy); |
|
1589 }; |
|
1590 |
|
1591 function d3_layout_treeSeparation(a, b) { |
|
1592 return a.parent == b.parent ? 1 : 2; |
|
1593 } |
|
1594 |
|
1595 // function d3_layout_treeSeparationRadial(a, b) { |
|
1596 // return (a.parent == b.parent ? 1 : 2) / a.depth; |
|
1597 // } |
|
1598 |
|
1599 function d3_layout_treeLeft(node) { |
|
1600 var children = node.children; |
|
1601 return children && children.length ? children[0] : node._tree.thread; |
|
1602 } |
|
1603 |
|
1604 function d3_layout_treeRight(node) { |
|
1605 var children = node.children, |
|
1606 n; |
|
1607 return children && (n = children.length) ? children[n - 1] : node._tree.thread; |
|
1608 } |
|
1609 |
|
1610 function d3_layout_treeSearch(node, compare) { |
|
1611 var children = node.children; |
|
1612 if (children && (n = children.length)) { |
|
1613 var child, |
|
1614 n, |
|
1615 i = -1; |
|
1616 while (++i < n) { |
|
1617 if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { |
|
1618 node = child; |
|
1619 } |
|
1620 } |
|
1621 } |
|
1622 return node; |
|
1623 } |
|
1624 |
|
1625 function d3_layout_treeRightmost(a, b) { |
|
1626 return a.x - b.x; |
|
1627 } |
|
1628 |
|
1629 function d3_layout_treeLeftmost(a, b) { |
|
1630 return b.x - a.x; |
|
1631 } |
|
1632 |
|
1633 function d3_layout_treeDeepest(a, b) { |
|
1634 return a.depth - b.depth; |
|
1635 } |
|
1636 |
|
1637 function d3_layout_treeVisitAfter(node, callback) { |
|
1638 function visit(node, previousSibling) { |
|
1639 var children = node.children; |
|
1640 if (children && (n = children.length)) { |
|
1641 var child, |
|
1642 previousChild = null, |
|
1643 i = -1, |
|
1644 n; |
|
1645 while (++i < n) { |
|
1646 child = children[i]; |
|
1647 visit(child, previousChild); |
|
1648 previousChild = child; |
|
1649 } |
|
1650 } |
|
1651 callback(node, previousSibling); |
|
1652 } |
|
1653 visit(node, null); |
|
1654 } |
|
1655 |
|
1656 function d3_layout_treeShift(node) { |
|
1657 var shift = 0, |
|
1658 change = 0, |
|
1659 children = node.children, |
|
1660 i = children.length, |
|
1661 child; |
|
1662 while (--i >= 0) { |
|
1663 child = children[i]._tree; |
|
1664 child.prelim += shift; |
|
1665 child.mod += shift; |
|
1666 shift += child.shift + (change += child.change); |
|
1667 } |
|
1668 } |
|
1669 |
|
1670 function d3_layout_treeMove(ancestor, node, shift) { |
|
1671 ancestor = ancestor._tree; |
|
1672 node = node._tree; |
|
1673 var change = shift / (node.number - ancestor.number); |
|
1674 ancestor.change += change; |
|
1675 node.change -= change; |
|
1676 node.shift += shift; |
|
1677 node.prelim += shift; |
|
1678 node.mod += shift; |
|
1679 } |
|
1680 |
|
1681 function d3_layout_treeAncestor(vim, node, ancestor) { |
|
1682 return vim._tree.ancestor.parent == node.parent |
|
1683 ? vim._tree.ancestor |
|
1684 : ancestor; |
|
1685 } |
|
1686 // Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk |
|
1687 // Modified to support a target aspect ratio by Jeff Heer |
|
1688 d3.layout.treemap = function() { |
|
1689 var hierarchy = d3.layout.hierarchy(), |
|
1690 round = Math.round, |
|
1691 size = [1, 1], // width, height |
|
1692 padding = null, |
|
1693 pad = d3_layout_treemapPadNull, |
|
1694 sticky = false, |
|
1695 stickies, |
|
1696 ratio = 0.5 * (1 + Math.sqrt(5)); // golden ratio |
|
1697 |
|
1698 // Compute the area for each child based on value & scale. |
|
1699 function scale(children, k) { |
|
1700 var i = -1, |
|
1701 n = children.length, |
|
1702 child, |
|
1703 area; |
|
1704 while (++i < n) { |
|
1705 area = (child = children[i]).value * (k < 0 ? 0 : k); |
|
1706 child.area = isNaN(area) || area <= 0 ? 0 : area; |
|
1707 } |
|
1708 } |
|
1709 |
|
1710 // Recursively arranges the specified node's children into squarified rows. |
|
1711 function squarify(node) { |
|
1712 var children = node.children; |
|
1713 if (children && children.length) { |
|
1714 var rect = pad(node), |
|
1715 row = [], |
|
1716 remaining = children.slice(), // copy-on-write |
|
1717 child, |
|
1718 best = Infinity, // the best row score so far |
|
1719 score, // the current row score |
|
1720 u = Math.min(rect.dx, rect.dy), // initial orientation |
|
1721 n; |
|
1722 scale(remaining, rect.dx * rect.dy / node.value); |
|
1723 row.area = 0; |
|
1724 while ((n = remaining.length) > 0) { |
|
1725 row.push(child = remaining[n - 1]); |
|
1726 row.area += child.area; |
|
1727 if ((score = worst(row, u)) <= best) { // continue with this orientation |
|
1728 remaining.pop(); |
|
1729 best = score; |
|
1730 } else { // abort, and try a different orientation |
|
1731 row.area -= row.pop().area; |
|
1732 position(row, u, rect, false); |
|
1733 u = Math.min(rect.dx, rect.dy); |
|
1734 row.length = row.area = 0; |
|
1735 best = Infinity; |
|
1736 } |
|
1737 } |
|
1738 if (row.length) { |
|
1739 position(row, u, rect, true); |
|
1740 row.length = row.area = 0; |
|
1741 } |
|
1742 children.forEach(squarify); |
|
1743 } |
|
1744 } |
|
1745 |
|
1746 // Recursively resizes the specified node's children into existing rows. |
|
1747 // Preserves the existing layout! |
|
1748 function stickify(node) { |
|
1749 var children = node.children; |
|
1750 if (children && children.length) { |
|
1751 var rect = pad(node), |
|
1752 remaining = children.slice(), // copy-on-write |
|
1753 child, |
|
1754 row = []; |
|
1755 scale(remaining, rect.dx * rect.dy / node.value); |
|
1756 row.area = 0; |
|
1757 while (child = remaining.pop()) { |
|
1758 row.push(child); |
|
1759 row.area += child.area; |
|
1760 if (child.z != null) { |
|
1761 position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); |
|
1762 row.length = row.area = 0; |
|
1763 } |
|
1764 } |
|
1765 children.forEach(stickify); |
|
1766 } |
|
1767 } |
|
1768 |
|
1769 // Computes the score for the specified row, as the worst aspect ratio. |
|
1770 function worst(row, u) { |
|
1771 var s = row.area, |
|
1772 r, |
|
1773 rmax = 0, |
|
1774 rmin = Infinity, |
|
1775 i = -1, |
|
1776 n = row.length; |
|
1777 while (++i < n) { |
|
1778 if (!(r = row[i].area)) continue; |
|
1779 if (r < rmin) rmin = r; |
|
1780 if (r > rmax) rmax = r; |
|
1781 } |
|
1782 s *= s; |
|
1783 u *= u; |
|
1784 return s |
|
1785 ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio)) |
|
1786 : Infinity; |
|
1787 } |
|
1788 |
|
1789 // Positions the specified row of nodes. Modifies `rect`. |
|
1790 function position(row, u, rect, flush) { |
|
1791 var i = -1, |
|
1792 n = row.length, |
|
1793 x = rect.x, |
|
1794 y = rect.y, |
|
1795 v = u ? round(row.area / u) : 0, |
|
1796 o; |
|
1797 if (u == rect.dx) { // horizontal subdivision |
|
1798 if (flush || v > rect.dy) v = v ? rect.dy : 0; // over+underflow |
|
1799 while (++i < n) { |
|
1800 o = row[i]; |
|
1801 o.x = x; |
|
1802 o.y = y; |
|
1803 o.dy = v; |
|
1804 x += o.dx = v ? round(o.area / v) : 0; |
|
1805 } |
|
1806 o.z = true; |
|
1807 o.dx += rect.x + rect.dx - x; // rounding error |
|
1808 rect.y += v; |
|
1809 rect.dy -= v; |
|
1810 } else { // vertical subdivision |
|
1811 if (flush || v > rect.dx) v = v ? rect.dx : 0; // over+underflow |
|
1812 while (++i < n) { |
|
1813 o = row[i]; |
|
1814 o.x = x; |
|
1815 o.y = y; |
|
1816 o.dx = v; |
|
1817 y += o.dy = v ? round(o.area / v) : 0; |
|
1818 } |
|
1819 o.z = false; |
|
1820 o.dy += rect.y + rect.dy - y; // rounding error |
|
1821 rect.x += v; |
|
1822 rect.dx -= v; |
|
1823 } |
|
1824 } |
|
1825 |
|
1826 function treemap(d) { |
|
1827 var nodes = stickies || hierarchy(d), |
|
1828 root = nodes[0]; |
|
1829 root.x = 0; |
|
1830 root.y = 0; |
|
1831 root.dx = size[0]; |
|
1832 root.dy = size[1]; |
|
1833 if (stickies) hierarchy.revalue(root); |
|
1834 scale([root], root.dx * root.dy / root.value); |
|
1835 (stickies ? stickify : squarify)(root); |
|
1836 if (sticky) stickies = nodes; |
|
1837 return nodes; |
|
1838 } |
|
1839 |
|
1840 treemap.size = function(x) { |
|
1841 if (!arguments.length) return size; |
|
1842 size = x; |
|
1843 return treemap; |
|
1844 }; |
|
1845 |
|
1846 treemap.padding = function(x) { |
|
1847 if (!arguments.length) return padding; |
|
1848 |
|
1849 function padFunction(node) { |
|
1850 var p = x.call(treemap, node, node.depth); |
|
1851 return p == null |
|
1852 ? d3_layout_treemapPadNull(node) |
|
1853 : d3_layout_treemapPad(node, typeof p === "number" ? [p, p, p, p] : p); |
|
1854 } |
|
1855 |
|
1856 function padConstant(node) { |
|
1857 return d3_layout_treemapPad(node, x); |
|
1858 } |
|
1859 |
|
1860 var type; |
|
1861 pad = (padding = x) == null ? d3_layout_treemapPadNull |
|
1862 : (type = typeof x) === "function" ? padFunction |
|
1863 : type === "number" ? (x = [x, x, x, x], padConstant) |
|
1864 : padConstant; |
|
1865 return treemap; |
|
1866 }; |
|
1867 |
|
1868 treemap.round = function(x) { |
|
1869 if (!arguments.length) return round != Number; |
|
1870 round = x ? Math.round : Number; |
|
1871 return treemap; |
|
1872 }; |
|
1873 |
|
1874 treemap.sticky = function(x) { |
|
1875 if (!arguments.length) return sticky; |
|
1876 sticky = x; |
|
1877 stickies = null; |
|
1878 return treemap; |
|
1879 }; |
|
1880 |
|
1881 treemap.ratio = function(x) { |
|
1882 if (!arguments.length) return ratio; |
|
1883 ratio = x; |
|
1884 return treemap; |
|
1885 }; |
|
1886 |
|
1887 return d3_layout_hierarchyRebind(treemap, hierarchy); |
|
1888 }; |
|
1889 |
|
1890 function d3_layout_treemapPadNull(node) { |
|
1891 return {x: node.x, y: node.y, dx: node.dx, dy: node.dy}; |
|
1892 } |
|
1893 |
|
1894 function d3_layout_treemapPad(node, padding) { |
|
1895 var x = node.x + padding[3], |
|
1896 y = node.y + padding[0], |
|
1897 dx = node.dx - padding[1] - padding[3], |
|
1898 dy = node.dy - padding[0] - padding[2]; |
|
1899 if (dx < 0) { x += dx / 2; dx = 0; } |
|
1900 if (dy < 0) { y += dy / 2; dy = 0; } |
|
1901 return {x: x, y: y, dx: dx, dy: dy}; |
|
1902 } |
|
1903 })(); |