diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml new file mode 100644 index 0000000..1ab4d16 --- /dev/null +++ b/.github/workflows/Build.yml @@ -0,0 +1,60 @@ +name: Build + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: windows-latest + strategy: + matrix: + build_type: [Debug, Release] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Checkout master branch + run: git checkout master + + # In order to build we need to use tests branch. + - name: Copy files from master branch to temporary folder + run: | + mkdir "${{github.workspace}}\master_temp" + + robocopy "${{github.workspace}}\" "${{github.workspace}}\master_temp" /NFL /E /XD "${{github.workspace}}\master_temp" + # Robocopy will return 1 if successful, so we need to exit with 0 to avoid triggering a failure for this step + if ($LastExitCode -eq 1) { + echo "All files were copied successfully. Continuing..." + exit 0 + } elseif ($LastExitCode -gt 7) { + echo "An error occurred during file copying. Exiting..." + exit $LastExitCode + } + + - name: Checkout tests branch + run: | + git checkout tests + + - name: Copy master branch files from temporary folder to MasterBranchCode folder + run: | + robocopy "${{github.workspace}}\master_temp" "${{github.workspace}}\MasterBranchCode" /NFL /E + # Robocopy will return 1 if successful, so we need to exit with 0 to avoid triggering a failure for this step + if ($LastExitCode -eq 1) { + echo "All files were copied successfully. Continuing..." + exit 0 + } elseif ($LastExitCode -gt 7) { + echo "An error occurred during file copying. Exiting..." + exit $LastExitCode + } + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{ matrix.build_type }} diff --git a/.github/workflows/ContinuousIntegration.yml b/.github/workflows/ContinuousIntegration.yml new file mode 100644 index 0000000..1c47e00 --- /dev/null +++ b/.github/workflows/ContinuousIntegration.yml @@ -0,0 +1,85 @@ +name: ContinuousIntegration + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + types: [opened, reopened, synchronize] + +jobs: + ContinuousIntegration: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Checkout master branch + run: git checkout master + + - name: Copy files from master branch to temporary folder + run: | + mkdir "${{github.workspace}}\master_temp" + + robocopy "${{github.workspace}}\" "${{github.workspace}}\master_temp" /NFL /E /XD "${{github.workspace}}\master_temp" + # Robocopy will return 1 if successful, so we need to exit with 0 to avoid triggering a failure for this step + if ($LastExitCode -eq 1) { + echo "All files were copied successfully. Continuing..." + exit 0 + } elseif ($LastExitCode -gt 7) { + echo "An error occurred during file copying. Exiting..." + exit $LastExitCode + } + + - name: Checkout tests branch + run: git checkout tests + + - name: Copy master branch files from temporary folder to MasterBranchCode folder + run: | + robocopy "${{github.workspace}}\master_temp" "${{github.workspace}}\MasterBranchCode" /NFL /E + # Robocopy will return 1 if successful, so we need to exit with 0 to avoid triggering a failure for this step + if ($LastExitCode -eq 1) { + echo "All files were copied successfully. Continuing..." + exit 0 + } elseif ($LastExitCode -gt 7) { + echo "An error occurred during file copying. Exiting..." + exit $LastExitCode + } + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build ${{github.workspace}}/build --config Release + + - name: Ensure all files are in place + run: | + Move "${{github.workspace}}\build\Release\Visual_Node_System_Tests.exe" "${{github.workspace}}\Visual_Node_System_Tests.exe" + + - name: Run test + run: | + $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo + $processStartInfo.FileName = "Visual_Node_System_Tests.exe" + + Write-Host "Starting the executable..." + $process = New-Object System.Diagnostics.Process + $process.StartInfo = $processStartInfo + $process.Start() | Out-Null + Write-Host "Executable started." + + Write-Host "Waiting for the process to exit..." + $process.WaitForExit(10000) # 10 seconds + if ($process.ExitCode -ne 0) { + Write-Host "Some tests failed. Please check the test results." + exit 1 + } + + - name: Print additional test results if failed + if: failure() + run: | + Get-Content "Results.xml" | ForEach-Object { + Write-Host $_ + } diff --git a/GroupComment.cpp b/GroupComment.cpp index 3606d96..c2e5484 100644 --- a/GroupComment.cpp +++ b/GroupComment.cpp @@ -52,21 +52,21 @@ void GroupComment::Draw() {} Json::Value GroupComment::ToJson() { - Json::Value result; - - result["ID"] = ID; - result["position"]["x"] = Position.x; - result["position"]["y"] = Position.y; - result["size"]["x"] = Size.x; - result["size"]["y"] = Size.y; - result["caption"] = Caption; - result["bMoveElementsWithComment"] = bMoveElementsWithComment; - result["BackgroundColor"]["x"] = BackgroundColor.x; - result["BackgroundColor"]["y"] = BackgroundColor.y; - result["BackgroundColor"]["z"] = BackgroundColor.z; - result["BackgroundColor"]["w"] = BackgroundColor.w; - - return result; + Json::Value Result; + + Result["ID"] = ID; + Result["position"]["x"] = Position.x; + Result["position"]["y"] = Position.y; + Result["size"]["x"] = Size.x; + Result["size"]["y"] = Size.y; + Result["caption"] = Caption; + Result["bMoveElementsWithComment"] = bMoveElementsWithComment; + Result["BackgroundColor"]["x"] = BackgroundColor.x; + Result["BackgroundColor"]["y"] = BackgroundColor.y; + Result["BackgroundColor"]["z"] = BackgroundColor.z; + Result["BackgroundColor"]["w"] = BackgroundColor.w; + + return Result; } void GroupComment::FromJson(Json::Value Json) diff --git a/README.md b/README.md index 729c1d4..a819dfa 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ -# Visual Node System - -![build](https://github.com/Azzinoth/VisualNodeSystem-Example/actions/workflows/Build.yml/badge.svg?branch=master) +
+

Visual Node System

