树布局 - intelligaia/d3 GitHub Wiki

WikiAPI--中文手册布局树布局

树布局使用Reingold–Tilford “tidy” algorithm算法生成整齐的链接点模型树状图。一个典型的例子:在软件的架构中,树布局可以用来组织软件的包及类的层级结构。

diagonal

像其他大多数布局一样,var tree = d3.layout.tree() 得到的即是一个对象也是一个函数;也就是说:可以像调用函数一样调用布局(tree()),也可以直接调用 tree 提供的一些 API 接口来改变其行为,如:```tree.children(Function),同样支持级联调用。

# d3.layout.tree()

创建一个树布局实例,使用默认的设置:

  • tree.sort = null;
  • tree.children = function(d) { return d.children; }, d.children 是一个数组;
  • tree.separation = function(a, b) { return a.parent == b.parent ? 1 : 2; }, 即:兄弟节点间是 1 倍距离、表亲兄弟间是 2 倍距离;(注:该 距离 是树布局基于当前 depth 层级计算出来基础间距;)

# tree(root)
# tree.nodes(root)

运行树布局,返回一个基于根结点 root 的所有结点的数组;树布局是 D3 层布局家族中的一部分,这些布局都遵循类似的基本结构:布局的输入参数是层次结构的根节点 root,输出值是一个经过计算的全部结点的位置的数组;每个节点中包含以下属性:

  • parent - 父节点引用,根节点为 null;
  • children - 所有子节点,叶子节点为 null;
  • depth - 当前节点所处层级(也叫深度);根节点为 0;
  • x - 计算得到的当前节点的计 x 坐标;
  • y - 计算得到的当前节点的计 y 坐标;

尽管树布局计算得到的 x 和 y 有 size 大小概念的味道,但是,这里的 x 和 y 依然可以理解为任意坐标系;举个例子说,把 x 看成是角度,把 y 看做是半径,这时 tree.size([360, 960]) 的设定就意味着,半径的取值范围是 0-960,角度的取值范围是 0-360,所以,树布局计算出来的每个节点的 x 和 y 实际表示的是径向的树结构(平面中的点可以是:直角坐标系的 [x, y] 或 极坐标系的 { θ, r })。

# tree.links(nodes)

根据给定的结点数组 nodes(比如:通过 tree.nodes(root) 返回的一组节点集),生成一组表示从父节点到子节点关系对象,叶子节点不包含该种“输出”关系;每个关系对象有两个属性:

  • source - 父节点的引用;
  • target - 子节点的引用;

该方法的设定目的是,用于检索一组有连接关系的节点集,并输出这种连接关系;通常会配合如diagonal(对角线形状生成器器)一起使用;如下:

svg.selectAll("path")
    .data(tree.links(nodes))
  .enter().append("path")
    .attr("d", d3.svg.diagonal());

# tree.children([children])

如果指定了 children 参数,则设置子节点访问器函数为 children;如果未指定,则返回当前的子节点访问器函数;默认的子节点访问器假定输入数据是一个有 children 的 KEY 的对象,形式如下:

function children(d) {
  return d.children;
}

【略过】Often, it is convenient to load the node hierarchy using d3.json, and represent the input hierarchy as a nested JSON object. For example:

{
 "name": "flare",
 "children": [
  {
   "name": "analytics",
   "children": [
    {
     "name": "cluster",
     "children": [
      {"name": "AgglomerativeCluster", "size": 3938},
      {"name": "CommunityStructure", "size": 3812},
      {"name": "MergeEdge", "size": 743}
     ]
    },
    {
     "name": "graph",
     "children": [
      {"name": "BetweennessCentrality", "size": 3534},
      {"name": "LinkDistance", "size": 5731}
     ]
    }
   ]
  }
 ]
}

访问器首先会在在层次结构的根节点被调用;如果访问器返回 null,则该节点会被认为是叶子节点,从而终止向下遍历;否则,访问器应返回一个包含所有子节点数据元素的数组。

# tree.separation([separation])

如果指定了 separation 参数,则设置相邻节点的间距计算器为 separation;如果未指定,则返回当前的间距计算器函数;默认的间距计算器函数形式如下:

function separation(a, b) {
  return a.parent == b.parent ? 1 : 2;
}

对于一些特殊的,如径向树图,差异化的间距值可以避免叶子节点间的锯齿感,因此,使用如下的间距计算器可以优化该问题:

function separation(a, b) {
  return (a.parent == b.parent ? 1 : 2) / a.depth;
}

间距计算器函数有两个入参:ab,该函数需要返回在这两个节点间的距离期望倍数ab 节点通常是相邻的兄弟;当然,也不排除树布局有意将两个表亲兄弟节点或更远的节点放置在“相邻”的位置。

注:上文提到的间距计算器函数的返回值被称做**倍数**,并不是来自原英文,而是译者结合测试、使用经验自译的;请参考原文。

# tree.size([size])

如果指定了 size 参数,则设置树布局可用的空间范围为指定的两元素数组 size;如果未指定,则返回当前的空间范围;默认是:[1, 1]size 的值可能被认为是:size[0] => x, size[1] => y;但是,这并不局限于平面直角坐标系,而适用于任意的坐标系,xy 只是一个标志位的作用;

举个例子说,把 x 看成是角度,把 y 看做是半径,这时 tree.size([360, 960]) 的设定就意味着,半径的取值范围是 0-960,角度的取值范围是 0-360,所以,树布局计算出来的每个节点的 x 和 y 实际表示的是径向的树结构(平面中的点可以是:直角坐标系的 [x, y] 或 极坐标系的 { θ, r })。

size 属性与 tree.nodeSize 不同时共存,设置了 size 就必须设置 nodeSize 为 null;

# tree.nodeSize([nodeSize])

如果指定了 nodeSize 参数,则为每个节点设置一个固定 size 的表示为 xy 的两元素数组;如果未指定,则返回当前的配置;默认值为 null,表示树布局使用整体配置的 tree.size 值来计算布局;同样,这里的所对应的 xy 也不局限于特定的坐标系。

nodeSize 属性与 tree.size 不同时共存,设置了 nodeSize 就必须设置 size 为 null;

# tree.sort([comparator])

如果指定了 comparator 参数,则启用排序算法并设置排序的比较器为 comparator 函数;如果未指定,则返回当前的比较器;默认的比较器为 null,表示不对原数据进行排序;和数组的原生 sort 函数一样,比较器也有类似的入参及返回值;一个降序排序的参考示例如下:

function comparator(a, b) {
  return b.value - a.value;
}

当然,还有更好的选择,参考:d3.ascendingd3.descending

# tree.value([value])

如果指定了 value 参数,则设置取值器为指定的 value 函数;如果未指定,则返回当前的取值器;默认值为 null;获取节点的 value 值当前并不会影响到树布局的计算中,只是层布局的一个通用 API 接口。


changelog

  • 【阿呆不呆】译于 2014-11-28
  • 大傻】校于 2014-12-07 10:20:53
  • 【二傻】校于 2016-07-27
    • 诸多解释性描述的校对;
    • 译文布局统一的校对;
    • 链接/锚点的校对;
    • 页头、页尾统一化;
⚠️ **GitHub.com Fallback** ⚠️