function defaultSeparation(a, b) { return a.parent === b.parent ? 1 : 2; } function meanX(children) { return children.reduce(meanXReduce, 0) / children.length; } function meanXReduce(x, c) { return x + c.x; } function maxY(children) { return 1 + children.reduce(maxYReduce, 0); } function maxYReduce(y, c) { return Math.max(y, c.y); } function leafLeft(node) { var children; while (children = node.children) node = children[0]; return node; } function leafRight(node) { var children; while (children = node.children) node = children[children.length - 1]; return node; } export default function() { var separation = defaultSeparation, dx = 1, dy = 1, nodeSize = false; function cluster(root) { var previousNode, x = 0; // First walk, computing the initial x & y values. root.eachAfter(function(node) { var children = node.children; if (children) { node.x = meanX(children); node.y = maxY(children); } else { node.x = previousNode ? x += separation(node, previousNode) : 0; node.y = 0; previousNode = node; } }); var left = leafLeft(root), right = leafRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; // Second walk, normalizing x & y to the desired size. return root.eachAfter(nodeSize ? function(node) { node.x = (node.x - root.x) * dx; node.y = (root.y - node.y) * dy; } : function(node) { node.x = (node.x - x0) / (x1 - x0) * dx; node.y = (1 - (root.y ? node.y / root.y : 1)) * dy; }); } cluster.separation = function(x) { return arguments.length ? (separation = x, cluster) : separation; }; cluster.size = function(x) { return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]); }; cluster.nodeSize = function(x) { return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null); }; return cluster; }