Skip to content

Commit

Permalink
Simplify subclassing of hierarchy layout.
Browse files Browse the repository at this point in the history
The subclasses can't use the same object as the parent class, because they are
functions. But, there's no reason to duplicate the code that rebinds the methods
onto the subclass.
  • Loading branch information
mbostock committed Jun 11, 2011
1 parent 953bebb commit d756caa
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 73 deletions.
61 changes: 25 additions & 36 deletions d3.layout.js
Expand Up @@ -529,17 +529,13 @@ d3.layout.partition = function() {
return nodes;
}

partition.sort = d3.rebind(partition, hierarchy.sort);
partition.children = d3.rebind(partition, hierarchy.children);
partition.value = d3.rebind(partition, hierarchy.value);

partition.size = function(x) {
if (!arguments.length) return size;
size = x;
return partition;
};

return partition;
return d3_layout_hierarchyRebind(partition, hierarchy);
};
d3.layout.pie = function() {
var value = Number,
Expand Down Expand Up @@ -1054,6 +1050,15 @@ d3.layout.hierarchy = function() {
return hierarchy;
}

// A method assignment helper for hierarchy subclasses.
function d3_layout_hierarchyRebind(object, hierarchy) {
object.sort = d3.rebind(object, hierarchy.sort);
object.children = d3.rebind(object, hierarchy.children);
object.links = d3_layout_hierarchyLinks;
object.value = d3.rebind(object, hierarchy.value);
return object;
}

function d3_layout_hierarchyChildren(d) {
return d.children;
}
Expand All @@ -1065,8 +1070,17 @@ function d3_layout_hierarchyValue(d) {
function d3_layout_hierarchySort(a, b) {
return b.value - a.value;
}

// Returns an array source+target objects for the specified nodes.
function d3_layout_hierarchyLinks(nodes) {
return d3.merge(nodes.map(function(parent) {
return (parent.children || []).map(function(child) {
return {source: parent, target: child};
});
}));
}
d3.layout.pack = function() {
var hierarchy = d3.layout.hierarchy(),
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort),
size = [1, 1];

function pack(d, i) {
Expand All @@ -1087,17 +1101,13 @@ d3.layout.pack = function() {
return nodes;
}

pack.sort = d3.rebind(pack, hierarchy.sort);
pack.children = d3.rebind(pack, hierarchy.children);
pack.value = d3.rebind(pack, hierarchy.value);

pack.size = function(x) {
if (!arguments.length) return size;
size = x;
return pack;
};

return pack.sort(d3_layout_packSort);
return d3_layout_hierarchyRebind(pack, hierarchy);
};

function d3_layout_packSort(a, b) {
Expand Down Expand Up @@ -1310,10 +1320,6 @@ d3.layout.cluster = function() {
return nodes;
}

cluster.sort = d3.rebind(cluster, hierarchy.sort);
cluster.children = d3.rebind(cluster, hierarchy.children);
cluster.links = d3_layout_treeLinks;

cluster.separation = function(x) {
if (!arguments.length) return separation;
separation = x;
Expand All @@ -1326,7 +1332,7 @@ d3.layout.cluster = function() {
return cluster;
};

return cluster;
return d3_layout_hierarchyRebind(cluster, hierarchy);
};

function d3_layout_clusterY(children) {
Expand Down Expand Up @@ -1465,7 +1471,7 @@ d3.layout.tree = function() {
deep = d3_layout_treeSearch(root, d3_layout_treeDeepest),
x0 = left.x - separation(left, right) / 2,
x1 = right.x + separation(right, left) / 2,
y1 = deep.depth;
y1 = deep.depth || 1;

// Clear temporary layout variables; transform x and y.
d3_layout_treeVisitAfter(root, function(node) {
Expand All @@ -1477,10 +1483,6 @@ d3.layout.tree = function() {
return nodes;
}

tree.sort = d3.rebind(tree, hierarchy.sort);
tree.children = d3.rebind(tree, hierarchy.children);
tree.links = d3_layout_treeLinks;

tree.separation = function(x) {
if (!arguments.length) return separation;
separation = x;
Expand All @@ -1493,18 +1495,9 @@ d3.layout.tree = function() {
return tree;
};

return tree;
return d3_layout_hierarchyRebind(tree, hierarchy);
};

// Returns an array source+target objects for the specified nodes.
function d3_layout_treeLinks(nodes) {
return d3.merge(nodes.map(function(parent) {
return (parent.children || []).map(function(child) {
return {source: parent, target: child};
});
}));
}

function d3_layout_treeSeparation(a, b) {
return a.parent == b.parent ? 1 : 2;
}
Expand Down Expand Up @@ -1740,10 +1733,6 @@ d3.layout.treemap = function() {
return nodes;
}

treemap.sort = d3.rebind(treemap, hierarchy.sort);
treemap.children = d3.rebind(treemap, hierarchy.children);
treemap.value = d3.rebind(treemap, hierarchy.value);

treemap.size = function(x) {
if (!arguments.length) return size;
size = x;
Expand All @@ -1769,6 +1758,6 @@ d3.layout.treemap = function() {
return treemap;
};

return treemap;
return d3_layout_hierarchyRebind(treemap, hierarchy);
};
})()
2 changes: 1 addition & 1 deletion d3.layout.min.js

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions examples/tree/tree-dynamic.html
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Node-Link Tree</title>
<script type="text/javascript" src="../../d3.js"></script>
<script type="text/javascript" src="../../d3.layout.js"></script>
<style type="text/css">

.node {
stroke: #fff;
stroke-width: 2px;
}

.link {
fill: none;
stroke: #000;
}

</style>
</head>
<body>
<script type="text/javascript">

var w = 960,
h = 500,
root = {},
data = [root],
tree = d3.layout.tree().size([w - 20, h - 20]),
diagonal = d3.svg.diagonal(),
duration = 750,
timer = setInterval(update, duration);

var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(10, 10)");

vis.selectAll("circle")
.data(tree(root))
.enter().append("svg:circle")
.attr("class", "node")
.attr("r", 3.5)
.attr("cx", x)
.attr("cy", y);

function update() {
if (data.length >= 500) return clearInterval(timer);

// Add a new datum to a random parent.
var d = {id: data.length}, parent = data[~~(Math.random() * data.length)];
if (parent.children) parent.children.push(d); else parent.children = [d];
data.push(d);

// Compute the new tree layout. We'll stash the old layout in the data.
var nodes = tree(root);

// Update the nodes…
var node = vis.selectAll("circle.node")
.data(nodes, nodeId);

// Enter any new nodes at the parent's previous position.
node.enter().append("svg:circle")
.attr("class", "node")
.attr("r", 3.5)
.attr("cx", function(d) { return d.parent.data.x0; })
.attr("cy", function(d) { return d.parent.data.y0; })
.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);

// Transition nodes to their new position.
node.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);

// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), linkId);

// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "circle")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: d.source.data.x0, y: d.source.data.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);

// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
}

function linkId(d) {
return d.source.data.id + "-" + d.target.data.id;
}

function nodeId(d) {
return d.data.id;
}

function x(d) {
return d.data.x0 = d.x;
}

function y(d) {
return d.data.y0 = d.y;
}

</script>
</body>
</html>
6 changes: 1 addition & 5 deletions src/layout/cluster.js
Expand Up @@ -39,10 +39,6 @@ d3.layout.cluster = function() {
return nodes;
}

cluster.sort = d3.rebind(cluster, hierarchy.sort);
cluster.children = d3.rebind(cluster, hierarchy.children);
cluster.links = d3_layout_treeLinks;

cluster.separation = function(x) {
if (!arguments.length) return separation;
separation = x;
Expand All @@ -55,7 +51,7 @@ d3.layout.cluster = function() {
return cluster;
};

return cluster;
return d3_layout_hierarchyRebind(cluster, hierarchy);
};

function d3_layout_clusterY(children) {
Expand Down
18 changes: 18 additions & 0 deletions src/layout/hierarchy.js
Expand Up @@ -78,6 +78,15 @@ d3.layout.hierarchy = function() {
return hierarchy;
}

// A method assignment helper for hierarchy subclasses.
function d3_layout_hierarchyRebind(object, hierarchy) {
object.sort = d3.rebind(object, hierarchy.sort);
object.children = d3.rebind(object, hierarchy.children);
object.links = d3_layout_hierarchyLinks;
object.value = d3.rebind(object, hierarchy.value);
return object;
}

function d3_layout_hierarchyChildren(d) {
return d.children;
}
Expand All @@ -89,3 +98,12 @@ function d3_layout_hierarchyValue(d) {
function d3_layout_hierarchySort(a, b) {
return b.value - a.value;
}

// Returns an array source+target objects for the specified nodes.
function d3_layout_hierarchyLinks(nodes) {
return d3.merge(nodes.map(function(parent) {
return (parent.children || []).map(function(child) {
return {source: parent, target: child};
});
}));
}
8 changes: 2 additions & 6 deletions src/layout/pack.js
@@ -1,5 +1,5 @@
d3.layout.pack = function() {
var hierarchy = d3.layout.hierarchy(),
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort),
size = [1, 1];

function pack(d, i) {
Expand All @@ -20,17 +20,13 @@ d3.layout.pack = function() {
return nodes;
}

pack.sort = d3.rebind(pack, hierarchy.sort);
pack.children = d3.rebind(pack, hierarchy.children);
pack.value = d3.rebind(pack, hierarchy.value);

pack.size = function(x) {
if (!arguments.length) return size;
size = x;
return pack;
};

return pack.sort(d3_layout_packSort);
return d3_layout_hierarchyRebind(pack, hierarchy);
};

function d3_layout_packSort(a, b) {
Expand Down
6 changes: 1 addition & 5 deletions src/layout/partition.js
Expand Up @@ -38,15 +38,11 @@ d3.layout.partition = function() {
return nodes;
}

partition.sort = d3.rebind(partition, hierarchy.sort);
partition.children = d3.rebind(partition, hierarchy.children);
partition.value = d3.rebind(partition, hierarchy.value);

partition.size = function(x) {
if (!arguments.length) return size;
size = x;
return partition;
};

return partition;
return d3_layout_hierarchyRebind(partition, hierarchy);
};

0 comments on commit d756caa

Please sign in to comment.