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