Skip to content
Skareeg Xigmatec edited this page Apr 13, 2020 · 9 revisions

Graphs

What is a Graph

A graph, or nodegraph, is a collection of nodes that are connected together to compute zero or many outputs from zero or many inputs. These inputs and outputs can be any data type that Proc Flow supports, including image data, vertex data, and audio. The graph itself acts as a node template and can you can create instances of the graph in other graphs, in which they are then called nodes.

What is a Node

A node is a instance of a graph. Please see the document on nodes.

What are Graph Versions

Graphs are actually versioned, and you can create a new version of a graph that will be saved separately alongside the previous version. This allows you to make breaking changes to your nodes functionality that would otherwise cause other graphs using it to stop working. If you know that a change will break another graph, simply save as a new version to keep the old graphs working while allowing yourself to extend the functionality of the old graph. Then just instance the new graph version instead of the old one.

Modifying Graphs

Be mindful when editing a graph that may be in use somewhere else as an instance in another graph.

How are Graph Structured

Graphs are stored in a folder of their version, with a "node.json" file inside of it. This file describes some basic information about the graph including the name, the author, and the graph's latest version.

Versions of a Graph

The numbered folders within a graph's folder are numbers representing the version of the graph inside the folder, and a new one is created whenever you save a nodegraph template as a new version.

How is "node.json" Structured

Please ensure you understand the node basics before continuing. Each of the numbered folders inside a graph folder represents the entire graph for that version. Specifically, the "version.json" file holds the entirety of the graph, with all of the nodes and connections between them stored inside. Any other files within the folder are versioned via the foldername as well, and represent assets that the nodes within the graph can use, such as images or audio.

The root json document above the numbered folders, the "graph.json" file, holds information about the graph as follows:

{
    "name": "node graph name",
    "uuid": <uuid>,
    "format": 1
}

The name is the name of your graph, and can be whatever you like, though naming it the same as other nodes may confuse others when searching for it.

The uuid is used to completely discern this node from another of the same name. They are randomly generated, and should not collide with any other. Note that you should absolutely not attempt to change this field unless you absolutely mean to. This identifier is how other nodes will refer to your node, along with it's name and the library and author it came from so that if a dependency is missing, you can know what it was.

The format number determine what version of the graph format it uses. This is for future proofing so that extensions can be made to this format without bracking backwards compatibility, same as the library format. This number has no relation to the graphs actual versioning.

Within each of the numbered folders there exists a "version.json" files that describes information for that version of the graph:

{
    "format": 1,
    "receives": [
        ...
    ],
    "sends": [
        ...
    ],
    "inputs": [
        ...
    ],
    "outputs": [
        ...
    ],
    "nodes": [
        ...
    ],
    "connections": [
        ...
    ]
}

Receives are commands that are recieved on the node, if any. The node will then repond to these commands.

Sends are commands that are sent from one node to others.

Inputs to the node are of a certain type and can be used within the graph to manipulate to perform some actions or transform into one of the outputs. They also appear as inputs when the graph is instanced as a node in a different graph.

Outputs from the node are so the node can be used in other graphs and within the ProcFlow library to be called by an external application.

The nodes array stores all of the nodes that you're graph is using, specifically their graph uuids, graph names, authors, libraries they came from, and individual node properties. Any pure data elements may be stored as files alongside the "node.json" file so that the document does not become too large.

The connections array stores all of the connections between the nodes.

The receives of the graph are as such:

...
    "receives": [
        {
            "name": "name of command",
            "uuid": <uuid>,
            "datatype": "int"
        },
        {
            "name": "name of other command",
            "uuid": <uuid>,
            "datatype": "float"
        }
    ]
...

The sends of the graph are as such:

...
    "sends": [
        {
            "name": "name of command",
            "uuid": <uuid>,
            "datatype": "int"
        },
        {
            "name": "name of other command",
            "uuid": <uuid>,
            "datatype": "float"
        }
    ]
...

The inputs of the graph are as such:

...
    "inputs": [
        {
            "name": "name of input",
            "uuid": <uuid>,
            "datatype": "int"
        },
        {
            "name": "name of other input",
            "uuid": <uuid>,
            "datatype": "float"
        }
    ]
