Kop4lyf
Kop4lyf

Reputation: 4590

D3 force directed graph: Performance issue in a complex graph

I created a D3 force directed graph having reactangles as nodes and links joining them at the end of the perimeter. Since I need to align the divs according to the rectaengles, I have created two svgs and links in svg1 and acc to z-index, above it are divs and above divs is second svg containing rectangles/nodes( having opacity 0 so that divs are visible). The issue I am having is that the graph is slow in IPAD and it's browser minimizes about 80% times on opening of the page on which the graph was drawn. On debugging, I saw that the problem is with my tick function, whose code is here,

force.start();
q = d3.geom.quadtree(nodes),count=0;
// define what to do one each tick of the animation
force.on("tick", function() {
    i = 0;
    //applying collision detection to avoid overlapping by default
    if(!mapCreated){
       while (++i < nodes.length) q.visit(collide(nodes[i]));
    }

    //this checks if node tries to move out (limitToCorners does that)
    node.attr("x", function(d) { return d.x = limitToCorners(d.x,"x"); })
    .attr("y", function(d) { return d.y = limitToCorners(d.y,"y"); });

    //this puts the link attribute to link directly to center of rectangle
    link.attr("x1", function(d) { return d.source.x+nodeModel.width/2; })
    .attr("y1", function(d) { return d.source.y+nodeModel.height/2; })
    .attr("x2", function(d) { return d.target.x+nodeModel.width/2; })
    .attr("y2", function(d) { return d.target.y+nodeModel.height/2; });

    //changing the CSS so that divs are in same place as the nodes
    changeGoalsPosition(refList);

    // changing the attributes of lines on svg so that it comes to the end of rectange (calculateLinkEndPoints does that)
   for(i=0;i<lines.length;i++){
      var obj = {};
      obj.source = {
            x: parseInt(linksRefList[i].attr("x1")),
            y: parseInt(linksRefList[i].attr("y1"))
      };
      obj.target = {
        x: parseInt(linksRefList[i].attr("x2")),
        y: parseInt(linksRefList[i].attr("y2"))
      };
      $(lines[i]).attr("x1", function(d) { return calculateLinkEndPoints(obj,"sx"); })
      .attr("y1", function(d) { return calculateLinkEndPoints(obj,"sy"); })
      .attr("x2", function(d) { return calculateLinkEndPoints(obj,"tx"); })
      .attr("y2", function(d) { return calculateLinkEndPoints(obj,"ty"); });
   }
 });    

Upvotes: 2

Views: 1541

Answers (1)

Lars Kotthoff
Lars Kotthoff

Reputation: 109232

The processing you're doing in the tick function is quite expensive. You basically have 2 options.

  • Reduce the number of nodes to be processed.
  • Reduce the number of processings of tick events.

Whether you can do the former will depend on your application. For the latter, you could do something like this to skip every other event.

var process = 1;
force.on("tick", function() {
    if(process) {
        // do processing
    }
    process = 1 - process;
});

You could of course skip more events in a similar fashion. At some point, you might notice that the layout becomes "jumpier" because of the skipped events. You can mitigate this problem by moving the elements to their new positions using a transition instead of simply setting them.

Upvotes: 4

Related Questions