Ethics
Ethics

Reputation: 21

How to position a node in the center by radius size?

I want to center nodes in a force directed chart by the size of the circle: we want the largest circle to be centered and the next largest circle around it.

Here is the JSFiddle: https://jsfiddle.net/5zrxgteh/1/

And here are the simulation and tick functions:

 var simulation = d3.forceSimulation()
            .force("center", d3.forceCenter().x(width / 2).y(height / 2))
            .force("charge", d3.forceManyBody().strength(5))
            .force("collide", d3.forceCollide().strength(1).radius(function (d) {
                return rScale(d.value * 1.2);
            }).iterations(3));

 simulation
            .nodes(data)
            .on("tick", function (d) {
                elemEnter.selectAll(".pulse")
                    .attr("cx", function (d) {
                        return d.x = Math.max(50, Math.min(width - 50, d.x));
                    })
                    .attr("cy", function (d) {
                        return d.y = Math.max(50, Math.min(height - 50, d.y));
                    });

                elemEnter.selectAll(".circle")
                    .attr("cx", function (d) {
                        return d.x = Math.max(50, Math.min(width - 50, d.x));
                    })
                    .attr("cy", function (d) {
                        return d.y = Math.max(50, Math.min(height - 50, d.y));
                    });

                elemEnter.selectAll("text")
                    .attr("x", function (d) {
                        return d.x = Math.max(50, Math.min(width - 50, d.x));
                    })
                    .attr("y", function (d) {
                        return d.y = Math.max(50, Math.min(height - 50, d.y));
                    });
            });

Upvotes: 2

Views: 389

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

If you stop to think what you want is complicated both mathematically and visually speaking, because if the biggest circles are next to the center and the distance to the center is proportional to the circle's diameter, then you'll have a lot of empty spaces next to the center, and the simulation won't deal nicely with it.

However, just for trying, this is a possible solution: ditch d3.forceCenter and use forceX and forceY instead, adjusting their strengths according to the circle's size.

For that to work, we first set up a scale:

var strengthScale = d3.scalePow()
    .exponent(2)
    .range([0, 1])
    .domain([0, d3.max(data, function(d) {
        return d.value;
    })]);

Then, we change the simulation:

var simulation = d3.forceSimulation()
    .force("x", d3.forceX(width / 2).strength(function(d) {
      return strengthScale(d.value);
    }))
    .force("y", d3.forceY(height / 2).strength(function(d) {
      return strengthScale(d.value);
    }))
    .force("charge", d3.forceManyBody().strength(5))
    .force("collide", d3.forceCollide().strength(1).radius(function(d) {
      return rScale(d.value * 1.2);
    }).iterations(30));

Here is the resulting JSFiddle: https://jsfiddle.net/mea9nygb/2/

Upvotes: 2

Related Questions