egui_node_graph
                                
                                 egui_node_graph copied to clipboard
                                
                                    egui_node_graph copied to clipboard
                            
                            
                            
                        many-to-many connections support
This patch is an attempt at adding many-to-many connection support without overhauling the library and minimal breaking changes to the user.
The core of the idea is to replace Graph::connections from SecondaryMap<InputId, OutputId> to SecondaryMap<InputId, HashSet<OutputId>> and roll from that.
Ports are parametrized by an argument specifying the maximum non-zero number of connections (not yet enforced), and if that number is greater than 1 a so called wide-port is drawn -- it's characterized by being longer than a typical port and connections made to it are spread out and can be disconnected individually.
Known limitations:
- Again, max connections per node is not enforced.
- HashSet<_>isn't an adequate data structure for storing the connected outputs -- we have no control over ordering, let alone the end user.
- API needs fleshing out.
- Dragging from a non-empty wide port always triggers a disconnect.
- ...?
I felt like #30 and #81 offer an overly complicated solution to the problem of many-to-many connections, so I'm messing around with a different approach. I'd appreciate comments regarding the general design of the feature.
Do note that this is purely an experiment to see if this approach can even lead to a workable solution that offers good UX and simple, flexible API.
This leads to the question: Do we care about the ordering of parameters? In your example, an addition operation would not care, since in those cases, as it is commutative. But what happens with operations that are not commutative?
Having sat on the issue for a while, these are my thoughts:
- Ordering of inputs is important not only because the consumer might care (honestly, I'd rather discourage using wide ports for non-commutative operations), but it's crucial from the UX-perspective. If the user is not able to order connections at will, unwanted intersections of connections are bound to come up.
- HashSet<_>seemed like an elegant structure for this and I hoped that ordering could come from someplace else, but this would make it more convoluted to access for the end user. I'll switch to just using a- Vec<_>.
Implemented ordered inputs, here's the current status: nodes_ordering.webm. Sorry for the invisible cursor
Screenshots
(The 
args label missing in the first image is fixed by the last commit, ignore that.)
Looks like this is feature-complete for now. I'll clean up the code, rebase and mark it as ready to review.
Amazing! :tada: Thanks a lot for the hard work @kamirr.
I'd say with the nice round of recent changes, this being merged would be a good time for a release. But I'm not sure if you're planning on tackling any other tasks, there's really no rush so we can wait.
I'd like to work on documentation before the next release, I'll get to that right after we get this PR merged.
Ps. I was a bit too eager to declare feature-completeness, max_connections still isn't enforced, but that's easy enough to address.
Should be ready for review:
- Rebased onto main.
- Some cleanups in UI.
- Deleted changes in the example app.
- Enforce max_connections.
The history of my changes is lost due to the rebase with squashes I did, but it can be browsed on the kek/many-to-many2 branch on my fork.
Thanks for all the amazing work :) I'll review this soon, but I already made a quick look and things are looking good.
I'm not sure how hard it would be to extend the current design into something that allows users to reorder the connections in a wide port. Essentially, replace that HashSet with a Vec
My 2 cents: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html is insertion-sorted :)