Change the collision behavior of many nodes stored in an array

不羁岁月 提交于 2019-12-08 08:17:35

问题


when using the force layout in d3.js it is possible to push nodes away with the help of the collision force by increasing an imaginary radius that surrounds the nodes.

I created a seperate button named button and i want to use .data() (to select a whole array) to increase the collide radius into 40 of many nodes when click on that button. For example when a filtered number of nodes is stored in an array called abc, i tried this code:

var node =......
.on("click", function(d, i) 
              {
    abc = start && start.path(d) || [];

    node.style("fill", function(n)
            {
                      if (n == start ) {

                 return "yellow";

                    } else if ( n == d){
                 return "green"
            }

                      else if (abc.includes(n)){
                 return "red"
            }

                      else {
                 return "lightgrey"
            }
    .....
});



        button.on("click", function(d) {
            d3.selectAll("circle").data().forEach(d => d.r = 6);
            d3.select(abc).data().r = 40;
            simulation.nodes(data);
            simulation.alpha(0.8).restart();
        })

I am able to click on 2 nodes and store these 2 nodes and all the nodes between them in the arrayabc. This is possible with the help of the d3.js function path()which returns the shortest way between 2 nodes.
But unfortunally it does not work. Maybe there is someone who can help me with the problem. The basic idea of pushing nodes away is already discussed in here: Using the force-layout physics for seperated elements Thanks so much!


回答1:


After several comments I finally have an idea of how you are filtering the node selection.

In the following demo, the circles have 4 different colours:

var colours = ["blue", "red", "green", "yellow"];

node.attr("fill", (d, i) => colours[i%4]);

So, when you click the button, we simply filter the nodes with "red" colour and increase their r property, making the collide radius increase, using each:

node.filter(function() {
    return d3.select(this).attr("fill") === "red"
}).each(d => d.r = 40);

If you want use data as a getter, you can do it with a forEach:

node.filter(function() {
    return d3.select(this).attr("fill") === "red"
}).data().forEach(d => d.r = 40);

Which has the same result.

Here is a demo, all red nodes will push away the other nodes after the click, with a collide radius of 40:

var svg = d3.select("svg");

var colours = ["blue", "red", "green", "yellow"];

var data = d3.range(30).map(d => ({
    r: 6
}));

var simulation = d3.forceSimulation(data)
    .force("x", d3.forceX(150).strength(0.05))
    .force("y", d3.forceY(75).strength(0.05))
    .force("collide", d3.forceCollide(function(d) {
        return d.r + 1;
    }));

var node = svg.selectAll(".circles")
    .data(data)
    .enter()
    .append("circle")
    .attr("r", d => d.r)
    .attr("fill", (d, i) => colours[i%4]);

d3.select("button").on("click", function(d) {
		node.filter(function(){
		 return d3.select(this).attr("fill") === "red"
		}).each(d=>d.r = 40);
    simulation.nodes(data);
    simulation.alpha(0.8).restart();
})

simulation.nodes(data)
    .on("tick", d => {
        node.attr("cx", d => d.x).attr("cy", d => d.y);
    });
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>
<br>
<svg></svg>


来源:https://stackoverflow.com/questions/42404896/change-the-collision-behavior-of-many-nodes-stored-in-an-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!