Search code examples
d3.jssimulation

simulation nodes not distribute with the rule


Below example try to put node from top to bottom but it overlapped each other now.

    test()
    
    function test() {
            var nodes = [
                    {"Name":"A","Count":10},
                    {"Name":"B","Count":8},
          {"Name":"C","Count":6},
          {"Name":"D","Count":5},
          {"Name":"E","Count":4},
          {"Name":"F","Count":4},
          {"Name":"G","Count":2},
          {"Name":"H","Count":7},
            ]
            
            var width = 600
            var height = 600
            var margin = 100
            var color = d3.scaleOrdinal(d3.schemeCategory10)
            
            let extentCount = d3.extent(nodes, d => d.Count)            
            let maxRadius = 50 
            
            let yScale = d3.scaleLinear()
                .domain(extentCount)
                .range([height - maxRadius, maxRadius])
            
            let rScale = d3.scaleSqrt()
                .domain(extentCount)
                .range([5, maxRadius])
            
            var svg = d3.select("body")
                    .append("svg")
                    .attr("width", width + margin + margin)
                    .attr("height", height + margin + margin)
                    .attr("class", "bubble")
                    .style('border','2px solid red')
                    .style('background','#ececec')
            
            var g = svg.append("g")
                .attr('transform', `translate(${margin},${margin})`)

            var simulation = d3.forceSimulation(nodes)
                    //.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(0).strength(1))
                    .force("forceX", d3.forceX().strength(.051).x(width/2))
                    .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
                    .force('collision', d3.forceCollide().radius(d => rScale(d.Count)))

            simulation.stop()
            
            var node = g.selectAll(".node")
                    .data(nodes)
                    .enter()
                    .append("g")
                    .attr("class", "node")
                    .attr("transform", function(d) {
                            return `translate(${d.x},${d.y})`;
                    });

            node.append("circle")
                    .attr("r", d => rScale(d.Count))
                    .style("fill", function(d,i) {
                            return color(i);
                    });

            node.append("text")
                    .attr("dy", ".2em")
                    .style("text-anchor", "middle")
                    .text(function(d) {
                            return d.Name.substring(0, rScale(d.Count) / 3);
                    })
                    .attr("font-family", "sans-serif")
                    .attr("font-size", function(d){
                            return rScale(d.Count)/5;
                    })
                    .attr("fill", "white");

            node.append("text")
                    .attr("dy", "1.3em")
                    .style("text-anchor", "middle")
                    .text(function(d) {
                            return d.Count;
                    })
                    .attr("font-family",  "Gill Sans", "Gill Sans MT")
                    .attr("font-size", function(d){
                            return d.r/5;
                    })
                    .attr("fill", "white");

            function ticked() {
          node
              .attr("cx", function(d){ return d.x; })
                            .attr("cy", function(d){ return d.y; })
            }
            
            simulation.on("tick",ticked)
            simulation.nodes(nodes)
            //simulation.force("link").links(links)
            simulation.alpha(1).restart()
    }
<script src="http://d3js.org/d3.v7.min.js" charset="utf-8"></script>


Solution

  • SVG <g> elements have no cy or cx attributes. Inside the tick function, it should be:

    node.attr("transform", function(d) {
        return `translate(${d.x},${d.y})`;
    });
    

    Here's your code with that change:

    test()
    
    function test() {
      var nodes = [{
          "Name": "A",
          "Count": 10
        },
        {
          "Name": "B",
          "Count": 8
        },
        {
          "Name": "C",
          "Count": 6
        },
        {
          "Name": "D",
          "Count": 5
        },
        {
          "Name": "E",
          "Count": 4
        },
        {
          "Name": "F",
          "Count": 4
        },
        {
          "Name": "G",
          "Count": 2
        },
        {
          "Name": "H",
          "Count": 7
        },
      ]
    
      var width = 600
      var height = 600
      var margin = 100
      var color = d3.scaleOrdinal(d3.schemeCategory10)
    
      let extentCount = d3.extent(nodes, d => d.Count)
      let maxRadius = 50
    
      let yScale = d3.scaleLinear()
        .domain(extentCount)
        .range([height - maxRadius, maxRadius])
    
      let rScale = d3.scaleSqrt()
        .domain(extentCount)
        .range([5, maxRadius])
    
      var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin + margin)
        .attr("height", height + margin + margin)
        .attr("class", "bubble")
        .style('border', '2px solid red')
        .style('background', '#ececec')
    
      var g = svg.append("g")
        .attr('transform', `translate(${margin},${margin})`)
    
      var simulation = d3.forceSimulation(nodes)
        //.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(0).strength(1))
        .force("forceX", d3.forceX().strength(.051).x(width / 2))
        .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
        .force('collision', d3.forceCollide().radius(d => rScale(d.Count)))
    
      simulation.stop()
    
      var node = g.selectAll(".node")
        .data(nodes)
        .enter()
        .append("g")
        .attr("class", "node");
    
    
      node.append("circle")
        .attr("r", d => rScale(d.Count))
        .style("fill", function(d, i) {
          return color(i);
        });
    
      node.append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
          return d.Name.substring(0, rScale(d.Count) / 3);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d) {
          return rScale(d.Count) / 5;
        })
        .attr("fill", "white");
    
      node.append("text")
        .attr("dy", "1.3em")
        .style("text-anchor", "middle")
        .text(function(d) {
          return d.Count;
        })
        .attr("font-family", "Gill Sans", "Gill Sans MT")
        .attr("font-size", function(d) {
          return d.r / 5;
        })
        .attr("fill", "white");
    
      function ticked() {
        node.attr("transform", function(d) {
          return `translate(${d.x},${d.y})`;
        });
      }
    
      simulation.on("tick", ticked)
      simulation.nodes(nodes)
      //simulation.force("link").links(links)
      simulation.alpha(1).restart()
    }
    <script src="http://d3js.org/d3.v7.min.js" charset="utf-8"></script>