【问题标题】:d3 forced directed nodes as text - calculate relative arrow pointing positiond3 强制定向节点作为文本 - 计算相对箭头指向位置
【发布时间】:2013-10-25 12:12:19
【问题描述】:

我想使用 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
        }
    });
});

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

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

【问题讨论】:

  • 不确定你的意思。您不需要计算箭头指向自己的位置,例如参见this example.
  • 我想将文本用作this example 显示的节点。箭头必须指向文本元素之外,但每个文本元素都有自己的 witdh。确切的问题是从 tick() 函数内的每个边缘获取源和目标坐标。
  • 你见过this question吗?

标签: javascript graph d3.js force-layout


【解决方案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...

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

【讨论】:

  • 谢谢。这个函数不完全是我需要的,但它会帮助我从节点文本计算边缘位置。我稍后会发布我的结果:)
【解决方案2】:

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;
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-15
    • 2018-12-24
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    相关资源
    最近更新 更多