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
+
+
+
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);