通过d3中的data属性创建节点集群的焦点? [英] Create node cluster's focal points by data attribute in d3?

查看:78
本文介绍了通过d3中的data属性创建节点集群的焦点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图基于组"之类的数据中的某个属性,将节点强制设置为强制布局中的不同群集.我正在改编Mike Bostock的多焦点部队布局示例中的代码(代码示例),并且我成功添加了自己的数据,但我无法指定有多少个群集以及如何将节点分配给群集.

I'm trying to force nodes into different clusters in force layout based on a certain attribute in the data like "group." I'm adapting the code from Mike Bostock's multi foci force layout example (code, example) and I've been successful in adding in my own data but I haven't been able to specify how many clusters there are and how to assign a node to a cluster.

我对d3和JavaScript相对较新,但是我找不到许多多焦点应用程序的示例.这是我的d3代码,感谢您的任何帮助或投入:

I'm relatively new to d3 and JavaScript and I haven't been able to find many examples of multi foci applications. Here's my d3 code, any help or input is appreciated:

    var width = 960,
    height = 500;

    var fill = d3.scale.category10();


    d3.json("test.json" , function(error, json){

       var root = json.nodes[0];

       root.radius = 0;
       root.fixed = true;  

      var force = d3.layout.force()
         .nodes(json.nodes)
         .size([width, height])
         .gravity(0.06)
         .charge(function(d, i) { return i ? 0 : -2000; })
         .on("tick", tick)
         .start();

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

      var elem = svg.selectAll(".elem")
         .data(json.nodes)
         .enter()
         .append("g")
         .attr("class", "elem");

  elem.append("circle")
  .attr("cx", function(d) { return d.x; })
  .attr("cy", function(d) { return d.y; })
  .attr("r", 40)
  .style("fill", function(d, i) { return fill(i & 3); })
  .style("stroke", function(d, i) { return d3.rgb(fill(i & 3)).darker(2); })
  .call(force.drag)
  .on("mousedown", function() { d3.event.stopPropagation(); });

  elem.append("text")
  .text(function(d){ return d.name; });

  svg.style("opacity", 1e-6)
    .transition()
    .duration(1000)
    .style("opacity", 1);

  d3.select("body")
    .on("mousedown", mousedown);

我一直在专门尝试弄清楚此滴答功能是如何工作的.我进行了一些研究,发现&"是按位运算符,我注意到在更改数量后即更改了集群的数量以及每个集群中的节点.但最好是我希望能够在这里指向d.group之类的东西来指定集群.

I've specifically been trying to figure out how this tick function is working. I did some research and found that the "&" is a bitwise operator and I noticed that changing the number after it is what is changing how many clusters there are and which nodes are in each. But preferably I would like to be able to point to something like d.group here to specify the cluster.

 function tick(e) {

    // Push different nodes in different directions for clustering.
    var k = 6 * e.alpha;
    json.nodes.forEach(function(o, i) {

        o.y += i & 3 ? k : -k;
        o.x += i & 2 ? k : -k;      
    });

 var q = d3.geom.quadtree(json.nodes),
      i = 0,
      n = json.nodes.length;

 while (++i < n) q.visit(collide(json.nodes[i]));

 svg.selectAll("circle")
     .attr("cx", function(d) { return d.x; })
     .attr("cy", function(d) { return d.y; });

 svg.selectAll("text")
     .attr("x", function(d) { return d.x; })
     .attr("y", function(d) { return d.y; });
 }

  function collide(node) {
  var r = node.radius + 16,
      nx1 = node.x - r,
      nx2 = node.x + r,
      ny1 = node.y - r,
      ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
          y = node.y - quad.point.y,
          l = Math.sqrt(x * x + y * y),
          r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / l * .5;
        node.x -= x *= l;
        node.y -= y *= l;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  };
}

svg.on("mousemove", function() {
  var p1 = d3.mouse(this);
  root.px = p1[0];
  root.py = p1[1];
  force.resume();
}); 

function mousedown() {
  json.nodes.forEach(function(o, i) {
  o.x += (Math.random() - .5) * 40;
  o.y += (Math.random() - .5) * 40;
});

force.resume();
}
});

*请注意,我还实现了碰撞检测以排斥节点,但目前我不认为这会影响群集.

*Note that I have also implemented collision detection to repel nodes but I don't think this is affecting the clusters at the moment.

我的数据当前存储在一个名为test.json的json文件中:

