Files
2026-06-28 13:51:16 +01:00

213 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Node Graph UI with DearImGui — Research
## Overview
Three main approaches exist for building a node graph UI with DearImGui:
1. Use a standalone library like **ImNodes** or **imgui-node-editor**
2. Build from scratch using ImGui's draw list API
3. Use a hybrid — custom canvas with ImGui widgets
---
## 1. Existing Libraries
### 1.1 imnodes (Nelarius)
- **Stars**: ~2.4k | **License**: MIT | **Status**: Active (last push 2024)
- **Files**: `imnodes.h`, `imnodes_internal.h`, `imnodes.cpp` — drop-in, no deps beyond ImGui
- **API style**: Immediate-mode, mirrors ImGui idioms
```cpp
imnodes::BeginNodeEditor();
imnodes::BeginNode(node_id);
imnodes::BeginNodeTitleBar();
ImGui::Text("Node Name");
imnodes::EndNodeTitleBar();
imnodes::BeginInputAttribute(pin_id);
ImGui::Text("input");
imnodes::EndAttribute();
imnodes::BeginOutputAttribute(pin_id);
ImGui::Text("output");
imnodes::EndAttribute();
imnodes::EndNode();
imnodes::EndNodeEditor();
```
**Strengths:**
- Minimal, dependency-free, easy to vendor
- True immediate-mode — user owns all state
- Pins auto-align with embedded ImGui widgets
- Simple `ImNodes::Link(id, from, to)` API
**Weaknesses:**
- Less feature-rich (no built-in minimap, no grouping, limited theming)
- No built-in serialization of layout
- Slower development velocity
**Under the hood:**
- Uses `ImDrawList::ChannelsSplit()` to layer node backgrounds behind UI
- Pins are detected via `ImGui::BeginGroup` bounding box capture
- Link picking uses hierarchical bezier subdivision
### 1.2 imgui-node-editor (thedmd / Michal Cichon)
- **Stars**: ~4.4k | **License**: MIT | **Status**: Active
- **Files**: `imgui_node_editor.h/.cpp` + `imgui_canvas.h/.cpp` — also drop-in
- **API style**: retained-state editor context, user draws content
```cpp
ax::NodeEditor::Begin("Editor");
ax::NodeEditor::BeginNode(node_id);
ax::NodeEditor::BeginPin(pin_id, ax::NodeEditor::PinKind::Input);
ImGui::Text("input");
ax::NodeEditor::EndPin();
ax::NodeEditor::EndNode();
ax::NodeEditor::End();
```
**Strengths:**
- Rich feature set: zoom/pan, minimap, selection, context menus, copy/paste
- Blueprint-UE4-inspired default theme
- Bézier curve links with flow animation
- Configurable zoom levels, drag/navigate/select button mapping
- `ImGuiEx::Canvas` can be used independently for custom infinite-workspace UIs
- Built-in serialization callbacks (`SaveSettings`/`LoadSettings`)
**Weaknesses:**
- Heavier than imnodes — more code, larger API surface
- Editor context is a retained object (less "pure" immediate mode)
- Can conflict with ImGui's own ID stack during complex widget embedding
**Key API patterns:**
| Concern | API |
|---|---|
| Create link | `BeginCreate()` / `QueryNewLink()` / `AcceptNewItem()` / `EndCreate()` |
| Delete | `BeginDelete()` / `QueryDeletedLink()` / `AcceptDeletedItem()` / `EndDelete()` |
| Suspend for popups | `Suspend()` / `Resume()` — pops out of canvas coordinate space |
| Styling | `PushStyleColor()` / `PushStyleVar()` — 20+ style variables |
### 1.3 ImNodeFlow (Fattorino)
- **Stars**: Newer (20242025) | **License**: MIT
- Even more feature-packed: node categories, commenting, layout algorithms
- Still maturing; less battle-tested than the two above
> **Recommendation for Prism**: start with **imgui-node-editor** if we want a polished editor quickly, or **imnodes** if we want minimal deps and full state control. The custom approach (next section) is best if we have very specific rendering needs.
---
## 2. Custom Node Graph from Scratch
Building a node graph manually using `ImDrawList` gives maximum control but requires handling:
### 2.1 Canvas / Coordinate System
An infinite-zoom canvas requires:
- An offset (`ImVec2`) and scale (`float`) transform
- Conversion functions between screen ↔ canvas space
- Clipping to the parent ImGui window
The `imgui-node-editor` library includes an `ImGuiEx::Canvas` utility that handles this standalone — it can be extracted and reused.
### 2.2 Rendering Nodes (DrawList)
Nodes are typically rendered in layers:
1. **Background layer**: grid dots/lines, selection rectangle
2. **Node bodies**: rounded rectangles (`AddRectFilled`)
3. **Node borders**: rect strokes, optionally thicker on hover/select
4. **Pins**: small circles or squares on left/right edges
5. **Links**: cubic Bézier curves between pin centers
6. **UI overlay**: selection handles, context menus
Use `ImDrawList::ChannelsSplit()` for correct z-ordering when mixing drawn shapes with ImGui widgets.
### 2.3 Interaction Handling
| Interaction | Implementation |
|---|---|
| Pan | Track middle-mouse drag → modify canvas offset |
| Zoom | Mouse wheel → modify scale (clamp to range, center on cursor) |
| Drag node | Hit-test node bodies (invis buttons or rect test), track delta → update node position |
| Select | Rectangular marquee — track shift+drag → compute selection rect → test intersection with node rects |
| Create link | Detect drag from pin, draw preview bezier, test against other pins on release |
| Delete | Keyboard shortcut, query selection, or context menu |
### 2.4 Link-Picking (Bezier Hit Test)
Cubic Bézier curves require a hierarchical hit test:
1. Subdivide curve into N segments
2. Find segment closest to mouse cursor
3. Recursively subdivide that segment
4. Return hit if distance < threshold
## 3. Architecture Patterns
### 3.1 Data Model vs. View Separation
From Guillaume Boissé's RogueEngine post:
- Define a **data model** independent of UI: `PrNode`, `PrGraph`, `PrPin`
- The data model is used both by the runtime (graph evaluation) and the editor (UI rendering)
- This enables easy serialization, undo/redo, and multi-context editing
### 3.2 Immediate-Mode Node Rendering Loop
```
For each node in graph:
BeginNode(node.id)
Render node title bar (colored rect + text)
For each input pin:
BeginInputPin(pin.id)
Render ImGui widget (e.g. DragFloat, ColorEdit)
EndInputPin()
For each output pin:
BeginOutputPin(pin.id)
Render ImGui widget
EndOutputPin()
EndNode()
For each link in graph:
DrawBezierLink(from_pos, to_pos, color, thickness)
```
Positions are stored per-node in user state and updated on drag.
### 3.3 DrawList Channels (Z-Order)
```cpp
draw_list->ChannelsSplit(3);
draw_list->ChannelsSetCurrent(0); // Background: grid, selection rect
// ... draw nodes, pins, links
draw_list->ChannelsSetCurrent(1); // UI: ImGui widgets inside nodes
// ... BeginNode/EndNode calls
draw_list->ChannelsSetCurrent(2); // Foreground: tooltips, drag previews
draw_list->ChannelsMerge();
```
### 3.4 Undo/Redo
Simplest viable approach (from RogueEngine / @voxagonlabs): serialize the entire project state on every change. Store snapshots in an undo stack. Works well for small-to-medium projects (node graphs are typically small data).
## 4. Summary Comparison
| Criteria | imnodes | imgui-node-editor | Custom |
|---|---|---|---|
| Integration effort | Copy 3 files | Copy 6-8 files | Full implementation |
| Feature depth | Basic | Rich (minimap, flow, groups, copy/paste) | Whatever you build |
| Immediate mode | Yes | Partial (retained context) | Yes |
| Performance | High | High | Depends on impl |
| Styling control | Minimal | Extensive | Full control |
| Serialization | None | Built-in callbacks | Build your own |
| Community / maturity | Mature, stable | Mature, active | N/A |
## 5. References
- **imnodes**: https://github.com/Nelarius/imnodes
- **imgui-node-editor**: https://github.com/thedmd/imgui-node-editor
- **ImNodeFlow**: https://github.com/Fattorino/ImNodeFlow
- **Blog post — Visual node graph with ImGui**: https://gboisse.github.io/posts/node-graph/
- **Blog post — Writing imnodes**: https://nelari.us/post/imnodes
- **ImGui issue #306** (node editor discussion): https://github.com/ocornut/imgui/issues/306
- **ImGui useful extensions wiki**: https://github.com/ocornut/imgui/wiki/Useful-Extensions
- **Voxagon undo/redo**: https://blog.voxagon.se/2018/07/10/undo-for-lazy-programmers.html