Search code examples
javascriptd3.jscircle-pack

What is the original radius function for circle pack?


I'm interested in tweaking the radius of the circles on the circle pack layout. For that I need to know how the original radius is calculated.

By reading the d3.js source code for pack layout it seems the default radius function is simply Math.sqrt of value for each node. But that is not really the case because I modified the D3.js original circle pack example adding a .radius(function(d){return Math.sqrt(d);}) and as you can see at bl.ocks.org/ecerulm/f0a36710e3 the radius of the circles are not the same.


Solution

  • The d3.layout.pack() uses Math.sqrt as radius function. But pack.nodes will apply a scale transform d3_layout_packTransform(node, x, y, k) to make the whole circle pack chart to fit if radius wasn't explicitly set. That is why if you apply you own function (even if its radius(Math.sqrt)) you will need to apply your own scaling after if you want to get the same result as with implicit radius.

    In the example below I explicitly set Math.sqrt as the radius function and then scale afterward to fit [diameter,diameter] with my own function pack_transform since d3_layout_packTranform is not accesible:

    var pack = d3.layout.pack()
      .value(function(d) { return d.size; })
      .radius(Math.sqrt)
      .size([diameter - 4, diameter - 4]);
    
    var packnodes = pack.nodes(root);
    var packroot = packnodes[0];
    var w = diameter, h = diameter;
    function pack_transform(node, k) {
        function inner_transform(node,cx,cy,k) { 
          var children = node.children;
          node.x = cx + k * (node.x-cx);
          node.y = cy + k * ( node.y-cy);
          node.r *= k;
          if (children) {
            var i = -1, n = children.length;
            while (++i < n) inner_transform(children[i],cx,cy,  k);
          }
        }
        return inner_transform(node,node.x,node.y,k);
    }
    pack_transform(packroot,  1 / Math.max(2 * packroot.r / w, 2 * packroot.r / h));