如何在D3.js中更改径向树的根

时间:2016-04-26 12:29:11

标签: javascript d3.js tree

我在D3.js中遇到了Radial树的问题

我想将所选节点(点击时)放在径向树的中心,让整个树重新适应这个新设置。在我的更新功能中,我可以将根设置为所选节点:

root = source; 

但是,这仅显示所选节点和子节点,但我希望父节点(及其子节点)位于所选节点之上,并且所选节点的子节点位于其底部。

我试过的是从父节点的子节点数组中删除所选节点。然后将此更改的父项作为所选节点的子项推送,但这会产生递归错误。

var index = source.parent.children.indexOf(source);
source.parent.children.splice(0, 1);
source.parent.parent = source;
source.children.push(source.parent);

完整代码here

我会感激任何帮助或指示。

谢谢

1 个答案:

答案 0 :(得分:0)

这是一个负责重排的递归函数:

function makeRoot(source) {
   if (!source.parent) return; //nothing to do if source is already root

   makeRoot(source.parent); //flip the higher branches recursively so that the parent becomes root of the tree 

   var index = source.parent.children.indexOf(source);
   source.parent.children.splice(index, 1);  //corrected here: you need to remove the right element from the children list
   source.parent.parent = source;
   source.children.push(source.parent);
   delete source.parent; //added: now that the original parent is linked, make source the root of the tree
}

我不知道如何强迫原始节点的孩子们被吸引到南方,其余的则被吸引到北方。首先要做的是知道每个节点必须在哪一侧(这很容易:默认情况下所有节点都是“北”,然后在source之前访问子树。致电makeRoot,并告诉他们“南方”)。但在那之后,我对d3的树形布局不够熟悉,无法强制执行“北/南”约束。

对于南北方向,

更新,您可以尝试以下操作:

  • 将指针oldParent=source.parent保留到所选节点的原始父级
  • 一旦布局完成,在更新svg之前,计算offsetX= 90- oldParent.x(父位置和北轴之间的差异 - 注意:我不确定北轴是否为90°,但它应该是0,90,180或270之一..只需尝试所有;))
  • 将所有节点移动offsetX(保持0到359之间的角度):

    nodes.forEach(function(d) {
        d.x = (d.x + offsetX) % 360;
    });
    
  • 然后你可以更新svg:整个树应该旋转,以便原始的父指向北...这应该足以获得你想要的视觉效果。

更新#2 另一种进行南北对齐的方法。

请参阅此处的小提琴:https://jsfiddle.net/qo4w0ktn/4/

这个想法是计算原始孩子中最左边和最右边叶子的角度,并旋转整个图表,直到这两个角度的平均值指向南方。这意味着您需要跟踪树中的“原始”子项,这是通过我的d.real_children属性完成的。函数leftmostLeafrightmostLeaf很简单(参见小提琴)。

  function rotateSouth(source, nodes) {
    var lx = leftmostLeaf(source);
    var rx = rightmostLeaf(source);

    if (lx>rx) rx += 360; //this happens if the interval overlap angle 0

    var offsetX= 180- (lx+rx)/2; //the median point (=(lx+rx)/2) should be pushed south (=180)

    nodes.forEach(function(d) {
      d.x = (d.x + offsetX) % 360; //rotate every node by the same offset
    });  
  }