Add cluster (dendogram) layout.
Based on the Protovis version. The only difference is that I've dropped the `orient`, `innerRadius` and `outerRadius` properties so that the D3 version is more flexible.
This commit is contained in:
Родитель
9f71e4e211
Коммит
6ebe9e5779
1
Makefile
1
Makefile
|
@ -105,6 +105,7 @@ d3.layout.js: \
|
|||
src/layout/pie.js \
|
||||
src/layout/stack.js \
|
||||
src/layout/hierarchy.js \
|
||||
src/layout/cluster.js \
|
||||
src/layout/tree.js \
|
||||
src/layout/treemap.js \
|
||||
src/end.js
|
||||
|
|
109
d3.layout.js
109
d3.layout.js
|
@ -739,6 +739,115 @@ function d3_layout_hierarchyValue(d) {
|
|||
function d3_layout_hierarchySort(a, b) {
|
||||
return b.value - a.value;
|
||||
}
|
||||
// Implements a hierarchical layout using the cluster (or dendogram) algorithm.
|
||||
d3.layout.cluster = function() {
|
||||
var hierarchy = d3.layout.hierarchy(),
|
||||
group = 0;
|
||||
|
||||
function cluster(d, i) {
|
||||
var nodes = hierarchy.call(this, d, i),
|
||||
root = nodes[0],
|
||||
leafCount = 0,
|
||||
leafIndex = .5 - group / 2;
|
||||
|
||||
/* Count the leaf nodes and compute the depth of descendants. */
|
||||
var p = undefined;
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
if (n.children) {
|
||||
n.depth = 1 + d3.max(n.children, function(n) { return n.depth; });
|
||||
} else {
|
||||
if (group && (p != n.parent)) {
|
||||
p = n.parent;
|
||||
leafCount += group;
|
||||
}
|
||||
leafCount++;
|
||||
n.depth = 0;
|
||||
}
|
||||
});
|
||||
var breadth = 1 / leafCount;
|
||||
var depth = 1 / root.depth;
|
||||
|
||||
/* Compute the unit breadth and depth of each node. */
|
||||
var p = undefined;
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
if (n.children) {
|
||||
n.breadth = d3_layout_clusterMean(n.children, function(n) { return n.breadth; });
|
||||
} else {
|
||||
if (group && (p != n.parent)) {
|
||||
p = n.parent;
|
||||
leafIndex += group;
|
||||
}
|
||||
n.breadth = breadth * leafIndex++;
|
||||
}
|
||||
n.depth = 1 - n.depth * depth;
|
||||
});
|
||||
|
||||
/* Compute breadth and depth ranges for space-filling layouts. */
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
n.minBreadth = n.children
|
||||
? n.children[0].minBreadth
|
||||
: (n.breadth - breadth / 2);
|
||||
n.maxBreadth = n.children
|
||||
? n.children[n.children.length - 1].maxBreadth
|
||||
: (n.breadth + breadth / 2);
|
||||
});
|
||||
d3_layout_clusterVisitBefore(root, function(n) {
|
||||
n.minDepth = n.parent
|
||||
? n.parent.maxDepth
|
||||
: 0;
|
||||
n.maxDepth = n.parent
|
||||
? (n.depth + root.depth)
|
||||
: (n.minDepth + 2 * root.depth);
|
||||
});
|
||||
root.minDepth = -depth;
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
cluster.sort = d3.rebind(cluster, hierarchy.sort);
|
||||
cluster.children = d3.rebind(cluster, hierarchy.children);
|
||||
cluster.value = d3.rebind(cluster, hierarchy.value);
|
||||
|
||||
cluster.group = function(x) {
|
||||
if (!arguments.length) return group;
|
||||
group = x;
|
||||
return cluster;
|
||||
};
|
||||
|
||||
return cluster;
|
||||
};
|
||||
|
||||
d3_layout_clusterVisitAfter = d3_layout_treeVisitAfter;
|
||||
|
||||
function d3_layout_clusterVisitBefore(node, callback) {
|
||||
function visit(node, previousSibling) {
|
||||
callback(node, previousSibling);
|
||||
var children = node.children;
|
||||
if (children) {
|
||||
var child,
|
||||
previousChild = null,
|
||||
i = -1,
|
||||
n = children.length;
|
||||
while (++i < n) {
|
||||
child = children[i];
|
||||
visit(child, previousChild);
|
||||
previousChild = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(node, null);
|
||||
}
|
||||
|
||||
function d3_layout_clusterSum(array, f) {
|
||||
var o = {};
|
||||
return array.reduce(f
|
||||
? function(p, d, i) { o.index = i; return p + f.call(o, d); }
|
||||
: function(p, d) { return p + d; }, 0);
|
||||
}
|
||||
|
||||
function d3_layout_clusterMean(array, f) {
|
||||
return d3_layout_clusterSum(array, f) / array.length;
|
||||
}
|
||||
// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
|
||||
d3.layout.tree = function() {
|
||||
var hierarchy = d3.layout.hierarchy(),
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>Flare Dendogram</title>
|
||||
<script type="text/javascript" src="../../d3.js"></script>
|
||||
<script type="text/javascript" src="../../d3.layout.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="cluster.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart"></div>
|
||||
<script type="text/javascript" src="cluster-radial.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,65 @@
|
|||
var w = 800,
|
||||
h = 800,
|
||||
m = 120;
|
||||
|
||||
var cluster = d3.layout.cluster()
|
||||
.sort(null)
|
||||
.group(true)
|
||||
.children(function(d) { return isNaN(d.value) ? d3.entries(d.value) : null; });
|
||||
|
||||
var vis = d3.select("#chart").append("svg:svg")
|
||||
.attr("width", w + 2 * m)
|
||||
.attr("height", h + 2 * m)
|
||||
.append("svg:g")
|
||||
.attr("transform", "translate(" + (w / 2 + m) + "," + (h / 2 + m) +")");
|
||||
|
||||
d3.json("flare.json", function(json) {
|
||||
var nodes = cluster(d3.entries(json)[0]);
|
||||
|
||||
var link = vis.selectAll("g.link")
|
||||
.data(nodes)
|
||||
.enter().append("svg:g")
|
||||
.attr("class", "link")
|
||||
.selectAll("line")
|
||||
.data(children)
|
||||
.enter();
|
||||
|
||||
link.append("svg:line")
|
||||
.attr("x1", function(d) { return x(d.parent); })
|
||||
.attr("y1", function(d) { return y(d.parent); })
|
||||
.attr("x2", function(d) { return x(d.child); })
|
||||
.attr("y2", function(d) { return y(d.child); });
|
||||
|
||||
var node = vis.selectAll("g.node")
|
||||
.data(nodes)
|
||||
.enter().append("svg:g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")rotate(" + (a(d)-90) + ")"; })
|
||||
|
||||
node.append("svg:circle")
|
||||
.attr("r", 4.5);
|
||||
|
||||
node.append("svg:text")
|
||||
.attr("dx", function(d) { return a(d) < 180 ? 8 : -8; })
|
||||
.attr("dy", ".31em")
|
||||
.attr("text-anchor", function(d) { return a(d) < 180 ? "start" : "end"; })
|
||||
.attr("transform", function(d) { return a(d) < 180 ? null : "rotate(180)"; })
|
||||
//.attr("transform", function(d) { return "rotate(" + (a(d.breadth) - 90) + ")"; })
|
||||
.text(function(d) { return d.data.key; });
|
||||
|
||||
// Returns parent+child objects for any children of `d`.
|
||||
function children(d) {
|
||||
return (d.children || []).map(function(v) {
|
||||
return {
|
||||
parent: d,
|
||||
child: v
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Radial scales for x and y.
|
||||
function a(d) { return d.breadth * 360; }
|
||||
function r(d) { return d.depth * w / 2; }
|
||||
function x(d) { return r(d) * Math.cos((a(d) - 90) / 180 * Math.PI); }
|
||||
function y(d) { return r(d) * Math.sin((a(d) - 90) / 180 * Math.PI); }
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
.node circle {
|
||||
fill: #fff;
|
||||
stroke: steelblue;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.node {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
.link {
|
||||
stroke: #ccc;
|
||||
stroke-width: 1.5px;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>Flare Dendogram</title>
|
||||
<script type="text/javascript" src="../../d3.js"></script>
|
||||
<script type="text/javascript" src="../../d3.layout.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="cluster.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart"></div>
|
||||
<script type="text/javascript" src="cluster.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
var w = 200,
|
||||
h = 2200,
|
||||
x = d3.scale.linear().range([0, w]),
|
||||
y = d3.scale.linear().range([0, h]);
|
||||
|
||||
var cluster = d3.layout.cluster()
|
||||
.sort(null)
|
||||
.group(true)
|
||||
.children(function(d) { return isNaN(d.value) ? d3.entries(d.value) : null; });
|
||||
|
||||
var vis = d3.select("#chart").append("svg:svg")
|
||||
.attr("width", w + 40 + 120)
|
||||
.attr("height", h)
|
||||
.append("svg:g")
|
||||
.attr("transform", "translate(40, 0)");
|
||||
|
||||
d3.json("flare.json", function(json) {
|
||||
var nodes = cluster(d3.entries(json)[0]);
|
||||
|
||||
var link = vis.selectAll("g.link")
|
||||
.data(nodes)
|
||||
.enter().append("svg:g")
|
||||
.attr("class", "link")
|
||||
.selectAll("line")
|
||||
.data(children)
|
||||
.enter();
|
||||
|
||||
link.append("svg:line")
|
||||
.attr("x1", function(d) { return x(d.parent.depth); })
|
||||
.attr("y1", function(d) { return y(d.parent.breadth); })
|
||||
.attr("x2", function(d) { return x(d.parent.depth); })
|
||||
.attr("y2", function(d) { return y(d.child.breadth); });
|
||||
link.append("svg:line")
|
||||
.attr("x1", function(d) { return x(d.parent.depth); })
|
||||
.attr("y1", function(d) { return y(d.child.breadth); })
|
||||
.attr("x2", function(d) { return x(d.child.depth); })
|
||||
.attr("y2", function(d) { return y(d.child.breadth); });
|
||||
|
||||
var node = vis.selectAll("g.node")
|
||||
.data(nodes)
|
||||
.enter().append("svg:g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function(d) { return "translate(" + x(d.depth) + "," + y(d.breadth) + ")"; })
|
||||
|
||||
node.append("svg:circle")
|
||||
.attr("r", 4.5);
|
||||
|
||||
node.append("svg:text")
|
||||
.attr("dx", function(d) { return d.children ? -8 : 8; })
|
||||
.attr("dy", 3)
|
||||
.attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
|
||||
.text(function(d) { return d.data.key; });
|
||||
|
||||
// Returns parent+child objects for any children of `d`.
|
||||
function children(d) {
|
||||
return (d.children || []).map(function(v) {
|
||||
return {
|
||||
parent: d,
|
||||
child: v
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,286 @@
|
|||
{
|
||||
"flare": {
|
||||
"analytics": {
|
||||
"cluster": {
|
||||
"AgglomerativeCluster": 3938,
|
||||
"CommunityStructure": 3812,
|
||||
"HierarchicalCluster": 6714,
|
||||
"MergeEdge": 743
|
||||
},
|
||||
"graph": {
|
||||
"BetweennessCentrality": 3534,
|
||||
"LinkDistance": 5731,
|
||||
"MaxFlowMinCut": 7840,
|
||||
"ShortestPaths": 5914,
|
||||
"SpanningTree": 3416
|
||||
},
|
||||
"optimization": {
|
||||
"AspectRatioBanker": 7074
|
||||
}
|
||||
},
|
||||
"animate": {
|
||||
"Easing": 17010,
|
||||
"FunctionSequence": 5842,
|
||||
"interpolate": {
|
||||
"ArrayInterpolator": 1983,
|
||||
"ColorInterpolator": 2047,
|
||||
"DateInterpolator": 1375,
|
||||
"Interpolator": 8746,
|
||||
"MatrixInterpolator": 2202,
|
||||
"NumberInterpolator": 1382,
|
||||
"ObjectInterpolator": 1629,
|
||||
"PointInterpolator": 1675,
|
||||
"RectangleInterpolator": 2042
|
||||
},
|
||||
"ISchedulable": 1041,
|
||||
"Parallel": 5176,
|
||||
"Pause": 449,
|
||||
"Scheduler": 5593,
|
||||
"Sequence": 5534,
|
||||
"Transition": 9201,
|
||||
"Transitioner": 19975,
|
||||
"TransitionEvent": 1116,
|
||||
"Tween": 6006
|
||||
},
|
||||
"data": {
|
||||
"converters": {
|
||||
"Converters": 721,
|
||||
"DelimitedTextConverter": 4294,
|
||||
"GraphMLConverter": 9800,
|
||||
"IDataConverter": 1314,
|
||||
"JSONConverter": 2220
|
||||
},
|
||||
"DataField": 1759,
|
||||
"DataSchema": 2165,
|
||||
"DataSet": 586,
|
||||
"DataSource": 3331,
|
||||
"DataTable": 772,
|
||||
"DataUtil": 3322
|
||||
},
|
||||
"display": {
|
||||
"DirtySprite": 8833,
|
||||
"LineSprite": 1732,
|
||||
"RectSprite": 3623,
|
||||
"TextSprite": 10066
|
||||
},
|
||||
"flex": {
|
||||
"FlareVis": 4116
|
||||
},
|
||||
"physics": {
|
||||
"DragForce": 1082,
|
||||
"GravityForce": 1336,
|
||||
"IForce": 319,
|
||||
"NBodyForce": 10498,
|
||||
"Particle": 2822,
|
||||
"Simulation": 9983,
|
||||
"Spring": 2213,
|
||||
"SpringForce": 1681
|
||||
},
|
||||
"query": {
|
||||
"AggregateExpression": 1616,
|
||||
"And": 1027,
|
||||
"Arithmetic": 3891,
|
||||
"Average": 891,
|
||||
"BinaryExpression": 2893,
|
||||
"Comparison": 5103,
|
||||
"CompositeExpression": 3677,
|
||||
"Count": 781,
|
||||
"DateUtil": 4141,
|
||||
"Distinct": 933,
|
||||
"Expression": 5130,
|
||||
"ExpressionIterator": 3617,
|
||||
"Fn": 3240,
|
||||
"If": 2732,
|
||||
"IsA": 2039,
|
||||
"Literal": 1214,
|
||||
"Match": 3748,
|
||||
"Maximum": 843,
|
||||
"methods": {
|
||||
"add": 593,
|
||||
"and": 330,
|
||||
"average": 287,
|
||||
"count": 277,
|
||||
"distinct": 292,
|
||||
"div": 595,
|
||||
"eq": 594,
|
||||
"fn": 460,
|
||||
"gt": 603,
|
||||
"gte": 625,
|
||||
"iff": 748,
|
||||
"isa": 461,
|
||||
"lt": 597,
|
||||
"lte": 619,
|
||||
"max": 283,
|
||||
"min": 283,
|
||||
"mod": 591,
|
||||
"mul": 603,
|
||||
"neq": 599,
|
||||
"not": 386,
|
||||
"or": 323,
|
||||
"orderby": 307,
|
||||
"range": 772,
|
||||
"select": 296,
|
||||
"stddev": 363,
|
||||
"sub": 600,
|
||||
"sum": 280,
|
||||
"update": 307,
|
||||
"variance": 335,
|
||||
"where": 299,
|
||||
"xor": 354,
|
||||
"_": 264
|
||||
},
|
||||
"Minimum": 843,
|
||||
"Not": 1554,
|
||||
"Or": 970,
|
||||
"Query": 13896,
|
||||
"Range": 1594,
|
||||
"StringUtil": 4130,
|
||||
"Sum": 791,
|
||||
"Variable": 1124,
|
||||
"Variance": 1876,
|
||||
"Xor": 1101
|
||||
},
|
||||
"scale": {
|
||||
"IScaleMap": 2105,
|
||||
"LinearScale": 1316,
|
||||
"LogScale": 3151,
|
||||
"OrdinalScale": 3770,
|
||||
"QuantileScale": 2435,
|
||||
"QuantitativeScale": 4839,
|
||||
"RootScale": 1756,
|
||||
"Scale": 4268,
|
||||
"ScaleType": 1821,
|
||||
"TimeScale": 5833
|
||||
},
|
||||
"util": {
|
||||
"Arrays": 8258,
|
||||
"Colors": 10001,
|
||||
"Dates": 8217,
|
||||
"Displays": 12555,
|
||||
"Filter": 2324,
|
||||
"Geometry": 10993,
|
||||
"heap": {
|
||||
"FibonacciHeap": 9354,
|
||||
"HeapNode": 1233
|
||||
},
|
||||
"IEvaluable": 335,
|
||||
"IPredicate": 383,
|
||||
"IValueProxy": 874,
|
||||
"math": {
|
||||
"DenseMatrix": 3165,
|
||||
"IMatrix": 2815,
|
||||
"SparseMatrix": 3366
|
||||
},
|
||||
"Maths": 17705,
|
||||
"Orientation": 1486,
|
||||
"palette": {
|
||||
"ColorPalette": 6367,
|
||||
"Palette": 1229,
|
||||
"ShapePalette": 2059,
|
||||
"SizePalette": 2291
|
||||
},
|
||||
"Property": 5559,
|
||||
"Shapes": 19118,
|
||||
"Sort": 6887,
|
||||
"Stats": 6557,
|
||||
"Strings": 22026
|
||||
},
|
||||
"vis": {
|
||||
"axis": {
|
||||
"Axes": 1302,
|
||||
"Axis": 24593,
|
||||
"AxisGridLine": 652,
|
||||
"AxisLabel": 636,
|
||||
"CartesianAxes": 6703
|
||||
},
|
||||
"controls": {
|
||||
"AnchorControl": 2138,
|
||||
"ClickControl": 3824,
|
||||
"Control": 1353,
|
||||
"ControlList": 4665,
|
||||
"DragControl": 2649,
|
||||
"ExpandControl": 2832,
|
||||
"HoverControl": 4896,
|
||||
"IControl": 763,
|
||||
"PanZoomControl": 5222,
|
||||
"SelectionControl": 7862,
|
||||
"TooltipControl": 8435
|
||||
},
|
||||
"data": {
|
||||
"Data": 20544,
|
||||
"DataList": 19788,
|
||||
"DataSprite": 10349,
|
||||
"EdgeSprite": 3301,
|
||||
"NodeSprite": 19382,
|
||||
"render": {
|
||||
"ArrowType": 698,
|
||||
"EdgeRenderer": 5569,
|
||||
"IRenderer": 353,
|
||||
"ShapeRenderer": 2247
|
||||
},
|
||||
"ScaleBinding": 11275,
|
||||
"Tree": 7147,
|
||||
"TreeBuilder": 9930
|
||||
},
|
||||
"events": {
|
||||
"DataEvent": 2313,
|
||||
"SelectionEvent": 1880,
|
||||
"TooltipEvent": 1701,
|
||||
"VisualizationEvent": 1117
|
||||
},
|
||||
"legend": {
|
||||
"Legend": 20859,
|
||||
"LegendItem": 4614,
|
||||
"LegendRange": 10530
|
||||
},
|
||||
"operator": {
|
||||
"distortion": {
|
||||
"BifocalDistortion": 4461,
|
||||
"Distortion": 6314,
|
||||
"FisheyeDistortion": 3444
|
||||
},
|
||||
"encoder": {
|
||||
"ColorEncoder": 3179,
|
||||
"Encoder": 4060,
|
||||
"PropertyEncoder": 4138,
|
||||
"ShapeEncoder": 1690,
|
||||
"SizeEncoder": 1830
|
||||
},
|
||||
"filter": {
|
||||
"FisheyeTreeFilter": 5219,
|
||||
"GraphDistanceFilter": 3165,
|
||||
"VisibilityFilter": 3509
|
||||
},
|
||||
"IOperator": 1286,
|
||||
"label": {
|
||||
"Labeler": 9956,
|
||||
"RadialLabeler": 3899,
|
||||
"StackedAreaLabeler": 3202
|
||||
},
|
||||
"layout": {
|
||||
"AxisLayout": 6725,
|
||||
"BundledEdgeRouter": 3727,
|
||||
"CircleLayout": 9317,
|
||||
"CirclePackingLayout": 12003,
|
||||
"DendrogramLayout": 4853,
|
||||
"ForceDirectedLayout": 8411,
|
||||
"IcicleTreeLayout": 4864,
|
||||
"IndentedTreeLayout": 3174,
|
||||
"Layout": 7881,
|
||||
"NodeLinkTreeLayout": 12870,
|
||||
"PieLayout": 2728,
|
||||
"RadialTreeLayout": 12348,
|
||||
"RandomLayout": 870,
|
||||
"StackedAreaLayout": 9121,
|
||||
"TreeMapLayout": 9191
|
||||
},
|
||||
"Operator": 2490,
|
||||
"OperatorList": 5248,
|
||||
"OperatorSequence": 4190,
|
||||
"OperatorSwitch": 2581,
|
||||
"SortOperator": 2023
|
||||
},
|
||||
"Visualization": 16540
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
<li><a href="chord/chord.html">chord</a></li>
|
||||
<li><a href="choropleth/choropleth.html">choropleth</a></li>
|
||||
<li><a href="contour/contour.html">contour</a></li>
|
||||
<li><a href="cluster/cluster.html">cluster</a></li>
|
||||
<li><a href="delaunay/delaunay.html">delaunay</a></li>
|
||||
<li><a href="donut/donut.html">donut</a></li>
|
||||
<li><a href="dot/dot.html">dot</a></li>
|
||||
|
|
|
@ -41,7 +41,7 @@ d3.json("flare.json", function(json) {
|
|||
.attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
|
||||
.attr("dy", ".31em")
|
||||
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
|
||||
.attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
|
||||
.attr("transform", function(d) { return (d.x < 180) ^ (d.children) ? null : "rotate(180)"; })
|
||||
.text(function(d) { return d.data.key; });
|
||||
|
||||
// Returns parent+child objects for any children of `d`.
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// Implements a hierarchical layout using the cluster (or dendogram) algorithm.
|
||||
d3.layout.cluster = function() {
|
||||
var hierarchy = d3.layout.hierarchy(),
|
||||
group = 0;
|
||||
|
||||
function cluster(d, i) {
|
||||
var nodes = hierarchy.call(this, d, i),
|
||||
root = nodes[0],
|
||||
leafCount = 0,
|
||||
leafIndex = .5 - group / 2;
|
||||
|
||||
/* Count the leaf nodes and compute the depth of descendants. */
|
||||
var p = undefined;
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
if (n.children) {
|
||||
n.depth = 1 + d3.max(n.children, function(n) { return n.depth; });
|
||||
} else {
|
||||
if (group && (p != n.parent)) {
|
||||
p = n.parent;
|
||||
leafCount += group;
|
||||
}
|
||||
leafCount++;
|
||||
n.depth = 0;
|
||||
}
|
||||
});
|
||||
var breadth = 1 / leafCount;
|
||||
var depth = 1 / root.depth;
|
||||
|
||||
/* Compute the unit breadth and depth of each node. */
|
||||
var p = undefined;
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
if (n.children) {
|
||||
n.breadth = d3_layout_clusterMean(n.children, function(n) { return n.breadth; });
|
||||
} else {
|
||||
if (group && (p != n.parent)) {
|
||||
p = n.parent;
|
||||
leafIndex += group;
|
||||
}
|
||||
n.breadth = breadth * leafIndex++;
|
||||
}
|
||||
n.depth = 1 - n.depth * depth;
|
||||
});
|
||||
|
||||
/* Compute breadth and depth ranges for space-filling layouts. */
|
||||
d3_layout_clusterVisitAfter(root, function(n) {
|
||||
n.minBreadth = n.children
|
||||
? n.children[0].minBreadth
|
||||
: (n.breadth - breadth / 2);
|
||||
n.maxBreadth = n.children
|
||||
? n.children[n.children.length - 1].maxBreadth
|
||||
: (n.breadth + breadth / 2);
|
||||
});
|
||||
d3_layout_clusterVisitBefore(root, function(n) {
|
||||
n.minDepth = n.parent
|
||||
? n.parent.maxDepth
|
||||
: 0;
|
||||
n.maxDepth = n.parent
|
||||
? (n.depth + root.depth)
|
||||
: (n.minDepth + 2 * root.depth);
|
||||
});
|
||||
root.minDepth = -depth;
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
cluster.sort = d3.rebind(cluster, hierarchy.sort);
|
||||
cluster.children = d3.rebind(cluster, hierarchy.children);
|
||||
cluster.value = d3.rebind(cluster, hierarchy.value);
|
||||
|
||||
cluster.group = function(x) {
|
||||
if (!arguments.length) return group;
|
||||
group = x;
|
||||
return cluster;
|
||||
};
|
||||
|
||||
return cluster;
|
||||
};
|
||||
|
||||
d3_layout_clusterVisitAfter = d3_layout_treeVisitAfter;
|
||||
|
||||
function d3_layout_clusterVisitBefore(node, callback) {
|
||||
function visit(node, previousSibling) {
|
||||
callback(node, previousSibling);
|
||||
var children = node.children;
|
||||
if (children) {
|
||||
var child,
|
||||
previousChild = null,
|
||||
i = -1,
|
||||
n = children.length;
|
||||
while (++i < n) {
|
||||
child = children[i];
|
||||
visit(child, previousChild);
|
||||
previousChild = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(node, null);
|
||||
}
|
||||
|
||||
function d3_layout_clusterSum(array, f) {
|
||||
var o = {};
|
||||
return array.reduce(f
|
||||
? function(p, d, i) { o.index = i; return p + f.call(o, d); }
|
||||
: function(p, d) { return p + d; }, 0);
|
||||
}
|
||||
|
||||
function d3_layout_clusterMean(array, f) {
|
||||
return d3_layout_clusterSum(array, f) / array.length;
|
||||
}
|
Загрузка…
Ссылка в новой задаче