链接和箭头终止在D3节点的边界 [英] Links and Arrowheads to terminate at borders of nodes in D3

查看:119
本文介绍了链接和箭头终止在D3节点的边界的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是如何创建一个网络可视化方案,使边缘和/或箭头终止在节点的边界。



我绘制一个有向图使用基于曲线链接基本模型的D3.js,并添加了如此处所述的标记箭头其他问题。我的可视化中的节点基于它们的属性来改变它们的大小和不透明度。这引入了两个问题:(1)当节点改变大小时,箭头不指向节点的边缘,以及(2)当它们部分透明时,边缘的尾部通过节点出现。



对于第一个问题,有几个解决方案可用:这一个旨在获得正确偏移的箭头,但它不影响链路端终结。还有一些解决方案的建议,这里,但我没有'看到任何实际完整的工作代码。 这个JS小提琴具有我想要的箭头外观,但代码是不透明的,不是



正如我所说,我的链接是基于Curved Links示例定义的:

  graph.links.forEach(function(link){
var s = nodes [link.source],
t =节点[link.target],
i = {},//中间节点
property1 = link.property1;
nodes.push(i);
links.push :s,target:i},{source:i,target:t});
bilinks.push([s,i,t,property1]);
}

然后,如果我对D3的工作原理的理解是基本正确的,以下代码:

  force.on(tick,function(){
link.attr ,函数(d){
if(d [0] == d [2]){
返回M+ d [0] .x +,+ d [0]。 y
+A+20,20 -50 1,1+(1.001 * d [2] .x)+,+(1.001 * d [2] .y)
;
} else {
returnM+ d [0] .x +,+ d [0] .y
+S+ d [1] .x + ,+ d [1] .y
++ d [2] .x +,+ d [2] .y;
}
} $ b node.attr(transform,function(d){
returntranslate(+ dx +,+ dy +);
});
} ;

所以我的问题是如何改变这个代码,正常)可视化方案,使得边缘和/或箭头在节点的边界处终止,即使它们改变大小。



我创建了一个 JS Fiddle 包括所有必需的位来查看和解决问题。

解决方案

/ div>

因为我没有得到任何回应,我继续前进,通过回答我自己的问题。结果,我想出的答案可能不是最好的,因为我仍然是新的这一切,但它的工作原理,它类似于 ...大量适用于处理弯曲链接和反身链接。



必要更改的核心是以下代码:

  force.on(tick,function(){
link.attr(d,function(d){
diffX0 = d [0] .x-d [1] .x;
diffY0 = d [0] .y - d [1] .y;
diffX2 = d [2] .x-d [1] .x;
diffY2 = d [2]。 (diffX2 * diffX2)+(diffY0 * diffY0));
pathLength12 = Math.sqrt((diffX2 * diffX2)+(diffX2 * diffX0) * diffY2));
offsetX0 = 1.00 *(diffX0 * d [0] .group)/ pathLength01;
offsetY0 = 1.00 *(diffY0 * d [0] .group)/ pathLength01;
offsetX2 =(4.0 *(diffX2 / Math.abs(diffX2)))+((diffX2 * d [2] .group)/ pathLength12);
offsetY2 =(4.0 *(diffY2 / Math.abs )))+((diffY2 * d [2] .group)/ pathLength12);

if(d [0] == d [2]){
returnM (d [0] .x)+,+(d [0] .y-d [0] .group)
+A+20,23 -50 1,1
+ b+(d [2] .x +(5.0 * 0.866)+(0.866 * d [2] 0.5)+(0.5 * d [2]。
} else {
returnM+(d [0] .x- offsetX0)+,+(d [0] .y- offsetY0)
+S (1.01 * d [1] .x)+,+(1.01 * d [1] .y)
++(d [2] .x-offsetX2)+ [2] .y - offsetY2);
}
});
node.attr(transform,function(d){
returntranslate(+ d.x +,+ d.y +);
}
});

这是为了使用箭头标记偏移到6(即。 attr(refX,6)),使得链接的末端几乎在箭头的中间,箭头向节点进一步延伸大约4个单位。箭头和链接尾部因此以不同的量偏移到节点边界,因此如果不使用有向图,您将需要调整目标端的偏移以匹配源端,并在边框上获得它们。



以下是更新的JSFiddle ,其中包括执行定向力布局所需的所有功能,包括:




  • JSON节点属性具有透明度的颜色圆圈

  • JSON链接属性的颜色曲线边和箭头

  • 链接在节点边界开始和结束,因此它们不与开始/结束节点重叠

  • 匹配反射边缘的样式(即源和目标是相同的节点)

  • 也支持更改边缘厚度和透明度



