Skip to content

Commit

Permalink
nds
Browse files Browse the repository at this point in the history
  • Loading branch information
zdenyhraz committed May 29, 2024
1 parent e0ddf27 commit 91727cb
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 38 deletions.
171 changes: 171 additions & 0 deletions src/Gui/Windows/NodeEditor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#pragma once
#include <imgui_node_editor.h>
#include "Microservice/Workflow.hpp"
namespace ed = ax::NodeEditor;

struct NodeEditor
{
// Struct to hold basic information about connection between
// pins. Note that connection (aka. link) has its own ID.
// This is useful later with dealing with selections, deletion
// or other operations.
struct LinkInfo
{
ed::LinkId Id;
ed::PinId InputId;
ed::PinId OutputId;
};

ed::EditorContext* m_Context = nullptr; // Editor context, required to trace a editor state.
bool m_FirstFrame = true; // Flag set for first frame only, some action need to be executed once.
ImVector<LinkInfo> m_Links; // List of live links. It is dynamic unless you want to create read-only view over nodes.
int m_NextLinkId = 100; // Counter to help generate link ids. In real application this will probably based on pointer to user data structure.
Workflow m_Workflow;

void OnStart()
{
ed::Config config;
config.SettingsFile = "BasicInteraction.json";
m_Context = ed::CreateEditor(&config);
m_Workflow.TestInitialize();
}

void OnStop() { ed::DestroyEditor(m_Context); }

void ImGuiEx_BeginColumn() { ImGui::BeginGroup(); }

void ImGuiEx_NextColumn()
{
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
}

void ImGuiEx_EndColumn() { ImGui::EndGroup(); }

void DrawNode(Microservice& microservice)
{
usize id = microservice.GetId();
ed::BeginNode(id);
ImGui::Text(microservice.GetName().c_str());

ImGuiEx_BeginColumn();
for (const auto& [name, param] : microservice.GetInputParameters())
{
ed::BeginPin(++id, ed::PinKind::Input);
ImGui::Text(fmt::format("-> {}", name).c_str());
ed::EndPin();
}

ImGuiEx_NextColumn();
for (const auto& [name, param] : microservice.GetOutputParameters())
{
ed::BeginPin(++id, ed::PinKind::Input);
ImGui::Text(fmt::format("{} ->", name).c_str());
ed::EndPin();
}

ImGuiEx_EndColumn();
ed::EndNode();
}

void OnFrame()
{
auto& io = ImGui::GetIO();
ImGui::Separator();

ed::SetCurrentEditor(m_Context);

// Start interaction with editor.
ed::Begin("Microservice Editor", ImVec2(0.0, 0.0f));

for (const auto& microservice : m_Workflow.GetMicroservices())
DrawNode(*microservice);

// for (const auto& connection : m_Workflow.GetMicroserviceConnections())
// ed::Link(linkInfo.Id, linkInfo.InputId, linkInfo.OutputId);

// Submit Links
for (auto& linkInfo : m_Links)
ed::Link(linkInfo.Id, linkInfo.InputId, linkInfo.OutputId);

//
// 2) Handle interactions
//

// Handle creation action, returns true if editor want to create new object (node or link)
if (ed::BeginCreate())
{
ed::PinId inputPinId, outputPinId;
if (ed::QueryNewLink(&inputPinId, &outputPinId))
{
// QueryNewLink returns true if editor want to create new link between pins.
//
// Link can be created only for two valid pins, it is up to you to
// validate if connection make sense. Editor is happy to make any.
//
// Link always goes from input to output. User may choose to drag
// link from output pin or input pin. This determine which pin ids
// are valid and which are not:
// * input valid, output invalid - user started to drag new link from input pin
// * input invalid, output valid - user started to drag new link from output pin
// * input valid, output valid - user dragged link over other pin, can be validated

if (inputPinId && outputPinId) // both are valid, let's accept link
{
// ed::AcceptNewItem() return true when user release mouse button.
if (ed::AcceptNewItem())
{
// Since we accepted new link, lets add one to our list of links.
m_Links.push_back({ed::LinkId(m_NextLinkId++), inputPinId, outputPinId});

// Draw new link.
ed::Link(m_Links.back().Id, m_Links.back().InputId, m_Links.back().OutputId);
}

// You may choose to reject connection between these nodes
// by calling ed::RejectNewItem(). This will allow editor to give
// visual feedback by changing link thickness and color.
}
}
}
ed::EndCreate(); // Wraps up object creation action handling.

// Handle deletion action
if (ed::BeginDelete())
{
// There may be many links marked for deletion, let's loop over them.
ed::LinkId deletedLinkId;
while (ed::QueryDeletedLink(&deletedLinkId))
{
// If you agree that link can be deleted, accept deletion.
if (ed::AcceptDeletedItem())
{
// Then remove link from your data.
for (auto& link : m_Links)
{
if (link.Id == deletedLinkId)
{
m_Links.erase(&link);
break;
}
}
}

// You may reject link deletion by calling:
// ed::RejectDeletedItem();
}
}
ed::EndDelete(); // Wrap up deletion action

// End of interaction with editor.
ed::End();

if (m_FirstFrame)
ed::NavigateToContent(0.0f);

ed::SetCurrentEditor(nullptr);

m_FirstFrame = false;
}
};
29 changes: 11 additions & 18 deletions src/Gui/Windows/NodeEditorWindow.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "NodeEditorWindow.hpp"
#include <imgui_node_editor.h>
#include "Microservice/Workflow.hpp"
#include <imgui_node_editor.h>

