A modern C++20 framework for creating interactive graphical node editors with hierarchical subgraph support, real-time evaluation, and a customizable ImGui interface.
- Complete Node Management: Creation, deletion, selection, and manipulation
- Connection System: Node-to-node connections with type validation
- Hierarchical Subgraphs: Full support for nested graph structures
- Real-time Evaluation: Dependency-ordered evaluation system
- Modern Interface: Interactive view with zoom, pan, and automatic centering
- Visual Groups: Logical organization of nodes into groups
- Connection Rerouting: Redirection points for complex connections
- Parameter Metadata: Optional node parameter descriptors for editor-side controls
- Event System: Real-time event notifications via callbacks
- Clean API: Intuitive interface for seamless integration
- C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
- CMake 3.31+
- OpenGL support
- ImGui, SDL2, and GoogleTest are fetched by CMake by default
cmake -S . -B cmake-build-debug -DBUILD_TESTS=ON "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
cmake --build cmake-build-debug --target AdvancedNodeEditor
cmake --build cmake-build-debug --target node_editor_tests
ctest --test-dir cmake-build-debug --output-on-failureOn Windows, the demo executable is generated as cmake-build-debug/AdvancedNodeEditor.exe
or in the build directory selected with -B.
#include "AdvancedNodeEditor/NodeEditorAPI.h"
using namespace NodeEditorCore;NodeEditorAPI editor;
editor.initialize();
editor.setWindowSize(1280, 720);NodeEditorAPI::NodeDefinition mathNode;
mathNode.type = "math_add";
mathNode.name = "Addition";
mathNode.category = "Math";
mathNode.description = "Adds two numeric values";
mathNode.iconSymbol = "+";
mathNode.inputs = {{"A", PinType::Blue}, {"B", PinType::Blue}};
mathNode.outputs = {{"Result", PinType::Green}};
editor.registerNodeType(mathNode);UUID nodeA = editor.createNode("math_add", "Addition 1", Vec2(100, 100));
UUID nodeB = editor.createNode("math_add", "Addition 2", Vec2(300, 100));
UUID connection = editor.connectNodes(nodeA, "Result", nodeB, "A");while (running) {
editor.beginFrame();
editor.render();
editor.endFrame();
}createNode(type, name, position)- Creates a new noderemoveNode(nodeId)- Removes a nodeselectNode(nodeId, append)- Selects a nodedeselectAllNodes()- Deselects all nodesgetSelectedNodes()- Gets selected nodes
connectNodes(startNode, outputPin, endNode, inputPin)- Connects two nodesdisconnectNodes(connectionId)- Disconnects nodesaddRerouteToConnection(connectionId, position)- Adds reroute point
centerView()- Centers view on all nodescenterOnNode(nodeId)- Centers view on specific nodezoomToFit(padding)- Fits all nodes in viewzoomToFitSelected(padding)- Fits selected nodes in viewsetViewPosition(position)- Sets view positionsetViewScale(scale)- Sets zoom level
createGraph(name)- Creates new graphswitchToGraph(graphId)- Switches to graphenterSubgraph(subgraphId)- Enters subgraphexitSubgraph()- Exits current subgraph
registerEvaluator(nodeType, evaluator)- Registers evaluation functionevaluateGraph(outputNodeId)- Evaluates graphsetConstantValue(nodeId, value)- Sets constant valuegetConstantValue(nodeId)- Gets constant valuesetPersistentOutputValue(nodeId, outputName, value)- Stores a named output value
addNodeCreatedListener(callback)- Node creation listeneraddNodeRemovedListener(callback)- Node removal listeneraddConnectionCreatedListener(callback)- Connection creation listeneraddConnectionRemovedListener(callback)- Connection removal listenerremoveNodeCreatedListener(handle)- Removes a registered node listenerremoveConnectionCreatedListener(handle)- Removes a registered connection listener
struct NodeDefinition {
struct ParameterDescriptor {
std::string name; // Stable parameter id
std::string label; // Display label
ParameterType type; // Float, Int, Bool, String, Vec3, File, Color, Enum
std::any defaultValue; // Optional default value
std::string drivenPin; // Optional pin controlled by this parameter
};
std::string type; // Unique node type
std::string name; // Display name
std::string category; // Organization category
std::string description; // Tooltip description
std::string iconSymbol; // Display icon
std::vector<std::pair<std::string, PinType>> inputs; // Input pins
std::vector<std::pair<std::string, PinType>> outputs; // Output pins
std::vector<ParameterDescriptor> parameters; // Optional UI parameters
};struct EvaluationResult {
std::any value; // Result value
std::vector<UUID> evaluationOrder; // Evaluation order
};editor.registerEvaluator("math_add", [](const std::vector<std::any>& inputs) -> std::any {
if (inputs.size() >= 2) {
try {
float a = std::any_cast<float>(inputs[0]);
float b = std::any_cast<float>(inputs[1]);
return a + b;
} catch (const std::bad_any_cast&) {
return 0.0f;
}
}
return 0.0f;
});std::vector<UUID> path = editor.traceConnectionPath(
startNodeId, "output",
endNodeId, "input"
);
for (const auto& connectionId : path) {
// Process each connection in path
}UUID groupId = editor.createGroup("Math Operations", Vec2(50, 50), Vec2(400, 300));
editor.addNodeToGroup(nodeA, groupId);
editor.addNodeToGroup(nodeB, groupId);ListenerHandle nodeCreated = editor.addNodeCreatedListener([](const UUID& nodeId) {
std::cout << "Node created: " << nodeId << std::endl;
});
ListenerHandle connectionCreated = editor.addConnectionCreatedListener([](const UUID& connectionId) {
std::cout << "Connection created: " << connectionId << std::endl;
});The framework supports various pin types through the PinType enumeration. You can extend the system to support custom types.
editor.executeCommand("custom_command", customData);NodeEditor* underlyingEditor = editor.getUnderlyingEditor();
// Direct access to advanced functionalityThe framework is designed to be extensible:
- Custom Rendering: Implement custom renderers
- Node Types: Create complex node types
- Evaluators: Flexible evaluation system
- Interface: Full UI customization
- Optimizations: Efficient rendering with automatic culling
- Memory: Intelligent resource management
- Multithreading: Parallel evaluation support
try {
editor.zoomToFit();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}- Automatic connection validation
- Pin type checking
- Cycle detection in graphs
- Fork the project
- Create a feature branch (
git checkout -b feature/new-feature) - Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/new-feature) - Create a Pull Request
MIT License
Copyright (c) 2025 Theo Baudoin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Advanced Node Editor Framework - Build powerful and intuitive node editors with C++20