还有一些其他的东西,你想调整自己的应用程序。例如,我向我的节点数据添加了一个 radius 变量,包括属性的适当缩放(当前 group )到圆的半径,那么对于圆的它 .attr(r,function(d){return d.radius;} 。以获得节点的圈子的 r 在强制函数中使用(我喜欢,我会喜欢,如果有人可以弄清楚),所以这是我的解决方案。 p>

我认为包装了很多可视化特性,我希望这是一个像D3这样的工具的标准,但是不可能找到,有些难以实现,但现在它完成,我希望这将节省一些其他人很多时间在D3中实施定向网络。


My question is how to create a network visualization scheme such that the edges and/or arrowheads terminate at the borders of the nodes.

I am drawing a directed graph using D3.js based on the Curved Links base model with added "marker" arrowheads as described in this other question. The nodes in my visualization vary their size and opacity based on their properties. This introduces two problems: (1) The arrowheads do not point to the edge of the nodes when the nodes change size, and (2) the tails of the edges appear through the nodes when they are partially transparent.

For the first problem, there are a few solutions available: this one purports to get the arrow heads offset correctly, but it does not affect the link end terminations. There are also suggestions of solutions here, but I didn't see any actual complete working code there. This JS fiddle has exactly the arrowhead look that I'd like, but the code is rather opaque and not modular in a way I can figure out how to apply to my own case.

As I said, my links are defined based on the Curved Links example:

graph.links.forEach(function(link) {
    var s = nodes[link.source],
        t = nodes[link.target],
        i = {}, // intermediate node
    property1 = link.property1;
    nodes.push(i);
    links.push({source: s, target: i}, {source: i, target: t});
    bilinks.push([s, i, t, property1]);
});

Then, if my loose understanding of how D3 works is basically correct, the links are drawn each tick via the following code:

force.on("tick", function() {
  link.attr("d", function(d) {
    if (d[0] == d[2]) {
      return "M" + d[0].x + "," + d[0].y
        + "A" + "20,20 -50 1,1 " + (1.001 * d[2].x) + "," + (1.001 * d[2].y) 
        ;
    } else {
     return "M" + d[0].x + "," + d[0].y
        + "S" + d[1].x + "," + d[1].y
        + " " + d[2].x + "," + d[2].y;
    }
  });
  node.attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });
});

So my question is how to change this code in a way that achieves the generally desired (and I think normal) visualization scheme such that the edges and/or arrowheads terminate at the border of the nodes even as they change size.

I've created a JS Fiddle that includes all the necessary bits to see and solve the problem. It also includes an adjustment for getting the arrowheads to match the links they are on, and that capability needs to be compatible with the solution to this issue.

解决方案

Since I wasn't getting any responses I went ahead and powered through answering my own question. As result, the answer I came up with is probably not the best because I'm still new to all this, but it works and it's similar to this answer...heavily adapted to handle the curved links and reflexive links.

The core of the necessary change is the following code:

force.on("tick", function() {
  link.attr("d", function(d) {
    diffX0 = d[0].x - d[1].x;
    diffY0 = d[0].y - d[1].y;
    diffX2 = d[2].x - d[1].x;
    diffY2 = d[2].y - d[1].y;
    pathLength01 = Math.sqrt((diffX0 * diffX0) + (diffY0 * diffY0));
    pathLength12 = Math.sqrt((diffX2 * diffX2) + (diffY2 * diffY2));
    offsetX0 = 1.00 * (diffX0 * d[0].group ) / pathLength01;
    offsetY0 = 1.00 * (diffY0 * d[0].group) / pathLength01;
    offsetX2 = (4.0 * (diffX2 / Math.abs(diffX2) )) + ((diffX2 * d[2].group) / pathLength12);
    offsetY2 = (4.0 * (diffY2 / Math.abs(diffY2) )) + ((diffY2 * d[2].group) / pathLength12);

    if (d[0] == d[2]) {
     return "M" + (d[0].x) + "," + (d[0].y - d[0].group)
        + "A" + "20,23 -50 1,1 "
        + " " + (d[2].x + (5.0 * 0.866) + (0.866 * d[2].group)) 
        + "," + (d[2].y + (5.0 * 0.5) + (0.5 * d[2].group));
    } else {
     return "M" + (d[0].x - offsetX0) + "," + (d[0].y - offsetY0)
        + "S" + (1.01 * d[1].x) + "," + (1.01 * d[1].y)
        + " " + (d[2].x - offsetX2) + "," + (d[2].y - offsetY2);
    }
  });
  node.attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });
});

This is designed to work with the arrowhead marker offset to 6 (i.e. .attr("refX", 6)) so that the end of the link is nearly in the middle of the arrowhead and the arrowhead extends about 4 units further toward the node. The arrowheads and the link tails are therefore offset by different amounts to the node border, so if you aren't using a directed graph you will need to adjust the target end's offset to match the source end and get them both right on the border.

Here is an updated JSFiddle that includes all the features necessary to do a directed force layout that includes:

  • Color circles by JSON node property with transparency
  • Color curved edges AND arrowheads by JSON link property
  • Links begin and end at node borders so they do not overlap the start/end nodes
  • Matches style for reflexive edges (that is, source and target are the same node)
  • Changing edge thickness and transparency is also supported

There are some other things that you'll want to tweak for your own application. For example, I added a radius variable to my node data that includes the proper scaling of the property (currently group) to the circle radius, then for the circle it's .attr("r", function(d) { return d.radius;}. I couldn't figure out a way to get the node's circle's r to use in the force function (which I prefer and I would love if somebody could figure out), so that was my work-around.

I think that wraps up a lot of visualization features that I expected to be standard for a tool like D3, but instead were impossible to find and somewhat difficult to implement. But now it's done, and I hope this will save some other people a lot of time in implementing directed networks in D3.

这篇关于链接和箭头终止在D3节点的边界的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