+ Build Status + Continuous Integration +
This library provides a powerful framework for creating and managing visual node systems. These are typically used in graphical programming environments, game logic builders, and AI behavior tree editors. +Master branch is free of tests to maintain its lightweightness. All test-related stuff (except GitHub Actions) is located in the [Tests Branch](https://github.com/Azzinoth/VisualNodeSystem/tree/tests) + ## Key Features - **Unchanged Dear Imgui**: This project does not depend on modifications to Dear Imgui. @@ -27,6 +31,11 @@ This library provides a powerful framework for creating and managing visual node - **JSON Serialization**: The library provides functionalities to serialize/deserialize the node data to/from JSON format. +- **Integrated Copy/Paste**: With the help of JSON serialization/deserialization, elements can be copied from one node area to the same or another node area. +
+ +
+ - **Node Factory For Child Node Creation**: The library includes a system for creating child nodes using node factory, which enables JSON serialization of custom nodes. - **Custom Context Menus**: The library supports the integration of custom context menus. diff --git a/SubSystems/VisualNodeArea/VisualNodeArea.cpp b/SubSystems/VisualNodeArea/VisualNodeArea.cpp index 455a2a5..69522af 100644 --- a/SubSystems/VisualNodeArea/VisualNodeArea.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeArea.cpp @@ -41,7 +41,8 @@ void NodeArea::SetPosition(const ImVec2 NewValue) void NodeArea::Update() { - InputUpdate(); + if (!NODE_CORE.bIsInTestMode) + InputUpdate(); for (int i = 0; i < static_cast(Nodes.size()); i++) { @@ -54,7 +55,8 @@ void NodeArea::Update() } ProcessSocketEventQueue(); - Render(); + if (!NODE_CORE.bIsInTestMode) + Render(); } void NodeArea::SetMainContextMenuFunc(void(*Func)()) @@ -66,6 +68,14 @@ void NodeArea::Clear() { bClearing = true; + for (int i = 0; i < static_cast(GroupComments.size()); i++) + { + DeleteGroupComment(GroupComments[i]); + i--; + } + GroupComments.clear(); + SelectedGroupComments.clear(); + for (int i = 0; i < static_cast(Nodes.size()); i++) { PropagateNodeEventsCallbacks(Nodes[i], DESTROYED); @@ -73,10 +83,13 @@ void NodeArea::Clear() DeleteNode(Nodes[i]); i--; } - + Nodes.clear(); + SelectedNodes.clear(); + SelectedRerouteNodes.clear(); + RenderOffset = ImVec2(0, 0); NodeAreaWindow = nullptr; - SelectedNodes.clear(); + SocketLookingForConnection = nullptr; SocketHovered = nullptr; @@ -94,7 +107,7 @@ void NodeArea::Reset() NodeEventsCallbacks.clear(); } -void NodeArea::SetNodeEventCallback(void(*Func)(Node*, NODE_EVENT)) +void NodeArea::AddNodeEventCallback(std::function Func) { if (Func != nullptr) NodeEventsCallbacks.push_back(Func); @@ -118,17 +131,6 @@ void NodeArea::SaveToFile(const char* FileName) const SaveFile.close(); } -bool NodeArea::IsNodeIDInList(const std::string ID, const std::vector List) -{ - for (size_t i = 0; i < List.size(); i++) - { - if (List[i]->GetID() == ID) - return true; - } - - return false; -} - void NodeArea::SaveNodesToFile(const char* FileName, std::vector Nodes) { if (Nodes.empty()) @@ -166,7 +168,7 @@ void NodeArea::ProcessConnections(const std::vector& Sockets, NodeSocket* CurrentSocket = Sockets[i]; for (const auto& ConnectedSocket : CurrentSocket->ConnectedSockets) { - if (IsNodeIDInList(ConnectedSocket->GetParent()->GetID(), SourceNodes)) + if (Node::IsNodeWithIDInList(ConnectedSocket->GetParent()->GetID(), SourceNodes)) { // Check maybe we already establish this connection. if (!IsAlreadyConnected(OldToNewSocket[CurrentSocket], OldToNewSocket[ConnectedSocket], TargetArea->Connections)) @@ -345,12 +347,22 @@ std::string NodeArea::ToJson() const return JsonText; } -NodeArea* NodeArea::FromJson(std::string JsonText) +void NodeArea::CopyNodesTo(NodeArea* SourceNodeArea, NodeArea* TargetNodeArea) { - NodeArea* NewArea = new NodeArea(); + const size_t NodeShift = TargetNodeArea->Nodes.size(); + CopyNodesInternal(SourceNodeArea->Nodes, TargetNodeArea, NodeShift); + for (size_t i = 0; i < SourceNodeArea->GroupComments.size(); i++) + { + GroupComment* CopyOfGroupComment = new GroupComment(*SourceNodeArea->GroupComments[i]); + TargetNodeArea->AddGroupComment(CopyOfGroupComment); + } +} + +void NodeArea::LoadFromJson(std::string JsonText) +{ if (JsonText.find("{") == std::string::npos || JsonText.find("}") == std::string::npos || JsonText.find(":") == std::string::npos) - return NewArea; + return; Json::Value root; JSONCPP_STRING err; @@ -358,10 +370,10 @@ NodeArea* NodeArea::FromJson(std::string JsonText) const std::unique_ptr reader(builder.newCharReader()); if (!reader->parse(JsonText.c_str(), JsonText.c_str() + JsonText.size(), &root, &err)) - return NewArea; + return; if (!root.isMember("nodes")) - return NewArea; + return; std::unordered_map LoadedNodes; std::vector NodesList = root["nodes"].getMemberNames(); @@ -380,13 +392,13 @@ NodeArea* NodeArea::FromJson(std::string JsonText) continue; } } - + NewNode->FromJson(root["nodes"][std::to_string(i)]); if (NewNode != nullptr) { LoadedNodes[NewNode->GetID()] = NewNode; - NewArea->AddNode(NewNode); + AddNode(NewNode); } } @@ -400,10 +412,10 @@ NodeArea* NodeArea::FromJson(std::string JsonText) std::string OutNodeID = root["connections"][ConnectionsList[i]]["out"]["node_ID"].asCString(); if (LoadedNodes.find(OutNodeID) != LoadedNodes.end() && LoadedNodes.find(InNodeID) != LoadedNodes.end()) - if (!NewArea->TryToConnect(LoadedNodes[OutNodeID], OutSocketID, LoadedNodes[InNodeID], InSocketID)) + if (!TryToConnect(LoadedNodes[OutNodeID], OutSocketID, LoadedNodes[InNodeID], InSocketID)) continue; - Connection* NewConnection = NewArea->Connections.back(); + Connection* NewConnection = Connections.back(); // First pass to fill information that does not depend other reroutes. std::vector RerouteList = root["connections"][ConnectionsList[i]]["reroute_connections"].getMemberNames(); @@ -448,7 +460,7 @@ NodeArea* NodeArea::FromJson(std::string JsonText) if (BeginRerouteID == NewConnection->RerouteNodes[k]->ID) BeginReroute = NewConnection->RerouteNodes[k]; } - + if (BeginReroute != nullptr) NewConnection->RerouteNodes[j]->BeginReroute = BeginReroute; } @@ -476,7 +488,7 @@ NodeArea* NodeArea::FromJson(std::string JsonText) { GroupComment* NewGroupComment = new GroupComment(); NewGroupComment->FromJson(root["GroupComments"][std::to_string(i)]); - NewArea->AddGroupComment(NewGroupComment); + AddGroupComment(NewGroupComment); } } @@ -484,21 +496,7 @@ NodeArea* NodeArea::FromJson(std::string JsonText) { float OffsetX = root["renderOffset"]["x"].asFloat(); float OffsetY = root["renderOffset"]["y"].asFloat(); - NewArea->SetRenderOffset(ImVec2(OffsetX, OffsetY)); - } - - return NewArea; -} - -void NodeArea::CopyNodesTo(NodeArea* SourceNodeArea, NodeArea* TargetNodeArea) -{ - const size_t NodeShift = TargetNodeArea->Nodes.size(); - CopyNodesInternal(SourceNodeArea->Nodes, TargetNodeArea, NodeShift); - - for (size_t i = 0; i < SourceNodeArea->GroupComments.size(); i++) - { - GroupComment* CopyOfGroupComment = new GroupComment(*SourceNodeArea->GroupComments[i]); - TargetNodeArea->AddGroupComment(CopyOfGroupComment); + SetRenderOffset(ImVec2(OffsetX, OffsetY)); } } @@ -510,42 +508,50 @@ void NodeArea::LoadFromFile(const char* FileName) const std::string FileData((std::istreambuf_iterator(NodesFile)), std::istreambuf_iterator()); NodesFile.close(); - NodeArea* NewNodeArea = NodeArea::FromJson(FileData); - NodeArea::CopyNodesTo(NewNodeArea, this); - - delete NewNodeArea; + LoadFromJson(FileData); +} + +Node* NodeArea::GetNodeByID(std::string NodeID) const +{ + for (size_t i = 0; i < Nodes.size(); i++) + { + if (Nodes[i]->GetID() == NodeID) + return Nodes[i]; + } + + return nullptr; } std::vector NodeArea::GetNodesByName(const std::string NodeName) const { - std::vector result; + std::vector Result; for (size_t i = 0; i < Nodes.size(); i++) { if (Nodes[i]->GetName() == NodeName) - result.push_back(Nodes[i]); + Result.push_back(Nodes[i]); } - return result; + return Result; } std::vector NodeArea::GetNodesByType(const std::string NodeType) const { - std::vector result; + std::vector Result; for (size_t i = 0; i < Nodes.size(); i++) { if (Nodes[i]->GetType() == NodeType) - result.push_back(Nodes[i]); + Result.push_back(Nodes[i]); } - return result; + return Result; } -int NodeArea::GetNodeCount() const +size_t NodeArea::GetNodeCount() const { - return static_cast(Nodes.size()); + return Nodes.size(); } -bool NodeArea::EmptyOrFilledByNulls(const std::vector Vector) +bool NodeArea::IsEmptyOrFilledByNulls(const std::vector Vector) { for (size_t i = 0; i < Vector.size(); i++) { @@ -635,7 +641,7 @@ std::vector NodeArea::GetConnectionSegments(const Connection* ImVec2 NodeArea::LocalToScreen(ImVec2 LocalPosition) const { ImVec2 WindowPosition = ImVec2(0.0f, 0.0f); - if (ImGui::GetCurrentContext()->CurrentWindow != nullptr) + if (ImGui::GetCurrentContext() != nullptr && ImGui::GetCurrentContext()->CurrentWindow != nullptr) WindowPosition = ImGui::GetCurrentWindow()->Pos; return WindowPosition + LocalPosition * Zoom + RenderOffset; @@ -644,7 +650,7 @@ ImVec2 NodeArea::LocalToScreen(ImVec2 LocalPosition) const ImVec2 NodeArea::ScreenToLocal(ImVec2 ScreenPosition) const { ImVec2 WindowPosition = ImVec2(0.0f, 0.0f); - if (ImGui::GetCurrentContext()->CurrentWindow != nullptr) + if (ImGui::GetCurrentContext() != nullptr && ImGui::GetCurrentContext()->CurrentWindow != nullptr) WindowPosition = ImGui::GetCurrentWindow()->Pos; return (ScreenPosition - WindowPosition - RenderOffset) / Zoom; @@ -683,7 +689,7 @@ bool NodeArea::IsRectsOverlaping(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVe return false; } -bool NodeArea::IsSecondRectInsideFirstOne(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVec2 SecondRectMin, ImVec2 SecondRectSize) +bool NodeArea::IsSecondRectInsideFirstOne(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVec2 SecondRectMin, ImVec2 SecondRectSize) const { if (SecondRectMin.x >= FirstRectMin.x && (SecondRectMin.x + SecondRectSize.x) <= (FirstRectMin.x + FirstRectSize.x) && @@ -693,13 +699,5 @@ bool NodeArea::IsSecondRectInsideFirstOne(ImVec2 FirstRectMin, ImVec2 FirstRectS return true; } - /*if (FirstRectMin.x < (SecondRectMin.x + SecondRectSize.x) && - (FirstRectMin.x + FirstRectSize.x) > SecondRectMin.x && - FirstRectMin.y < (SecondRectMin.y + SecondRectSize.y) && - (FirstRectMin.y + FirstRectSize.y) > SecondRectMin.y) - { - return true; - }*/ - return false; } \ No newline at end of file diff --git a/SubSystems/VisualNodeArea/VisualNodeArea.h b/SubSystems/VisualNodeArea/VisualNodeArea.h index 674b40e..c4e2fd1 100644 --- a/SubSystems/VisualNodeArea/VisualNodeArea.h +++ b/SubSystems/VisualNodeArea/VisualNodeArea.h @@ -71,6 +71,15 @@ namespace VisNodeSys NodeArea(); ~NodeArea(); + static NodeArea* CreateNodeArea(std::vector Nodes, const std::vector GroupComments); + static void CopyNodesTo(NodeArea* SourceNodeArea, NodeArea* TargetNodeArea); + + std::string ToJson() const; + void SaveToFile(const char* FileName) const; + void LoadFromJson(std::string JsonText); + void LoadFromFile(const char* FileName); + void SaveNodesToFile(const char* FileName, std::vector Nodes); + ImVec2 GetPosition() const; void SetPosition(ImVec2 NewValue); @@ -85,57 +94,65 @@ namespace VisNodeSys Node* GetHovered() const; std::vector GetSelected(); - - std::vector GetNodesByName(std::string NodeName) const; - std::vector GetNodesByType(std::string NodeType) const; + void UnSelectAll(); bool IsMouseHovered() const; bool IsFillingWindow(); void SetIsFillingWindow(bool NewValue); - int GetNodeCount() const; - + void Update(); void Clear(); void Reset(); + + // *********************** Nodes ************************ + Node* GetNodeByID(std::string NodeID) const; + std::vector GetNodesByName(std::string NodeName) const; + std::vector GetNodesByType(std::string NodeType) const; + void AddNode(Node* NewNode); void DeleteNode(const Node* Node); + size_t GetNodeCount() const; + void AddNodeEventCallback(std::function Func); + void RunOnEachNode(void(*Func)(Node*)); + void RunOnEachConnectedNode(Node* StartNode, void(*Func)(Node*)); + void PropagateUpdateToConnectedNodes(const Node* CallerNode) const; + + bool TriggerSocketEvent(NodeSocket* CallerNodeSocket, NodeSocket* TriggeredNodeSocket, NODE_SOCKET_EVENT EventType); + bool TriggerOrphanSocketEvent(Node* Node, NODE_SOCKET_EVENT EventType); + + // *********************** Group Comments ************************ + GroupComment* GetGroupCommentByID(std::string GroupCommentID) const; + std::vector GetGroupCommentsByName(std::string GroupCommentName) const; + void AddGroupComment(GroupComment* NewGroupComment); void DeleteGroupComment(GroupComment* GroupComment); + size_t GetGroupCommentCount() const; + + std::vector GetNodesInGroupComment(GroupComment* GroupCommentToCheck) const; + std::vector GetRerouteNodesInGroupComment(GroupComment* GroupCommentToCheck) const; + std::vector GetGroupCommentsInGroupComment(GroupComment* GroupCommentToCheck) const; + void SetMainContextMenuFunc(void(*Func)()); - void PropagateUpdateToConnectedNodes(const Node* CallerNode) const; - void SetNodeEventCallback(void(*Func)(Node*, NODE_EVENT)); - std::string ToJson() const; - void SaveToFile(const char* FileName) const; - void LoadFromFile(const char* FileName); - void SaveNodesToFile(const char* FileName, std::vector Nodes); - void RunOnEachNode(void(*Func)(Node*)); - void RunOnEachConnectedNode(Node* StartNode, void(*Func)(Node*)); - void UnSelectAll(); + void GetAllElementsAABB(ImVec2& Min, ImVec2& Max) const; ImVec2 GetAllElementsAABBCenter() const; ImVec2 GetRenderedViewCenter() const; + + // *********************** Connections ************************ bool TryToConnect(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex); bool TryToConnect(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID); - bool TriggerSocketEvent(NodeSocket* CallerNodeSocket, NodeSocket* TriggeredNodeSocket, NODE_SOCKET_EVENT EventType); - bool TriggerOrphanSocketEvent(Node* Node, NODE_SOCKET_EVENT EventType); + + bool TryToDisconnect(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex); + bool TryToDisconnect(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID); + + bool IsConnected(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex); + bool IsConnected(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID); std::vector> GetConnectionSegments(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex) const; std::vector> GetConnectionSegments(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID) const; bool AddRerouteNodeToConnection(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex, size_t SegmentToDivide, ImVec2 Position); bool AddRerouteNodeToConnection(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID, size_t SegmentToDivide, ImVec2 Position); - static bool IsAlreadyConnected(NodeSocket* FirstSocket, NodeSocket* SecondSocket, const std::vector& Connections); - static void ProcessConnections(const std::vector& Sockets, - std::unordered_map& OldToNewSocket, - NodeArea* TargetArea, size_t NodeShift, const std::vector& SourceNodes); - static void CopyNodesInternal(const std::vector& SourceNodes, NodeArea* TargetArea, const size_t NodeShift = 0); - - static NodeArea* CreateNodeArea(std::vector Nodes, const std::vector GroupComments); - static NodeArea* FromJson(std::string JsonText); - static void CopyNodesTo(NodeArea* SourceNodeArea, NodeArea* TargetNodeArea); - static bool IsNodeIDInList(std::string ID, std::vector List); - static bool EmptyOrFilledByNulls(const std::vector Vector); - bool GetConnectionStyle(Node* Node, bool bOutputSocket, size_t SocketIndex, ConnectionStyle& Style) const; void SetConnectionStyle(Node* Node, bool bOutputSocket, size_t SocketIndex, ConnectionStyle NewStyle); private: @@ -200,7 +217,7 @@ namespace VisNodeSys ImVec2 RenderOffset = ImVec2(0.0, 0.0); void(*MainContextMenuFunc)() = nullptr; void RenderDefaultMainContextMenu(ImVec2 LocalMousePosition); - std::vector NodeEventsCallbacks; + std::vector> NodeEventsCallbacks; std::queue SocketEventQueue; void PropagateNodeEventsCallbacks(Node* Node, NODE_EVENT EventToPropagate) const; @@ -209,6 +226,13 @@ namespace VisNodeSys std::vector GetAllConnections(const NodeSocket* Socket) const; Connection* GetConnection(const NodeSocket* FirstSocket, const NodeSocket* SecondSocket) const; + static bool IsAlreadyConnected(NodeSocket* FirstSocket, NodeSocket* SecondSocket, const std::vector& Connections); + static void ProcessConnections(const std::vector& Sockets, + std::unordered_map& OldToNewSocket, + NodeArea* TargetArea, size_t NodeShift, const std::vector& SourceNodes); + static void CopyNodesInternal(const std::vector& SourceNodes, NodeArea* TargetArea, const size_t NodeShift = 0); + static bool IsEmptyOrFilledByNulls(const std::vector Vector); + void Delete(Connection* Connection); void Delete(RerouteNode* RerouteNode); void Delete(GroupComment* GroupComment); @@ -279,10 +303,9 @@ namespace VisNodeSys bool IsConnectionInRegion(Connection* Connection, const int Steps); bool IsRectsOverlaping(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVec2 SecondRectMin, ImVec2 SecondRectSize); - bool IsSecondRectInsideFirstOne(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVec2 SecondRectMin, ImVec2 SecondRectSize); + bool IsSecondRectInsideFirstOne(ImVec2 FirstRectMin, ImVec2 FirstRectSize, ImVec2 SecondRectMin, ImVec2 SecondRectSize) const; bool IsRectInMouseSelectionRegion(ImVec2 RectMin, ImVec2 RectSize); bool IsRectUnderMouse(ImVec2 RectMin, ImVec2 RectSize); - bool IsGroupCommentCaptionUnderMouse(GroupComment* GroupComment); bool IsGroupCommentRightPartUnderMouse(GroupComment* GroupComment); diff --git a/SubSystems/VisualNodeArea/VisualNodeAreaInput.cpp b/SubSystems/VisualNodeArea/VisualNodeAreaInput.cpp index 5e2ad73..7006c5c 100644 --- a/SubSystems/VisualNodeArea/VisualNodeAreaInput.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeAreaInput.cpp @@ -700,7 +700,8 @@ void NodeArea::KeyboardInputUpdate() if (!reader->parse(NodesToImport.c_str(), NodesToImport.c_str() + NodesToImport.size(), &data, &err)) return; - NodeArea* NewNodeArea = NodeArea::FromJson(NodesToImport); + NodeArea* NewNodeArea = new NodeArea(); + NewNodeArea->LoadFromJson(NodesToImport); // ***************** Place new nodes in center of a view space ***************** const ImVec2 ViewCenter = GetRenderedViewCenter(); diff --git a/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp b/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp index 206d8d8..3c32925 100644 --- a/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp @@ -47,20 +47,41 @@ void NodeArea::Delete(Connection* Connection) i--; } + std::vector NodesToNotify; for (int i = 0; i < static_cast(Connection->In->ConnectedSockets.size()); i++) { if (Connection->In->ConnectedSockets[i] == Connection->Out) { - Node* parent = Connection->In->ConnectedSockets[i]->Parent; - if (!bClearing) - PropagateNodeEventsCallbacks(parent, BEFORE_DISCONNECTED); + Node* Parent = Connection->In->ConnectedSockets[i]->Parent; + NodesToNotify.push_back(Parent); + } + } + + for (int i = 0; i < static_cast(Connection->Out->ConnectedSockets.size()); i++) + { + if (Connection->Out->ConnectedSockets[i] == Connection->In) + { + Node* Parent = Connection->Out->ConnectedSockets[i]->Parent; + NodesToNotify.push_back(Parent); + } + } + + if (!bClearing) + { + for (size_t i = 0; i < NodesToNotify.size(); i++) + { + PropagateNodeEventsCallbacks(NodesToNotify[i], BEFORE_DISCONNECTED); + } + } + for (int i = 0; i < static_cast(Connection->In->ConnectedSockets.size()); i++) + { + if (Connection->In->ConnectedSockets[i] == Connection->Out) + { Connection->In->ConnectedSockets.erase(Connection->In->ConnectedSockets.begin() + i, Connection->In->ConnectedSockets.begin() + i + 1); + // To-Do : Add some variation of disconnected event, like DISCONNECTED_INCOMING Connection->In->Parent->SocketEvent(Connection->In, Connection->Out, bClearing ? DESTRUCTION : DISCONNECTED); i--; - - if (!bClearing) - PropagateNodeEventsCallbacks(parent, AFTER_DISCONNECTED); } } @@ -68,15 +89,9 @@ void NodeArea::Delete(Connection* Connection) { if (Connection->Out->ConnectedSockets[i] == Connection->In) { - Node* parent = Connection->Out->ConnectedSockets[i]->Parent; - if (!bClearing) - PropagateNodeEventsCallbacks(parent, BEFORE_DISCONNECTED); - Connection->Out->ConnectedSockets.erase(Connection->Out->ConnectedSockets.begin() + i, Connection->Out->ConnectedSockets.begin() + i + 1); + // To-Do : Add some variation of disconnected event, like DISCONNECTED_OUTGOING i--; - - if (!bClearing) - PropagateNodeEventsCallbacks(parent, AFTER_DISCONNECTED); } } @@ -86,7 +101,15 @@ void NodeArea::Delete(Connection* Connection) { delete Connection; Connections.erase(Connections.begin() + i, Connections.begin() + i + 1); - return; + break; + } + } + + if (!bClearing) + { + for (size_t i = 0; i < NodesToNotify.size(); i++) + { + PropagateNodeEventsCallbacks(NodesToNotify[i], AFTER_DISCONNECTED); } } } @@ -173,19 +196,19 @@ void NodeArea::DeleteNode(const Node* Node) for (size_t j = 0; j < Nodes[i]->Input.size(); j++) { - auto connections = GetAllConnections(Nodes[i]->Input[j]); - for (size_t p = 0; p < connections.size(); p++) + auto Connections = GetAllConnections(Nodes[i]->Input[j]); + for (size_t p = 0; p < Connections.size(); p++) { - Delete(connections[p]); + Delete(Connections[p]); } } for (size_t j = 0; j < Nodes[i]->Output.size(); j++) { - auto connections = GetAllConnections(Nodes[i]->Output[j]); - for (size_t p = 0; p < connections.size(); p++) + auto Connections = GetAllConnections(Nodes[i]->Output[j]); + for (size_t p = 0; p < Connections.size(); p++) { - Delete(connections[p]); + Delete(Connections[p]); } } @@ -204,19 +227,19 @@ void NodeArea::PropagateUpdateToConnectedNodes(const Node* CallerNode) const for (size_t i = 0; i < CallerNode->Input.size(); i++) { - auto connections = GetAllConnections(CallerNode->Input[i]); - for (size_t j = 0; j < connections.size(); j++) + auto Connections = GetAllConnections(CallerNode->Input[i]); + for (size_t j = 0; j < Connections.size(); j++) { - connections[j]->In->GetParent()->SocketEvent(connections[j]->In, connections[j]->Out, UPDATE); + Connections[j]->In->GetParent()->SocketEvent(Connections[j]->In, Connections[j]->Out, UPDATE); } } for (size_t i = 0; i < CallerNode->Output.size(); i++) { - auto connections = GetAllConnections(CallerNode->Output[i]); - for (size_t j = 0; j < connections.size(); j++) + auto Connections = GetAllConnections(CallerNode->Output[i]); + for (size_t j = 0; j < Connections.size(); j++) { - connections[j]->In->GetParent()->SocketEvent(connections[j]->In, connections[j]->Out, UPDATE); + Connections[j]->In->GetParent()->SocketEvent(Connections[j]->In, Connections[j]->Out, UPDATE); } } } @@ -235,10 +258,10 @@ bool NodeArea::TryToConnect(const Node* OutNode, const size_t OutNodeSocketIndex NodeSocket* OutSocket = OutNode->Output[OutNodeSocketIndex]; NodeSocket* InSocket = InNode->Input[InNodeSocketIndex]; - char* msg = nullptr; - const bool result = InSocket->GetParent()->CanConnect(InSocket, OutSocket, &msg); + char* Message = nullptr; + const bool Result = InSocket->GetParent()->CanConnect(InSocket, OutSocket, &Message); - if (result) + if (Result) { PropagateNodeEventsCallbacks(OutSocket->GetParent(), BEFORE_CONNECTED); PropagateNodeEventsCallbacks(InSocket->GetParent(), BEFORE_CONNECTED); @@ -255,7 +278,112 @@ bool NodeArea::TryToConnect(const Node* OutNode, const size_t OutNodeSocketIndex PropagateNodeEventsCallbacks(InSocket->GetParent(), AFTER_CONNECTED); } - return result; + return Result; +} + +bool NodeArea::TryToDisconnect(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex) +{ + if (OutNode == nullptr || InNode == nullptr) + return false; + + if (OutNode->Output.size() <= OutNodeSocketIndex) + return false; + + if (InNode->Input.size() <= InNodeSocketIndex) + return false; + + NodeSocket* OutSocket = OutNode->Output[OutNodeSocketIndex]; + NodeSocket* InSocket = InNode->Input[InNodeSocketIndex]; + + Connection* Connection = GetConnection(OutSocket, InSocket); + if (Connection == nullptr) + return false; + + Delete(Connection); + return true; +} + +bool NodeArea::TryToDisconnect(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID) +{ + if (OutNode == nullptr || InNode == nullptr) + return false; + + size_t OutSocketIndex = 0; + for (size_t i = 0; i < OutNode->Output.size(); i++) + { + if (OutNode->Output[i]->GetID() == OutSocketID) + { + OutSocketIndex = i; + break; + } + } + + size_t InSocketIndex = 0; + for (size_t i = 0; i < InNode->Input.size(); i++) + { + if (InNode->Input[i]->GetID() == InSocketID) + { + InSocketIndex = i; + break; + } + } + + return TryToDisconnect(OutNode, OutSocketIndex, InNode, InSocketIndex); +} + +bool NodeArea::IsConnected(const Node* OutNode, size_t OutNodeSocketIndex, const Node* InNode, size_t InNodeSocketIndex) +{ + if (OutNode == nullptr || InNode == nullptr) + return false; + + if (OutNode->Output.size() <= OutNodeSocketIndex) + return false; + + if (InNode->Input.size() <= InNodeSocketIndex) + return false; + + NodeSocket* OutSocket = OutNode->Output[OutNodeSocketIndex]; + NodeSocket* InSocket = InNode->Input[InNodeSocketIndex]; + + Connection* Connection = GetConnection(OutSocket, InSocket); + if (Connection == nullptr) + return false; + + for (size_t i = 0; i < OutSocket->ConnectedSockets.size(); i++) + { + if (OutSocket->ConnectedSockets[i] == InSocket) + return true; + } + + return false; +} + +bool NodeArea::IsConnected(const Node* OutNode, std::string OutSocketID, const Node* InNode, std::string InSocketID) +{ + if (OutNode == nullptr || InNode == nullptr) + return false; + + size_t OutSocketIndex = 0; + for (size_t i = 0; i < OutNode->Output.size(); i++) + { + if (OutNode->Output[i]->GetID() == OutSocketID) + { + OutSocketIndex = i; + break; + } + } + + size_t InSocketIndex = 0; + for (size_t i = 0; i < InNode->Input.size(); i++) + { + if (InNode->Input[i]->GetID() == InSocketID) + { + InSocketIndex = i; + break; + } + } + + return IsConnected(OutNode, OutSocketIndex, InNode, InSocketIndex); } void NodeArea::RunOnEachNode(void(*Func)(Node*)) @@ -285,7 +413,8 @@ void NodeArea::RunOnEachConnectedNode(Node* StartNode, void(*Func)(Node*)) CurrentNodes.push_back(StartNode); if (bWasNodeSeen(StartNode)) return; - while (!EmptyOrFilledByNulls(CurrentNodes)) + + while (!IsEmptyOrFilledByNulls(CurrentNodes)) { for (int i = 0; i < static_cast(CurrentNodes.size()); i++) { @@ -540,6 +669,29 @@ bool NodeArea::AddRerouteNodeToConnection(const Node* OutNode, std::string OutSo return AddRerouteNodeToConnection(OutNode, OutSocketIndex, InNode, InSocketIndex, SegmentToDivide, Position); } +GroupComment* NodeArea::GetGroupCommentByID(std::string GroupCommentID) const +{ + for (size_t i = 0; i < GroupComments.size(); i++) + { + if (GroupComments[i]->GetID() == GroupCommentID) + return GroupComments[i]; + } + + return nullptr; +} + +std::vector NodeArea::GetGroupCommentsByName(std::string GroupCommentName) const +{ + std::vector Result; + for (size_t i = 0; i < GroupComments.size(); i++) + { + if (GroupComments[i]->GetCaption() == GroupCommentName) + Result.push_back(GroupComments[i]); + } + + return Result; +} + void NodeArea::AddGroupComment(GroupComment* NewGroupComment) { if (NewGroupComment == nullptr) @@ -567,48 +719,81 @@ void NodeArea::DeleteGroupComment(GroupComment* GroupComment) } } -void NodeArea::AttachElemetnsToGroupComment(GroupComment* GroupComment) +size_t NodeArea::GetGroupCommentCount() const { - GroupComment->AttachedNodes.clear(); - GroupComment->AttachedRerouteNodes.clear(); - GroupComment->AttachedGroupComments.clear(); - - if (!GroupComment->bMoveElementsWithComment) - return; + return GroupComments.size(); +} - const ImVec2 Position = GroupComment->GetPosition(); - const ImVec2 Size = GroupComment->GetSize(); +std::vector NodeArea::GetNodesInGroupComment(GroupComment* GroupCommentToCheck) const +{ + std::vector Result; + if (GroupCommentToCheck == nullptr) + return Result; for (size_t i = 0; i < Nodes.size(); i++) { if (Nodes[i]->GetStyle() == DEFAULT) { - if (IsSecondRectInsideFirstOne(Position, Size, Nodes[i]->GetPosition(), Nodes[i]->GetSize())) - GroupComment->AttachedNodes.push_back(Nodes[i]); + if (IsSecondRectInsideFirstOne(GroupCommentToCheck->GetPosition(), GroupCommentToCheck->GetSize(), Nodes[i]->GetPosition(), Nodes[i]->GetSize())) + Result.push_back(Nodes[i]); } else if (Nodes[i]->GetStyle() == CIRCLE) { - if (IsSecondRectInsideFirstOne(Position, Size, Nodes[i]->GetPosition(), ImVec2(NODE_DIAMETER, NODE_DIAMETER))) - GroupComment->AttachedNodes.push_back(Nodes[i]); + if (IsSecondRectInsideFirstOne(GroupCommentToCheck->GetPosition(), GroupCommentToCheck->GetSize(), Nodes[i]->GetPosition(), ImVec2(NODE_DIAMETER, NODE_DIAMETER))) + Result.push_back(Nodes[i]); } } + return Result; +} + +std::vector NodeArea::GetRerouteNodesInGroupComment(GroupComment* GroupCommentToCheck) const +{ + std::vector Result; + if (GroupCommentToCheck == nullptr) + return Result; + for (size_t i = 0; i < Connections.size(); i++) { for (size_t j = 0; j < Connections[i]->RerouteNodes.size(); j++) { const ImVec2 ReroutePosition = Connections[i]->RerouteNodes[j]->Position; - if (IsSecondRectInsideFirstOne(Position, Size, ReroutePosition, ImVec2(GetRerouteNodeSize() / Zoom, GetRerouteNodeSize() / Zoom))) - GroupComment->AttachedRerouteNodes.push_back(Connections[i]->RerouteNodes[j]); + if (IsSecondRectInsideFirstOne(GroupCommentToCheck->GetPosition(), GroupCommentToCheck->GetSize(), ReroutePosition, ImVec2(GetRerouteNodeSize() / Zoom, GetRerouteNodeSize() / Zoom))) + Result.push_back(Connections[i]->RerouteNodes[j]); } } + return Result; +} + +std::vector NodeArea::GetGroupCommentsInGroupComment(GroupComment* GroupCommentToCheck) const +{ + std::vector Result; + if (GroupCommentToCheck == nullptr) + return Result; + for (size_t i = 0; i < GroupComments.size(); i++) { - if (GroupComments[i] == GroupComment) + if (GroupComments[i] == GroupCommentToCheck) continue; - if (IsSecondRectInsideFirstOne(Position, Size, GroupComments[i]->GetPosition(), GroupComments[i]->GetSize())) - GroupComment->AttachedGroupComments.push_back(GroupComments[i]); + if (IsSecondRectInsideFirstOne(GroupCommentToCheck->GetPosition(), GroupCommentToCheck->GetSize(), GroupComments[i]->GetPosition(), GroupComments[i]->GetSize())) + Result.push_back(GroupComments[i]); } + + return Result; +} + +void NodeArea::AttachElemetnsToGroupComment(GroupComment* GroupComment) +{ + GroupComment->AttachedNodes.clear(); + GroupComment->AttachedRerouteNodes.clear(); + GroupComment->AttachedGroupComments.clear(); + + if (!GroupComment->bMoveElementsWithComment) + return; + + GroupComment->AttachedNodes = GetNodesInGroupComment(GroupComment); + GroupComment->AttachedRerouteNodes = GetRerouteNodesInGroupComment(GroupComment); + GroupComment->AttachedGroupComments = GetGroupCommentsInGroupComment(GroupComment); } \ No newline at end of file diff --git a/VisualNode.cpp b/VisualNode.cpp index 76008c5..6d899e3 100644 --- a/VisualNode.cpp +++ b/VisualNode.cpp @@ -150,32 +150,32 @@ std::string Node::GetType() const Json::Value Node::ToJson() { - Json::Value result; + Json::Value Result; - result["ID"] = ID; - result["nodeType"] = Type; - result["nodeStyle"] = Style; - result["position"]["x"] = Position.x; - result["position"]["y"] = Position.y; - result["size"]["x"] = Size.x; - result["size"]["y"] = Size.y; - result["name"] = Name; + Result["ID"] = ID; + Result["nodeType"] = Type; + Result["nodeStyle"] = Style; + Result["position"]["x"] = Position.x; + Result["position"]["y"] = Position.y; + Result["size"]["x"] = Size.x; + Result["size"]["y"] = Size.y; + Result["name"] = Name; for (size_t i = 0; i < Input.size(); i++) { - result["input"][std::to_string(i)]["ID"] = Input[i]->GetID(); - result["input"][std::to_string(i)]["name"] = Input[i]->GetName(); - result["input"][std::to_string(i)]["type"] = Input[i]->GetType(); + Result["input"][std::to_string(i)]["ID"] = Input[i]->GetID(); + Result["input"][std::to_string(i)]["name"] = Input[i]->GetName(); + Result["input"][std::to_string(i)]["type"] = Input[i]->GetType(); } for (size_t i = 0; i < Output.size(); i++) { - result["output"][std::to_string(i)]["ID"] = Output[i]->GetID(); - result["output"][std::to_string(i)]["name"] = Output[i]->GetName(); - result["output"][std::to_string(i)]["type"] = Output[i]->GetType(); + Result["output"][std::to_string(i)]["ID"] = Output[i]->GetID(); + Result["output"][std::to_string(i)]["name"] = Output[i]->GetName(); + Result["output"][std::to_string(i)]["type"] = Output[i]->GetType(); } - return result; + return Result; } void Node::FromJson(Json::Value Json) @@ -270,42 +270,48 @@ ImVec2 Node::GetClientRegionPosition() return ClientRegionMin; } -size_t Node::InputSocketCount() const +size_t Node::GetInputSocketCount() const { return Input.size(); } -size_t Node::OutSocketCount() const +size_t Node::GetOutputSocketCount() const { return Output.size(); } std::vector Node::GetNodesConnectedToInput() const { - std::vector result; + std::vector Result; for (size_t i = 0; i < Input.size(); i++) { for (size_t j = 0; j < Input[i]->ConnectedSockets.size(); j++) { - result.push_back(Input[i]->ConnectedSockets[j]->GetParent()); + if (IsNodeWithIDInList(Input[i]->ConnectedSockets[j]->GetParent()->GetID(), Result)) + continue; + + Result.push_back(Input[i]->ConnectedSockets[j]->GetParent()); } } - return result; + return Result; } std::vector Node::GetNodesConnectedToOutput() const { - std::vector result; + std::vector Result; for (size_t i = 0; i < Output.size(); i++) { for (size_t j = 0; j < Output[i]->ConnectedSockets.size(); j++) { - result.push_back(Output[i]->ConnectedSockets[j]->GetParent()); + if (IsNodeWithIDInList(Output[i]->ConnectedSockets[j]->GetParent()->GetID(), Result)) + continue; + + Result.push_back(Output[i]->ConnectedSockets[j]->GetParent()); } } - return result; + return Result; } bool Node::OpenContextMenu() @@ -349,4 +355,20 @@ void Node::SetCouldBeMoved(bool NewValue) NodeArea* Node::GetParentArea() const { return ParentArea; +} + +bool Node::CouldBeDestroyed() const +{ + return bShouldBeDestroyed; +} + +bool Node::IsNodeWithIDInList(const std::string ID, const std::vector List) +{ + for (size_t i = 0; i < List.size(); i++) + { + if (List[i]->GetID() == ID) + return true; + } + + return false; } \ No newline at end of file diff --git a/VisualNode.h b/VisualNode.h index da45816..3ae2e9f 100644 --- a/VisualNode.h +++ b/VisualNode.h @@ -65,6 +65,8 @@ namespace VisNodeSys virtual bool OpenContextMenu(); void UpdateClientRegion(); + + static bool IsNodeWithIDInList(std::string ID, std::vector List); public: Node(std::string ID = ""); Node(const Node& Src); @@ -90,8 +92,8 @@ namespace VisNodeSys virtual Json::Value ToJson(); virtual void FromJson(Json::Value Json); - size_t InputSocketCount() const; - size_t OutSocketCount() const; + size_t GetInputSocketCount() const; + size_t GetOutputSocketCount() const; std::vector GetNodesConnectedToInput() const; std::vector GetNodesConnectedToOutput() const; @@ -104,6 +106,8 @@ namespace VisNodeSys bool CouldBeMoved() const; void SetCouldBeMoved(bool NewValue); + bool CouldBeDestroyed() const; + NodeArea* GetParentArea() const; }; } \ No newline at end of file diff --git a/VisualNodeCore.cpp b/VisualNodeCore.cpp index 7723c58..d7b3853 100644 --- a/VisualNodeCore.cpp +++ b/VisualNodeCore.cpp @@ -15,6 +15,13 @@ const char* FontBase64Part_5 = R"(PgJ2OIY4AnkqiSoCZgN2A4YDA2QzdDOEMwNrL3sviy8DBS void NodeCore::InitializeFonts() { + auto ImGuiContext = ImGui::GetCurrentContext(); + if (ImGuiContext == nullptr || bIsInTestMode) + { + bIsFontsInitialized = false; + return; + } + // If no font has been loaded up to this point, load the default ImGui font. if (ImGui::GetIO().Fonts->Fonts.Size == 0) ImGui::GetIO().Fonts->AddFontDefault(); diff --git a/VisualNodeCore.h b/VisualNodeCore.h index a9e701c..3551760 100644 --- a/VisualNodeCore.h +++ b/VisualNodeCore.h @@ -56,6 +56,8 @@ while (iterator != map.end()) \ \ return result; +#define VISUAL_NODE_SYSTEM_VERSION "0.1.0" + class NodeCore { SINGLETON_PRIVATE_PART(NodeCore) @@ -63,8 +65,11 @@ return result; friend class NodeArea; friend class NodeSystem; + bool bIsInTestMode = false; + std::string GetUniqueID(); + bool bIsFontsInitialized = false; std::vector Fonts; void InitializeFonts(); diff --git a/VisualNodeSystem.cpp b/VisualNodeSystem.cpp index e842a02..e22b6d4 100644 --- a/VisualNodeSystem.cpp +++ b/VisualNodeSystem.cpp @@ -6,8 +6,9 @@ NodeSystem* NodeSystem::Instance = nullptr; NodeSystem::NodeSystem() {} NodeSystem::~NodeSystem() {} -void NodeSystem::Initialize() +void NodeSystem::Initialize(bool bTestMode) { + NODE_CORE.bIsInTestMode = bTestMode; NODE_CORE.InitializeFonts(); } diff --git a/VisualNodeSystem.h b/VisualNodeSystem.h index 1512461..0117252 100644 --- a/VisualNodeSystem.h +++ b/VisualNodeSystem.h @@ -12,7 +12,7 @@ namespace VisNodeSys public: SINGLETON_PUBLIC_PART(NodeSystem) - void Initialize(); + void Initialize(bool bTestMode = false); NodeArea* CreateNodeArea(); void DeleteNodeArea(const NodeArea* NodeArea);