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

Refactor ForceSimulation to allow for more dynamism #205

Merged
merged 7 commits into from
Jun 24, 2024

Conversation

regexident
Copy link
Contributor

@regexident regexident commented Jun 23, 2024

This admittedly major refactor of ForceSimulation resolves #198, #202, #203 and #204, while adding support for highly dynamic, yet efficient simulations.

Changes

Improvements

Bug Fixes

New features

Example changes

  • Adds all sorts of controls to "Force Graph" example for simulation forces and parameters

Copy link

changeset-bot bot commented Jun 23, 2024

⚠️ No Changeset found

Latest commit: 5e911f7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Jun 23, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
layerchart ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 24, 2024 1:20pm

@regexident
Copy link
Contributor Author

FYI: I added "collide easing" (as described in #198) to the "Force Graph" example for illustration purposes. I don't expect/intend it to remain there. This goes for most of the example changes in this PR, honestly.

@techniq
Copy link
Owner

techniq commented Jun 23, 2024

@regexident Wow, this looks great, thanks! Will take me a bit to read through fully (definitely more complicated implementation from before), but makes sense at first pass.

I did notice a regression with the Force Group example no longer clumping. My guess is related to "does not re-initialize forces on unrelated prop-changes".

CleanShot.2024-06-23.at.10.02.08.mp4

Also, what do you say we keep the changes you made to Force Graph as a new "Force Debug" or "Force Playground" example?

Comment on lines 51 to 95
$: {
// Any time the `stopped` prop gets toggled we
// update the running state of the simulation:
updateStateOnChangeOf({
stopped,
_static,
});
}

$: {
// Any time the `static` prop gets toggled we
// either attach or detach our internal event listeners:
updateEventListenersOnChangeOf({ _static });
}

$: {
// Any time the `$data` store gets changed we
// pass them to the internal d3 simulation object:
updateNodesOnChangeOf({ data: $data });
}

$: {
// Any time the `forces` prop gets changed we
// pass them to the internal d3 simulation object:
updateForcesOnChangeOf({ forces });
}

$: {
// Any time the `alpha` prop gets changed we
// pass it to the internal d3 simulation object:
updateAlphaOnChangeOf({ alpha });
}

$: {
// Any time any of the the alpha props get changed we
// pass them all to the internal d3 simulation object
// (they are cheap, so passing them as a batch is fine!):
updateSettingsOnChangeOf({
alphaTarget,
alphaMin,
alphaDecay,
velocityDecay,
_static,
});
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we couldn't move each of these function implementations inline to the related $: { ... } statements. I don't think they are needed to hide reactivity from Svelte (sometimes a trick I use, especially for circular references). I think this would help readability. I spot checked most of them and they looked to be called once.

We could also add a runStaticOrResumeDynamicSimulation() to simplify them as well.

function runStaticOrResumeDynamicSimulation() {
    if (_static) {
      runStaticSimulation();
    } else {
      resumeDynamicSimulation(alpha);
    }
}

Copy link
Contributor Author

@regexident regexident Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[…] I don't think they are needed to hide reactivity from Svelte (sometimes a trick I use, especially for circular references). […]

That's actually exactly why I added them at some point in the refactor. 😄
But on second glance it looks like in the state they ended up in no (major) hiding is necessary anymore, so I'll try to inline them where possible. 👍🏻


let nodes: any[] = [];

const simulation = forceSimulation().stop(); //.nodes(nodesFromData());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove //.nodes(nodesFromData());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch!

@regexident
Copy link
Contributor Author

@regexident Wow, this looks great, thanks! Will take me a bit to read through fully (definitely more complicated implementation from before), but makes sense at first pass.

Yeah, it is indeed quite a bit more complex. 😅 But at the benefit of being much, much more efficient and also more versatile.

I did notice a regression with the Force Group example no longer clumping. My guess is related to "does not re-initialize forces on unrelated prop-changes".

CleanShot.2024-06-23.at.10.02.08.mp4

Oh, I missed that button, good catch, I'll look into it!

Also, what do you say we keep the changes you made to Force Graph as a new "Force Debug" or "Force Playground" example?

Either that or I would just add them to the "Force Graph" example directly. Minus some of the bells and whistles that were only meant as a demonstration of things this refactor enables, such as the controls for "State", "Status", "Charge Distance Min", "Charge Distance Max", and "Collide Strength Target" (which I would just replace with a simple "Collide Strength" control).

@regexident
Copy link
Contributor Author

@techniq One significant (but intentional) change of this refactor is that <ForceSimulation … static={false}> now needs manual reheating (by either adjusting alpha for an instant re-heat, or alphaTarget for a smoother re-heat).

I think that this behavior is a net-positive, as it …

  • a) now closely resembles d3-force's behavior, making it fairly straight-forward to port d3 visualizations to LayerChart.
  • b) is highly versatile in what fine-tuned behavior is supported (e.g. reduced re-heating on drag, etc.)
  • c) instant full-on reheating (as prior to this PR) is still possibly by re-setting alpha={1}

It's also important to note that with today's ForceSimulation any changes to cached forces already require manual re-heating, but with the big caveat of alpha not updating, so alpha={1} might actually get swallowed by Svelte, if that's what it was set to before.

So in a way things are more consistent now.

@regexident
Copy link
Contributor Author

@techniq I've adapted all other examples under "Force", resolving any regressions (like the one you mentioned above) and making use of more efficient force caching.

Copy link
Owner

@techniq techniq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thanks!

@techniq techniq merged commit cc3e78f into techniq:main Jun 24, 2024
4 checks passed
@regexident regexident deleted the dynamic-force-simulation branch June 24, 2024 14:21
regexident added a commit to regexident/layerchart that referenced this pull request Jun 26, 2024
regexident added a commit to regexident/layerchart that referenced this pull request Jun 26, 2024
techniq pushed a commit that referenced this pull request Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment