可以为强制定向布局指定自定义强制函数吗?

时间:2015-03-08 14:43:36

标签: d3.js graph-layout

我想尝试使用强制导向图布局的替代族力函数。

对于每个节点n_i,我可以定义一个"强制函数" f_i这样

  • f_i ( n_i )完全相同;和
  • f_i ( n_j ),其中n_i != n_j是节点n_i上由于某个其他节点n_j而产生的力。

节点n_i上的净力应该是力f_i ( n_j )的矢量和,其中n_j的范围超过所有其他节点 1

有没有办法告诉d3.js在布局算法中使用这些自定义强制函数

[d3.js的强制导向布局的documentation描述了可以调整其内置强制函数的各种方法,但是我还没有找到指定方法的方法。完全不同的力函数,即通过调整内置力函数的参数无法实现的力函数。]


1 IOW,除了从其强制函数n_i计算出的力之外,没有其他/额外的力量应该作用于节点f_i

2 个答案:

答案 0 :(得分:2)

要实现此目的,您需要创建自己的自定义布局。我没有这方面的教程,但是现有的力布局的源代码应该是一个很好的起点,因为它的声音,你的自定义布局的结构将非常类似。

答案 1 :(得分:0)

是的,你可以。积分转到Shan Carter和他的bl.ocks example



let margin = {
  top: 100,
  right: 100,
  bottom: 100,
  left: 100
};

let width = 960,
  height = 500,
  padding = 1.5, // separation between same-color circles
  clusterPadding = 6, // separation between different-color circles
  maxRadius = 12;

let n = 200, // total number of nodes
  m = 10, // number of distinct clusters
  z = d3.scaleOrdinal(d3.schemeCategory20),
  clusters = new Array(m);

let svg = d3.select('body')
  .append('svg')
  .attr('height', height)
  .attr('width', width)
  .append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

let nodes = d3.range(200).map(() => {
  let i = Math.floor(Math.random() * m),
    radius = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
    d = {
      cluster: i,
      r: radius
    };
  if (!clusters[i] || (radius > clusters[i].r)) clusters[i] = d;
  return d;
});

let circles = svg.append('g')
  .datum(nodes)
  .selectAll('.circle')
  .data(d => d)
  .enter().append('circle')
  .attr('r', (d) => d.r)
  .attr('fill', (d) => z(d.cluster))
  .attr('stroke', 'black')
  .attr('stroke-width', 1);

let simulation = d3.forceSimulation(nodes)
  .velocityDecay(0.2)
  .force("x", d3.forceX().strength(.0005))
  .force("y", d3.forceY().strength(.0005))
  .force("collide", collide) // <<-------- CUSTOM FORCE
  .force("cluster", clustering)//<<------- CUSTOM FORCE 
  .on("tick", ticked);

function ticked() {
  circles
    .attr('cx', (d) => d.x)
    .attr('cy', (d) => d.y);
}

// Custom 'clustering' force implementation.
function clustering(alpha) {
  nodes.forEach(function(d) {
    var cluster = clusters[d.cluster];
    if (cluster === d) return;
    var x = d.x - cluster.x,
      y = d.y - cluster.y,
      l = Math.sqrt(x * x + y * y),
      r = d.r + cluster.r;
    if (l !== r) {
      l = (l - r) / l * alpha;
      d.x -= x *= l;
      d.y -= y *= l;
      cluster.x += x;
      cluster.y += y;
    }
  });
}
// Custom 'collide' force implementation.
function collide(alpha) {
  var quadtree = d3.quadtree()
    .x((d) => d.x)
    .y((d) => d.y)
    .addAll(nodes);

  nodes.forEach(function(d) {
    var r = d.r + maxRadius + Math.max(padding, clusterPadding),
      nx1 = d.x - r,
      nx2 = d.x + r,
      ny1 = d.y - r,
      ny2 = d.y + r;
    quadtree.visit(function(quad, x1, y1, x2, y2) {

      if (quad.data && (quad.data !== d)) {
        var x = d.x - quad.data.x,
          y = d.y - quad.data.y,
          l = Math.sqrt(x * x + y * y),
          r = d.r + quad.data.r + (d.cluster === quad.data.cluster ? padding : clusterPadding);
        if (l < r) {
          l = (l - r) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          quad.data.x += x;
          quad.data.y += y;
        }
      }
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
    });
  });
}
&#13;
<!doctype html>
<meta charset="utf-8">

<body>
  <script src="//d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

此处还有in-depth对该主题的更多看法。