ktsu.ImGui.NodeEditor 3.26.0

Prefix Reserved
dotnet add package ktsu.ImGui.NodeEditor --version 3.26.0
                    
NuGet\Install-Package ktsu.ImGui.NodeEditor -Version 3.26.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ktsu.ImGui.NodeEditor" Version="3.26.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ktsu.ImGui.NodeEditor" Version="3.26.0" />
                    
Directory.Packages.props
<PackageReference Include="ktsu.ImGui.NodeEditor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ktsu.ImGui.NodeEditor --version 3.26.0
                    
#r "nuget: ktsu.ImGui.NodeEditor, 3.26.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ktsu.ImGui.NodeEditor@3.26.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ktsu.ImGui.NodeEditor&version=3.26.0
                    
Install as a Cake Addin
#tool nuget:?package=ktsu.ImGui.NodeEditor&version=3.26.0
                    
Install as a Cake Tool

ktsu.ImGui.NodeEditor

NuGet License

ImGui.NodeEditor is a visual node editor built on ImNodes, with the graph itself kept away from the drawing. NodeEditorEngine owns nodes, links and the physics that lays them out and knows nothing about ImGui; NodeEditorRenderer draws whatever the engine holds; NodeEditorInputHandler turns a frame's interactions into requests the engine can accept or refuse. Nodes can be declared as ordinary types decorated with ktsu.NodeGraph attributes and instantiated by reflection.

Features

  • Separation of concerns: business logic (NodeEditorEngine), rendering (NodeEditorRenderer), and input (NodeEditorInputHandler) are separate objects, so the graph can be built and tested without a renderer
  • Tuning panel: PhysicsSettingsPanel draws every layout setting, grouped and captioned, so a graph can be tuned while it is on screen
  • Attribute-based nodes: AttributeBasedNodeFactory reads ktsu.NodeGraph attributes off a type — or every decorated type in an assembly — and creates nodes with the right pins
  • Physics is opt-in: the simulation does nothing until PhysicsSettings.Enabled is set, so a host that positions nodes itself pays nothing for it
  • Type-aware connections: TryCreateLink returns a result with a message rather than throwing, and pin compatibility comes from the same rules the metadata declares
  • Physics-based layout: nodes repel, links pull, and the graph settles; powered by ktsu.ForceDirectedLayout, with per-frame stability and energy readings for debug overlays
  • Drag-aware: nodes being dragged are excluded from the simulation, and the renderer reports position and size changes back to the engine

Installation

Package Manager Console

Install-Package ktsu.ImGui.NodeEditor

.NET CLI

dotnet add package ktsu.ImGui.NodeEditor

Package Reference

<PackageReference Include="ktsu.ImGui.NodeEditor" Version="x.y.z" />

ImNodes must be initialized before the editor draws. ktsu.ImGui.App detects and sets up the extension automatically; in a host that does not, initialize ImNodes yourself as its bindings document.

Usage Examples

Basic Example

using System.Numerics;

using Hexa.NET.ImGui;

using ktsu.ImGui.NodeEditor;

private readonly NodeEditorEngine engine = new();
private readonly NodeEditorRenderer renderer = new();
private readonly NodeEditorInputHandler input = new();

// Build a graph
Node source = engine.CreateNode(new Vector2(200, 200), "Source", [], ["Value"]);
Node target = engine.CreateNode(new Vector2(500, 200), "Target", ["Source.Value"], []);
engine.TryCreateLink(source.OutputPins[0].Id, target.InputPins[0].Id);

