D3JS Force网络:链接到使用transform fnt中的公式分组的节点

时间:2015-09-28 23:33:09

标签: javascript d3.js

我正在将一个公式应用于D3js强制网络图中的群集节点。我不知道如何将我的链接连接到新的群集节点位置。

对于我更改了原始代码的节点:

force.on("tick", function() {
      node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

按类别(nodeCategory)聚类节点的代码:

node.attr("transform", function(d) {
  var xm = d.x + intensity*Math.cos(angle*nodeGroup(d.nodeCategory));
  var ym = d.y + intensity*Math.sin(angle*nodeGroup(d.nodeCategory));
  return "translate(" + xm + "," + ym + ")";
});

现在,节点根据数据中的类别成功聚类。但是,我不知道如何更新链接(边缘)的代码。链接不再连接到节点并反映非群集节点位置:

edges.attr("x1", function(d) { return d.source.x; })
     .attr("y1", function(d) { return d.source.y; })
     .attr("x2", function(d) { return d.target.x; })
     .attr("y2", function(d) { return d.target.y; });

如何将转化应用于链接?任何帮助将不胜感激!

编辑:这是一个说明问题的jsfiddle

1 个答案:

答案 0 :(得分:1)

问题是链接位于错误的位置,因为节点通过分组变换从它们的(d.x,d.y)位置移位。因此,d.source.xd.source.y不是您想要链接的位置。

您需要更新d.x和d.y以反映节点的真实位置,以便链接位于您想要的位置。

通常的做法是......

force.on("tick", function(e) {

    node.attr("transform", function(d) {
      d.x += (intensity*Math.cos(angle*nodeGroup(d.nodeCategory)) - d.x)*e.alpha;
      d.y += (intensity*Math.sin(angle*nodeGroup(d.nodeCategory)) - d.y)*e.alpha;
      return "translate(" + d.x + "," + d.y + ")";
    });
    edges.attr("x1", function(d) { return d.source.x; })
         .attr("y1", function(d) { return d.source.y; })
         .attr("x2", function(d) { return d.target.x; })
         .attr("y2", function(d) { return d.target.y; });
}

这个想法是让位置调节器具有不同的增益,这是力α的函数。这样,节点随时间衰减的速度就会随之衰减。

这是在上下文中......

//Width and height for SVG area
    var w = 500;
    var h = 200;

    // nb_group, angle, intensity: Used in clustering the nodes  
    var nb_group=4;
    var angle = 2*Math.PI/nb_group;
    var intensity = 100;
    
    var svg = d3.select("body").append("svg")
              .attr("width", w)
              .attr("height", h)

    var colors = d3.scale.category10()
              .range(["#FFFF00",  //YELLOW
                      "#377eb8",  //BLUE
                      "#4daf4a",  //GREEN
                      "#e41a1c",  //RED
                      ]);  
    var dataset = {
        "nodes":[
            {"id":0,"name":"A","nodeCategory":"1"},
            {"id":1,"name":"AA","nodeCategory":"1"},
            {"id":2,"name":"B","nodeCategory":"2"},
            {"id":3,"name":"BB","nodeCategory":"2"},
            {"id":4,"name":"C","nodeCategory":"3"},
            {"id":5,"name":"CC","nodeCategory":"3"},
            {"id":6,"name":"D","nodeCategory":"4"},
            {"id":7,"name":"DD","nodeCategory":"4"},
            {"id":8,"name":"DDD","nodeCategory":"4"}
        ],
        "edges":[
            {"source":0,"target":2,"value":""},
            {"source":1,"target":3,"value":""},
            {"source":2,"target":4,"value":""},
            {"source":3,"target":5,"value":""},
            {"source":4,"target":6,"value":""},
            {"source":5,"target":7,"value":""},
            {"source":6,"target":8,"value":""},
            {"source":7,"target":0,"value":""},
            {"source":8,"target":1,"value":""}
       ]
    }
    var force = d3.layout.force()
                  .nodes(dataset.nodes)
                  .links(dataset.edges)
                  .gravity(.5)
                  .charge(-100)
                  .linkDistance(10)
                  .size([w, h])
                  .start();
    var drag = force.drag()
                    .on("dragstart", dragstart);
    var edges = svg.selectAll("line")
                 .data(dataset.edges)
                 .enter()
                 .append("line")
                  .style("stroke", "black");
    var nodes = svg.selectAll("g.node")
                 .data(dataset.nodes)
                 .enter()
                 .append("g")
                 .on("dblclick", dblclick)
                 .call(drag);
    nodes.append("circle")
         .attr("r", 10)
         .style("fill", function(d) { return colors(d.nodeCategory); })
         .style("stroke", "black") 
         // Mousover Node - highlight node by fading the node colour during mouseover
         .on('mouseover', function(d){
            var nodeSelection = d3.select(this).style({opacity:'0.5'});
         })
         //Mouseout Node  - bring node back to full colour   
         .on('mouseout', function(d){
             var nodeSelection= d3.select(this).style({opacity:'1.0',}) 
         })
    // dx sets how close to the node the label appears
    nodes.append("text")
         .attr("class", "nodetext")
         .attr("dx", 12)
         .attr("dy", ".35em")
         .text(function(d) { return d.name });            // Just the name
    // Edge Paths
    var edgepaths = svg.selectAll(".edgepath")
                       .data(dataset.edges)
                       .enter()
                       .append('path')
                       .attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
                           'id':function(d,i) {return 'edgepath'+i}})
                       .style("pointer-events", "none");

    force.on("tick", function(e) {
        // position regulator for nodes must update d.x and d.y BEFORE links are positioned
        nodes.attr("transform", function(d) {
              d.x += (intensity*Math.cos(angle*(d.nodeCategory)) + w/2 - d.x)*e.alpha;
              d.y += (intensity*Math.sin(angle*(d.nodeCategory)) + h/2 - d.y)*e.alpha;
              return "translate(" + d.x + "," + d.y + ")";
        });

        edges.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });
        // ORIGINAL transform for Nodes:  
        // nodes.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
        // New Transform for nodes:
        // PROBLEM HERE   
        edgepaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
                                            //console.log(d)
                                            return path});       
    });     
    
    // Double click to 'unfix' the node and have forces start to act on it again.
    function dblclick(d) {
      d3.select(this).classed("fixed", d.fixed = false);
    }
    // Set the "fixed" property of the dragged node to TRUE when a dragstart event is initiated,
    //   - removes "forces" from acting on that node and changing its position.
    function dragstart(d) {
      d3.select(this).classed("fixed", d.fixed = true);
    }
body { margin: 0; }
svg { outline: 1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>