Similar to a graph, this framework contains data as nodes and edges. One correction: in the editor's terminology, the graph edges are known as connections.
The NodeEditor
instance stores this data in a normalized format, specifically as two distinct lists containing objects of the following type:
{ id: <string> }
for a nodes{ id: <string>, source: <string>, target: <string> }
for a connectionsInitializing the editor involves using a basic scheme without any supplementary fields to start with:
import { NodeEditor, BaseSchemes, getUID } from 'rete'
const editor = new NodeEditor<BaseSchemes>()
When dealing with graph data, you have the option to create arbitrary identifiers for nodes and connections, or utilize the existing ones.
const a = { id: getUID() }
const b = { id: getUID() }
const connection = { id: getUID(), source: a.id, target: b.id }
await editor.addNode(a)
await editor.addNode(b)
await editor.addConnection(connection)
We can now retrieve a list of newly added nodes and connections
editor.getNodes() // returns [a, b]
editor.getConnections() // returns [connection]
You have all the necessary methods to process the graph, such as retrieving a list of input connections or all incoming nodes, as we will discuss in the following sections.
The following code shows how to retrieve a list of incoming and outgoing connections for node
:
const connections = editor.getConnections()
const incomingConnections = connections.filter(connection => connection.target === node.id)
const outgoingConnections = connections.filter(connection => connection.source === node.id)
We can use variables from the previous section to retrieve incoming or outgoing nodes:
const incomers = incomingConnections.map(connection => editor.getNode(connection.source))
const outgoers = outgoingConnections.map(connection => editor.getNode(connection.target))
In general, this is sufficient for simple cases, but if there are more than one connections between nodes, we will have to remove duplicates:
Array.from(new Set(incomers))
Array.from(new Set(outgoers))
The previously mentioned approaches are fairly flexible, but they require the implementation of more advanced methods on your own. Fortunately, the rete-structures
package offers such methods divided into 4 categories:
Install the dependency
npm i rete-structures
Use the following import statement and declaration
import { structures } from 'rete-structures'
const graph = structures(editor)
graph.nodes()
graph.connections()
There are other graph
methods that serve distinct purposes, demonstrated below with code and an interactive preview. The resulting nodes are prominent and the preview allows users to select nodes and observe changes.
The execution of any of the methods usually yields nodes that were not discarded, as well as their connections if both of their nodes are present.
Nodes without incoming connections are known as root nodes
structures(graph).roots()
Leaf nodes are those that have no outgoing connections
structures(graph).leaves()
Incoming nodes directly connected to the selected node
structures(graph).incomers(selectedNodeId)
Outgoing nodes directly connected to the selected node
structures(graph).outgoers(selectedNodeId)
Every incoming node, as well as its own incoming nodes and so on
structures(graph).predecessors(selectedNodeId)
Every outgoing node, as well as its own outgoing nodes and so on
structures(graph).successors(selectedNodeId)
Filtering can be applied to both nodes and connections
structures(graph).filter(Boolean, ({ source, target }) => source === selectedNodeId || target === selectedNodeId)
The following examples shows the cases where selected node serves as the second graph.
The union of a graph and a node from this graph gives the same graph
structures(editor).union({ nodes: [selectedNode], connections: [] })
By subtracting the node from the graph, you can obtain a new graph without that node
structures(editor).difference({ nodes: [selectedNode], connections: [] })
By intersecting the graph with the node from this graph, you'll get a new graph that includes only the selected node
structures(editor).intersection({ nodes: [selectedNode], connections: [] })
This category pertains to graphs that contain nodes with a parent-child relationship, specifically, nodes that have a parent
field defined and are nested within other nodes.
The list of direct descendant, namely children
structures(editor).children((n) => n.id === selectedNodeId);
The list of parents
structures(editor).parents((n) => n.id === selectedNodeId);
The list of all descendants, including children, grandchildren and successive generations
structures(editor).descendants((n) => n.id === selectedNodeId);
The list of all ancestors, from parents to great-grandparents and beyond
structures(editor).ancestors((n) => n.id === selectedNodeId);
Nodes without a specified parent
property
structures(editor).orphans();
Nodes that have a common parent with the selected node, even if it has no parent
structures(editor).siblings((n) => n.id === selectedNodeId)