766 lines
23 KiB
Markdown
766 lines
23 KiB
Markdown
# ImGui Node Editor - Architecture Overview
|
|
|
|
## Table of Contents
|
|
1. [Introduction](#introduction)
|
|
2. [Core Architecture](#core-architecture)
|
|
3. [Editor Context](#editor-context)
|
|
4. [Rendering System](#rendering-system)
|
|
5. [Pin Rendering](#pin-rendering)
|
|
6. [Block System](#block-system)
|
|
7. [Link System](#link-system)
|
|
8. [Helper Functions](#helper-functions)
|
|
|
|
---
|
|
|
|
## Introduction
|
|
|
|
The imgui-node-editor is a visual node graph editor built on top of Dear ImGui. This document describes the current architecture after recent refactoring that focused on modularity and maintainability.
|
|
|
|
### Key Characteristics
|
|
- **Immediate Mode**: No retained scene graph - everything rebuilt each frame
|
|
- **Type-Safe IDs**: `NodeId`, `PinId`, `LinkId` prevent mixing different entity types
|
|
- **Modular Design**: Functionality split into focused files (_render, _links, _store, _selection, _tools, _animation)
|
|
- **Precise Pin Positioning**: Pin renderers track exact pivot positions and bounds for accurate link connections
|
|
|
|
---
|
|
|
|
## Core Architecture
|
|
|
|
### File Structure
|
|
|
|
The library is organized into specialized modules:
|
|
|
|
```
|
|
imgui-node-editor/
|
|
├── imgui_node_editor.h # Public API
|
|
├── imgui_node_editor_internal.h # Internal structures and declarations
|
|
├── imgui_node_editor.cpp # Core editor context implementation
|
|
├── imgui_node_editor_render.cpp # Rendering logic
|
|
├── imgui_node_editor_links.cpp # Link interaction and control points
|
|
├── imgui_node_editor_store.cpp # Object storage and queries
|
|
├── imgui_node_editor_selection.cpp # Selection management
|
|
├── imgui_node_editor_tools.cpp # Utility functions
|
|
├── imgui_node_editor_animation.cpp # Animation controllers
|
|
└── imgui_node_editor_api.cpp # Public API implementation
|
|
```
|
|
|
|
### Namespace Structure
|
|
|
|
```cpp
|
|
namespace ax::NodeEditor::Detail {
|
|
// Internal implementation
|
|
class EditorContext { /* ... */ };
|
|
|
|
// Common alias used throughout
|
|
namespace ed = ax::NodeEditor::Detail;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Editor Context
|
|
|
|
### EditorContext Class
|
|
|
|
The heart of the editor is `EditorContext` (defined in `imgui_node_editor_internal.h`), which manages the entire editor state.
|
|
|
|
**Key Responsibilities**:
|
|
- Canvas navigation (pan, zoom)
|
|
- Object management (nodes, pins, links)
|
|
- User interactions (create, delete, select)
|
|
- Drawing coordination
|
|
- Animation playback
|
|
|
|
### The End() Method - Main Frame Coordinator
|
|
|
|
`void ed::EditorContext::End()` in `imgui_node_editor_render.cpp` is the main orchestrator called at the end of each frame. It coordinates all rendering and interaction logic.
|
|
|
|
```cpp
|
|
void ed::EditorContext::End()
|
|
{
|
|
auto& io = ImGui::GetIO();
|
|
const auto control = BuildControl(IsFocused());
|
|
|
|
// 1. Update hover/interaction state
|
|
UpdateControlState(control);
|
|
|
|
// 2. Handle control point dragging for guided links
|
|
HandleControlPointDragging();
|
|
|
|
// 3. Draw all visual elements
|
|
DrawNodes(); // Node visuals
|
|
DrawLinks(); // Link curves
|
|
DrawSelectionHighlights(control); // Selected objects
|
|
DrawHoverHighlight(control, IsSelecting); // Hovered objects
|
|
DrawAnimations(); // Flow animations
|
|
|
|
// 4. Process user actions
|
|
ProcessCurrentAction(control); // Active action (dragging, etc.)
|
|
SelectNextAction(control); // Determine next action
|
|
|
|
// 5. Manage Z-order and channels
|
|
BringActiveNodeToFront(control, isDragging);
|
|
SortNodesByGroupAndZOrder(sortGroups);
|
|
ArrangeNodeChannels();
|
|
|
|
// 6. Finalize rendering
|
|
DrawGrid(); // Background grid
|
|
FinalizeDrawChannels(); // Transform clip rects
|
|
MergeChannelsAndFinishCanvas(); // Merge and draw border
|
|
|
|
// 7. Cleanup
|
|
PostFrameCleanup(); // Reset state, save settings
|
|
}
|
|
```
|
|
|
|
**Location**: `imgui_node_editor_render.cpp:394-445`
|
|
|
|
### Extracted Helper Methods
|
|
|
|
The `End()` method was refactored from ~620 lines into focused helper methods:
|
|
|
|
#### Rendering Helpers (`imgui_node_editor_render.cpp`)
|
|
- `UpdateControlState()` - Updates hover and double-click states
|
|
- `DrawNodes()` - Renders all visible nodes
|
|
- `DrawLinks()` - Renders all visible links
|
|
- `DrawSelectionHighlights()` - Highlights selected objects
|
|
- `DrawHoverHighlight()` - Highlights hovered object
|
|
- `DrawAnimations()` - Renders animation effects
|
|
- `DrawGrid()` - Draws background grid
|
|
- `ArrangeNodeChannels()` - Manages ImDrawList channels for proper Z-order
|
|
- `FinalizeDrawChannels()` - Transforms clip rects for channels
|
|
- `MergeChannelsAndFinishCanvas()` - Merges channels and draws border
|
|
- `PostFrameCleanup()` - Post-frame cleanup and state reset
|
|
|
|
#### Link Interaction Helpers (`imgui_node_editor_links.cpp`)
|
|
- `HandleControlPointDragging()` - Manages dragging of link control points
|
|
- `HandleGuidedLinkInteractions()` - Double-click to add/remove control points
|
|
- `AddControlPointToGuidedLink()` - Adds a waypoint to a guided link
|
|
- `ConvertLinkToGuidedMode()` - Converts auto link to guided mode
|
|
- `ShowControlPointHoverCursor()` - Sets cursor when hovering control points
|
|
|
|
#### Action Helpers (`imgui_node_editor_render.cpp`)
|
|
- `ProcessCurrentAction()` - Processes the currently active action
|
|
- `ProcessNavigateAction()` - Handles canvas navigation
|
|
- `SelectNextAction()` - Determines next action based on priority
|
|
|
|
#### Z-Order Helpers (`imgui_node_editor_render.cpp`)
|
|
- `BringActiveNodeToFront()` - Brings active node/group to front
|
|
- `SortNodesByGroupAndZOrder()` - Sorts nodes for correct rendering order
|
|
|
|
---
|
|
|
|
## Rendering System
|
|
|
|
### Drawing Channels
|
|
|
|
The editor uses ImGui's channel system to control draw order:
|
|
|
|
```cpp
|
|
// Channel allocation (from imgui_node_editor.cpp)
|
|
const int c_BackgroundChannel_SelectionRect = 0; // Selection rectangle
|
|
const int c_UserChannel_Content = 1; // User canvas content
|
|
const int c_UserChannel_Grid = 2; // Background grid
|
|
const int c_UserChannel_HintsBackground = 3; // Hint backgrounds
|
|
const int c_UserChannel_Hints = 4; // Hint content
|
|
const int c_LinkChannel_Selection = 5; // Selected links
|
|
const int c_LinkChannel_Links = 6; // Regular links
|
|
const int c_LinkChannel_Flow = 7; // Flow animations
|
|
const int c_LinkChannel_NewLink = 8; // Link being created
|
|
const int c_NodeStartChannel = 9; // First node channel
|
|
|
|
// Each node gets 2 channels (background + foreground)
|
|
const int c_ChannelsPerNode = 2;
|
|
```
|
|
|
|
### Node Rendering
|
|
|
|
Nodes are drawn by the application using the builder pattern. The editor provides the infrastructure:
|
|
|
|
```cpp
|
|
ed::Begin("Node Editor");
|
|
|
|
// Application draws nodes
|
|
for (auto& node : nodes) {
|
|
ed::BeginNode(node.ID);
|
|
// ... custom rendering ...
|
|
ed::EndNode();
|
|
}
|
|
|
|
// Application draws links
|
|
for (auto& link : links) {
|
|
ed::Link(link.ID, link.StartPin, link.EndPin);
|
|
}
|
|
|
|
ed::End(); // Coordinates everything via EditorContext::End()
|
|
```
|
|
|
|
---
|
|
|
|
## Pin Rendering
|
|
|
|
### PinRenderer Architecture
|
|
|
|
The blueprints example uses a specialized pin rendering system for precise positioning.
|
|
|
|
**Location**: `examples/blueprints-example/utilities/pin_renderer.h/cpp`
|
|
|
|
### Class Hierarchy
|
|
|
|
```cpp
|
|
// Base class for all pin renderers
|
|
class PinRendererBase
|
|
{
|
|
public:
|
|
virtual void BeginPin(ed::PinId id, ed::PinKind kind) = 0;
|
|
virtual void EndPin() = 0;
|
|
virtual ImVec2 GetPivotPosition() const = 0; // Link connection point
|
|
virtual ImRect GetRenderBounds() const = 0; // Hit-test area
|
|
virtual ImVec2 GetRelativeOffset() const = 0; // Offset from node origin
|
|
|
|
protected:
|
|
float m_Alpha = 1.0f;
|
|
ImVec2 m_LastPivotPosition; // Cached pivot (screen space)
|
|
ImRect m_LastRenderBounds; // Cached bounds (screen space)
|
|
};
|
|
```
|
|
|
|
### ParameterPinRenderer
|
|
|
|
Renders data parameter pins (Int, Float, String, etc.) with icons and labels.
|
|
|
|
```cpp
|
|
class ParameterPinRenderer : public PinRendererBase
|
|
{
|
|
public:
|
|
struct Config {
|
|
float iconSize = 24.0f;
|
|
float iconInnerScale = 0.75f;
|
|
float spacing = 4.0f;
|
|
bool showLabels = true;
|
|
ImVec2 padding = ImVec2(8, 4);
|
|
};
|
|
|
|
void Render(ed::PinId pinId, ed::PinKind kind, const Pin& pin,
|
|
bool isLinked, App* app, const Config* overrideConfig = nullptr);
|
|
|
|
// Returns exact pivot point where links should connect
|
|
ImVec2 GetPivotPosition() const override;
|
|
|
|
// Returns full visual bounds for hit-testing
|
|
ImRect GetRenderBounds() const override;
|
|
};
|
|
```
|
|
|
|
**Key Features**:
|
|
- Renders icon + label for parameter pins
|
|
- Automatically positions icon based on pin direction (left/right)
|
|
- Tracks exact pivot position for link connection
|
|
- Supports alpha blending for inactive pins
|
|
- Configurable appearance via `Config` struct
|
|
|
|
**Usage**:
|
|
```cpp
|
|
ParameterPinRenderer paramRenderer;
|
|
|
|
// Render input pin
|
|
paramRenderer.Render(pin.ID, ed::PinKind::Input, pin, isLinked, app);
|
|
|
|
// Store position data for link routing
|
|
pin.LastPivotPosition = paramRenderer.GetPivotPosition();
|
|
pin.LastRenderBounds = paramRenderer.GetRenderBounds();
|
|
pin.HasPositionData = true;
|
|
```
|
|
|
|
### FlowPinRenderer
|
|
|
|
Renders execution flow pins with square markers at node edges.
|
|
|
|
```cpp
|
|
class FlowPinRenderer : public PinRendererBase
|
|
{
|
|
public:
|
|
struct Config {
|
|
float pinSize = 12.0f;
|
|
float edgeOffset = 4.0f;
|
|
float rounding = 2.0f;
|
|
float borderWidth = 2.0f;
|
|
ImU32 fillColor = IM_COL32(255, 255, 255, 255);
|
|
ImU32 borderColor = IM_COL32(32, 32, 32, 255);
|
|
};
|
|
|
|
void Render(ed::PinId pinId, ed::PinKind kind, const Pin& pin,
|
|
App* app, const Config* overrideConfig = nullptr);
|
|
};
|
|
```
|
|
|
|
**Key Features**:
|
|
- Renders small square at node edge
|
|
- Positioned precisely at block boundary
|
|
- Distinct visual from parameter pins
|
|
- Minimal screen space usage
|
|
|
|
### Pin Position Tracking
|
|
|
|
The `Pin` struct stores rendered position data:
|
|
|
|
```cpp
|
|
struct Pin
|
|
{
|
|
ed::PinId ID;
|
|
Node* Node;
|
|
std::string Name;
|
|
PinType Type;
|
|
PinKind Kind;
|
|
|
|
// Position tracking (updated during rendering)
|
|
ImVec2 LastPivotPosition; // Where links connect (screen space)
|
|
ImRect LastRenderBounds; // Full pin area for hit testing
|
|
bool HasPositionData; // True after first render
|
|
|
|
// Get position relative to node
|
|
ImVec2 GetRelativePivotPosition() const;
|
|
};
|
|
```
|
|
|
|
**Why This Matters**: Accurate pin positions are critical for:
|
|
- Link waypoint generation
|
|
- Link end-point alignment
|
|
- Hit-testing during link creation
|
|
- Visual polish (no gaps or misalignments)
|
|
|
|
---
|
|
|
|
## Block System
|
|
|
|
### ParameterizedBlock
|
|
|
|
Represents a standard blueprint-style block with parameters and flow pins.
|
|
|
|
**Location**: `examples/blueprints-example/blocks/block.cpp`
|
|
|
|
**Structure**:
|
|
```cpp
|
|
class ParameterizedBlock : public BlockBase
|
|
{
|
|
std::vector<Pin> InputParameters; // Data inputs (top area)
|
|
std::vector<Pin> OutputParameters; // Data outputs (top area)
|
|
Pin FlowInputPin; // Execution input (left edge)
|
|
Pin FlowOutputPin; // Execution output (right edge)
|
|
};
|
|
```
|
|
|
|
**Rendering Pattern** (lines 25-220):
|
|
```cpp
|
|
void ParameterizedBlock::Render()
|
|
{
|
|
ed::BeginNode(ID);
|
|
ImGui::BeginVertical("node");
|
|
|
|
// Header
|
|
ImGui::BeginHorizontal("header");
|
|
// ... render title ...
|
|
ImGui::EndHorizontal();
|
|
|
|
// Input Parameters (left side)
|
|
ImGui::BeginHorizontal("inputs");
|
|
ImGui::BeginVertical("input_params");
|
|
for (auto& pin : InputParameters) {
|
|
ParameterPinRenderer renderer;
|
|
renderer.Render(pin.ID, ed::PinKind::Input, pin, isLinked, app);
|
|
|
|
// Store position for link routing
|
|
pin.LastPivotPosition = renderer.GetPivotPosition();
|
|
pin.LastRenderBounds = renderer.GetRenderBounds();
|
|
pin.HasPositionData = true;
|
|
}
|
|
ImGui::EndVertical();
|
|
|
|
// Middle spacer
|
|
ImGui::Spring(1);
|
|
|
|
// Output Parameters (right side)
|
|
ImGui::BeginVertical("output_params");
|
|
for (auto& pin : OutputParameters) {
|
|
ParameterPinRenderer renderer;
|
|
renderer.Render(pin.ID, ed::PinKind::Output, pin, isLinked, app);
|
|
|
|
pin.LastPivotPosition = renderer.GetPivotPosition();
|
|
pin.LastRenderBounds = renderer.GetRenderBounds();
|
|
pin.HasPositionData = true;
|
|
}
|
|
ImGui::EndVertical();
|
|
ImGui::EndHorizontal();
|
|
|
|
// Flow Pins (edges)
|
|
FlowPinRenderer flowRenderer;
|
|
|
|
// Flow input (left edge)
|
|
flowRenderer.Render(FlowInputPin.ID, ed::PinKind::Input, FlowInputPin, app);
|
|
FlowInputPin.LastPivotPosition = flowRenderer.GetPivotPosition();
|
|
FlowInputPin.HasPositionData = true;
|
|
|
|
// Flow output (right edge)
|
|
flowRenderer.Render(FlowOutputPin.ID, ed::PinKind::Output, FlowOutputPin, app);
|
|
FlowOutputPin.LastPivotPosition = flowRenderer.GetPivotPosition();
|
|
FlowOutputPin.HasPositionData = true;
|
|
|
|
ImGui::EndVertical();
|
|
ed::EndNode();
|
|
}
|
|
```
|
|
|
|
### ParameterNode
|
|
|
|
Compact nodes representing variables or constants (no flow pins).
|
|
|
|
**Location**: `examples/blueprints-example/blocks/parameter_node.cpp`
|
|
|
|
**Three Display Modes**:
|
|
|
|
1. **Name Only** (lines 96-154) - Just the variable name
|
|
2. **Name + Value** (lines 156-252) - Name with editable value
|
|
3. **Small Box** (lines 254-348) - Minimal representation
|
|
|
|
**Rendering Example** (Name Only mode):
|
|
```cpp
|
|
void ParameterNode::RenderNameOnly()
|
|
{
|
|
ed::BeginNode(ID);
|
|
|
|
// Just the name and output pin
|
|
ImGui::BeginHorizontal("content");
|
|
ImGui::TextUnformatted(Name.c_str());
|
|
ImGui::Spring(0);
|
|
|
|
// Output pin
|
|
ParameterPinRenderer paramRenderer;
|
|
paramRenderer.Render(output.ID, ed::PinKind::Output, output, isLinked, app);
|
|
|
|
// Store position
|
|
output.LastPivotPosition = paramRenderer.GetPivotPosition();
|
|
output.LastRenderBounds = paramRenderer.GetRenderBounds();
|
|
output.HasPositionData = true;
|
|
|
|
ImGui::EndHorizontal();
|
|
ed::EndNode();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Link System
|
|
|
|
### Link Structure
|
|
|
|
```cpp
|
|
struct Link
|
|
{
|
|
ed::LinkId ID;
|
|
ed::PinId StartPinID; // Output pin
|
|
ed::PinId EndPinID; // Input pin
|
|
ImColor Color;
|
|
|
|
// Guided link waypoints
|
|
std::vector<ImVec2> ControlPoints; // User-defined waypoints
|
|
bool IsGuided = false; // Guided vs auto-routed
|
|
};
|
|
```
|
|
|
|
### Link Modes
|
|
|
|
**Auto Mode** (default):
|
|
- Editor automatically routes link curve
|
|
- Uses bezier or spline curves
|
|
- No user-defined waypoints
|
|
|
|
**Guided Mode**:
|
|
- User controls link path via waypoints
|
|
- Double-click link to add control point
|
|
- Drag control points to adjust path
|
|
- Double-click control point to remove
|
|
|
|
### Link Rendering
|
|
|
|
Links are drawn in `DrawLinks()` (`imgui_node_editor_render.cpp:129-170`):
|
|
|
|
```cpp
|
|
void EditorContext::DrawLinks()
|
|
{
|
|
for (auto& link : m_Links)
|
|
{
|
|
if (!link.IsVisible())
|
|
continue;
|
|
|
|
if (link.IsGuided()) {
|
|
// Draw guided link with control points
|
|
ed::Link(link.ID, link.StartPinID, link.EndPinID,
|
|
link.Color, 2.0f, link.ControlPoints);
|
|
} else {
|
|
// Draw auto-routed link
|
|
ed::Link(link.ID, link.StartPinID, link.EndPinID,
|
|
link.Color, 2.0f);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Control Point Interaction
|
|
|
|
**Dragging** (`HandleControlPointDragging()` in `imgui_node_editor_links.cpp:25-50`):
|
|
```cpp
|
|
void EditorContext::HandleControlPointDragging()
|
|
{
|
|
if (!m_DraggedControlPoint.IsValid())
|
|
return;
|
|
|
|
// Update position while dragging
|
|
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
|
m_DraggedControlPoint.Position = ImGui::GetMousePos();
|
|
}
|
|
|
|
// Finish drag
|
|
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
|
m_DraggedControlPoint.Clear();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Adding/Removing** (`HandleGuidedLinkInteractions()` in `imgui_node_editor_links.cpp:52-85`):
|
|
```cpp
|
|
void EditorContext::HandleGuidedLinkInteractions(const Control& control)
|
|
{
|
|
if (control.HotLink && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
|
{
|
|
auto link = FindLink(control.HotLink);
|
|
if (!link) return;
|
|
|
|
if (control.HoveredControlPoint >= 0) {
|
|
// Remove control point
|
|
link->ControlPoints.erase(
|
|
link->ControlPoints.begin() + control.HoveredControlPoint);
|
|
} else {
|
|
// Add control point at mouse position
|
|
AddControlPointToGuidedLink(link, ImGui::GetMousePos());
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Waypoint Generation
|
|
|
|
When creating a link, waypoints are generated to connect pins accurately.
|
|
|
|
**Critical Function**: `GetPinPosition()` in `app-render.cpp:48-112`
|
|
|
|
```cpp
|
|
static ImVec2 GetPinPosition(Pin* pin, const ImVec2& nodePos, const ImVec2& nodeSize)
|
|
{
|
|
// Use stored position data from PinRenderer if available
|
|
if (pin->HasPositionData)
|
|
{
|
|
// Get current node position
|
|
ImVec2 currentNodePos = ed::GetNodePosition(pin->Node->ID);
|
|
|
|
// Calculate relative offset
|
|
ImVec2 relativeOffset = pin->LastPivotPosition - currentNodePos;
|
|
|
|
// Apply offset to requested position
|
|
// (Handles cases where nodePos != currentNodePos during creation)
|
|
return nodePos + relativeOffset;
|
|
}
|
|
|
|
// Fallback to approximation for unrendered pins
|
|
// ... (approximation based on node size and pin type) ...
|
|
}
|
|
```
|
|
|
|
**Why This Works**:
|
|
1. Pin renderers store exact pivot position during rendering
|
|
2. Position is stored in screen space
|
|
3. Convert to relative offset from node origin
|
|
4. Apply offset to current/target node position
|
|
5. Result: pixel-perfect link connection
|
|
|
|
### Link Direction
|
|
|
|
Link direction is determined by pin type and position:
|
|
|
|
```cpp
|
|
static ImVec2 GetPinDirection(Pin* pin)
|
|
{
|
|
// Use pin renderer data if available
|
|
if (pin->HasPositionData) {
|
|
// Calculate from pin position relative to node center
|
|
ImVec2 nodePos = ed::GetNodePosition(pin->Node->ID);
|
|
ImVec2 nodeSize = ed::GetNodeSize(pin->Node->ID);
|
|
ImVec2 nodeCenter = nodePos + nodeSize * 0.5f;
|
|
ImVec2 pinPos = pin->LastPivotPosition;
|
|
|
|
ImVec2 dir = pinPos - nodeCenter;
|
|
float len = sqrtf(dir.x * dir.x + dir.y * dir.y);
|
|
return len > 0.0f ? ImVec2(dir.x / len, dir.y / len) : ImVec2(1, 0);
|
|
}
|
|
|
|
// Fallback: standard left/right
|
|
return pin->Kind == PinKind::Input ? ImVec2(-1, 0) : ImVec2(1, 0);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Helper Functions
|
|
|
|
### app-render.cpp Utilities
|
|
|
|
**Location**: `examples/blueprints-example/app-render.cpp`
|
|
|
|
Key helper functions used throughout the blueprints example:
|
|
|
|
#### Pin Queries (lines 48-145)
|
|
|
|
```cpp
|
|
// Get exact pin position for link routing
|
|
static ImVec2 GetPinPosition(Pin* pin, const ImVec2& nodePos, const ImVec2& nodeSize);
|
|
|
|
// Get pin direction vector for link curves
|
|
static ImVec2 GetPinDirection(Pin* pin);
|
|
|
|
// Check if pin has an active connection
|
|
bool IsPinLinked(ed::PinId pinId);
|
|
```
|
|
|
|
#### Icon Rendering (lines 147-320)
|
|
|
|
```cpp
|
|
// Draw pin icon (circle, square, flow, etc.)
|
|
void DrawPinIcon(const Pin& pin, bool connected, int alpha, const ImVec2& size);
|
|
|
|
// Get icon color based on pin type
|
|
ImColor GetIconColor(PinType type);
|
|
|
|
// Icon types
|
|
enum class IconType { Flow, Circle, Square, Grid, RoundSquare, Diamond };
|
|
```
|
|
|
|
#### Link Rendering (lines 550-890)
|
|
|
|
```cpp
|
|
// Render a single link with proper curve
|
|
void DrawLink(const Link& link);
|
|
|
|
// Render flow animation on link
|
|
void DrawFlowAnimation(const Link& link, float time);
|
|
|
|
// Get link curve points for rendering
|
|
std::vector<ImVec2> GetLinkCurve(const Link& link);
|
|
```
|
|
|
|
#### Interaction Helpers (lines 1450-1610)
|
|
|
|
```cpp
|
|
// Handle link creation
|
|
void HandleLinkCreation();
|
|
|
|
// Handle node/link deletion
|
|
void HandleDeletion();
|
|
|
|
// Handle node selection
|
|
void HandleSelection();
|
|
|
|
// Show context menu
|
|
void ShowContextMenu();
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
### Core Editor Flow
|
|
|
|
```
|
|
Frame Start
|
|
↓
|
|
ImGui::NewFrame()
|
|
↓
|
|
ed::Begin()
|
|
↓
|
|
App Renders Nodes/Links
|
|
↓
|
|
ed::End() → EditorContext::End()
|
|
├─ UpdateControlState()
|
|
├─ HandleControlPointDragging()
|
|
├─ DrawNodes()
|
|
├─ DrawLinks()
|
|
├─ DrawSelectionHighlights()
|
|
├─ DrawHoverHighlight()
|
|
├─ DrawAnimations()
|
|
├─ ProcessCurrentAction()
|
|
├─ SelectNextAction()
|
|
├─ BringActiveNodeToFront()
|
|
├─ SortNodesByGroupAndZOrder()
|
|
├─ ArrangeNodeChannels()
|
|
├─ DrawGrid()
|
|
├─ FinalizeDrawChannels()
|
|
├─ MergeChannelsAndFinishCanvas()
|
|
└─ PostFrameCleanup()
|
|
↓
|
|
ImGui::Render()
|
|
↓
|
|
Frame End
|
|
```
|
|
|
|
### Key Files Reference
|
|
|
|
| File | Purpose | Key Functions |
|
|
|------|---------|---------------|
|
|
| `imgui_node_editor.cpp` | Core editor implementation | `Begin()`, node/link creation |
|
|
| `imgui_node_editor_render.cpp` | Rendering coordination | `End()`, `DrawNodes()`, `DrawLinks()` |
|
|
| `imgui_node_editor_links.cpp` | Link interactions | `HandleControlPointDragging()`, waypoint management |
|
|
| `imgui_node_editor_internal.h` | Internal declarations | `EditorContext` class, all helper declarations |
|
|
| `utilities/pin_renderer.h/cpp` | Pin rendering | `ParameterPinRenderer`, `FlowPinRenderer` |
|
|
| `blocks/block.cpp` | Standard blocks | `ParameterizedBlock::Render()` |
|
|
| `blocks/parameter_node.cpp` | Parameter nodes | `ParameterNode::Render()` modes |
|
|
| `app-render.cpp` | Rendering helpers | `GetPinPosition()`, `DrawPinIcon()` |
|
|
|
|
### Data Flow for Pin Positioning
|
|
|
|
```
|
|
1. App calls pin renderer
|
|
ParameterPinRenderer::Render()
|
|
↓
|
|
2. Renderer calculates layout
|
|
Layout layout = CalculateLayout(...)
|
|
↓
|
|
3. Renderer stores position
|
|
m_LastPivotPosition = layout.pivotPoint
|
|
m_LastRenderBounds = layout.combinedRect
|
|
↓
|
|
4. App stores in Pin struct
|
|
pin.LastPivotPosition = renderer.GetPivotPosition()
|
|
pin.LastRenderBounds = renderer.GetRenderBounds()
|
|
pin.HasPositionData = true
|
|
↓
|
|
5. Link system uses stored position
|
|
GetPinPosition(pin) → returns pin.LastPivotPosition
|
|
↓
|
|
6. Waypoints generated with exact positions
|
|
CreateLink() → GenerateWaypoints() → pixel-perfect alignment
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The ImGui Node Editor architecture is built around:
|
|
|
|
1. **EditorContext** - Central coordinator managing all state
|
|
2. **Modular Files** - Focused modules for rendering, links, selection, etc.
|
|
3. **End() Method** - Main frame orchestrator split into focused helpers
|
|
4. **Pin Renderers** - Precise positioning system for perfect link alignment
|
|
5. **Block System** - Flexible node types (blocks, parameters, comments)
|
|
6. **Link System** - Auto and guided modes with waypoint editing
|
|
7. **Helper Functions** - Utilities in app-render.cpp for common operations
|
|
|
|
This design provides a clean separation of concerns while maintaining the immediate-mode paradigm of Dear ImGui.
|