score:1

Accepted answer

you need to fill the whole rect with guard nodes of the correct size.

If you want to see the guard circles un-comment part of the code.

There are 2 g elements, one containing the guard nodes and the other the blue nodes.

Edit

If you make the X-force a bit stronger the nodes get closer to the actual centers.

.force('x', d3.forceX()
    .x( (d, i) => (d.type === "circle") ? circleCenters[i % 3] : d.fx )
    .strength(0.3))

const nodes = d3.range(100).map(d => ({radius: 5, type: "circle"}));

const walls = [{}, {}, {}, {}].map((_, index) => ({
    fx: 200 * index,
    fy: 100,
    width: 100,
    height: 10,
    radius: 5,
    type: "wall"
}));

const circleCenters = [100, 300, 500];

// construct "invisible" circles covering the rects
var invCircles = [];
walls.forEach(e => {
    d3.range(e.fx+3, e.fx+e.width-3, 3).forEach(cx => {
        invCircles.push({
            fx: cx,
            fy: e.fy + e.radius,
            radius: e.radius,
            type: e.type
        });
    });
});

d3.forceSimulation(nodes.concat(invCircles))
    .force('charge', d3.forceManyBody().strength(10))
    .force('x', d3.forceX().x( (d, i) => (d.type === "circle") ? circleCenters[i % 3] : d.fx ).strength(0.3))
    .force('y', d3.forceY().y(100))
    .force('collision', d3.forceCollide().radius(d => d.radius))
    .on('tick', ticked);

var wallGeom = d3.select('svg').append('g').attr('class', 'wall');
var circlesGeom = d3.select('svg').append('g').attr('class', 'circles');

wallGeom.selectAll('rect')
    .data(walls)
    .enter()
    .append('rect')
    .attr('width', d => d.width )
    .attr('height', d => d.height )
    .attr('fill', 'red')
    .attr('x', d => d.fx)
    .attr('y', d => d.fy);

// wallGeom.selectAll('circle')
//     .data(invCircles)
//     .enter()
//     .append('circle')
//       .attr('fill', 'yellow')
//       .attr('r', d => d.radius)
//       .attr('cx', d => d.fx)
//       .attr('cy', d => d.fy);

function ticked() {

    const u = d3.select('svg')
        .select('.circles')
        .selectAll('circle')
        .data(nodes);

    u.enter()
        .append('circle')
        .attr('fill', 'blue')
        .attr('r', d => d.radius)
      .merge(u)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
<svg width="90vw" height="90vh"></svg>


Related Query

More Query from same tag