namespace ed = ax::NodeEditor;
ed::EditorContext* m_Context = nullptr;
Expand All @@ -10,6 +10,7 @@ void NodeEditorWindow::Initialize()
ed::Config config;
config.SettingsFile = "Simple.json";
m_Context = ed::CreateEditor(&config);
nex.OnStart();
}

void NodeEditorWindow::Render()
Expand All @@ -21,22 +22,7 @@ void NodeEditorWindow::Render()
if (ImGui::Button("Test"))
LaunchAsync([&]() { Test(); });

ed::SetCurrentEditor(m_Context);
ed::Begin("My Editor", ImVec2(0.0, 0.0f));
int uniqueId = 1;
// Start drawing nodes.
ed::BeginNode(uniqueId++);
ImGui::Text("Node A");
ed::BeginPin(uniqueId++, ed::PinKind::Input);
ImGui::Text("-> In");
ed::EndPin();
ImGui::SameLine();
ed::BeginPin(uniqueId++, ed::PinKind::Output);
ImGui::Text("Out ->");
ed::EndPin();
ed::EndNode();
ed::End();
ed::SetCurrentEditor(nullptr);
NodeEditorTest();

ImGui::EndTabItem();
}
Expand All @@ -45,5 +31,12 @@ void NodeEditorWindow::Render()
void NodeEditorWindow::Test()
{
Workflow wrk;
wrk.Test();
wrk.TestInitialize();
wrk.Run();
LOG_SUCCESS("Workflow test completed");
}

void NodeEditorWindow::NodeEditorTest()
{
nex.OnFrame();
}
4 changes: 4 additions & 0 deletions src/Gui/Windows/NodeEditorWindow.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#pragma once
#include "Window.hpp"
#include "NodeEditor.hpp"

