D3 Force Layout : How to force a group of node to stay in a given area

前端 未结 1 1341
北恋
北恋 2021-01-15 23:23

In a D3 force layout graph, i try to force certain nodes to stay in a given area, based on their group.

There is a center node, which is fixed. I want nodes

相关标签:
1条回答
  • 2021-01-15 23:57

    You have to use forceX, which:

    Creates a new positioning force along the x-axis towards the given position x. If x is not specified, it defaults to 0.

    In this demo (based on this Bostock's code), the nodes array contains a group:

    nodes:[
        {"id": "A", "group": 1},
        {"id": "B", "group": 2},
        {"id": "C", "group": 2},
        //etc...
    ]
    

    Using this group value, we can position the nodes to the left or to the right. This is just an example, change it according to your code:

    .force("x", d3.forceX(function(d){
        if(d.group === 2){
            return width/3
        } else if (d.group === 3){
            return 2*width/3
        } else {
            return width/2 
        }
    }))
    

    So, if the nodes belongs to group 2 (cyan nodes), we position it at 1/3 of the width, and if it belongs to group 3 (orange nodes) we position it at 2/3 of the width.

    The positioning is not perfect, because other parameters can influence it, as the charge's strength and the link's distance.

    This is a demo:

    var graph = {
    nodes:[
    	{"id": "A", "group": 1},
    	{"id": "B", "group": 2},
    	{"id": "C", "group": 2},
    	{"id": "D", "group": 2},
    	{"id": "E", "group": 2},
    	{"id": "F", "group": 3},
    	{"id": "G", "group": 3},
    	{"id": "H", "group": 3},
    	{"id": "I", "group": 3}
    ],
    links:[
    {"source": "A", "target": "B", "value": 1},
    {"source": "A", "target": "C", "value": 1},
    {"source": "A", "target": "D", "value": 1},
    {"source": "A", "target": "E", "value": 1},
    {"source": "A", "target": "F", "value": 1},
    {"source": "A", "target": "G", "value": 1},
    {"source": "A", "target": "H", "value": 1},
    {"source": "A", "target": "I", "value": 1},
    ]
    };
    
    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height");
    
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100))
        .force("charge", d3.forceManyBody())
    		.force("x", d3.forceX(function(d){
    			if(d.group === 2){
    				return width/3
    			} else if (d.group === 3){
    				return 2*width/3
    			} else {
    				return width/2 
    			}
    		}))
        .force("y", d3.forceY(height/2))
        .force("center", d3.forceCenter(width / 2, height / 2));
    
      var link = svg.append("g")
          .attr("class", "links")
        .selectAll("line")
        .data(graph.links)
        .enter().append("line")
          .attr("stroke-width", function(d) { return Math.sqrt(d.value); });
    
      var node = svg.append("g")
          .attr("class", "nodes")
        .selectAll("circle")
        .data(graph.nodes)
        .enter().append("circle")
          .attr("r", 5)
          .attr("fill", function(d) { return color(d.group); })
          .call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended));
    
      node.append("title")
          .text(function(d) { return d.id; });
    
      simulation
          .nodes(graph.nodes)
          .on("tick", ticked);
    
      simulation.force("link")
          .links(graph.links);
    
      function ticked() {
        link
            .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; });
    
        node
            .attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });
      }
    
    function dragstarted(d) {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }
    
    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    
    function dragended(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }
    .links line {
      stroke: #999;
      stroke-opacity: 0.6;
    }
    
    .nodes circle {
      stroke: #fff;
      stroke-width: 1.5px;
    }
    <svg width="400" height="300"></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>

    0 讨论(0)
提交回复
热议问题