deargui-vpl/docs/groups-container-architecture.md

7.7 KiB

BehaviorGraph Container-Based Architecture

Core Principle: All nodes, links, blocks exist within containers. Containers manage, run, and render their contents.

Key Insight: The app can have multiple root containers, each loaded from a separate file. Each root container represents a separate graph/document/workspace.

App
├── RootContainer1 (loaded from file1.json)
│   ├── Node A
│   ├── Node B
│   └── BehaviorGraph Container (Group 1)
│       ├── Node C
│       └── BehaviorGraph Container (Nested Group)
│           └── Node D
│
├── RootContainer2 (loaded from file2.json)
│   ├── Node E
│   └── Node F
│
└── RootContainer3 (loaded from file3.json)
    └── BehaviorGraph Container (Group 2)
        └── Node G

Architecture Overview

Container Hierarchy

  • App Level
    • Has multiple root containers (one per loaded file/graph)
    • Each root container is a separate workspace/graph
  • Root Container (one per file/graph)
    • Contains top-level nodes for that graph
    • Contains top-level groups for that graph
    • Loaded from a single file
  • Group Containers (BehaviorGraph instances)
    • Contain inner nodes
    • Can contain nested groups
    • Have their own pin interface (for external connections)

Key Benefits

  1. No Duplication: Each node exists in exactly ONE container
  2. Clear Ownership: Container owns its nodes/links
  3. Clean Separation: Containers handle management, execution, rendering
  4. Natural Nesting: Groups are just containers within containers
  5. Unified Interface: Root and groups use same container interface
  6. State Management: Containers have flags (hidden, active/disabled, running, error)

App Structure Changes

2. Group Creation

After:

  • Create BehaviorGraph container within current active root container
  • Move selected nodes from active root container → group container
  • Move links (internal links stay, external links need group pins)
  • Group container owns these nodes now
  • Groups belong to the root container they were created in

3. Rendering

  • Render active root container (or all root containers if multi-view)
  • Root container renders its nodes
  • Root container renders its child containers (groups)
  • Each container renders its own contents
  • Different root containers can be in different tabs/views

4. Runtime Execution

After:

  • Active root container runs (or all if running all graphs)
  • Container iterates its nodes
  • If node is a container (BehaviorGraph), call container->Run()
  • Nested containers run recursively
  • Each root container executes independently (or can be cross-container?)

5. Serialization

Before:

  • Save all nodes
  • Save groups with m_ParentGroup references
  • Link up later

After:

  • Each root container saves to its own file
  • Save container hierarchy (root container + all nested groups)
  • Each container saves its nodes/links
  • Natural tree structure per file
  • Multiple files = multiple root containers

Introduces:

  • Clean ownership model
  • Natural nesting (containers in containers)
  • Unified interface (root = container, groups = containers)
  • Easier serialization (tree structure)
  • Cleaner rendering (containers render themselves)
  • State management (hidden, active/disabled, running, error flags)

New Approach (Container)

App::m_RootContainers = [root1, root2]
RootContainer1::m_Nodes = [node1, node2]
RootContainer1::m_Children = [BehaviorGraphContainer1]
BehaviorGraphContainer1::m_Nodes = [node3, node4]  // MOVED from root1
RootContainer2::m_Nodes = [node5, node6]

// Nodes exist in exactly ONE container
// No duplication, no parent tracking needed
// Each root container is independent (separate file/graph)

Architecture Decision: Container-Based vs Node-Based

Core Principle

Everything lives in a container. Containers own their contents (nodes, links, nested containers).

Root Container (implicit, always exists)
├── Node A
├── Node B
├── BehaviorGraph Container (Group 1)
│   ├── Node C
│   ├── Node D
│   └── BehaviorGraph Container (Nested Group)
│       └── Node E
└── Node F

Key Concepts

  1. Single Ownership: Each node/link exists in exactly ONE container
  2. Hierarchical: Containers can contain other containers (natural nesting)
  3. Unified Interface: Root and groups use the same container API
  4. Clean Separation: Container handles management, execution, rendering

Implementation Structure

class Container
{
    // Ownership
    std::vector<Node*> m_Nodes;       // Nodes in this container
    std::vector<Link*> m_Links;       // Links in this container  
    std::vector<Container*> m_Children; // Nested containers (groups)
    
    // Interface (for groups only - root has no interface)
    std::vector<GroupPin> m_InputFlowPins;
    std::vector<GroupPin> m_OutputFlowPins;
    std::vector<GroupPin> m_InputParamPins;
    std::vector<GroupPin> m_OutputParamPins;
    
    // Virtual connections (group pin → inner node pin)
    std::map<ed::PinId, ed::PinId> m_GroupToInnerPins;
    
    // Management
    void AddNode(Node* node);
    void RemoveNode(Node* node);
    void AddLink(Link* link);
    void RemoveLink(Link* link);
    
    // Execution
    void Run(App* app);  // Execute container contents
    
    // Rendering
    void Render(App* app, Pin* newLinkPin);
    
    // Query
    Container* FindContainer(NodeId nodeId);
};

class BehaviorGraph : public Container, public ParameterizedBlock
{
    // BehaviorGraph IS a container
    // Also acts as a block (for runtime execution)
    // Has group-specific UI (collapsed/expanded modes)
};

class App
{
    Container* m_RootContainer;  // Default container for top-level items
    
    // Convenience methods
    Container* GetRootContainer() { return m_RootContainer; }
    Container* FindContainerForNode(NodeId nodeId);
    
    // Backward compatibility (optional - can maintain flattened view)
    std::vector<Node*> GetAllNodes();  // Recursive flattening
    std::vector<Link*> GetAllLinks();  // Recursive flattening
};

Migration Path

Phase 2: Make BehaviorGraph a Container

  1. BehaviorGraph inherits from Container
  2. BehaviorGraph also inherits from ParameterizedBlock (block interface)
  3. Group creation moves nodes from root → BehaviorGraph container

Phase 3: Update All Systems

  1. Rendering: Container-based (root renders self + children)
  2. Execution: Container-based (root runs, nested containers run recursively)
  3. Serialization: Container tree structure
  4. Queries: Container-aware lookup methods

Benefits Comparison

Container-Based (New):

  • Single ownership (node owned by exactly one container)
  • No m_ParentGroup needed (container owns it)
  • Unified patterns (all containers same interface)
  • Natural serialization (save container tree)
  • Clear ownership (container owns contents)

Decision: Container-Based Architecture

Alternative: Can implement groups first with node-based approach, then refactor later (but creates technical debt).

Next Steps

  1. Update Implementation Plan - Reflect container architecture
  2. Design Container API - Define exact interface
  3. Implement Root Container - Move existing code
  4. Implement BehaviorGraph Container - Group functionality
  5. Update All Systems - Rendering, execution, serialization

See docs/groups-container-architecture.md for detailed container design.