D3.JS Force simulation collision issues

36 Views Asked by At

There is an updated version of this gist. The blue node is a parent one, while orange are children. Basically, the task is to attract children to parent by adding links.

enter image description here

That's the outcome, I'm looking for, however sometimes children could be stopped and stuck by another green dots, like this

enter image description here

And that's really annoying. I'm not sure, but there's probably something wrong with collisions.

Any ideas?

var graph;

var currentLinks = 1;

//  svg and sizing
var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory10);

var link = svg.append("g").selectAll(".link"),
    node = svg.append("g").selectAll(".node");

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody().strength(function(d) { return 50;}))
    .force("collide", d3.forceCollide().radius(d => 10 + 10))
    .force("center", d3.forceCenter(width / 2, height / 2));

d3.select("#switch-btn").on("click", function() {
    
    currentLinks++;
    if(currentLinks > 5) { currentLinks = 1}
    update();
    
});
    
d3.select("#reset-btn").on("click", function() {
    
   location.reload();
    
});

var graph = {
    
    "nodes": [
        { "id": "0", "group": "2" },
        { "id": "1", "group": "3" },
        { "id": "2", "group": "3" },
        { "id": "3", "group": "1" },
        { "id": "4", "group": "1" },
        { "id": "5", "group": "3" },
        { "id": "6", "group": "3" },
        { "id": "7", "group": "1" },
        { "id": "8", "group": "1" },
        { "id": "9", "group": "1" },
        { "id": "10", "group": "1" },
        { "id": "11", "group": "1" },
        { "id": "12", "group": "1" },
        { "id": "13", "group": "1" },
        { "id": "14", "group": "1" },
        { "id": "15", "group": "1" },
        { "id": "16", "group": "1" },
        { "id": "17", "group": "1" }
    ],
    "links1": [
        { "source": "0", "target": "0", "id": "0"},
        { "source": "1", "target": "1", "id": "1"},
        { "source": "2", "target": "2", "id": "2"},
        { "source": "3", "target": "3", "id": "3"},
        { "source": "4", "target": "4", "id": "4"},
        { "source": "5", "target": "5", "id": "5"},
        { "source": "6", "target": "6", "id": "6"},
        { "source": "7", "target": "7", "id": "7"},
        { "source": "8", "target": "8", "id": "8"},
        { "source": "9", "target": "9", "id": "9"},
        { "source": "10", "target": "10", "id": "10"},
        { "source": "11", "target": "11", "id": "11"},
        { "source": "12", "target": "12", "id": "12"},
        { "source": "13", "target": "13", "id": "13"},
        { "source": "14", "target": "14", "id": "14"},
        { "source": "15", "target": "15", "id": "15"},
        { "source": "16", "target": "16", "id": "16"},
        { "source": "17", "target": "17", "id": "17"}
    ],
    "links2": [
        { "source": "0", "target": "5", "id": "0"}
        
    ],
    "links3": [
        { "source": "0", "target": "5", "id": "0"},
        { "source": "0", "target": "2", "id": "0"}
        
    ],
     "links4": [
        { "source": "0", "target": "5", "id": "0"},
        { "source": "0", "target": "2", "id": "0"},
        { "source": "0", "target": "6", "id": "0"}
        
    ],
    "links5": [
        { "source": "0", "target": "5", "id": "0"},
        { "source": "0", "target": "2", "id": "0"},
        { "source": "0", "target": "6", "id": "0"},
        { "source": "0", "target": "1", "id": "0"}
        
    ]
 };

update();
    
function reset() {
    
    link = svg.append("g").selectAll(".link"),
    node = svg.append("g").selectAll(".node");

    simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody().strength(function(d) { return 50;}))
    .force("collide", d3.forceCollide().radius(d => 10 + 10))
    .force("center", d3.forceCenter(width / 2, height / 2));
    
    update();
    
}
    
function update() {

    link = link.data(graph["links" + currentLinks]);
        
    // Remove old links
    link.exit().remove();

    // Create new links as needed.  
    link = link.enter().append("line")
        .attr("class", "link")
        .merge(link);

    node = node.data(graph.nodes);

    node.exit().remove();

    node = node.enter().append("circle")
    .attr("class", "node")
    .attr("r", 10)
    .attr("fill", function(d) {return color(d.group);})
    .merge(node);

    simulation.nodes(graph.nodes).on("tick", ticked);

    simulation.force("link").links(graph["links" + currentLinks]);

    simulation.alphaTarget(.3).restart();
    
}

function ticked() {
    
    link.attr("x1", function(d) { return d.source.x; })
    .attr("y1", function(d) { return d.source.y; })
    .attr("x2", function(d) { return d.target.x; })
    .attr("y2", function(d) { return d.target.y; });

    node.attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    
}
.buttons {
  position: absolute;
  top: 1em;
  left: 1em;
}

.node {
    stroke: white;
    stroke-width: 2px;
}

.link {
    stroke: gray;
    stroke-width: 2px;
}
<div class="buttons">
<button type="button" id="switch-btn">update bubbles</button>
<button type="button" id="reset-btn">reset</button>
</div>
<svg width="500" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>

0

There are 0 best solutions below