rete-engine
— це пакет, який реалізує два підходи до обробки графів: Dataflow і Control flow
Підхід потоку даних зосереджений виключно на даних, де цільовий вузол запитує дані від вхідних вузлів. Обробка графа відбувається зліва направо, передаючи вихідні дані вузлів як вхідні аргументи до вихідних вузлів.
Цей підхід зазвичай використовується в продуктах з редакторами вузлів, таких як Blender.
Наведений нижче код представляє основні конструкції, необхідні для роботи DataflowEngine
:
data
: цей метод приймає дані від вхідних вузлівfetch
ініціює обхід графа, починаючи з цільового вузла та відвідуючи всі його попередникиimport { ClassicPreset } from 'rete-engine'
import { DataflowEngine } from 'rete-engine'
const { Node, Socket } = ClassicPreset
class NodeAdd extends Node<{ left: Socket, right: Socket }, { value: Socket }, { }> {
constructor() {
// ініціалізувати елементи керування та порти
}
// обов'язковий метод
data(inputs: { left?: number[], right?: number[] }): { value: number } {
const left = inputs.left[0] || 0
const right = inputs.right[0] || 0
return {
value: left + right
}
}
}
const engine = new DataflowEngine<Schemes>()
editor.use(engine)
const nodeOutput = await engine.fetch(resultNode.id)
Потік керування — це підхід обходу вузлів, який дозволяє визначити спосіб передавати керування наступним вузлам.
Обробка починається з початкового вузла, який визначає, як контроль передається через його вихідні з'єднання. Наприклад, це може бути затримка або розгалуження. Найближчим прикладом є UE4 Blueprints.
import { ControlFlowEngine } from 'rete-engine'
const { Node, Socket } = ClassicPreset
class Log extends Node<{ enter: Socket }, { out: Socket }, {}> {
constructor() {
// ініціалізувати порти
}
// обов'язковий метод
execute(input: 'enter', forward: (output: 'out') => void) {
console.log('log something')
forward('out')
}
}
const engine = new ControlFlowEngine<Schemes>()
editor.use(engine)
engine.execute(startNode.id)
Крім того, ці підходи можна комбінувати. Наприклад, порти з назвою 'exec' можна використовувати для керування потоком, тоді як інші порти керують даними.
const controlflow = new ControlFlowEngine<Schemes>(node => {
return {
inputs: () => ['exec'],
outputs: () => ['exec']
}
})
const dataflow = new DataflowEngine<Schemes>(({ inputs, outputs }) => {
return {
inputs: () => Object.keys(inputs).filter(name => name !== 'exec'),
outputs: () => Object.keys(outputs).filter(name => name !== 'exec')
}
})
Крім того, ви можете безпосередньо використовувати класи Dataflow
і ControlFlow
, надаючи точніший контроль над обробкою графів.
import { ControlFlow, Dataflow } from 'rete-engine'
const control = new ControlFlow(editor)
const dataflow = new Dataflow(editor)
control.add(startNode, {
inputs: () => [],
outputs: () => ['exec'],
async execute(input, forward) {
const inputs = await dataflow.fetchInputs(startNode.id)
forward('exec')
}
})
dataflow.add(startNode, {
inputs: () => ['data'],
outputs: () => ['data'],
data(fetchInputs) {
const inputs = await fetchInputs()
const data = {
data: inputs.data[0] // пересилання вхідних даних (за умови, що існує лише одне вхідне з’єднання з портом "data")
}
return data
}
})
Ця версія двигуна містить переглянуті підходи до обробки графіків і усуває недоліки попередньої версії, яка спочатку була орієнтована на жорсткий потік даних без підтримки рекурсії.
Коли мова заходить про обробку графів, немає універсального рішення для всіх. У простих випадках ви можете використовувати DataflowEngine
і ControlFlowEngine
, а в більш складних випадках ви можете використовувати ControlFlow
і Dataflow
або написати власне рішення, вивчивши вихідний код пакету rete-engine