1

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

我认为它应该在 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
        }
    });
});

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

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

4

2 回答 2

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...

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

于 2013-10-18T07:50:57.190 回答
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;
  }
于 2017-10-10T07:58:04.737 回答