Search code examples
javascriptsvgd3.jscollision-detectiontransition

Collision/overlap detection of circles in a d3 transition


I'm using d3 to animate a route (path) on a map. When the route reaches a point along the route I'd like to popup some information.

Most of my code is based on the following example. http://bl.ocks.org/mbostock/1705868. I'm really just trying to determine if there is a way to detect when the transitioning circle collides or overlaps any of the stationary circles in this example.


Solution

  • You can detect collision in your tween function. Define a collide function to be called from inside the tween function as follows:

    function collide(node){
        var trans = d3.transform(d3.select(node).attr("transform")).translate,
          x1 = trans[0],
          x2 = trans[0] + (+d3.select(node).attr("r")),
          y1 = trans[1],
          y2 = trans[1] + (+d3.select(node).attr("r"));
    
      var colliding = false;
      points.each(function(d,i){
        var ntrans = d3.transform(d3.select(this).attr("transform")).translate,
          nx1 = ntrans[0],
          nx2 = ntrans[0] + (+d3.select(this).attr("r")),
          ny1 = ntrans[1],
          ny2 = ntrans[1] + (+d3.select(this).attr("r"));
    
    
          if(!(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1))
            colliding=true;
      })
    
      return colliding;
    }
    

    Where points are the stationary points, and node is the transitioning element. What collide does is check whether node overlaps with any of the points (as shown in collision detection example here).

    Because we need the node to be passed to the tween function, we replace attrTween used in Mike's example, with tween:

    circle.transition()
          .duration(10000)
          .tween("attr", translateAlong(path.node()))
          .each("end", transition);
    

    Finally, the tween function calling our collide:

    function translateAlong(path) {
      var l = path.getTotalLength();
      return function(d, i, a) {
        return function(t) {
          var p = path.getPointAtLength(t * l);
    
          d3.select(this).attr("transform","translate(" + p.x + "," + p.y + ")");
    
          if(collide(this))
            d3.select(this).style("fill", "red")
           else
            d3.select(this).style("fill", "steelblue")
        };
      };
    }
    

    See the full demo here