deargui-vpl/applications/nodehub/containers/root_container.cpp
2026-02-03 18:25:25 +01:00

286 lines
8.2 KiB
C++

#include "root_container.h"
#include "../app.h"
#include "../types.h"
#include "../blocks/block.h"
#include "../blocks/parameter_node.h"
#include <imgui_node_editor.h>
namespace ed = ax::NodeEditor;
RootContainer::RootContainer(const std::string& filename, int id)
: Container(id, "Root"), m_Filename(filename), m_IsDirty(false)
{
}
RootContainer::~RootContainer()
{
// Clean up BlockInstance and ParameterInstance for all nodes BEFORE maps are destroyed
// This prevents memory leaks and potential double-free issues
// NOTE: Nodes may have already had their instances deleted via DeleteNodeAndInstances,
// so we check for nullptr and validate pointers to avoid double-free
for (auto& pair : m_Nodes)
{
Node& node = pair.second;
// Clean up BlockInstance (for block-based nodes)
// Only delete if pointer is valid and not already deleted
if (node.BlockInstance)
{
// Validate pointer is not corrupted or already freed
uintptr_t ptrValue = reinterpret_cast<uintptr_t>(node.BlockInstance);
if (ptrValue != 0xFFFFFFFFFFFFFFFFULL && ptrValue > 0x1000) // Basic sanity check
{
try {
delete node.BlockInstance;
} catch (...) {
// Silently ignore exceptions during shutdown cleanup
}
}
node.BlockInstance = nullptr;
}
// Clean up ParameterInstance (for parameter nodes)
// Only delete if pointer is valid and not already deleted
if (node.ParameterInstance)
{
// Validate pointer is not corrupted or already freed
uintptr_t ptrValue = reinterpret_cast<uintptr_t>(node.ParameterInstance);
if (ptrValue != 0xFFFFFFFFFFFFFFFFULL && ptrValue > 0x1000) // Basic sanity check
{
try {
delete node.ParameterInstance;
} catch (...) {
// Silently ignore exceptions during shutdown cleanup
}
}
node.ParameterInstance = nullptr;
}
}
// Clear maps explicitly (though they'll be destroyed automatically after this)
// This makes the order of destruction clear
m_Nodes.clear();
m_Links.clear();
// Base Container destructor will clean up child containers
}
Node* RootContainer::FindNode(ed::NodeId id)
{
if (!id)
return nullptr;
auto it = m_Nodes.find(id);
if (it != m_Nodes.end())
return &it->second;
return nullptr;
}
Link* RootContainer::FindLink(ed::LinkId id)
{
if (!id)
return nullptr;
auto it = m_Links.find(id);
if (it != m_Links.end())
return &it->second;
return nullptr;
}
Pin* RootContainer::FindPin(ed::PinId id, App* app)
{
if (!id)
return nullptr;
// Search all nodes in this container
for (auto& pair : m_Nodes)
{
Node& node = pair.second;
// Validate node pointer isn't corrupted before accessing pins
uintptr_t nodePtrValue = reinterpret_cast<uintptr_t>(&node);
if (nodePtrValue < 0x1000 || nodePtrValue == 0xFFFFFFFFFFFFFFFFULL)
continue;
// Check inputs
for (auto& pin : node.Inputs)
{
if (pin.ID == id)
{
// Ensure pin's Node pointer is correct (should be set by BuildNode after AddNode)
if (pin.Node != &node)
{
pin.Node = &node; // Fix to point to actual node in container
}
return &pin;
}
}
// Check outputs
for (auto& pin : node.Outputs)
{
if (pin.ID == id)
{
// Ensure pin's Node pointer is correct (should be set by BuildNode after AddNode)
if (pin.Node != &node)
{
printf("[FindPin] FIXING pin %d - Node pointer incorrect (was %p, setting to %p)\n",
id.Get(), (void*)pin.Node, (void*)&node);
fflush(stdout);
pin.Node = &node; // Fix to point to actual node in container
}
return &pin;
}
}
}
// Also search in child containers recursively
for (Container* child : m_Children)
{
if (Pin* pin = child->FindPin(id, app))
return pin;
}
return nullptr;
}
std::vector<Node*> RootContainer::GetAllNodes() const
{
std::vector<Node*> nodes;
nodes.reserve(m_Nodes.size());
for (auto& pair : m_Nodes)
{
nodes.push_back(const_cast<Node*>(&pair.second)); // Const cast needed
}
return nodes;
}
std::vector<Link*> RootContainer::GetAllLinks() const
{
std::vector<Link*> links;
links.reserve(m_Links.size());
for (auto& pair : m_Links)
{
links.push_back(const_cast<Link*>(&pair.second)); // Const cast needed
}
return links;
}
Node* RootContainer::AddNode(const Node& node)
{
auto [it, inserted] = m_Nodes.emplace(node.ID, node);
if (!inserted)
return nullptr; // Node with this ID already exists
// Also add to container's ID list
m_NodeIds.push_back(node.ID);
return &it->second;
}
bool RootContainer::RemoveNode(ed::NodeId id)
{
auto it = m_Nodes.find(id);
if (it != m_Nodes.end())
{
m_Nodes.erase(it);
// Remove from ID list
auto idIt = std::find(m_NodeIds.begin(), m_NodeIds.end(), id);
if (idIt != m_NodeIds.end())
m_NodeIds.erase(idIt);
return true;
}
return false;
}
Link* RootContainer::AddLink(const Link& link)
{
auto [it, inserted] = m_Links.emplace(link.ID, link);
if (!inserted)
return nullptr; // Link with this ID already exists
// Also add to container's ID list
m_LinkIds.push_back(link.ID);
return &it->second;
}
bool RootContainer::RemoveLink(ed::LinkId id)
{
auto it = m_Links.find(id);
if (it != m_Links.end())
{
m_Links.erase(it);
// Remove from ID list
auto idIt = std::find(m_LinkIds.begin(), m_LinkIds.end(), id);
if (idIt != m_LinkIds.end())
m_LinkIds.erase(idIt);
return true;
}
return false;
}
void RootContainer::Run(App* app)
{
// Only execute if active (not disabled)
if (IsDisabled())
return;
// Set running flag
SetRunning(true);
// Execute blocks in this container
// Note: The actual execution logic is still in App::ExecuteRuntimeStep()
// which will iterate through m_Nodes. We'll update that to use the container.
// For now, this is a placeholder.
// Execute child containers (groups) - only if active
for (auto* child : m_Children)
{
if (child->IsDisabled())
continue;
// Child containers (groups) will be executed by runtime system
// when their inputs are activated
}
// Clear running flag
SetRunning(false);
}
void RootContainer::Render(App* app, Pin* newLinkPin)
{
// Only render if not hidden
if (IsHidden())
return;
// Render this container's nodes
// Note: Actual rendering is still in App::RenderNodes()
// which will iterate through m_Nodes. We'll update that to use the container.
// For now, this is a placeholder.
// Render links between nodes in this container
// Note: Actual link rendering is still in App::RenderLinks()
// We'll update that to use the container.
// Render child containers (groups) - only if not hidden
for (auto* child : m_Children)
{
if (child->IsHidden())
continue;
// Child containers (groups) will render themselves
child->Render(app, newLinkPin);
}
}