具有相同方向的多个链接的d3网络 [英] d3 Network with Multiple Links in the Same Direction

查看:71
本文介绍了具有相同方向的多个链接的d3网络的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试更改移动专利诉讼"示例,以允许一个方向上的多个链接.

我有数据(是的,我知道Jim实际上不是Pam的老板):

source          target          relationship          
Michael Scott   Jan Levenson    pro
Jan Levenson    Michael Scott   personal
Jim Halpert     Pam Beasley     pro
Jim Halpert     Pam Beasley     personal

美孚专利诉讼示例的多路径功能允许正确显示前两行(两个弧线).但是,最后两行仅显示一个混合弧.

问题:如何允许将具有相同方向性的链接显示为多个弧而不是单个弧?

这是我的弧码(从移动专利示例中直接摘录):

function tick() {
  path.attr("d", linkArc);
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function linkArc(d) {
  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

任何帮助都将不胜感激.谢谢!

解决方案

可能有一些潜在的方法,一种很快就想到了:对节点之间的每种类型关系使用不同的路径生成器.您必须具有一个指示关系性质的属性(您的问题中有此属性),然后使用该属性来设置路径对齐方式.

在下面的代码段中,我检查一下正在绘制什么关系,并且与专业关系弧半径相比,将个人关系中的弧半径减小了50%.相关部分是:

function linkArc(d) {

  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  if(d.relationship == "pro") { 
     return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
  else {
    return "M" + d.source.x + "," + d.source.y + "A" + (dr * 0.3) + "," + (dr * 0.3) + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
}

这是实践中的全部内容:

 var links = [
  { source: "Michael Scott",
    target:"Jan Levenson",
    relationship: "pro"
  },
  { source:"Jan Levenson",
    target:"Michael Scott",
    relationship: "Personal"
  },
  { source: "Jim Halpert",
    target: "Pam Beasley",
    relationship: "pro"
  },
  {
    source: "Jim Halpert",
    target: "Pam Beasley",
    relationship: "Personal" 
  }
  ]
  
  var nodes = {};

// Compute the distinct nodes from the links.
links.forEach(function(link) {
  link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
  link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});

var width = 960,
    height = 500;

var force = d3.layout.force()
    .nodes(d3.values(nodes))
    .links(links)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
    .data(["suit", "licensing", "resolved"])
  .enter().append("marker")
    .attr("id", function(d) { return d; })
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("path")
    .attr("d", "M0,-5L10,0L0,5");

var path = svg.append("g").selectAll("path")
    .data(force.links())
  .enter().append("path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

var circle = svg.append("g").selectAll("circle")
    .data(force.nodes())
  .enter().append("circle")
    .attr("r", 6)
    .call(force.drag);

var text = svg.append("g").selectAll("text")
    .data(force.nodes())
  .enter().append("text")
    .attr("x", 8)
    .attr("y", ".31em")
    .text(function(d) { return d.name; });

// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
  path.attr("d", linkArc);
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function linkArc(d) {

  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  if(d.relationship == "pro") { 
     return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
  else {
    return "M" + d.source.x + "," + d.source.y + "A" + (dr * 0.3) + "," + (dr * 0.3) + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
} 

 .link {
  fill: none;
  stroke: #666;
  stroke-width: 1.5px;
}

#licensing {
  fill: green;
}

.link.licensing {
  stroke: green;
}

.link.resolved {
  stroke-dasharray: 0,2 1;
}

circle {
  fill: #ccc;
  stroke: #333;
  stroke-width: 1.5px;
}

text {
  font: 10px sans-serif;
  pointer-events: none;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
} 

 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

I am attempting to alter the Mobile Patent Suits example to allow for multiple links in one direction.

I have data (yes, I know Jim isn't actually Pam's boss):

source          target          relationship          
Michael Scott   Jan Levenson    pro
Jan Levenson    Michael Scott   personal
Jim Halpert     Pam Beasley     pro
Jim Halpert     Pam Beasley     personal

The multi-path functionality of the Mobil Patents Suit example allows the first two rows to be presented correctly (two arcs). However, only one blended arc is presented for the last two rows.

Question: How do I allow links with the same directionality to be shown as multiple arcs rather than a single arc?

Here is my arc code (ripped straight from the Mobile Patents Example):

function tick() {
  path.attr("d", linkArc);
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function linkArc(d) {
  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

Any help at all would be greatly appreciated. Thank you!

解决方案

There are probable a few potential approaches for this, one comes to mind rather quickly: use a different path generator for each type relationship between the nodes. You'll have to have a property indicating the nature of the relationship (which you have in your question), and use that to set the path alignment.

In the snippet below I check to see what relationship is being drawn, and reduce the radius of the arc in a personal relationship by 50% as compared to the professional relationship arc radius. The relevant part is:

function linkArc(d) {

  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  if(d.relationship == "pro") { 
     return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
  else {
    return "M" + d.source.x + "," + d.source.y + "A" + (dr * 0.3) + "," + (dr * 0.3) + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
}

Here's the whole thing in practice:

var links = [
  { source: "Michael Scott",
    target:"Jan Levenson",
    relationship: "pro"
  },
  { source:"Jan Levenson",
    target:"Michael Scott",
    relationship: "Personal"
  },
  { source: "Jim Halpert",
    target: "Pam Beasley",
    relationship: "pro"
  },
  {
    source: "Jim Halpert",
    target: "Pam Beasley",
    relationship: "Personal" 
  }
  ]
  
  var nodes = {};

// Compute the distinct nodes from the links.
links.forEach(function(link) {
  link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
  link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});

var width = 960,
    height = 500;

var force = d3.layout.force()
    .nodes(d3.values(nodes))
    .links(links)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
    .data(["suit", "licensing", "resolved"])
  .enter().append("marker")
    .attr("id", function(d) { return d; })
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("path")
    .attr("d", "M0,-5L10,0L0,5");

var path = svg.append("g").selectAll("path")
    .data(force.links())
  .enter().append("path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

var circle = svg.append("g").selectAll("circle")
    .data(force.nodes())
  .enter().append("circle")
    .attr("r", 6)
    .call(force.drag);

var text = svg.append("g").selectAll("text")
    .data(force.nodes())
  .enter().append("text")
    .attr("x", 8)
    .attr("y", ".31em")
    .text(function(d) { return d.name; });

// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
  path.attr("d", linkArc);
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function linkArc(d) {

  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);
  if(d.relationship == "pro") { 
     return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
  else {
    return "M" + d.source.x + "," + d.source.y + "A" + (dr * 0.3) + "," + (dr * 0.3) + " 0 0,1 " + d.target.x + "," + d.target.y;
  }
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

.link {
  fill: none;
  stroke: #666;
  stroke-width: 1.5px;
}

#licensing {
  fill: green;
}

.link.licensing {
  stroke: green;
}

.link.resolved {
  stroke-dasharray: 0,2 1;
}

circle {
  fill: #ccc;
  stroke: #333;
  stroke-width: 1.5px;
}

text {
  font: 10px sans-serif;
  pointer-events: none;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

这篇关于具有相同方向的多个链接的d3网络的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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