// Draw it, once per frame
void DrawGraph(float deltaTime)
{
    renderer.Render(engine, ImGui.GetContentRegionAvail());

    // The renderer measures what ImNodes actually laid out; hand that back to the engine
    foreach ((int id, Vector2 position) in renderer.GetNodePositionUpdates(engine))
    {
        engine.UpdateNodePosition(id, position);
    }

    foreach ((int id, Vector2 dimensions) in renderer.GetNodeDimensionUpdates(engine))
    {
        engine.UpdateNodeDimensions(id, dimensions);
    }

    // Apply what the user did this frame
    InputEvents events = input.ProcessInput();
    foreach (LinkCreationRequest request in events.LinkCreationRequests)
    {
        engine.TryCreateLink(request.FromPinId, request.ToPinId);
    }

    foreach (int linkId in events.LinkDeletionRequests)
    {
        engine.RemoveLink(linkId);
    }

    engine.SetDraggedNodes(renderer.CurrentlyDraggedNodes);
    engine.UpdatePhysics(deltaTime);
}

Nodes from decorated types

using ktsu.NodeGraph;

[Node("Add")]
public class AddNode
{
    [InputPin("A")] public double A { get; set; }
    [InputPin("B")] public double B { get; set; }
    [OutputPin("Sum")] public double Sum { get; private set; }

    [NodeExecute]
    public void Execute() => Sum = A + B;
}

AttributeBasedNodeFactory factory = new(engine);
factory.RegisterNodeType<AddNode>();
factory.RegisterNodeTypesFromAssembly(typeof(AddNode).Assembly);

Node node = factory.CreateNode<AddNode>(new Vector2(100, 100));

GetAllNodeDefinitions() returns the registered definitions, which is what a "add node" menu is built from: each one carries the display name, category, tags, execution mode, deprecation state and pin list read off the attributes.

