10

I have bubble chart based on this tutorial.

I have enabled dragging of bubbles with following code. This makes individual circles draggable, but while dragging a circle other circles don't get auto adjusted. I am using pack circle algorithm, please let me know is that possible with this algorithm.

This is my code for dragging:

// draggable
if(this.dragging){
    var drag = d3.behavior.drag()
    .on("drag", function( d, i) {
        var selection = d3.selectAll( '.selected');

        if( selection[0].indexOf( this)==-1) {
            selection.classed( "selected", false);
            selection = d3.select( this);
            selection.classed( "selected", true);
        } 

        selection.attr("transform", function( d, i) {
            d.x += d3.event.dx;
            d.y += d3.event.dy;
            return "translate(" + [ d.x,d.y ] + ")"
        })
        // reappend dragged element as last 
        // so that its stays on top 
        this.parentNode.appendChild( this);
        d3.event.sourceEvent.stopPropagation();
    });
    node.call( drag);
}
VividD
  • 10,456
  • 6
  • 64
  • 111
Dhanesh Mane
  • 147
  • 11
  • You would need to run the pack layout again on drag with the data for the node that you're dragging removed. – Lars Kotthoff Aug 08 '14 at 08:15
  • Lars thanks for the comment but I am new to d3 and dont no how I can rerun the layout with same data. can you suggest some example if possible. Thanks – Dhanesh Mane Aug 08 '14 at 09:24
  • This is done in [this treemap](http://bl.ocks.org/mbostock/4063582) for example when you switch between size and count. – Lars Kotthoff Aug 08 '14 at 10:38
  • @LarsKotthoff I am facing the same problem and I have reproduced it here: http://jsfiddle.net/rdesai/sqvov74j/2/ As you suggested, I am running the pack layout again, but its resisting the drag of the bubble. What am I missing? – Rahul Desai Aug 12 '14 at 05:46
  • Not sure what you're doing there in the drag handler, all you need to do is set the transform accordingly: http://jsfiddle.net/sqvov74j/3/ – Lars Kotthoff Aug 12 '14 at 08:11
  • @LarsKotthoff Yes, I have achieved dragging previously, but what I am looking for is that if I drag a bubble which is deep inside the cloud, the other bubbles should occupy the empty space created after dragging. – Rahul Desai Aug 12 '14 at 08:58
  • As I've said, you need to rerun the pack layout without the data for that node. So you need to filter the data for the node out, then pass that to the pack layout and finally redraw everything. – Lars Kotthoff Aug 12 '14 at 09:09
  • @LarsKotthoff How do I filter the data? In the fiddle that I first shared (http://jsfiddle.net/rdesai/sqvov74j/2/) I redrew everything (line # 446). To filter out the selected node I did `removeChild()` but it didnt help. – Rahul Desai Aug 12 '14 at 09:21
  • Line 446 doesn't redraw anything, it just runs the pack layout again. To filter the data, you have to go through every element of your original data (the data, not the DOM). – Lars Kotthoff Aug 12 '14 at 09:51
  • @LarsKotthoff I have been trying it but no success yet. In my code: http://jsfiddle.net/rdesai/sqvov74j/7/, `d.className` gives the text of the bubble that is being dragged, but I am not sure how to filter the data since there is no key `className` in the input data. – Rahul Desai Aug 22 '14 at 11:23
  • You have access to the entire data of the bubble being dragged, so you can filter with that. – Lars Kotthoff Aug 22 '14 at 11:58
  • I think you will have to use the force layout to get the other circles to move while dragging. Here's an example based on your code: http://codepen.io/anon/pen/LqDzn – jeroen.verhoest Sep 08 '14 at 09:32

1 Answers1

1

Here's some code that might achieve the effect you're looking for. It's largely attributable to Mike Bostock's Bubble Chart and Collision Detection examples.

This uses D3's pack layout to initially position the nodes. Then a force layout is used to "auto adjust" the other circles when dragging a node.

Working example at bl.ocks.org

var width = 900;
var height = 500;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var nodes = d3.range(128).map(function () { return {radius: Math.random() * 16 + 8}; });
var nodesCopy = $.extend(true, [], nodes);

function dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

function collide(node) {
  var r = node.radius + 16;
  var nx1 = node.x - r;
  var nx2 = node.x + r;
  var ny1 = node.y - r;
  var ny2 = node.y + r;
  return function (quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x;
      var y = node.y - quad.point.y;
      var l = Math.sqrt(x * x + y * y);
      var npr = node.radius + quad.point.radius;
      if (l < npr) {
        l = (l - npr) / l * 0.5;
        x *= l;
        node.x -= x;
        y *= l;
        node.y -= y;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  };
}

function packup() {
  var pack = d3.layout.pack()
      .sort(null)
      .size([width, height])
      .padding(0)
      .value(function (d) { return d.radius; });

  svg.selectAll(".node")
      .data(pack.nodes({"children": nodes})
      .filter(function (d) { return !d.children; }))
    .enter().append("circle")
      .attr("r", function (d) { return d.radius; })
      .attr("cx", function (d) { return d.x; })
      .attr("cy", function (d) { return d.y; });
}

function forceup() {
  var force = d3.layout.force()
      .nodes(nodes)
      .gravity(0.05)
      .charge(0)
      .size([width, height])
      .start();

  var drag = force.drag().on("dragstart", dragstart);

  force.on("tick", function () {
    var q = d3.geom.quadtree(nodes);
    var i = 0;
    var n = nodes.length;

    while (++i < n) {
      q.visit(collide(nodes[i]));
    }

    svg.selectAll("circle")
        .attr("cx", function (d) { return d.x; })
        .attr("cy", function (d) { return d.y; });
  });

  d3.selectAll("circle")
    .on("dblclick", dblclick)
    .call(drag);
}

function reset() {
  svg.selectAll("*").remove();
  nodes = $.extend(true, [], nodesCopy);
  packup();
  forceup();
}

packup();
forceup();
rphv
  • 5,409
  • 3
  • 29
  • 47