My data is currently stored in a json file called test.json:

 {
  "nodes":[
    {
        "name":     "Null",
        "radius":   40,
        "color":    "#ff0000",
        "gravity": 0.05,
        "group": 1
        },
        {
        "name":     "One",
        "radius":   40,
        "color":    "#ffff00",
        "gravity": 0.05,
        "group": 1
        },
        {
        "name":     "Two",
        "radius":   40,
        "color":    "#33cc33",
        "gravity": 0.2,
        "group": 1
        },
        {
        "name":     "Three",
        "radius":   40,
        "color":    "#3399ff",
        "gravity": 0.9,
        "group": 1
        },
        {
        "name":     "Four",
        "radius":   40,
        "color":    "#ffff00",
        "gravity": 0.05,
        "group": 6
        },
        {
        "name":     "Five",
        "radius":   40,
        "color":    "#33cc33",
        "gravity": 0.2,
        "group": 6
        },
        {
        "name":     "Six",
        "radius":   40,
        "color":    "#3399ff",
        "gravity": 0.9,
        "group": 6
        }             

  ]
}

推荐答案

所有集群工作都在这里进行:

All the clustering work takes place here:

// Push different nodes in different directions for clustering.
var k = 6 * e.alpha;
json.nodes.forEach(function(o, i) {
    o.y += i & 3 ? k : -k;
    o.x += i & 2 ? k : -k;      
});

诚然,在这个特定的示例中,我不知道它是如何工作的.似乎是间接的,很难理解.更一般而言,这是您要进行群集的操作:

Admittedly, I don't get how it works in this particular example. It seems indirect and hard to understand. More generally, this is what you want to do in order to cluster:

force.on("tick", function(e) {
  var k = e.alpha * .1;
  nodes.forEach(function(node) {
    var center = ...; // here you want to set center to the appropriate [x,y] coords
    node.x += (center.x - node.x) * k;
    node.y += (center.y - node.y) * k;
  });

它直接摘自此示例,您可以查看源代码看代码.

It's taken straight out of this example and you can view source to see the code.

在此代码中,更容易理解在跳动时如何将节点推近所需的焦点.因此,现在您需要提出一种根据节点的group参数将节点映射到焦点的方法,以便您填充该var center = ...;行.

In this code, it's easier to understand how, on tick, the nodes are pushed closer to a desired focal point. So now you need to come up with a way to map a node to a focal point based on its group param so that you fill in that var center = ...; line.

首先,您需要获取json.nodes中所有组的清单. d3.nest()对此很有帮助:

First, you need to get an inventory of all the groups in json.nodes. d3.nest() is good for that:

var groups = d3.nest()
  .key(function(d) { return d.group; })
  .map(json.nodes)

这将为您提供组到节点的映射.由于您的示例json中只有2个组("1""6"),因此看起来像这样:

That will give you a mapping of groups to node. Since your example json has just 2 groups in it ("1" and "6"), it'll look like this:

{
  "1": [
    {
      "name": "Null",
      "radius": 40,
      "color": "#ff0000",
      "gravity": 0.05,
      "group": 1
    },
    {
      "name": "One",
      "radius": 40,
      "color": "#ffff00",
      "gravity": 0.05,
      "group": 1
    },
    {
      "name": "Two",
      "radius": 40,
      "color": "#33cc33",
      "gravity": 0.2,
      "group": 1
    },
    {
      "name": "Three",
      "radius": 40,
      "color": "#3399ff",
      "gravity": 0.9,
      "group": 1
    }
  ],
  "6": [
    {
      "name": "Four",
      "radius": 40,
      "color": "#ffff00",
      "gravity": 0.05,
      "group": 6
    },
    {
      "name": "Five",
      "radius": 40,
      "color": "#33cc33",
      "gravity": 0.2,
      "group": 6
    },
    {
      "name": "Six",
      "radius": 40,
      "color": "#3399ff",
      "gravity": 0.9,
      "group": 6
    }
  ]
}

然后,您可以遍历groups并根据需要为每个组分配一个中心点.您如何做到这一点取决于您要实现的目标.也许您希望将焦点分布在屏幕中心周围的圆圈中.由您决定...但是目标是最终得到groups["1"].center等于{x:123, y:456}之类的东西,因为这样您就可以从上方将其插入滴答处理程序:

Then you can loop over groups and assign each group a center point as you'd like. How you do that depends on what you're trying to achieve. Perhaps you'd like to distribute the focal points in a circle around the center of the screen. It's up to you... But the goal is to end up with something where groups["1"].center equals something like {x:123, y:456}, because then you can plug that back into the tick handler from above:

force.on("tick", function(e) {
  var k = e.alpha * .1;
  nodes.forEach(function(node) {
    var center = groups[node.group].center;
    node.x += (center.x - node.x) * k;
    node.y += (center.y - node.y) * k;
  });

(希望)瞧!

这篇关于通过d3中的data属性创建节点集群的焦点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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