diff --git a/Inc/Global_Variables.hpp b/Inc/Global_Variables.hpp index 52bd9c0..d5e4962 100644 --- a/Inc/Global_Variables.hpp +++ b/Inc/Global_Variables.hpp @@ -5,15 +5,15 @@ #include "imgui_impl_win32.h" #include "imgui_impl_dx11.h" +#include "Workflow_Editor.hpp" +#include "Type_Descriptions.hpp" + extern "C" { #include "windows.h" #include "d3d11.h" } -#include "Type_Descriptions.hpp" -#include "Workflow-Editor.hpp" - // 主窗体句柄 extern HWND Main_Window_hWnd; @@ -31,6 +31,6 @@ extern Input_File_Node *input_dlt_file_list_head; extern size_t input_dlt_file_count; // 节点编辑器 -extern Workflow_Editor_Class Workflow_Editor; +extern class Workflow_Editor Workflow_Editor; #endif \ No newline at end of file diff --git a/Inc/ID_Generator.hpp b/Inc/ID_Generator.hpp new file mode 100644 index 0000000..4ad38b0 --- /dev/null +++ b/Inc/ID_Generator.hpp @@ -0,0 +1,25 @@ +#ifndef __ID_GENERATOR__ +#define __ID_GENERATOR__ + +#include + +// 唯一ID生成器 +class Independent_ID_Generator +{ +protected: + // 连续ID串 + typedef struct + { + int begin; + int end; + } Continuous_ID_Section; + std::vector continuous_id_list; + +public: + // 获取ID + int Request_ID(void); + // 释放ID true-> success / false -> fail + bool Release_ID(const int ID); +}; + +#endif \ No newline at end of file diff --git a/Inc/Nodes_And_Connectors.hpp b/Inc/Nodes_And_Connectors.hpp new file mode 100644 index 0000000..e501e06 --- /dev/null +++ b/Inc/Nodes_And_Connectors.hpp @@ -0,0 +1,214 @@ +#ifndef __NODES_AND_CONNECTORS__ +#define __NODES_AND_CONNECTORS__ + +#include +#include +#include "imgui.h" +#include "imnodes.h" +#include "ID_Generator.hpp" + +// =====================================接口抽象====================================================== + +class Abs_Connector // 抽象接口类 +{ +protected: + typedef enum + { + CONNECTOR_TYPE_UNKNOWN, // 未知连接点类型 + CONNECTOR_TYPE_OUTPUT, // 输出 + CONNECTOR_TYPE_INPUT, // 输入 + } Connector_Type_Enum; // 接口类型枚举 + + // ID 分配器 + Independent_ID_Generator *ID_Generator; + + int id; // ID + Connector_Type_Enum type; // 类型标识 + const char *socket_str; // 套接字字符串 + + Abs_Connector *provider; // 提供者 + std::vector receiver_list; // 接收方 + void *product; // 产物 + +public: + // 显式禁用默认构造 + Abs_Connector() = delete; + + // 必要构造 + Abs_Connector(Independent_ID_Generator *ID_Generator, Connector_Type_Enum type, const char *socket_str); + + // 析构 + ~Abs_Connector(); + + // 连接到目标连接点 + virtual bool Connect_To(Abs_Connector *target); + + // 断开连接 + virtual void Disconnect(void); + + // 注册产物 + virtual bool Register_Product(void *product, size_t size); + + // 获取上级节点 + virtual Abs_Connector *Get_Provider(void); + + // 获取产物 + virtual void *Get_Product(void); + + // 获取ID + virtual int Get_ID(void); + + // 绘制连接点 + virtual void Show(void) = 0; +}; + +// =====================================节点抽象====================================================== + +class Abs_Node // 抽象节点类 +{ +protected: + typedef enum + { + NODE_TYPE_UNKNOWN, // 未知节点类型 + NODE_TYPE_MSG_LINE_INPUT, // 消息输入节点 + NODE_TYPE_CSV_EXPORTER, // CSV输出节点 + } Node_Type_Enum; // 节点类型枚举 + + // ID 分配器 + Independent_ID_Generator *Node_ID_Generator; // 节点ID生成器 + Independent_ID_Generator *Connector_ID_Generator; // 连接点ID生成器 + + int id; // ID + Node_Type_Enum type; // 类型 + ImVec2 initial_position; // 初始位置 + + bool close_flag; // 关闭标志位 + + // 接口列表 + std::vector> Connector_List; + +public: + // 显式禁用默认构造 + Abs_Node() = delete; + + // 必要构造 + Abs_Node(Independent_ID_Generator *Node_ID_Generator, + Independent_ID_Generator *Connector_ID_Generator, + Node_Type_Enum type, + ImVec2 initial_position = ImVec2(0, 0)); + + // 析构 + ~Abs_Node(); + + // 绘制节点 + virtual void Show(void) = 0; + + // 执行处理流程 + virtual bool Execute_Process(void) = 0; + + // 获取关闭信号 + virtual bool Get_CloseFlag(void); + + // 获取接口列表 + std::vector> &Get_Connector_List(void); +}; + +// ====================================接口类声明================================================ + +// DLT信息输出接口 +class MSG_OutPut_Connector : public Abs_Connector +{ +public: + // 显式禁用默认构造 + MSG_OutPut_Connector() = delete; + + // 必要构造 + explicit MSG_OutPut_Connector(Independent_ID_Generator *ID_Generator); + + // 绘制接口 + virtual void Show(void); +}; + +// DLT信息输入接口 +class MSG_InPut_Connector : public Abs_Connector +{ +public: + // 显式禁用默认构造 + MSG_InPut_Connector() = delete; + + // 必要构造 + explicit MSG_InPut_Connector(Independent_ID_Generator *ID_Generator); + + // 绘制接口 + virtual void Show(void); +}; + +// CSV 列输入接口 +class CSV_Column_Input_Connector : public Abs_Connector +{ +public: + // 显式禁用默认构造 + CSV_Column_Input_Connector() = delete; + + // 必要构造 + explicit CSV_Column_Input_Connector(Independent_ID_Generator *ID_Generator); + + // 绘制接口 + virtual void Show(void); +}; + +// CSV 列输出接口 +class CSV_Column_Output_Connector : public Abs_Connector +{ +public: + // 显式禁用默认构造 + CSV_Column_Output_Connector() = delete; + + // 必要构造 + explicit CSV_Column_Output_Connector(Independent_ID_Generator *ID_Generator); + + // 绘制接口 + virtual void Show(void); +}; + +// ====================================节点类声明================================================ + +// 接口测试节点 +class Connector_Test_Node : public Abs_Node +{ +public: + // 显式禁用默认构造 + Connector_Test_Node() = delete; + + // 必要构造 + Connector_Test_Node(Independent_ID_Generator *Node_ID_Generator, + Independent_ID_Generator *Connector_ID_Generator, + ImVec2 initial_position = ImVec2(0, 0)); + + // 绘制节点 + virtual void Show(void); + + // 执行处理流程 + virtual bool Execute_Process(void); +}; + +// DLT信息输入类,起始点 +class MSG_Input_Node : public Abs_Node +{ +public: + // 显式禁用默认构造 + MSG_Input_Node() = delete; + + // 必要构造 + MSG_Input_Node(Independent_ID_Generator *Node_ID_Generator, + Independent_ID_Generator *Connector_ID_Generator, + ImVec2 initial_position = ImVec2(0, 0)); + + // 绘制节点 + virtual void Show(void); + + // 执行处理流程 + virtual bool Execute_Process(void); +}; + +#endif \ No newline at end of file diff --git a/Inc/Workflow_Editor.hpp b/Inc/Workflow_Editor.hpp index a30456d..9cf7484 100644 --- a/Inc/Workflow_Editor.hpp +++ b/Inc/Workflow_Editor.hpp @@ -1,89 +1,58 @@ #ifndef __WORKFLOW_EDITOR_HPP__ #define __WORKFLOW_EDITOR_HPP__ -#include "imgui.h" - -extern "C" -{ -#include "stdint.h" -} - #include - -// // 并行工作链 -// class Parallel_Working_List -// { -// private: -// // 节点计数 -// size_t node_count; - -// // 内部迭代器 -// class iterator -// { -// }; - -// public: -// iterator begin() { return iterator(arr); } -// iterator end() { return iterator(arr + SIZE); } -// }; - -// 节点类型枚举 -typedef enum -{ - NODE_TYPE_MSG_LINE_INPUT, // 消息输入节点 - NODE_TYPE_CSV_EXPORTER, // CSV输出节点 -} NodeType; - -// 节点基类 -class Node_Class -{ -protected: - int id; // 节点ID - bool initial_state; // 初始状态 - ImVec2 initial_position; // 初始位置 - -public: - // 初始化列表构造 - explicit Node_Class(int id, ImVec2 initial_position = ImVec2(0, 0)) : id(id), initial_state(true), initial_position(initial_position) {} - int getID() { return this->id; } - - /** - * 显示节点并获取状态(子类必须实现) - * @param null - * @return 0 -> normal : 1-> close - */ - virtual bool show_and_get_state(void) = 0; - - virtual ~Node_Class() = default; // 抽象基类必须有虚析构函数 -}; - -// 消息输出节点 -class MsgLine_Input_Node_Class : public Node_Class -{ -public: - explicit MsgLine_Input_Node_Class(int id, ImVec2 initial_position = ImVec2(0, 0)) : Node_Class(id, initial_position) {}; - bool show_and_get_state(void) override; -}; +#include +#include "imgui.h" +#include "imnodes.h" +#include "ID_Generator.hpp" +#include "Nodes_And_Connectors.hpp" // 节点编辑器类 -class Workflow_Editor_Class +class Workflow_Editor { protected: - // 节点列表 - std::vector Node_List; + typedef enum + { + NODE_TYPE_UNKNOWN, // 未知节点类型 + NODE_TYPE_MSG_LINE_INPUT, // 消息输入节点 + NODE_TYPE_CSV_EXPORTER, // CSV输出节点 + } Node_Type_Enum; // 节点类型枚举 + + // ID 分配器 + Independent_ID_Generator Connector_ID_Generator; // 连接点ID生成器 + Independent_ID_Generator Node_ID_Generator; // 节点ID生成器 + Independent_ID_Generator Edge_ID_Generator; // 边ID生成器 + + // 边定义 + typedef struct + { + int id = -1; // 边ID + int source_connector_id = -1; // 源连接点 + int target_connector_id = -1; // 目标连接点 + } Edge; + + // 数据池 + std::vector> Connector_Pool; // 连接点池 + std::vector> Node_Pool; // 节点池 + std::vector Edge_Pool; // 边池 + + /** + * 顶层索引为启示工作流节点(N个元素即N个处理流程) + * 次级索引为工作流分层 + * 三级索引为平级工作流节点 + * 不参与工作流节点仅存于散点列表 + */ + std::vector>>> Process_Route; // 工作层级路由 + std::vector> Separate_Node_List; // 散点列表 + + // =====================================函数声明================================================= + // 新增节点 true -> success / false -> failed + bool Add_Node(Node_Type_Enum type, ImVec2 initial_position = ImVec2(0, 0)); public: - // 构造函数 - Workflow_Editor_Class(); - - // 析构函数 - ~Workflow_Editor_Class(); - - // 显示工作流节点编辑器 + // 绘制节点编辑器 void Show(void); - - // 新增节点 - void Add_Node(NodeType type, ImVec2 initial_position = ImVec2(0, 0)); }; #endif \ No newline at end of file diff --git a/Src/Global_Variables.cpp b/Src/Global_Variables.cpp index c8064f5..be69199 100644 --- a/Src/Global_Variables.cpp +++ b/Src/Global_Variables.cpp @@ -17,4 +17,4 @@ Input_File_Node *input_dlt_file_list_head; size_t input_dlt_file_count; // 节点编辑器 -Workflow_Editor_Class Workflow_Editor; \ No newline at end of file +class Workflow_Editor Workflow_Editor; \ No newline at end of file diff --git a/Src/ID_Generator.cpp b/Src/ID_Generator.cpp new file mode 100644 index 0000000..6a4d84b --- /dev/null +++ b/Src/ID_Generator.cpp @@ -0,0 +1,98 @@ +#include "ID_Generator.hpp" + +// 请求ID +int Independent_ID_Generator::Request_ID() +{ + // 没有元素时 + if (this->continuous_id_list.size() == 0) + { + this->continuous_id_list.push_back({0, 0}); + + // 判定分配情况 + if (this->continuous_id_list.size() == 0) + return -1; + else + return 0; + } + + // 第一段未从最小 ID 开启 + auto first_iterator = this->continuous_id_list.begin(); + if (first_iterator->begin != 0) + { + if (first_iterator->begin == 1) + first_iterator->begin--; + else + { + // 划分新段 + size_t pre_size = this->continuous_id_list.size(); + first_iterator = this->continuous_id_list.insert(first_iterator, {0, 0}); + if (!(this->continuous_id_list.size() > pre_size)) + return -1; + } + return 0; + } + + // 仅第一段时 + if (this->continuous_id_list.size() == 1) + { + first_iterator->end++; + return first_iterator->end; + } + + // 有两段及以上时,判断是否出现合并 + auto second_iterator = first_iterator + 1; + if (first_iterator->end + 2 == second_iterator->begin) + { + int target_id = first_iterator->end + 1; + first_iterator->end = second_iterator->end; + this->continuous_id_list.erase(second_iterator); + return target_id; + } + else + { + first_iterator->end++; + return first_iterator->end; + } + + return -1; +} + +// 释放ID true-> success / false -> fail +bool Independent_ID_Generator::Release_ID(const int ID) +{ + // 遍历查找所属片 + for (auto iterator = this->continuous_id_list.begin(); iterator != this->continuous_id_list.end(); iterator++) + { + // 找到对应的ID所属片 + if (ID >= iterator->begin && ID <= iterator->end) + { + // 散点删除 + if (iterator->begin == iterator->end) + this->continuous_id_list.erase(iterator); + else if (ID == iterator->begin) // 前端点 + iterator->begin++; + else if (ID == iterator->end) // 后端点 + iterator->end--; + else // 中间段 + { + // 插入后返回新的迭代器 + size_t pre_size = this->continuous_id_list.size(); + iterator = this->continuous_id_list.insert(iterator, {iterator->begin, ID - 1}); + + // 判定分配是否成功 + if (!(this->continuous_id_list.size() > pre_size)) + return false; + + // 记录ID + iterator++; + iterator->begin = ID + 1; + } + + // 完成并退出 + return true; + } + } + + // 没有找到所属片 + return false; +} diff --git a/Src/Nodes_And_Connectors.cpp b/Src/Nodes_And_Connectors.cpp new file mode 100644 index 0000000..f4ecb49 --- /dev/null +++ b/Src/Nodes_And_Connectors.cpp @@ -0,0 +1,378 @@ +#include "Nodes_And_Connectors.hpp" +#include "imgui_internal.h" + +// 客制化圆形带X按钮 +bool CircleButtonWithX(const char *id, float radius) +{ + ImGuiWindow *window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + // 创建ID + ImGuiID button_id = window->GetID(id); + + // 计算按钮位置和大小 + ImVec2 pos = window->DC.CursorPos; + ImVec2 size(radius * 2, radius * 2); + ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); + + // 处理交互 + ImGui::ItemSize(bb); + if (!ImGui::ItemAdd(bb, button_id)) + return false; + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, button_id, &hovered, &held); + + // 绘制 + ImU32 col = ImGui::GetColorU32( + held ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button); + + // 获取绘制列表 + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + ImVec2 center = ImVec2(pos.x + radius, pos.y + radius); + + // 绘制圆形 + draw_list->AddCircleFilled(center, radius, col, 32); + + // 绘制边框(可选) + draw_list->AddCircle(center, radius, ImGui::GetColorU32(ImGuiCol_Border), 32, 1.0f); + + // 计算X的线条 + float cross_radius = radius * 0.5f; // X的大小 + float thickness = radius * 0.15f; // X的粗细 + + // 绘制X的两条线 + draw_list->AddLine( + ImVec2(center.x - cross_radius, center.y - cross_radius), + ImVec2(center.x + cross_radius, center.y + cross_radius), + ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X + thickness); + draw_list->AddLine( + ImVec2(center.x + cross_radius, center.y - cross_radius), + ImVec2(center.x - cross_radius, center.y + cross_radius), + ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X + thickness); + + return pressed; +} + +// ================================================================================================= + +// 必要构造 +Abs_Connector::Abs_Connector(Independent_ID_Generator *ID_Generator, + Connector_Type_Enum type, + const char *socket_str) + : ID_Generator(nullptr), id(-1), type(CONNECTOR_TYPE_UNKNOWN), socket_str(nullptr) +{ + // 分配ID + this->id = ID_Generator->Request_ID(); + if (this->id != -1) + { + this->ID_Generator = ID_Generator; + this->type = type; + this->socket_str = socket_str; + } + + // 指针初始化 + this->product = nullptr; + this->provider = nullptr; +}; + +// 析构 +Abs_Connector::~Abs_Connector() +{ + // 释放产物 + if (this->product != nullptr) + free(product); + // 断开连接 + this->Disconnect(); + // 释放ID + this->ID_Generator->Release_ID(this->id); +} + +// 连接到目标连接点 +bool Abs_Connector::Connect_To(Abs_Connector *target) +{ + // 判定接口是否匹配 + if (strcmp(target->socket_str, this->socket_str) == 0) + { + // 写连接关系 + if ((this->type == CONNECTOR_TYPE_OUTPUT) && (target->type == CONNECTOR_TYPE_INPUT)) + { + target->provider = this; + this->receiver_list.push_back(target); + return true; + } + if ((this->type == CONNECTOR_TYPE_INPUT) && (target->type == CONNECTOR_TYPE_OUTPUT)) + { + target->receiver_list.push_back(this); + this->provider = target; + return true; + } + } + return false; +} + +// 断开连接 +void Abs_Connector::Disconnect(void) +{ + // 受点断开供方连接 + if (this->type == CONNECTOR_TYPE_INPUT) + { + // 断开供方对自身的记录 + for (auto iterator = this->provider->receiver_list.begin(); iterator != this->provider->receiver_list.end(); iterator++) + { + // 供方断开自身列表记录 + if ((*iterator) == this) + { + this->provider->receiver_list.erase(iterator); + break; + } + } + // 断开自身对供方的记录 + this->provider = nullptr; + } + // 供方断开所有受点的供方记录 + if (this->type == CONNECTOR_TYPE_OUTPUT) + { + // 断开受点的供方记录 + for (auto receiver : this->receiver_list) + receiver->provider = nullptr; + + // 清空受点列表 + decltype(this->receiver_list) empty_list; + this->receiver_list.swap(empty_list); + } +} + +// 注册产物 +bool Abs_Connector::Register_Product(void *product, size_t size) +{ + // 非输出类接口 + if (this->type != CONNECTOR_TYPE_OUTPUT) + return false; + + // 释放之前的产物 + if (this->product != nullptr) + free(this->product); + + // 分配空间 + this->product = malloc(size); + if (this->product == nullptr) + return false; + + // 复制内容 + memcpy(this->product, product, size); + return true; +} + +// 获取上级节点 +Abs_Connector *Abs_Connector::Get_Provider(void) +{ + return this->provider; +} + +// 获取产物 +void *Abs_Connector::Get_Product(void) +{ + // 输出节点返回直接产物 + if (this->type == CONNECTOR_TYPE_OUTPUT) + return this->product; + + // 输入节点返回间接产物(提供者方产物) + if (this->type == CONNECTOR_TYPE_INPUT) + { + if (this->provider == nullptr) + return nullptr; + return this->provider->product; + } + + // 未知类型 + return nullptr; +} + +// 获取ID +int Abs_Connector::Get_ID(void) +{ + return this->id; +} + +// ================================================================================================= + +// 必要构造 +Abs_Node::Abs_Node(Independent_ID_Generator *Node_ID_Generator, + Independent_ID_Generator *Connector_ID_Generator, + Node_Type_Enum type, + ImVec2 initial_position) + : Node_ID_Generator(nullptr), + Connector_ID_Generator(nullptr), + id(-1), + type(NODE_TYPE_UNKNOWN), + initial_position(initial_position) +{ + // 分配ID + this->id = Node_ID_Generator->Request_ID(); + + if (this->id != -1) + { + this->Node_ID_Generator = Node_ID_Generator; + this->Connector_ID_Generator = Connector_ID_Generator; + this->type = type; + } + + this->close_flag = false; +} + +// 析构 +Abs_Node::~Abs_Node() +{ + // 释放ID + this->Node_ID_Generator->Release_ID(this->id); +} + +// 获取关闭信号 +bool Abs_Node::Get_CloseFlag(void) +{ + return this->close_flag; +} + +// 获取接口列表 +std::vector> &Abs_Node::Get_Connector_List(void) +{ + return this->Connector_List; +} + +// ================================================================================================= + +// 必要构造 +MSG_OutPut_Connector::MSG_OutPut_Connector(Independent_ID_Generator *ID_Generator) : Abs_Connector(ID_Generator, CONNECTOR_TYPE_OUTPUT, "DLT MSG") {} + +// 绘制接口 +void MSG_OutPut_Connector::Show(void) +{ + // 输出接口点 + ImNodes::BeginOutputAttribute(this->id, 1); + ImNodes::EndOutputAttribute(); +} + +// 必要构造 +MSG_InPut_Connector::MSG_InPut_Connector(Independent_ID_Generator *ID_Generator) : Abs_Connector(ID_Generator, CONNECTOR_TYPE_INPUT, "DLT MSG") {} + +// 绘制接口 +void MSG_InPut_Connector::Show(void) +{ + // 输出接口点 + ImNodes::BeginInputAttribute(this->id, 1); + ImNodes::EndInputAttribute(); +} + +// ================================================================================================= + +// 必要构造 +MSG_Input_Node::MSG_Input_Node(Independent_ID_Generator *Node_ID_Generator, + Independent_ID_Generator *Connector_ID_Generator, + ImVec2 initial_position) + : Abs_Node(Node_ID_Generator, + Connector_ID_Generator, + NODE_TYPE_MSG_LINE_INPUT, + initial_position) +{ + // 判定是否成功申请ID + if (this->id != -1) + { + // 消息输出节点 + this->Connector_List.push_back(std::make_shared(Connector_ID_Generator)); + this->Connector_List.push_back(std::make_shared(Connector_ID_Generator)); + } +} + +// 绘制节点 +void MSG_Input_Node::Show(void) +{ + + // auto ImNodesStyle = ImNodes::GetStyle(); + // auto ImGuiStyle = ImGui::GetStyle(); + + if (this->close_flag == true) + return; + + // 初始化位置设定 + if ((this->initial_position.x != 0) || (this->initial_position.y != 0)) + { + ImNodes::SetNodeScreenSpacePos(this->id, this->initial_position); + this->initial_position = ImVec2(0, 0); + } + + // 绘制节点 + ImNodes::BeginNode(this->id); + { + + // 标题部分 + ImNodes::BeginNodeTitleBar(); + { + ImGui::Text(u8"DLT-信息输入节点"); + ImGui::SameLine(); + { + ImGui::TextDisabled(u8"(?)"); + // 上下边距为调整 + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); + + if (ImGui::BeginItemTooltip()) + { + ImGui::Text("Node ID : %d\n", this->id); + ImGui::Separator(); + ImGui::Text(u8"每个处理流程开始时,输出一条DLT消息;\n每个此类节点输出的消息均会独立遍历所有输入文件;"); + ImGui::EndTooltip(); + } + + ImGui::PopStyleVar(); + } + ImGui::SameLine(); + // ImGui::Dummy(ImVec2(node_width - ImGui::CalcTextSize(u8"DLT-信息输入节点").x - ImGui::GetTextLineHeight(), 0.0f)); + + // 节点宽度调整 + ImGui::Dummy(ImVec2(20.0f, 0.0f)); + + ImGui::SameLine(); + // 绘制关闭按钮 + if (CircleButtonWithX("#X", ImGui::GetTextLineHeight() / 2)) + this->close_flag = true; + } + ImNodes::EndNodeTitleBar(); + + // 节点主体部分 + { + } + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y)); + + ImVec2 dimension = ImNodes::GetNodeDimensions(this->id); + ImVec2 text_size = ImGui::CalcTextSize(u8"OUT ->"); + if (dimension.x > text_size.x) + { + ImGui::Dummy(ImVec2(dimension.x - text_size.x - (ImNodes::GetStyle().NodePadding.x + ImGui::GetStyle().ItemSpacing.x) * 2, 0.0f)); + ImGui::SameLine(); + } + ImGui::Text(u8"OUT ->"); + ImGui::SameLine(); + + /** + * @todo 自定义接口绘制 + */ + for (auto Connector : this->Connector_List) + { + Connector->Show(); + } + + ImGui::PopStyleVar(); + } + ImNodes::EndNode(); +} + +// 执行处理流程 +bool MSG_Input_Node::Execute_Process(void) +{ + return true; +}; \ No newline at end of file diff --git a/Src/Workflow_Editor.cpp b/Src/Workflow_Editor.cpp index be91b45..c174c59 100644 --- a/Src/Workflow_Editor.cpp +++ b/Src/Workflow_Editor.cpp @@ -1,161 +1,117 @@ -#include "Workflow-Editor.hpp" -#include "imgui.h" +#include "Workflow_Editor.hpp" #include "imgui_internal.h" -#include "imnodes.h" -#include -// 构造函数 -Workflow_Editor_Class::Workflow_Editor_Class() {} - -// 析构函数 -Workflow_Editor_Class::~Workflow_Editor_Class() +// 新增节点 +bool Workflow_Editor::Add_Node(Node_Type_Enum type, ImVec2 initial_position) { - // 释放资源 - for (auto &pNode : this->Node_List) - delete pNode; -} + // 新节点 + std::shared_ptr p_New_Node; -// 客制化圆形带X按钮 -bool CircleButtonWithX(const char *id, float radius) -{ - ImGuiWindow *window = ImGui::GetCurrentWindow(); - if (window->SkipItems) - return false; - - // 创建ID - ImGuiID button_id = window->GetID(id); - - // 计算按钮位置和大小 - ImVec2 pos = window->DC.CursorPos; - ImVec2 size(radius * 2, radius * 2); - ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); - - // 处理交互 - ImGui::ItemSize(bb); - if (!ImGui::ItemAdd(bb, button_id)) - return false; - - bool hovered, held; - bool pressed = ImGui::ButtonBehavior(bb, button_id, &hovered, &held); - - // 绘制 - ImU32 col = ImGui::GetColorU32( - held ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered - : ImGuiCol_Button); - - // 获取绘制列表 - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - ImVec2 center = ImVec2(pos.x + radius, pos.y + radius); - - // 绘制圆形 - draw_list->AddCircleFilled(center, radius, col, 32); - - // 绘制边框(可选) - draw_list->AddCircle(center, radius, ImGui::GetColorU32(ImGuiCol_Border), 32, 1.0f); - - // 计算X的线条 - float cross_radius = radius * 0.5f; // X的大小 - float thickness = radius * 0.15f; // X的粗细 - - // 绘制X的两条线 - draw_list->AddLine( - ImVec2(center.x - cross_radius, center.y - cross_radius), - ImVec2(center.x + cross_radius, center.y + cross_radius), - ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X - thickness); - draw_list->AddLine( - ImVec2(center.x + cross_radius, center.y - cross_radius), - ImVec2(center.x - cross_radius, center.y + cross_radius), - ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)), // 白色X - thickness); - - return pressed; -} - -// 消息行输入节点绘制函数 -bool MsgLine_Input_Node_Class::show_and_get_state() -{ - // 节点宽度 - const float node_width = 200.0f; - bool delete_flag = false; - - // 初始化位置设定 - if (this->initial_state) + // 记录节点 + switch (type) { - ImNodes::SetNodeScreenSpacePos(this->id, this->initial_position); - this->initial_state = false; + case NODE_TYPE_MSG_LINE_INPUT: // 消息行输入 (头节点) + + // 创建节点 + p_New_Node = std::make_shared(&this->Node_ID_Generator, + &this->Connector_ID_Generator, + initial_position); + break; + + // // 记录到节点池和接口池 + // this->Node_Pool.push_back(p_New_Node); + // for (auto p_Connector : p_New_Node->Get_Connector_List()) + // this->Connector_Pool.push_back(p_Connector); + + // // 编辑工作路由 + // { + // // 起始层级只有起始节点 + // std::vector> Begin_Layer; + // Begin_Layer.push_back(p_New_Node); + + // // 层级列表仅有起始层级 + // std::vector>> Layer_List; + // Layer_List.push_back(Begin_Layer); + + // // 插入工作路由表 + // this->Process_Route.push_back(Layer_List); + // } + // return true; + + case NODE_TYPE_CSV_EXPORTER: // CSV输出器 + break; + default: + return false; } - // 绘制节点 - ImNodes::BeginNode(this->id); + if (p_New_Node.get() != nullptr) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y)); - // 标题部分 - ImNodes::BeginNodeTitleBar(); - { - ImGui::TextUnformatted(u8"DLT-信息输入节点"); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(node_width - ImGui::CalcTextSize(u8"DLT-信息输入节点").x - ImGui::GetTextLineHeight(), 0.0f)); - ImGui::SameLine(); - // 绘制关闭按钮 - if (CircleButtonWithX("#X", ImGui::GetTextLineHeight() / 2)) - delete_flag = true; - } - ImNodes::EndNodeTitleBar(); + // 将新增的节点及其接口记录到对应的池 + this->Node_Pool.push_back(p_New_Node); + for (auto p_Connector : p_New_Node->Get_Connector_List()) + this->Connector_Pool.push_back(p_Connector); - // // 正文部分 - // ImGui::Text(u8"DLT消息输入流\n每次输出一条消息"); - // ImGui::Text(u8"(节点ID : %d)", this->id); - - // 链接点 - ImNodes::BeginOutputAttribute(0, 1); - // const float label_width = ImGui::CalcTextSize(u8"DLT消息行->").x; - // ImGui::Indent(label_width); - ImGui::Dummy(ImVec2(node_width - ImGui::CalcTextSize("(?) DLT MSG ->").x, 0.0f)); - ImGui::SameLine(); - - // Help Mark - { - ImGui::TextDisabled("(?) "); - if (ImGui::BeginItemTooltip()) - { - ImGui::TextUnformatted(u8"每个处理流程开始时,输出一条DLT消息;\n每个此类节点输出的消息均会独立遍历所有输入文件;"); - ImGui::EndTooltip(); - } - } - - ImGui::SameLine(); - ImGui::TextUnformatted("DLT MSG ->"); - ImNodes::EndOutputAttribute(); - - ImGui::PopStyleVar(); + // 记录到散点池 + this->Separate_Node_List.push_back(p_New_Node); + return true; } - ImNodes::EndNode(); - - // 返回节点状态 - return delete_flag; + return false; } -// 显示工作流节点编辑器 -void Workflow_Editor_Class::Show(void) +// 绘制节点编辑器 +void Workflow_Editor::Show(void) { - // 浅色主题 + // 浅色主题节点编辑器窗体 ImNodes::StyleColorsLight(); ImNodes::BeginNodeEditor(); - ImGui::PopStyleVar(); { - // 显示所有节点 - for (auto iterator = this->Node_List.begin(); iterator != this->Node_List.end(); iterator++) + for (auto iterator = this->Node_Pool.begin(); iterator != this->Node_Pool.end(); iterator++) { - if ((*iterator)->show_and_get_state()) + auto Connector_List = (*iterator)->Get_Connector_List(); + if ((*iterator)->Get_CloseFlag() == true) { - delete *iterator; + // 删除列表中所有与该节点接口相关的边并从接口池中删除 + for (auto pConnector : Connector_List) + { + int connector_id = pConnector->Get_ID(); + // 遍历边池 + for (auto iterator = this->Edge_Pool.begin(); iterator != this->Edge_Pool.end(); iterator++) + { + if ((iterator->source_connector_id == connector_id) || (iterator->target_connector_id == connector_id)) + { + // 注销边ID + this->Edge_ID_Generator.Release_ID(connector_id); + // 注销边记录,迭代器删除后会返回下一个位置的迭代器 + iterator = this->Edge_Pool.erase(iterator); + iterator--; + } + } - // 迭代器删除后会返回下一个位置的迭代器 - iterator = this->Node_List.erase(iterator); + // 从接口池中移除 + for (auto iterator = this->Connector_Pool.begin(); iterator != this->Connector_Pool.end(); iterator++) + { + if (*iterator == pConnector) + { + // 注销节点记录 + iterator = this->Connector_Pool.erase(iterator); + break; + } + } + } + + /** + * @todo 从工作路由中删除该节点并调整工作路由层级顺序 + * @todo 可能不需要对外暴露接口池 + */ + + // 从节点池中删除该节点,迭代器删除后会返回下一个位置的迭代器 + iterator = this->Node_Pool.erase(iterator); iterator--; } + else + // 绘制节点 + (*iterator)->Show(); } // 右键菜单 @@ -164,8 +120,11 @@ void Workflow_Editor_Class::Show(void) ImNodes::IsEditorHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Right); + // 设置弹窗周边Padding ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); - if (!ImGui::IsAnyItemHovered() && open_popup) + + // if (!ImGui::IsAnyItemHovered() && open_popup) + if (open_popup) { ImGui::OpenPopup("RightClick_Popup"); } @@ -174,77 +133,85 @@ void Workflow_Editor_Class::Show(void) { ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup(); // 节点添加菜单 - if (ImGui::BeginMenu("Add Nodes")) + if (ImGui::BeginMenu(u8"创建节点")) { - if (ImGui::MenuItem("Msg input node.")) + if (ImGui::MenuItem(u8"DLT信息输入节点")) this->Add_Node(NODE_TYPE_MSG_LINE_INPUT, click_pos); ImGui::EndMenu(); } ImGui::EndPopup(); } + ImGui::PopStyleVar(); } - { - // ImNodes::BeginNode(1); - // ImNodes::BeginNodeTitleBar(); + // 显示所有的连线 + for (auto Edge : this->Edge_Pool) + ImNodes::Link(Edge.id, Edge.source_connector_id, Edge.target_connector_id); - // // ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); // 绘制风格栈压入窗口圆角为0 - // // ImGui::Button("X"); - // // ImGui::PopStyleVar(); - // if (CircleButtonWithX("#X", ImGui::GetTextLineHeight() / 2)) - // { - // }; - - // ImGui::SameLine(); - // ImGui::TextUnformatted("Output Node"); - - // ImNodes::EndNodeTitleBar(); - - // ImGui::Text("Test Format %%d :%d", 123); - - // ImGui::Button("Click"); - - // ImGui::SameLine(); - // static bool check = false; - // ImGui::Checkbox("", &check); - - // ImNodes::EndNode(); - } + // 缩略图 + ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight); } - ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight); ImNodes::EndNodeEditor(); -} -// 添加指定类型节点 -void Workflow_Editor_Class::Add_Node(NodeType type, ImVec2 initial_position) -{ - // 获取可用ID - auto get_id = [&]() -> int + // 处理连线 { - int target_id = 0; - for (auto &pNode : this->Node_List) - if (target_id == pNode->getID()) - target_id++; - else - break; - return target_id; - }; + int source_id = -1, target_id = -1; + if (ImNodes::IsLinkCreated(&source_id, &target_id)) + { - // 记录节点 - switch (type) - { - case NODE_TYPE_MSG_LINE_INPUT: // 消息行输入 - this->Node_List.push_back(new MsgLine_Input_Node_Class(get_id(), initial_position)); - break; - case NODE_TYPE_CSV_EXPORTER: // CSV输出器 - default: - break; + // 查找接口 + bool find = false; + std::shared_ptr p_source_connector; + std::shared_ptr p_target_connector; + for (auto pNode : this->Node_Pool) + { + for (auto pConnector : pNode->Get_Connector_List()) + { + if (pConnector->Get_ID() == source_id) + p_source_connector = pConnector; + if (pConnector->Get_ID() == target_id) + p_target_connector = pConnector; + if ((p_source_connector.get() != nullptr) && (p_target_connector.get() != nullptr)) + { + find = true; + break; + } + } + + if (find) + break; + } + + // 判定查找结果 + if (find) + { + // 输入类型节点只允许一个父接口,输出类型节点可以有多个子接口 + if (p_target_connector->Get_Provider() != nullptr) + { + int origin_source_id = p_target_connector->Get_Provider()->Get_ID(); + for (auto iterator = this->Edge_Pool.begin(); iterator != this->Edge_Pool.end(); iterator++) + { + if (iterator->source_connector_id == origin_source_id && iterator->target_connector_id == target_id) + { + this->Edge_ID_Generator.Release_ID(iterator->id); + this->Edge_Pool.erase(iterator); + break; + } + } + } + + // 尝试连接 + if (p_source_connector->Connect_To(p_target_connector.get()) == true) + { + // 记录到边池 + this->Edge_Pool.push_back({this->Edge_ID_Generator.Request_ID(), source_id, target_id}); + + /** + * @todo 调整工作路由层级 + */ + } + } + } } - - // 重新排序确保后续ID分配正常 - std::sort(this->Node_List.begin(), - this->Node_List.end(), - [](Node_Class *a, Node_Class *b) -> bool - { return a->getID() < b->getID(); }); -} \ No newline at end of file +}