Two things to know about registration. A class node also gets an Instance output pin (and input pins for its constructor's parameters), so it can be chained onward. And RegisterNodeTypesFromAssembly skips abstract types — which in IL includes every static class — so a [Node] method parked on a static holder class has to be registered by naming that holder: factory.RegisterNodeType(typeof(MathNodes)).

Tuning the layout

engine.UpdatePhysicsSettings(new PhysicsSettings
{
    Enabled = true,
    RepulsionStrength = 1_200_000.0,
    LinkSpringStrength = 0.5,
    RestLinkLength = 225.0,
});

// Readings worth putting behind a debug toggle
float energy = engine.TotalSystemEnergy;
(int substeps, float substepDelta) = engine.LastPhysicsStepInfo;
renderer.RenderDebugOverlays(engine, editorPosition, editorSize, showDebug: true);

API Reference

NodeEditorEngine

The graph and its physics. No ImGui calls.

Name Return Type Description
Nodes IReadOnlyList<Node> Every node
Links IReadOnlyList<Link> Every link
GravityCenter Vector2 The point the layout pulls toward
TotalSystemEnergy float Current energy, for stability readouts
LastPhysicsStepInfo (int SubstepCount, float SubstepDeltaTime) What the last UpdatePhysics actually ran
CreateNode(Vector2, string, int, int) Node Creates a node with a number of unnamed pins
CreateNode(Vector2, string, List<string>, List<string>) Node Creates a node with named pins
TryCreateLink(int, int) LinkCreationResult Attempts a connection; the result carries success, a message, and the link
RemoveLink(int) / RemoveNode(int) bool Removes a link or node
UpdateNodePosition(int, Vector2) / UpdateNodeDimensions(int, Vector2) void Feeds measured layout back in
SetDraggedNodes(IReadOnlySet<int>) void Excludes dragged nodes from the simulation
UpdatePhysicsSettings(PhysicsSettings) void Replaces the physics settings
UpdatePhysics(float) void Advances the layout by a frame delta

NodeEditorRenderer

Name Return Type Description
Render(NodeEditorEngine, Vector2) void Draws every node and link through ImNodes
GetNodePositionUpdates(NodeEditorEngine) Dictionary<int, Vector2> Positions ImNodes moved since the last frame
GetNodeDimensionUpdates(NodeEditorEngine) Dictionary<int, Vector2> Sizes ImNodes measured
RenderDebugOverlays(...) void Force and stability overlays
CurrentlyDraggedNodes IReadOnlySet<int> Nodes the user is dragging this frame

PhysicsSettingsPanel

Name Return Type Description
Draw(ref PhysicsSettings) bool Draws every tunable the simulation has, grouped by force and captioned; true when the user changed one
DrawDiagnostics(NodeEditorEngine) void Energy, whether it has settled, substep count and rate

The forces interact, so none can be judged alone: raising repulsion changes what the spring's rest length means, and levelling links only works in the room repulsion made. The panel therefore exposes the whole of PhysicsSettings rather than a chosen subset — a setting that is not on it is one nobody can reach without recompiling. Every control marks itself with ktsu.ImGui.Probes, so a UI test can address it by the label the user sees.

PhysicsSettings settings = engine.PhysicsSettings;
if (PhysicsSettingsPanel.Draw(ref settings))
{
    engine.UpdatePhysicsSettings(settings);
}

PhysicsSettingsPanel.DrawDiagnostics(engine);

NodeEditorInputHandler

ProcessInput() returns InputEvents, holding LinkCreationRequests (LinkCreationRequest(FromPinId, ToPinId)) and LinkDeletionRequests.

AttributeBasedNodeFactory

Name Return Type Description
RegisterNodeType<T>() / RegisterNodeType(Type) void Registers a decorated type
RegisterNodeTypesFromAssembly(Assembly) void Registers every decorated type in an assembly
CreateNode<T>(Vector2) / CreateNode(Type, Vector2) Node Creates a node from a registered type
CreateMethodNode(MethodInfo, Vector2) Node Creates a node from a decorated method
GetNodeDefinition(Type) / GetNodeDefinition(MethodInfo) NodeDefinition? The metadata read off a registration
GetAllNodeDefinitions() IEnumerable<NodeDefinition> Every registration, for building menus

Domain models

Node(Id, Position, Name, InputPins, OutputPins, Dimensions, Velocity, Force, IsPinned), Link(Id, OutputPinId, InputPinId) and Pin(Id, Direction, Name, DisplayName) are records; PinDirection is Input or Output.

Acknowledgments

  • Dear ImGui - The immediate mode GUI library the editor draws into
  • Hexa.NET.ImGui - The .NET bindings for Dear ImGui, and for Hexa.NET.ImNodes, the node editor extension this renders through
  • ktsu.Semantics - ktsu.Semantics.Quantities for typed quantities in the physics settings

ktsu.NodeGraph supplies the node metadata and ktsu.ForceDirectedLayout the physics; both ship from this repository.

Contributing

Contributions are welcome! For feature requests, bug reports, or questions, please open an issue on the GitHub repository. If you would like to contribute code, please open a pull request with your changes.

License

ImGui.NodeEditor is licensed under the MIT License. See LICENSE.md for more information.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on ktsu.ImGui.NodeEditor:

Package Downloads
ktsu.Coder.Graph

A flexible and extensible .NET library for representing code as language-agnostic Abstract Syntax Trees, serializing them to human-readable YAML for round-trip storage and version control, and generating source in C#, Python, JavaScript and C++. Provides strongly-typed AST nodes for classes, functions, parameters, statements, expressions and typed literals, with a plugin-based architecture for adding target languages, full deep-cloning support and custom metadata on any node. Ships an ImGui node-graph editor over the same AST, with a force-directed layout, an inspector for every node's properties, undo/redo, and a desktop application built on it.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.26.0 0 9/8/2026
3.25.0 0 9/8/2026
3.24.0 0 9/8/2026
3.23.0 0 9/8/2026
3.22.0 7 9/8/2026
3.21.0 19 9/8/2026
3.20.0 68 9/8/2026
3.19.0 74 9/8/2026

## v3.26.0 (minor)

Changes since v3.25.0:

- Use Ensure.NotNull for the panel's argument guards ([@Claude](https://github.com/Claude))
- [minor] Put the whole layout tuning surface on a reusable panel ([@Claude](https://github.com/Claude))