...

The outputs are much the same:

...
    "outputs": [
        {
            "name": "name of output",
            "uuid": <uuid>,
            "datatype": "string"
        },
        {
            "name": "name of another ouput",
            "uuid": <uuid>,
            "datatype": "float",
            "dimensions": 3,
            "expandable": true
        }
    ]
...

Each input and output has a name and uuid, as well as a type describing in a simple string what type of data is accepts or produces. Note the extra fields, dimensions and expandable. If the dimensions field is present, the type is made into an array of those dimensions, so 1 for a list, 2 for an image, 3 for voxels, and so forth. The expandable field is for node that can accept an arbitrary number of output links to a single input. The graph editor will not allow you to hook multiple values into a single input unless the expandable field is set to true.

The nodes are stored as such:

...
    "nodes": [
        {
            "uuid": <uuid>,
            "x": 32.0,
            "y": 16.0,
            "graph": {
                "name": "name of graph of node",
                "uuid": <uuid>,
                "library": "library its from",
                "version": 1
            }
        },
        {
            "uuid": <uuid>,
            "x": 16.0,
            "y": 16.0,
            "data": [
                {
                    "name": "nameofdata",
                    "value": "relativefilename.data"
                }
            ]
            "graph": {
                "name": "name of graph of node",
                "uuid": <uuid>,
                "library": "library its from",
                "version": 1
            }
        }
    ]
...

The uuid is the id of this particular instance in the graph.

The x and y fields are for the location in the graph itself, of course.

The data field, if present, is a collection of internal data properties specific to that node. These are primarily used to either load in or otherwise repesent specific data types other than the standard string, number, or boolean types, although than can hold those types if they wish. Many times, they are used to hold matrix data, large arrays, or point to a file or files that reside somewhere relative to the graph or a specific file location, which is not generally recommended unless they are large in size. They can be created by the user explicitly or can be created on the fly depending on the situation. Most of the time, different creation/editor nodes will store these and output their results. (e.g. A spline editor will have one of these.)

The graph object describes the graph that this node is instancing, and includes the name of the graph, the uuid of the graph, the library that it is from, and the version that this node is using.

The connections between the nodes look as follows:

...
    "connections": [
        {
            "output": {
                "node": <uuid>,
                "pin": <uuid>
            },
            "input": {
                "node": <uuid>,
                "pin": <uuid>
            }
        },
        {
            "output": {
                "node": <uuid>,
                "pin": <uuid>
            },
            "input": {
                "node": <uuid>,
                "pin": <uuid>
            }
        },
        {
            "output": {
                "node": <uuid>,
                "pin": <uuid>
            },
            "input": {
                "node": <uuid>,
                "property": "progress"
            }
        },
        {
            "send": {
                "node": <uuid>,
                "pin": <uuid>
            },
            "receive": {
                "node": <uuid>,
                "pin": <uuid>
            }
        },
        {
            "output": {
                "value": "random constant string"
            },
            "input": {
                "node": <uuid>,
                "pin": <uuid>
            }
        },
    ]
...

Each object in the array represents a connection between an output of a node to the input of another node within the graph.

Both the output and the input are designated by their node uuid and corresponding input or output uuid. In some cases, many outputs will link to a single input, allowing for multiple value inputs. In other cases, no pin id will be specified, and instead the input is actually going to be a property of the actual node itself, like its progress or its name. Other cases may see an output be a value of text, numbers, or even a true or false, for those node inputs who are marked as "valueable", meaning that the user can directly input a value in the graph editor.

Difference between receives/sends and inputs/outputs

Inputs are tied to outputs of nodes before it, in Request/Reply fashion, and the progress of the current node will not continue until the input is gathered. Receives actually cause some action to be performed that may not be IO based, and as such do not necessarily follow the Request/Reply pattern, but can instead fire and forget commands to a target node.

When the node as a whole is executed, it will request the outputs of its graph, which are individual nodes that will request outputs from nodes within the graph that output to them.

Alternatively, a start node will execute, or an individual node will be run by the user, which will execute based on which command was sent.

Clone this wiki locally