Sverchok / SverchokRedux

A reimplementation of Sverchok
GNU General Public License v3.0
4 stars 2 forks source link

Ideas about nodes #3

Open ly29 opened 9 years ago

ly29 commented 9 years ago

This isn't a finished model.

  1. UI and execution should be separated
  2. The execution unit, in which ever form, it takes shouldn't have to access the node but be created with it as input.
  3. Individual nodes shouldn't parse the whole data tree (in general) but only individual bits of data
  4. For practical purposes the most meaningful size of individual data is the mesh, a lot of data is only valid for
  5. A lot of flexibility and possibility would come from building the possibility of serializing the data into the system.

How should this be accomplished?

Let us at looks some different models to create nodes.

SN1

import numpy as np

def sv_main(n_items=1500, q_factor_x=2, q_factor_y=2, seed=6):

    in_sockets = [
        ['s', 'n_items', n_items],
        ['s', 'q_factor_x', q_factor_x],
        ['s', 'q_factor_y', q_factor_y],
        ['s', 'seed', seed]]

    np.random.seed(seed)
    points=np.random.uniform(0.0,1.0,size = (n_items,2))
    points *= (1000, 200)

    a = points[:,0]
    b = points[:,1]
    a = np.floor(a / q_factor_x) * q_factor_x
    b = np.floor(b / q_factor_y) * q_factor_y
    c = np.array([0.0 for i in b])
    d = np.column_stack((a,b,c))

    # consumables
    Verts = [d.tolist()]

    out_sockets = [
        ['v', 'Verts', Verts]
    ]

    return in_sockets, out_sockets

SN2

from itertools import chain

class DeleteLooseVerts(SvScriptSimpleFunction):
    inputs = [("v", "verts"), ("s", "pol")]
    outputs = [("v", "verts"), ("s", "pol")]

    @staticmethod
    def function(*args, **kwargs):
        ve, pe = args       
        indx = set(chain.from_iterable(pe))
        v_index = sorted(set(chain.from_iterable(pe)))
        v_out = [ve[i] for i in v_index]
        mapping = dict(((j, i) for i, j in enumerate(v_index)))
        p_out = [tuple(map(mapping.get, p)) for p in pe]
        return v_out, p_out
shader gabor_noise(
    point Point = P,
    vector Direction = vector(1, 0, 0),
    int Anisotropic = 0,
    float Bandwidth = 1.0,
    float Impulses = 16,
    output float Gabor = 0.8)
{
    Gabor = noise("gabor", Point,
                  "direction", Direction,
                  "anisotropic", Anisotropic,
                  "do_filter", 1, // Set to 0 to disable filtering/anti-aliasing 
                  "bandwidth", Bandwidth,
                  "impulses", Impulses);
}

Basically there are more or less identical, as I see the most easily inspectable one with types etc is more complex SN2.

What I propose is a python class that provides a functional unit that can be executed on individual pieces of the mesh.

ly29 commented 9 years ago

Progressing slowly.

import numpy as np

def linspace(start : float = 0.0, stop : float = 1.0, count : int = 10) -> [("linspace", np.array)]:
    return np.linspace(start, stop, num)

linspace.label = "Linear space"

From this I produce a node. But no connection the other way, the code doesn't do anything more than inspect the node and producing a ui element automatically from the above.

skarmavbild 2015-11-15 kl 20 03 17

zeffii commented 9 years ago

using a class factory? :)

ly29 commented 9 years ago

Yeah. Ugly thing.

def node_factory_from_func(func):
    annotations = func.__annotations__
    if not annotations:
        return None
    class_name = "SvRxNode{}".format(func.__name__)
    bases = (SvRxNode, bpy.types.Node)

    sig = inspect.signature(func)
    inputs_template = []
    for name, parameter in sig.parameters.items():
        s = Socket(annotations[name], name, default_value = parameter.default)
        inputs_template.append(s)

    ret_values = annotations["return"]

    outputs_template = [Socket(socket_type, name) for name, socket_type in ret_values]

    node_dict = {}
    node_dict["bl_idname"] = class_name
    node_dict["bl_label"] = getattr(func, "label", func.__name__)
    node_dict["bl_icon"] = 'OUTLINER_OB_EMPTY'
    node_dict["inputs_template"] = inputs_template
    node_dict["outputs_template"] = outputs_template

    node_class = type(class_name, bases, node_dict)
    return node_class
zeffii commented 9 years ago

reminds me of: https://github.com/zeffii/SoundPetal/blob/master/core/node_factory.py

ly29 commented 9 years ago

Yeah, it was you that introduced me to type(). Nice stuff, annotations just takes it a step further.

zeffii commented 9 years ago

i'm nodding my head with a big YES :) __annotations__ and inspect.signature are the way to go.

ly29 commented 9 years ago

ordered dict would be nice, but maybe not as pythonic. I will clean things bit up a bit, mostly dead code removal, and upload to github. Then next step is serialize the node layout followed. Then make the back end. It is quite rough but I think know how I want it to work.

nortikin commented 9 years ago

@zeffii take care of mother's health, hope its to be all right after all.

ly29 commented 9 years ago

Of course there is big problem with the annotations above, np.array isn't the type of an numpy array np.ndarray is. Furthermore the type of numbers inside an numpy array isn't float or int How much details should be exposed to the user? float, int etc are nice looking, type, it certainly beats numpy.float64, therefore I propose a series of conversions rules for types.

Sensible things like np.float64 <-> float

Below is a schedule of numpy types matched to python types.

(<class 'numpy.datetime64'>, <class 'NoneType'>)
(<class 'numpy.uint64'>, <class 'int'>)
(<class 'numpy.int64'>, <class 'int'>)
<class 'numpy.void'> could not be converted
(<class 'numpy.bool_'>, <class 'bool'>)
(<class 'numpy.timedelta64'>, <class 'int'>)
(<class 'numpy.float16'>, <class 'float'>)
(<class 'numpy.uint8'>, <class 'int'>)
(<class 'numpy.int8'>, <class 'int'>)
(<class 'numpy.complex64'>, <class 'complex'>)
(<class 'numpy.float32'>, <class 'float'>)
(<class 'numpy.uint16'>, <class 'int'>)
(<class 'numpy.int16'>, <class 'int'>)
(<class 'numpy.object_'>, <class 'int'>)
(<class 'numpy.complex128'>, <class 'complex'>)
(<class 'numpy.float64'>, <class 'float'>)
(<class 'numpy.uint32'>, <class 'int'>)
(<class 'numpy.int32'>, <class 'int'>)
(<class 'numpy.bytes_'>, <class 'bytes'>)
(<class 'numpy.complex256'>, <class 'numpy.complex256'>)
(<class 'numpy.float128'>, <class 'numpy.float128'>)
(<class 'numpy.uint64'>, <class 'int'>)
(<class 'numpy.int64'>, <class 'int'>)
(<class 'numpy.str_'>, <class 'str'>)

Type conversion code from stack overflow http://stackoverflow.com/questions/9452775/converting-numpy-dtypes-to-native-python-types

def get_type_convert(np_type):
    convert_type = type(np.zeros(1,np_type).tolist()[0])
    return (np_type, convert_type)