Packing different sized circles into rectangle - d3.js

后端 未结 5 454
北荒
北荒 2020-11-27 12:59

I was trying to pack circles of different sizes into a rectangular container, not packing in circular container that d3.js bundled with, under

5条回答
  •  迷失自我
    2020-11-27 13:40

    There's a much better way to do this -- using Mitchell's Best Fit algorithm.

    This is the general pattern:

    function drawCircles() { 
      var w = (parseInt(d3.select(".circles-div").style('width'), 10)) * 0.34,
        h = 350;
    
      d3.csv('dataset.csv', function(error, data) {
    
        var maxRadius = 8, // maximum radius of circle
          padding = 3, // padding between circles; also minimum radius
          margin = {top: maxRadius, right: maxRadius, bottom: maxRadius, left: maxRadius},
          width = w - margin.left - margin.right,
          height = h - margin.top - margin.bottom;
    
         var color = d3.scale.linear()
          .domain([50,10])
          .range(['#666','#efefef'])
          .interpolate(d3.interpolateHcl);
    
        var logscale = d3.scale.linear()
            .range([0,8]);
    
        logscale.domain([0,500])
    
        var k = 1, // initial number of candidates to consider per circle
            m = 100, // initial number of circles to add per frame
            n = data.length, // remaining number of circles to add
            newCircle = bestCircleGenerator(maxRadius, padding);
    
        var svg = d3.select(".circles-div").append("svg")
            .attr("width", w)
            .attr("height", h)
          .append("g")
            .attr('class','bubbles')
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
        d3.timer(function() {
          for (var i = 0; i < m && --n >= 0; ++i) {
    
            var maxR = logscale(data[n]['Radius_value'])
    
            var circle = newCircle(k);
    
            svg.append("circle")
                .attr("cx", circle[0])
                .attr("cy", circle[1])
                .attr("r", 0)
                .style('fill', color(data[n]['Color_value']))
              .transition()
                .attr("r", logscale(data[n]['Radius_value']));
    
            if (k < 500) k *= 1.01, m *= .998;
          }
          return !n;
        });
    
        function bestCircleGenerator(maxRadius, padding) {
    
          var quadtree = d3.geom.quadtree().extent([[0, 0], [width, height]])([]),
          searchRadius = maxRadius * 2,
          maxRadius2 = maxRadius * maxRadius;
    
          return function(k) {
    
            var bestX, bestY, bestDistance = 0;
    
            for (var i = 0; i < k || bestDistance < padding; ++i) {
              var x = Math.random() * width,
                  y = Math.random() * height,
                  rx1 = x - searchRadius,
                  rx2 = x + searchRadius,
                  ry1 = y - searchRadius,
                  ry2 = y + searchRadius,
                  minDistance = maxRadius; // minimum distance for this candidate
    
              quadtree.visit(function(quad, x1, y1, x2, y2) {
                if (p = quad.point) {
                  var p,
                      dx = x - p[0],
                      dy = y - p[1],
                      d2 = dx * dx + dy * dy,
                      r2 = p[2] * p[2];
                  if (d2 < r2) return minDistance = 0, true; // within a circle
                  var d = Math.sqrt(d2) - p[2];
                  if (d < minDistance) minDistance = d;
                }
                return !minDistance || x1 > rx2 || x2 < rx1 || y1 > ry2 || y2 < ry1; // or outside search radius
              });
    
              if (minDistance > bestDistance) bestX = x, bestY = y, bestDistance = minDistance;
            }
    
            var best = [bestX, bestY, bestDistance - padding];
            quadtree.add(best);
            return best;
          };
        }
    
        });
    
      }
    

    See for example with random data.

提交回复
热议问题