Skip to content

Commit

Permalink
Remove d3.hierarchyTopDown.
Browse files Browse the repository at this point in the history
Instead, the hierarchy layouts now take simple hierarchical input data as they
did in D3 3.x, and d3.hierarchy now takes tabular input data and converts it
into a simple hierarchy. This avoids having the hierarchical layouts mutate
their inputs. (I’ve omitted the children accessor from the hierarchy layouts,
but I might bring it back.)
  • Loading branch information
mbostock committed Mar 24, 2016
1 parent 5938e09 commit 4d87612
Show file tree
Hide file tree
Showing 30 changed files with 528 additions and 589 deletions.
106 changes: 50 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,21 @@ If you use NPM, `npm install d3-hierarchy`. Otherwise, download the [latest rele

### Hierarchies

Before you can compute a hierarchical layout, you need a hierarchical data structure: a [root node](#hierarchy-nodes). If you have tabular data as input, such as a CSV file, use [d3.hierarchyBottomUp](#hierarchyBottomUp) to organize the rows into a hierarchy. If you already have hierarchical data, such as a JSON file, use [d3.hierarchyTopDown](#hierarchyTopDown) to derive the corresponding hierarchy. You can also construct [hierarchy nodes](#hierarchy-nodes) manually.
Before you can compute a hierarchical layout, you need a hierarchical data structure: a root node. If you already have hierarchical data, such as a JSON file, you can pass it directly to the hierarchical layout. Otherwise, if you have tabular input data such as a CSV file, use [d3.hierarchy](#hierarchy) to organize the rows into a hierarchy.

<a name="hierarchyBottomUp" href="#hierarchyBottomUp">#</a> d3.<b>hierarchyBottomUp</b>()
<a name="hierarchy" href="#hierarchy">#</a> d3.<b>hierarchy</b>()


<a name="_bottomUp" href="#_bottomUp">#</a> <i>bottomUp</i>(<i>data</i>)

… Returns a root [*node*](#hierarchy-nodes).

<a name="bottomUp_id" href="#bottomUp_id">#</a> <i>bottomUp</i>.<b>id</b>([<i>function</i>])


<a name="bottomUp_parentId" href="#bottomUp_parentId">#</a> <i>bottomUp</i>.<b>parentId</b>([<i>function</i>])
<a name="_hierarchy" href="#_hierarchy">#</a> <i>hierarchy</i>(<i>data</i>)


<a name="hierarchyTopDown" href="#hierarchyTopDown">#</a> d3.<b>hierarchyTopDown</b>()
<a name="hierarchy_id" href="#hierarchy_id">#</a> <i>hierarchy</i>.<b>id</b>([<i>function</i>])


<a name="_topDown" href="#_topDown">#</a> <i>topDown</i>(<i>data</i>)

… Returns a root [*node*](#hierarchy-nodes).

<a name="topDown_id" href="#topDown_id">#</a> <i>topDown</i>.<b>children</b>([<i>function</i>])
<a name="hierarchy_parentId" href="#hierarchy_parentId">#</a> <i>bottomUp</i>.<b>parentId</b>([<i>function</i>])


Expand All @@ -48,55 +36,69 @@ Before you can compute a hierarchical layout, you need a hierarchical data struc

<a name="hierarchyNode" href="#hierarchyNode">#</a> d3.<b>hierarchyNode</b>(<i>data</i>)

Constructs a root hierarchy node from the specified hierarchical *data*. The specified *data* must be an object representing the root node, and may have a *data*.children property specifying an array of data representing the children of the root node; each descendant child *data* may also have *data*.children. For example:

```json
{
"name": "Eve",
"children": [
{
"name": "Cain"
},
{
"name": "Seth",
"children": [
{
"name": "Enos",
},
{
"name": "Noam"
}
]
},
{
"name": "Abel"
},
{
"name": "Awan",
"children": [
{
"name": "Enoch"
}
]
},
{
"name": "Azura"
}
]
}
```

This method is typically not called directly; instead it is used by hierarchical layouts such as [d3.treemap](#treemap) to initialize root nodes.

<a name="node_data" href="#node_data">#</a> <i>node</i>.<b>data</b>


<a name="node_value" href="#node_value">#</a> <i>node</i>.<b>value</b>

A reference to the data associated with this node, as specified to the [constructor](#hierarchyNode).

<a name="node_parent" href="#node_parent">#</a> <i>node</i>.<b>parent</b>

A reference to the parent node; null for the root node.

<a name="node_children" href="#node_children">#</a> <i>node</i>.<b>children</b>


<a name="node_each" href="#node_each">#</a> <i>node</i>.<b>each</b>(<i>function</i>)


<a name="node_eachBefore" href="#node_eachBefore">#</a> <i>node</i>.<b>eachBefore</b>(<i>function</i>)


<a name="node_eachAfter" href="#node_eachAfter">#</a> <i>node</i>.<b>eachAfter</b>(<i>function</i>)


<a name="node_revalue" href="#node_revalue">#</a> <i>node</i>.<b>revalue</b>(<i>function</i>)


<a name="node_sort" href="#node_sort">#</a> <i>node</i>.<b>sort</b>(<i>function</i>)

An array of child nodes, if any; undefined for leaf nodes.

<a name="node_ancestors" href="#node_ancestors">#</a> <i>node</i>.<b>ancestors</b>()

Returns the array of ancestors nodes, starting with this node, then followed by each parent up to the root.

<a name="node_descendants" href="#node_descendants">#</a> <i>node</i>.<b>descendants</b>()

Returns the array of descendant nodes, starting with this node, then followed by each child in topological order.

<a name="node_leaves" href="#node_leaves">#</a> <i>node</i>.<b>leaves</b>()

Returns the array of leaf nodes; a subset of this node’s [descendants](#node_descendants) including only nodes with no children.

### Treemapping

Expand Down Expand Up @@ -229,11 +231,3 @@ Before you can compute a hierarchical layout, you need a hierarchical data struc
<a name="pack_padding" href="#pack_padding">#</a> <i>pack</i>.<b>padding</b>([<i>padding</i>])


<a name="packCircles" href="#packCircles">#</a> d3.<b>packCircles</b>(<i>circles</i>)


<a name="packEnclosingCircle" href="#packEnclosingCircle">#</a> d3.<b>packEnclosingCircle</b>(<i>circles</i>)

5 changes: 1 addition & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
export {version} from "./build/package";
export {default as hierarchyBottomUp} from "./src/hierarchy/bottomUp";
export {default as hierarchyTopDown} from "./src/hierarchy/topDown";
export {default as hierarchy} from "./src/hierarchy";
export {default as hierarchyNode} from "./src/node/index";
export {default as pack} from "./src/pack/index";
export {default as packCircles} from "./src/pack/circles";
export {default as packEnclosingCircle} from "./src/pack/enclosingCircle";
export {default as partition} from "./src/partition/index";
export {default as treemap} from "./src/treemap/index";
export {default as treemapBinary} from "./src/treemap/binary";
Expand Down
File renamed without changes.
38 changes: 17 additions & 21 deletions src/hierarchy/bottomUp.js → src/hierarchy.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
import newNode from "../node/index";
import {required} from "./accessors";
import visitBefore from "./visitBefore";

var keyPrefix = "$"; // Protect against keys like “__proto__”.
var keyPrefix = "$", // Protect against keys like “__proto__”.
reserved = {id: 1, children: 1};

function defaultId(d) {
return d.id;
}

function defaultParentId(d) {
return d.parent;
return d.parentId;
}

export default function() {
var id = defaultId,
parentId = defaultParentId;

function hierarchy(data) {
var root,
var d,
k,
i,
n = data.length,
d,
root,
parent,
node,
nodes = new Array(n),
nodeId,
nodeKey,
nodeByKey = {},
nodes = new Array(n),
node,
parent;
nodeByKey = {};

for (i = 0; i < n; ++i) {
nodes[i] = node = newNode(d = data[i]), node.index = i;
d = data[i], node = nodes[i] = {};
for (k in d) if (!(k in reserved)) node[k] = d[k];
if ((nodeId = id(d, i, data)) != null) {
nodeKey = keyPrefix + (node.id = nodeId += "");
if (nodeKey in nodeByKey) throw new Error("duplicate: " + nodeId);
Expand All @@ -37,28 +40,21 @@ export default function() {
}

for (i = 0; i < n; ++i) {
node = nodes[i], nodeId = parentId(d = data[i], i, data);
node = nodes[i], nodeId = parentId(data[i], i, data);
if (nodeId == null) {
if (root) throw new Error("multiple roots");
root = node;
} else {
parent = nodeByKey[keyPrefix + nodeId];
if (!parent) throw new Error("missing: " + nodeId);
node.parent = parent;
if (parent.children) parent.children.push(node);
else parent.children = [node];
}
}

if (!root) throw new Error("cycle");

root.eachBefore(function(node) {
if (!nodes[node.index]) throw new Error("cycle");
node.depth = node.parent ? node.parent.depth + 1 : 0;
nodes[node.index] = null, --n;
});

if (n !== 0) throw new Error("cycle");
if (!root) throw new Error("no root");
visitBefore(root, function() { --n; });
if (n > 0) throw new Error("cycle");

return root;
}
Expand Down
41 changes: 0 additions & 41 deletions src/hierarchy/topDown.js

This file was deleted.

9 changes: 9 additions & 0 deletions src/hierarchySort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import visitBefore from "./visitBefore";

export default function(node, compare) {
visitBefore(node, function(node) {
if (node.children) {
node.children.sort(compare);
}
});
}
6 changes: 4 additions & 2 deletions src/node/revalue.js → src/hierarchyValue.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export default function(value) {
return this.eachAfter(function(node) {
import visitAfter from "./visitAfter";

export default function(node, value) {
visitAfter(node, function(node) {
var sum = +value(node.data) || 0,
children = node.children,
i = children && children.length;
Expand Down
6 changes: 4 additions & 2 deletions src/node/ancestors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export default function() {
var node = this, nodes = [this];
while (node = node.parent) nodes.push(node);
var node = this, nodes = [node];
while (node = node.parent) {
nodes.push(node);
}
return nodes;
}
6 changes: 5 additions & 1 deletion src/node/descendants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import visit from "../visit";

export default function() {
var nodes = [];
this.each(function(node) { nodes.push(node); });
visit(this, function(node) {
nodes.push(node);
});
return nodes;
}
35 changes: 23 additions & 12 deletions src/node/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
import node_ancestors from "./ancestors";
import node_descendants from "./descendants";
import node_leaves from "./leaves";
import node_each from "./each";
import node_eachAfter from "./eachAfter";
import node_eachBefore from "./eachBefore";
import node_revalue from "./revalue";
import node_sort from "./sort";

export default function node(data) {
return new Node(data);
var root = new Node(data),
node = root,
nodes = [root],
child,
children,
i,
n;

while ((node = nodes.pop()) != null) {
if ((children = node.data.children) && (n = children.length)) {
node.children = new Array(n);
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new Node(children[i]));
child.parent = node;
child.depth = node.depth + 1;
}
}
}

return root;
}

function Node(data) {
this.data = data;
this.depth = 0;
this.parent = null;
}

Node.prototype = node.prototype = {
constructor: Node,
ancestors: node_ancestors,
descendants: node_descendants,
leaves: node_leaves,
each: node_each,
eachAfter: node_eachAfter,
eachBefore: node_eachBefore,
revalue: node_revalue,
sort: node_sort
leaves: node_leaves
};
8 changes: 7 additions & 1 deletion src/node/leaves.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import visit from "../visit";

export default function() {
var leaves = [];
this.each(function(node) { if (!node.children) leaves.push(node); });
visit(this, function(node) {
if (!node.children) {
leaves.push(node);
}
});
return leaves;
}
Loading

0 comments on commit 4d87612

Please sign in to comment.