# 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 (2024–2025) | **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
