d3强制定向节点作为文本 - 计算相对箭头指向位置

时间:2013-10-17 06:06:11

标签: javascript graph d3.js force-layout

我想使用d3中的强制布局算法可视化有向图。 节点应在矩形like this内显示为其名称。 我的问题是计算箭头应该指向的矩形之外的位置。

我认为应该在tick()函数内完成。为了避免像角度计算这样的极端努力,我可以使用截距定理。

我不知道如何获取将使用属性设置的边的参数,我无法找到一个示例。

var force = d3.layout.force().nodes(dataset.nodes).links(dataset.edges)...
var edges = svg.selectAll("line").data(dataset.edges).enter().append("line")...
var nodes = svg.selectAll("text").data(dataset.nodes).enter().append("text")...

force.on("tick", function(){
    // Why does the following function not work? How to implement this correctly?
    edges.attr(function(d){ 
        var x1 = d.source.x
        var y1 = d.source.y
        var x2 = d.target.x
        var y2 = d.target.y
        // ... calulate new source and targetcoordinates ... I can do this myself 
        return {
            "x1": newSourceX,
            "y1": newSourceY,
            "x2": newTargetX,
            "y2": newTargetY
        }
    });
});

如何为每个边缘实现函数以提取任何源和目标参数并保存新位置?

您认为这是性能最佳的解决方案吗?

2 个答案:

答案 0 :(得分:1)

尝试使用以下内容作为起点。在tick()中调用updateLines(“。links”)将行设置为仅到达给定节点的半径,而不是矩形的边缘。

var updateLines = function (selection) {
    selection.each( function(d) {
        var x1 = d.source.x,
            y1 = d.source.y,
            x2 = d.target.x,
            y2 = d.target.y,
            rad = Math.atan((y2-y1)/(x2-x1)),
            offsetX1 = d.source.r * -Math.cos(rad),
            offsetY1 = d.source.r * Math.sin(rad),
            offsetX2 = d.target.r * Math.cos(rad),
            offsetY2 = d.target.r * -Math.sin(rad);
        d3.select(this)
        .attr("x1", function() {
            if (x2 - x1 >= 0) {
                return x1 - offsetX1;
            } else {
                return x1 + offsetX1;
            }                
        })
        .attr("y1", function() {
            if (x2 - x1 >= 0) {
                return y1 + offsetY1;
            } else {
                return y1 -  offsetY1;
            }
        })
        .attr("x2", function() {
            if (x2 - x1 >= 0) {
                return x2 - offsetX2;
            } else {
                return x2 + offsetX2;
            }
        })
        .attr("y2", function() {
            if (x2 - x1 >= 0) {
                return y2 + offsetY2;
            } else {
                return y2 -  offsetY2;
            }
        })
    })
}

我知道您正试图避免使用角度,但我认为修改上述功能以解决您的需求并不会太复杂。当比较一个节点的坐标与另一个节点的坐标时,将它们之间的“偏移”x和y值描绘成三角形,从而形成斜边。如果查看节点的文本框并想象通过此文本框的邻居节点的关系线,则在那里形成的三角形形状与由所讨论的两个节点的偏移值形成的三角形相同。

因此,所有三个角度都相同,这意味着只要您知道文本框的尺寸,就可以轻松找到需要应用于给定节点坐标的两个值。

您需要做的另一项信息是考虑两个节点坐标之间创建的角度是否大于或小于节点中心坐标(0,0)之间创建的角度。文本框边缘相对于中心的坐标:

var yourBool = Math.atan(offsetX/offsetY) > Math.atan( textbox.width / 2 / textbox.height / 2 )
// or Math.atan(y/x) and so on...

我认为实现这一点不会导致图表明显滞后,但是,为了回答您的上一个问题,我确信有更好的方法来实现此功能。

答案 1 :(得分:0)

Walters的回答需要一些更改来正确处理线端调整,因为源和目标的x和y坐标都需要包含在增加或减去计算调整的确定中。例如,要计算X2调整:

  .attr("x2", function() {
    if ((y2 >= y1) && (x2 >= x1)) return  x2 - offsetX2;
    if ((y2 >= y1) && (x2 < x1)) return  x2 + offsetX2;
    if ((y2 < y1) && (x2 >= x1)) return  x2 - offsetX2;
    if ((y2 < y1) && (x2 < x1)) return  x2 + offsetX2;
  }