Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider a .forces() method that gets/sets the forces map #43

Closed
nfcampos opened this issue Jul 11, 2016 · 3 comments
Closed

Consider a .forces() method that gets/sets the forces map #43

nfcampos opened this issue Jul 11, 2016 · 3 comments

Comments

@nfcampos
Copy link

While building a react component that renders a force layout chart I'm finding that my work would be easier if there was a method on the simulation that allowed me to get all forces on the simulation, so that I can compare to props passed to the component when the props change. It would also be useful to be able to set all the forces simultaneously, but the hard one to do outside of d3-force is getter, because without there's no way of knowing what forces are currently assigned to the simulation.
Would you consider adding such a method?
I'd be happy to send a PR if yes.
Thanks either way,
Nuno

@mbostock
Copy link
Member

Hmm. Well, simulation.forces would need to return a copy, and make a defensive copy, of the map to prevent the map from being modified without force.initialize being called. And using simulation.forces to set new forces is potentially slower than simulation.force if it causes some of existing forces to be re-initialized… Also it exposes the fact that the simulation uses d3.map under the hood, which I’m not wild about.

A more conservative approach might be to just add a simulation.forces getter method which takes no arguments and returns an array of {key, value} objects representing the bound forces. It could be implemented as:

forces: function() {
  return forces.entries();
},

That seems like the most conservative approach as far as enabling inspection goes. The downside is that it’s not as easy to make bulk changes to the registered forces.

@nfcampos
Copy link
Author

@mbostock Thanks for the reply!
Yes, the getter like you described would be enough for my needs. My need for it comes down to the way I'm thinking about the props of the force layout component, ie.

<ForceLayout
  nodes={[...]}
  forces={{
    link: forceLink(...),
    center: forceCenter(...),
  }}
/>

and the component internally takes both nodes and forces and creates the simulation. (this is why I'd need the getter, to be able to know which of the forces passed in now are new.)
I'm however not sure whether it'd be better to just accept an already configured simulation object as a prop and avoid all this.

const sim = forceSimulation()
  .nodes([...])
  .force('link', forceLink())
  .force('center', forceCenter())

<ForceLayout
  simulation={sim}
/>

do you have any opinion either way?

@Fil
Copy link
Member

Fil commented Jun 25, 2020

Closing due to inactivity. Note that another possibility is to create

function logger(simulation) {
  simulation.forces = new Map();
  simulation.addforce = simulation.force;
  simulation.force = (name, force) => (
    simulation.forces.set(name, force), simulation.addforce(name, force)
  );
  return simulation;
}

then call simulation = logger(d3.forceSimulation()) .force("x", …).force("collide", …)

simulation.forces will then hold the map of forces (see https://observablehq.com/d/d999cd71c09c86ef).

@Fil Fil closed this as completed Jun 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants