Nelarius / imnodes

A small, dependency-free node editor for dear imgui
MIT License
2.01k stars 244 forks source link

Editor navigation mini-map #94

Closed briancairl closed 3 years ago

briancairl commented 3 years ago

Some background

Something that seems useful for large graphs is a way of quickly navigating to nodes in some sort of "zoomed out" context. I searched through some old repo issues to find that this has been brought up in the past, but isn't practically achievable with ImGui in its current state.

One alternative to zooming in the editor panel that I wanted to try was a navigable mini-map of the node editor context panel.

Prototype design

I've made a prototype version of this based on some basic design details:

https://user-images.githubusercontent.com/1093236/112758246-26998000-8fa2-11eb-9945-27a1a73b48c7.mp4

https://user-images.githubusercontent.com/1093236/112758257-3618c900-8fa2-11eb-8e0b-cdfd7576d7da.mp4

https://user-images.githubusercontent.com/1093236/112758959-bb51ad00-8fa5-11eb-931e-7ae67045145e.mp4

Implementation

I've added some code for the current prototype here using the existing imnodes implementation and ImGui functions. Some details are left a bit vague, namely, the containers storing node/link IDs and how I'm mapping from link-to-node IDs. Previously, I was using a function I added to the imnodes library in my local fork to get nodes IDs from links IDs without having to do any external structures to hold on to the link-to-node mapping, specifically.

However, I propose that this functionality could be made into an complimentary library feature, since all the necessary information for rendering it is available in the imnodes internals.

I imagine it as a widget which:

Questions

briancairl commented 3 years ago

Apologies about the videos not showing the mouse cursor. I was having some issues with my usual screen capture program.

Nelarius commented 3 years ago

Thanks for the great write-up @briancairl ! 👍

Reframing the minimap as just another view of the editor context and using the interaction with the minimap to drive the main editor view seems like a good way to think about this feature.

Regarding your questions

Some additional thoughts There are pros and cons to having this feature as an integrated library feature.

Some of the things you mentioned are definitely pros. Imnodes could easily do the heavy lifting, since it already has everything it needs to render a minimap. Imnodes could also use the actual node geometry, i.e. the rectangle shape, to render the node proxies, something which can't be done (or would be really inconvenient to do) from user code.

Having a MiniMap function as a part of the Imnodes header is also a drawback -- Imnodes can only render a minimap after EndNodeEditor has been called. Only at that point do we actually have all the information we need: the nodes, link positions, as well as which node is actually hovered or selected. So this would somewhat restrict where the user can place a minimap, as they can't render it in the same window before the node editor canvas (without repositioning the internal cursor, etc.). If the minimap was created by the user, based on the user-data, they could render it anywhere they want. But they pros might outweigh the cons in this case.

What do you think about showing multiple links between nodes? I think currently they are rendered as a single bezier curve, but I suppose we could actually show them as distinct curves. What that help orienting the user in the minimap, or would it introduce too much clutter, I wonder? 🤔

Finally, a heads-up. Don't get too attached to the current internals of the library. I'm going to try to spend the next few days doing some much-needed clean-up and refactoring, and the changes are probably all going to cause massive conflicts with any other work 😅 The first pr is up: https://github.com/Nelarius/imnodes/pull/95 There's a few internal complexities that I would like to try resolve entirely, having to do with the object pool, and the pervasive use of indices to refer to the internal objects.

briancairl commented 3 years ago

@Nelarius To address one of these points, since its been on my mind:

Having a MiniMap function as a part of the Imnodes header is also a drawback -- Imnodes can only render a minimap after EndNodeEditor has been called. Only at that point do we actually have all the information we need: the nodes, link positions, as well as which node is actually hovered or selected

This came to mind when I sat down to look over this the other day, though I didn't think about it so carefully when I made my prototype implementation :sweat_smile: . I think this is true if the user is given the ability to place the mini-map in any window/context. Even so, it can be done if the last draw info for nodes/links/etc. is available when the mini-map is drawn. I'm not a huge fan of depending on old draw info from the last frame, but its a way that this could be done.

Conversely, drawing the mini-map as an editor overlay (like a heads-up display) could avoid the dependence on old draw data altogether. BeginNodeEditor could have an extra flag to enable the mini-map overlay, and EndNodeEditor could draw the mini-map, if enabled.

imnodes::BeginNodeEditor(ImNodesNodeEditor_EnableMiniMap);

// draw nodes and links

imnodes::EndNodeEditor();  // <-- mini-map overlay computations/interaction handling/drawing done here

image

This, of course, make Begin/EndNodeEditor more complex, which you might not want. It also introduces a new problem around the sizing and positioning of the mini map. I'm not sure that mini-map sizing is something that needs to be exposed to the user, or can be auto-magically sized given the size of the editor context. As for positioning, I think this is a simpler matter. the ImNodesNodeEditor_EnableMiniMap could be more specific to allow the used to place the mini-map in different corners of the editor:

enum {
  ImNodesNodeEditor_EnableMiniMapTopLeft,
  ImNodesNodeEditor_EnableMiniMapBottomLeft,
  // etc.
}

There are also other pay-for-use draw backs like the amount of editor real estate the mini-map takes up as an overlay. There's a bunch of fiddling to do around relative sizing and opacity when focused/not to make the mini-map as innocuous as possible when not being used for navigation.

As for the other points, I'll give these some more thought when I put my head to it after you've completed your internal cleanups -- I was just about to start work on this again, so thanks for letting me know before the merge conflicts! :smile:

Let me know what you think about the mini-map as a HUD.

Nelarius commented 3 years ago

Rendering the minimap as an overlay seems a neat way of solving the EndNodeEditor dependency problem. Seems like a better user experience than relying on the previous frame's data 🤔 Although, like you mentioned, it probably will require a bit of fiddling to make the user experience nice.

A quick note about complexity. If we can keep the interface simple and orthogonal to the rest of the interface in imnodes.h than I don't think the actual implementation size matters (unintended complexity is a different matter! 😄 ). The proposed interface, a single Minimap function, does a lot of work for the user 🙂 It doesn't interact in any way with the rest of the functions in the header (except for, perhaps, some additional customizability), so in this way I think it is quite an ideal interface.

Which brings me to the refactoring. I've merged all the bigger structural changes I'll do for the time being. It's mostly formatting changes which went in. imnodes.cpp contains unintended complexity, and I've started rewriting some of the internals from scratch: https://github.com/Nelarius/imnodes/commits/internal-refactor It's going to take quite a while, though -- thus, no need to wait for me on this if you have the drive to tinker with this now. Merge conflicts are something which can be figured out, should it come to it 🙂

briancairl commented 3 years ago

@Nelarius Here's a first pass https://github.com/Nelarius/imnodes/pull/99

Among other things, the default colors probably need some tuning :sweat_smile:

briancairl commented 3 years ago

Closing, since feature was added with https://github.com/Nelarius/imnodes/pull/99.