Specialized components
Visual scripting
Visual script graph
20 min
overview the visual script graph is the foundational component of the ir engine visual scripting system it serves as the container and organizational structure for visual programming, providing a canvas where users arrange and connect nodes to create logical flows similar to a flowchart or blueprint, the graph defines both the components of a script and the relationships between them this chapter explores the concept, structure, and implementation of visual script graphs within the ir engine core concepts graph as a container a visual script graph functions as the primary container for all elements of a visual script nodes the graph holds all the visual script nodes that represent individual operations or functions connections it maintains the links between nodes that define how data and execution flow variables it manages script level variables that can be accessed by multiple nodes custom events it contains definitions for custom events that can trigger execution flows the graph not only stores these elements but also defines their relationships and the overall structure of the script execution flow one of the graph's most important functions is defining the execution flow of the script execution typically begins at designated entry point nodes it follows the connections between nodes in the direction specified by the links the graph's structure determines branching, looping, and conditional execution paths when execution reaches terminal nodes with no outgoing connections, that particular flow path ends this flow based approach allows users to visualize program execution in a way that's more intuitive than traditional text based code implementation graph data structure the visual script graph is implemented as a structured data object that contains all the necessary information for the script // simplified from src/engine/graphs/graph ts type graphinstance = { name string; // descriptive name for the graph nodes record\<string, nodeinstance>; // collection of all nodes in the graph variables record\<string, variable>; // graph level variables customevents record\<string, customevent>; // custom event definitions metadata? metadata; // additional information about the graph }; this structure serves as the in memory representation of a complete visual script, containing all the components needed for execution graph api the graph provides an interface ( igraph ) that allows nodes and other components to interact with it // simplified from src/engine/graphs/graph ts interface igraph { readonly variables record\<string, variable>; // access to variables readonly customevents record\<string, customevent>; // access to custom events readonly values valuetypemap; // access to value types readonly getdependency \<t>(id string) => t | undefined; // dependency injection } this interface enables nodes to access and modify graph variables trigger or respond to custom events retrieve information about available value types access external dependencies or services graph serialization to save and load visual scripts, graphs are serialized to and from json format // simplified from src/engine/graphs/io/graphjson ts type graphjson = { name string; nodes nodejson\[]; variables variablejson\[]; customevents customeventjson\[]; metadata? metadatajson; }; the serialization process involves writing converting the in memory graphinstance to a json structure using writegraphtojson reading reconstructing a graphinstance from json using readgraphfromjson validation ensuring the graph structure is valid and consistent this serialization enables graphs to be stored in files, shared between users, and version controlled graph operations creating a graph a new graph can be created programmatically // simplified concept from src/engine/graphs/graph ts function creategraph(name string, registry registry) graphinstance { return { name, nodes {}, variables {}, customevents {}, metadata { created date now() } }; } in practice, graphs are typically created through the visual editor interface, which provides a more intuitive way to build scripts adding nodes to a graph nodes are added to a graph using a function like // simplified concept from src/engine/graphs/graph ts function addnodetograph( graph graphinstance, nodedefinition nodedefinition, position vector2 ) nodeinstance { const nodeid = generateuniqueid(); const node = createnode(nodedefinition, nodeid, position); graph nodes\[nodeid] = node; return node; } this function creates a new node instance based on a node definition assigns it a unique identifier positions it on the graph canvas adds it to the graph's collection of nodes connecting nodes connections between nodes are established by creating links between their sockets // simplified concept from src/engine/graphs/graph ts function connectnodes( graph graphinstance, sourcenodeid string, sourcesocketid string, targetnodeid string, targetsocketid string ) boolean { const sourcenode = graph nodes\[sourcenodeid]; const targetnode = graph nodes\[targetnodeid]; if (!sourcenode || !targetnode) return false; const sourcesocket = sourcenode outputs\[sourcesocketid]; const targetsocket = targetnode inputs\[targetsocketid]; if (!sourcesocket || !targetsocket) return false; if (!aresocketscompatible(sourcesocket, targetsocket)) return false; targetsocket connectedto = { nodeid sourcenodeid, socketid sourcesocketid }; return true; } this function retrieves the source and target nodes finds the specific output and input sockets verifies that the connection is valid (compatible types) establishes the link between the sockets graph validation before execution, graphs are validated to ensure they have a consistent structure // simplified from src/engine/graphs/validation/validategraph ts function validategraph(graph graphinstance) validationresult { const errors validationerror\[] = \[]; // check for orphaned nodes (no connections) for (const nodeid in graph nodes) { const node = graph nodes\[nodeid]; if (isorphaned(node, graph)) { errors push({ type 'orphaned node', nodeid, message `node ${node name} (${nodeid}) is not connected to any other node` }); } } // check for invalid connections for (const nodeid in graph nodes) { const node = graph nodes\[nodeid]; for (const inputid in node inputs) { const input = node inputs\[inputid]; if (input connectedto && !isvalidconnection(input connectedto, graph)) { errors push({ type 'invalid connection', nodeid, socketid inputid, message `invalid connection to input ${inputid} on node ${nodeid}` }); } } } // additional validation checks return { valid errors length === 0, errors }; } validation helps catch issues like orphaned nodes with no connections invalid connections between incompatible sockets missing required connections circular dependencies that could cause infinite loops example use case let's examine a simple alarm system implemented as a visual script graph graph lr subgraph "graph simple alarm" trigger\[event ontriggeractivated] > playsound\[action play alarm sound] end in this example the graph contains two nodes an event node ( ontriggeractivated ) and an action node ( play alarm sound ) there is one execution link connecting the output of the event node to the input of the action node when the trigger is activated, execution flows from the event node to the action node the action node plays the alarm sound the corresponding graph data structure might look like // conceptual representation const alarmgraph graphinstance = { name "simple alarm", nodes { "node1" { id "node1", type "ontriggeractivated", position { x 100, y 100 }, inputs {}, outputs { "exec" { type "execution", connectedto { nodeid "node2", socketid "exec" } } } }, "node2" { id "node2", type "playsound", position { x 300, y 100 }, inputs { "exec" { type "execution", connectedto { nodeid "node1", socketid "exec" } }, "soundfile" { type "string", value "alarm wav" } }, outputs {} } }, variables {}, customevents {} }; graph loading workflow when a visual script is loaded, the graph goes through several processing stages sequencediagram participant useraction as user/editor participant rfj as readgraphfromjson() participant jsonfile as graph json participant reg as registry participant gi as graphinstance useraction >>rfj load "myalarmscript json" rfj >>jsonfile read data jsonfile >>rfj json content rfj >>reg get node definitions, value types reg >>rfj definitions rfj >>gi create nodes, variables, links gi >>rfj constructed graph rfj >>useraction graph ready to use! this process involves reading the serialized graph data from a json file consulting the registry to understand node types and value types creating node instances based on their definitions establishing connections between nodes setting up variables and custom events validating the reconstructed graph next steps with an understanding of the visual script graph as the container and organizational structure for visual scripts, the next chapter explores the individual building blocks that populate these graphs visual script nodes next visual script node docid 4try9x6ejgbr6dvr5e b7