2

I am trying to use d3.js circle packing example to fill a bunch of svg circles with images using SVG's pattern fill. My source images are 800x600, but the circles will be of varying sizes. I've set it up as follows:

  var patterns = defs.selectAll("pattern")
      .data(nodes.filter(function(d){ return !d.children }))
      .enter()
      .append('pattern')
      .attr('id',function(d){
        return 'id'+d.id
      })
      .attr('x','0')
      .attr('y','0')
      .attr('height',function(d){ return d.r*2})
      .attr('width',function(d){ return d.r*2*1.333333})
      .append('image')
      .attr('x','0')
      .attr('y','0')
      .attr('height',function(d){ return d.r*2})
      .attr('width',function(d){ return d.r*2*1.333333})
      .attr('xlink:href',function(d){
        return 'img/img' + d.image;
      })

  var circle = svg.selectAll("circle")
      .data(nodes)
    .enter().append("circle")
      .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
      .style("fill", function(d) {
        if (!d.children){
          return 'url(#id' + d.id + ')';
        } else {
          return d.children ? color(d.depth) : null;
        }

      })

so that my DOM renders like this:

<defs id="cdef">
    <pattern id="id65" x="0" y="0" height="326.8534904318234" width="435.8045449579344">
        <image x="0" y="0" height="326.8534904318234" width="435.8045449579344" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="img/img65.png"></image>
    </pattern>
    ...
</defs>

But when I do this, the images in the circle are super blown-out (see attached image). Any idea what's going on?enter image description here

mheavers
  • 29,530
  • 58
  • 194
  • 315

1 Answers1

4

I figured this out.

The height and width on a pattern element seem to function sort of like a scale / percentage. So a width of "1" means it will fill the whole element it is set to. A width of ".25" means 25% of that element. Then, within the <pattern> you would specify the height and width of the image as the actual pixel value of the height and width of the circle they are filling, so in this case my code was changed to:

  var patterns = defs.selectAll("pattern")
      .data(nodes.filter(function(d){ return !d.children }))
      .enter()
      .append('pattern')
      .attr('id',function(d){
        return 'id'+d.id
      })
      .attr('x','0')
      .attr('y','0')
      .attr('height','1')
      .attr('width','1')
      .append('image')
      .attr('height',function(d){ return d.r*2})
      .attr('width',function(d){ return d.r*2*1.333333})
      .attr('xlink:href',function(d){
        return 'img/img' + d.image;
      })

and then, because the circle pack layout zooms in, I had to make sure to change the defs of the image as well, so:

function zoomTo(v) {
    var k = diameter / v[2]; view = v;
    defs.selectAll('image').attr('width',function(d){
      return d.r*2*1.333333*k
    }).attr('height',function(d){
      return d.r*2*k
    });
    node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
    circle.attr("r", function(d) { return d.r * k; });


  }

is needed if you're modifying the bl.ocks.org example mentioned above.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
mheavers
  • 29,530
  • 58
  • 194
  • 315
  • Did you tried to use the `patternUnits` attribute? I did something similar with images in svg with below settings: `.attr("patternUnits", "objectBoundingBox")`, and`.attr({"viewBox": "0 0 1 1" })` – DevGrowth.Tech Nov 26 '15 at 10:23
  • Thankyou, had so much trouble with the same problem. – user2457956 Apr 13 '16 at 23:43