class NodeEditorWindow : public Window
{
void NodeEditorTest();
void Test();

NodeEditor nex;

public:
void Initialize() override;
void Render() override;
Expand Down
45 changes: 38 additions & 7 deletions src/Microservice/Microservice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class Microservice
std::type_index type = std::type_index(typeid(void));
std::any value;

auto operator<=>(const MicroserviceParameter&) const = default;

void Reset()
{
type = std::type_index(typeid(void));
Expand All @@ -29,19 +31,30 @@ class Microservice
};

struct MicroserviceConnection
{
Microservice* outputMicroservice;
Microservice* inputMicroservice;

auto operator<=>(const MicroserviceConnection&) const = default;
};

struct MicroserviceParameterConnection
{
Microservice* outputMicroservice;
Microservice* inputMicroservice;
std::string outputParameterName;
std::string inputParameterName;

auto operator<=>(const MicroserviceParameterConnection&) const = default;
};

std::string microserviceName;
usize microserviceId;
std::unordered_map<std::string, MicroserviceParameter> inputParameters;
std::unordered_map<std::string, MicroserviceParameter> outputParameters;

std::vector<MicroserviceConnection> inputParameterConnections;
std::vector<MicroserviceConnection> outputParameterConnections;
std::vector<MicroserviceParameterConnection> inputParameterConnections;
std::vector<MicroserviceParameterConnection> outputParameterConnections;

std::vector<std::shared_ptr<Microservice>> outputMicroservices;

Expand Down Expand Up @@ -146,14 +159,21 @@ class Microservice
typeName.erase(posMs, std::string("Microservice").length());

static usize idx = 0;
microserviceName = fmt::format("{}{}", typeName, idx++);
microserviceId = idx += 100; // for unique input/output pin ids
microserviceName = fmt::format("{}{}", typeName, microserviceId);
}

public:
virtual ~Microservice() {}

const std::string& GetName() { return microserviceName; }

usize GetId() { return microserviceId; }

const std::unordered_map<std::string, MicroserviceParameter>& GetInputParameters() { return inputParameters; }

const std::unordered_map<std::string, MicroserviceParameter>& GetOutputParameters() { return outputParameters; }

void Initialize()
{
GenerateMicroserviceName();
Expand All @@ -180,14 +200,25 @@ class Microservice
throw std::runtime_error(fmt::format("Cannot connect microservice '{}' parameter '{}' to microservice '{}' parameter '{}' - input parameter not found",
outputMicroservice->GetName(), outputParameterName, inputMicroservice->GetName(), inputParameterName));

outputMicroservice->outputParameterConnections.emplace_back(outputMicroservice.get(), inputMicroservice.get(), outputParameterName, inputParameterName);
inputMicroservice->inputParameterConnections.emplace_back(outputMicroservice.get(), inputMicroservice.get(), outputParameterName, inputParameterName);
MicroserviceParameterConnection connection(outputMicroservice.get(), inputMicroservice.get(), outputParameterName, inputParameterName);
if (not std::ranges::any_of(outputMicroservice->outputParameterConnections, [&connection](const auto& conn) { return conn == connection; }))
outputMicroservice->outputParameterConnections.push_back(connection);
else
LOG_WARNING(
"Ignoring duplicate output parameter connection: {}:{} -> {}:{}", outputMicroservice->GetName(), outputParameterName, inputMicroservice->GetName(), inputParameterName);
if (not std::ranges::any_of(inputMicroservice->inputParameterConnections, [&connection](const auto& conn) { return conn == connection; }))
inputMicroservice->inputParameterConnections.push_back(connection);
else
LOG_WARNING(
"Ignoring duplicate input parameter connection: {}:{} -> {}:{}", outputMicroservice->GetName(), outputParameterName, inputMicroservice->GetName(), inputParameterName);
}

static void Connect(std::shared_ptr<Microservice> outputMicroservice, std::shared_ptr<Microservice> inputMicroservice) // main execution flow: notify when done processing
{
// TODO: make this resilient to being called multiple times
outputMicroservice->outputMicroservices.push_back(inputMicroservice);
if (not std::ranges::any_of(outputMicroservice->outputMicroservices, [&inputMicroservice](const auto& ms) { return ms.get() == inputMicroservice.get(); }))
outputMicroservice->outputMicroservices.push_back(inputMicroservice);
else
LOG_WARNING("Ignoring duplicate microservice connection: {} -> {}", outputMicroservice->GetName(), inputMicroservice->GetName());
}

bool HasInputConnection() const { return inputParameterConnections.size() > 0; }
Expand Down
Loading

0 comments on commit 91727cb

Please sign in to comment.