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

7.9 KiB
Raw Permalink Blame History

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
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
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

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)

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