Is it possible to get a list of Forces currently active in a D3 Force Simulation?

203 views Asked by At

Just say I have a d3-force simulation, and I add some forces like this:

const simulation = d3
    .forceSimulation()
    .nodes(dots)
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink(links))
    .force("center", d3.forceCenter());
    .on("tick", tick)

Is it possible to list all those forces? Something like a simulation.getForces() method for instance? And have it return ["charge", "link", "center"].

1

There are 1 answers

0
Ruben Helsloot On

I'm afraid not. From the source it looks like the forces Map is not exposed at all to the outside. The only workaround I can think is to either wrap the simulation.force object and store your own registry (which is hacky), or to keep an array of all possible values and see if simulation.force(name) returns anything:

const width = 600,
  height = 600;

const svg = d3.select("body")
  .append("svg")
  .attr("viewBox", [-width / 2, -height / 2, width, height]);

const g = svg.append("g");

const nodes = Array
  .from({
    length: 20
  })
  .map(() => {
    const [x, y] = d3.pointRadial(2 * Math.PI * Math.random(), 200);
    return {
      id: Math.random(),
      x,
      y
    };
  });

simulation = d3.forceSimulation()
  .force("collide", d3.forceCollide().radius(20))
  .force("manyBody", d3.forceManyBody().strength(30))
  .force("center", d3.forceCenter().strength(0.01))
  .alpha(0.1)
  .alphaDecay(0)
  .nodes(nodes)
  .on("tick", () => {
    g.selectAll("circle:not(.exit)")
      .data(simulation.nodes(), d => d.id)
      .join(
        enter => enter.append("circle")
        .attr("fill", d => d3.interpolateSinebow(d.id))
        .attr("r", 1)
        .transition()
        .duration(2000)
        .attr("r", 19)
        .selection(),
        update => update
        .attr("cx", d => d.x)
        .attr("cy", d => d.y),
        exit => exit
        .classed("exit", true)
        .transition()
        .duration(2000)
        .attr("fill", "#eee")
        .attr("r", 1)
        .remove()
      );
  });

const allPossibleForces = [
  "x",
  "y",
  "manyBody",
  "collide",
  "center"
];

const allForces = allPossibleForces.filter(v => simulation.force(v) !== undefined);
console.log(allForces);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>