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

Creating a new boundary force #115

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

john-guerra
Copy link

Many times when I'm doing network visualizations I want to guarantee that the nodes can't leave the canvas. Positioning forces can help on this for smaller networks, but when the number of nodes increase, it is easy to get nodes outside the view point. Moreover, given rectangular canvas, I want to use all the corners too for the visualization and centering forces usually display everything on a circle. Therefore I created a d3.forceBoundary that keeps nodes inside a boundary and decrementally reduces its strength towards the middle so it can play with other forces. Please let me know if you would consider adding this to d3 (and thanks for the hard work!)

@vasturiano
Copy link
Contributor

vasturiano commented Apr 15, 2018

@john-guerra just chiming in to mention my experience is also that this force type would be super useful! It is so often needed that I think it earned its place within d3-force core.
If not however, then it should definitely be published as an external force plugin (and listed on d3-force-registry).

Just a few small remarks:

  • The strength feature is great. In different cases you sometimes want nodes to bounce off the container walls or not. Was thinking maybe the option could be called elasticity, alluding to the coefficient of restitution in elastic collisions.
    [EDIT] Please ignore above. I understand the strength parameter has a different nature, to pull the outside nodes to the boundary lines. So, do you think an additional "collision elasticity" parameter would still make sense for this force type, or should perhaps be a separate force altogether?

  • Is it possible to specify a radius or similar on the nodes, so that the whole node is kept inside the container, and prevent the node's body from crossing the boundary line. Basically, the collision point would respect the node's surface, not its center coordinates.

And thanks for putting this together!

@john-guerra
Copy link
Author

@vasturiano glad you liked it, and thanks for the recommendations

@hornj
Copy link

hornj commented Jul 30, 2018

I took the code from this merge request and implemented it as a d3 plugin, but it seems like there's a bug in how it works. In my visualization I create the d3.force element, and add the boundary force, and add the nodes array - everything works fine.

Then later I add a new force.nodes(). With an active forceBoundary, all the nodes end up stuck at the center of the graph. This happens even if I do a .initialize(nodes) on the forceBoundary. To get them to continue to position properly, I have to completely readd the force like so:

linksForce.force("boundary", d3.forceBoundary(-(svgWidth/2), -(svgHeight/2), svgWidth/2, svgHeight/2).initialize(currentNodes));

From my understanding of the documentation, I should just be able to use the .initialize method, and not recreate the entire force. If you'd like I can try to create a code sample to show the issue.

@john-guerra

@john-guerra
Copy link
Author

@hornj Yes, can you please create an example of your problem?

You don't really need to call the initialize function by yourself, d3 would do that for you. I'll recommend using positive values on your boundary, the library should work with negative values too, but I always find it easier to wrap my head around positive values.

Here is an observable using a simple network with negative values like your https://beta.observablehq.com/@john-guerra/d3-force-directed-graph-with-force-boundary
And here is the original observable I made to explain it
https://beta.observablehq.com/@john-guerra/d3-force-boundary

@hornj
Copy link

hornj commented Aug 1, 2018

@john-guerra I'm working on creating an example, but you're right the initialize function doesn't have anything to do with it. It returns undefined, so the line of code I posted was effectively removing the boundary force.

I'll try to find a concrete example and let you know.

node.vy += getVx(halfY[i], node.y, strengthsY[i], borderz[i], alpha);
} else {
node.vx = 0;
node.vy = 0;
Copy link

Choose a reason for hiding this comment

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

I tried this and found that setting vx/vy to zero broke my simulation (i'm applying forceBoundry last).
most likely this else should be removed

Copy link

Choose a reason for hiding this comment

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

this may be what @hornj encountered

Copy link
Author

Choose a reason for hiding this comment

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

Thanks @kumavis, I'll check it out

@kumavis
Copy link

kumavis commented Apr 18, 2019

+1 for this feature

@nathanvogel
Copy link

Small question: how this different from using forceX and forceY like here? Pro/cons?

      .force("x", d3.forceX())
      .force("y", d3.forceY())

Thanks for the plugin in any case, I'm already using it now 😄

@john-guerra
Copy link
Author

@nathanvogel positional forces are great, but when you have a larger disjoint graph, your nodes will tend to leave the viewport. See this notebook where increased the value of charge for the ManyBody force. Try turning on and off the forceBoundary (when off it will use just positional forces), and let me know if it makes sense https://observablehq.com/d/186feb840a7e5b06.

Also @kumavis @hornj @vasturiano, I published the library as an npm module https://www.npmjs.com/package/d3-force-boundary, I think it still has bugs, but it works nicely with observable

@nathanvogel
Copy link

@john-guerra Awesome! Thanks a lot for the Observable, the effects are super clear 👌 (in my first use case the difference was minimal, but this will get useful when I get more data into my project)

@vasturiano
Copy link
Contributor

@john-guerra nice! I've added it to the list at d3-force-registry.

@hornj
Copy link

hornj commented May 8, 2019

@john-guerra Thanks for publishing this. I'm planning to try this again soon. I ended up giving up when I tried to use this before as something in the code or how I implemented it was causing my graphs to just disappear at certain points.

@Fil Fil mentioned this pull request 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

Successfully merging this pull request may close these issues.

5 participants