How to implement collision avoidance of points in d3.js v5?

时间:2019-04-16 22:15:38

标签: javascript d3.js collision-detection scatter-plot

I am trying to plot a scatter-plot without any collision between the points. Every point represents one politician and the distance between two points represents their cooperation in voting; therefore, the distances do not have to be exact.

I managed to create the scatter-plot, but got stuck on the collision problem. I tried many tutorials and implementations, but the points either did not move at all, or disappeared when collided.

All the implementations that I tried used function forceSimulation with collision force.

This is my current version, where the forceSimulation has no effect on the resulting canvas. It looks exactly the same as if it is commented out.

init of the data: data = [{x:num, y:num, radius:num, xstart:num, ystart:num}, ...]

   var data = [];
   for (var x =0; x < positions.length; x++) {
       data.push({
           "xstart": positions[x][0],
           "ystart": positions[x][1],

       });
    }
  data.forEach(function(d) {
    d.x = xscale(d.xstart);
    d.y = yscale(d.ystart);
    d.radius = radius;
  });

creation of points:

var dots = g.selectAll("scatter-dots");

dots
    .data(data)
    .enter().append("circle")
        .attr("cx", function (d) { return d.x; } )
        .attr("cy", function (d) { return d.y; } )
        .attr("r", radius)
        // additional attributes
        .each(function (d, i) {
            d3.select(this)
                .attr("id", "poslanec_"+i)
                .attr("class", "poslanec")
                .append("title")
                    .text(osoby[keys[o][i]]["cele_jmeno"]);
        });

the problematic part: collision avoidance

var simulation = d3.forceSimulation(data)
        .force("collision", d3.forceCollide(radius))
        .on("tick", update);

function update () {
    dots
        .attr("cx", function(d) {return d.x;})
        .attr("cy", function(d) {return d.y;});

// this log prints an unchanged array
   console.log(this.nodes());
}

I expected that each colliding point would be moved slightly to the side (according to its radius) so no points would overlap and only touch their neighbors.

Thank you in advance for any insights!

1 个答案:

答案 0 :(得分:0)

我发现某个点的某些版本的轴不会自动更新,而是存储在速度的vx / vy中;因此,解决方案是从轴上减去速度。

即使不是很漂亮,我的解决方案:

function update () {
    var nodes = this.nodes();
    nodes.forEach(function (d, i) {
        //selecting by an id of a point
        d3.select('#poslanec_'+nodes[i].id)
            .attr("cx", (nodes[i]["x"] - nodes[i]["vx"]))
            .attr("cy", (nodes[i]["y"] - nodes[i]["vy"]));
    });     
}