nortikin / sverchok

Sverchok
http://nortikin.github.io/sverchok/
GNU General Public License v3.0
2.23k stars 232 forks source link

Nodes workflow requirements #3955

Open Durman opened 3 years ago

Durman commented 3 years ago

Problem statement

Creating of new nodes is quite a big part of Sverckok development. It is not optimized now and has some problems. This issue is intend to clear what problems should be solved during nodes coding.

Problems

To do such tests now is nearly impossible or quite hard task. It's not only node should be created in a Blender node tree but also node should be connected with other sockets where fake data should be placed.

Probably someday we would like to use Sverchok somewhere else. Now with node code tightly bounded with Blender UI it's nearly impossible.

If we would like to have really comfortable workflow the process of evaluation of each node should be cancelable. It can be done the easier the more breakpoints it has. It also requires changing in update system and most likely they should be done first.

My guess is that until nodes are using socket.sv_get and socket.sv_set methods it's impossible to do any multithreading. The solution could be to put data management to update system.

Vectorization code is repeated in each node and could be separated from their code.

Some part of UI node code probably could be auto generated but as it shows previous experiment it is hard task. Probably it does not worth efforts.

Durman commented 3 years ago

I will take Find closest value node for prototyping new API on its example.

2021-03-06_08-38-53

The simplest solution of solving many problems above is to create separate function for each node with implementation of main logic.

def find_closest_value_node(**props):
    data = props.get('data')
    value = props.get('value')
    mode = props.get('mode')

    # main logic

    # node can have different output
    if mode == 'a':
        return out_value, out_index
    else:
        return out_value

class BlenderNode:
    ...
    def process(self):

        # this code will be still bounded to UI and can't be tested
        # but my believe is that this code will be outdated sooner or later anyway 
        # and node functions can be used directly by new update system

        result = find_closest_value_node(data=self.inputs['Data'].sv_get(deepcopy=False), ..., mode=self.mode)
        self.outputs['Closest value'].sv_set(result[0])

Usage

from nodes.find_closest_value import find_closest_value_node

result = find_closest_value_node(data=a, value=b, mode=c)
self.assertEqual(result, expecting_result)

Possible usage is to create pipeline from nodes

from nodes import node_a, node_b, node_c

verts, faces = node_a(param1=a, param2=b)
data = node_b(param1=c)
result = node_c(param1=verts, param2=faces, param3=data)
sockets_properties = {
    'socket_a': {'vectorize' = True},
    'socket_b': {'vectorize' = False}}

@vectorize(**sockets_properties)
def find_closest_value_node(**props):
   ...

Solution is to convert node function into iterator

@suspend(**sockets_properties)
def find_closest_value_node(**props):
    ...

def suspend(f, *, **sock_props):
    if f is None:
        return lambda func: suspend(func, **sock_props)

    def inner(**kwargs):
        for sock_params in vectorize(**kwargs):
            yield f(**sock_params)

    return inner

In general this looks like flexible and simple solution. However there are some concerns of passing data via such functions. A node can have huge number of input sockets, parameters and output sockets. Also inputs and output sockets can be changed by switching node parameters. All this can lead to greater number of mistakes in passing parameters from node function to node function.