Node API

This page claims to define all aspects of node creation. A brief introduction to node creation is represented on this page. Api documentation of base class of all nodes can be found here. Also read the Alpha/Beta Node State section before creating new node.

The Code of a new node should be created in a separate file. The file should be placed in one of the available categories in the nodes folder.

Usually the structure of the file looks like:

# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE

import bpy

class Node:
    ...

def register():
    ...

def unregister():
    ...

Class Definition

To create a node in Sverchok means to create a node class. The naming convention for node classes is SV + name of the node + Node

Standard Mix-ins for all node classes are sverchok.node_tree.SverchCustomTreeNode and bpy.types.Node

Optional Mix-ins can be found in sverchok.utils.nodes_mixins

The Node class documentation string serves two purposes:

  1. Adding key words for searching

  2. Adding a tool tip into the “Add Node” menu

There are two mandatory class variables:

  1. bl_idname should repeat the name of the class

  2. bl_label is the common name of the node, used in the Node header and in the “Add Node” menus

Tip

Node label can be defined dynamically in the draw_label method which should return a string.

Also the class can define an optional bl_icon variable with a string of a standard Blender icon for the node. To find all standard icons the Icon Viewer add-on can be used, which is included in Blender by default.

It’s possible to define custom icons by including an sv_icon variable. All custom icons are stored in the sverchok.ui.icons folder in png format. The variable value should repeat the png file name without extension and in upper case.

class SvSetMeshAttributeNode(SverchCustomTreeNode, bpy.types.Node):
    """
    Triggers: set mesh attribute
    Tooltip: It adds an attribute to a mesh
    """
    bl_idname = 'SvSetMeshAttributeNode'
    bl_label = 'Set Mesh Attribute'
    bl_icon = 'SORTALPHA'

    def draw_label(self):
        return "Label1" if self.prop else "Label2"

Creating Node Custom Properties

Properties are created in the same way as other areas of Blender. Blender documentation

After properties are added they can be used as node buttons or socket properties. Also they can be used for internal usage but it’s usually better to use Custom properties (node['prop_name']) instead.

Note

Using node properties for displaying on sockets is deprecated. Usually sockets can define their own properties.

To make a node react to property changes they should define the update argument. If no extra logic is required the argument can be defined thus update=data_structure.updateNode.

In the case that a property change should cause sockets to be changed (created, removed..etc) this can be done by creating a custom update method (usually in the node class) and passing a reference to it in the update argument of the property. The method expects to get the self and context parameters.

Tip

Also there is a direct method to update the node but it can’t be passed as an argument to the update parameter directly. Instead it’s possible to use lambda expression: update=lambda self, context: self.process_node(context)

class NodeClass:

    # ...

    def update_type(self, context):
        # some logic
        updateNode(self, context)

    some_mode: bpy.props.BoolProperty(update=updateNode)
    another_mode: bpy.props.BoolProperty(update=update_type)

Draft Properties

Nodes can have draft properties which will be used instead of normal ones in draft mode of a tree. Draft properties are defined in the same way as normal ones. Also the node should use DraftMode mix-in, define draft_properties_mapping class variable with mapping between standard properties and draft ones, and does_support_draft_mode method which should return boolean value.

class NodeClass(sverchok.utils.nodes_mixins.DraftMode):

    # ...

    some_mode: bpy.props.BoolProperty(update=updateNode)
    some_mode_draft: bpy.props.BoolProperty(name='[D] Some Mode', update=updateNode)

    draft_properties_mapping = dict(some_mode = 'some_mode_draft')

    def does_support_draft_mode(self):
        return True

Enum Properties

Enums are created in the same way as in other Blender UI parts. In case Enums are generated dynamically they always should be stored somewhere in Python memory. There are known cases when Blender crashes during rendering when UI expose dynamic enums which do not store their content.

Tip

There is now utils.handle_blender_data.keep_enum_reference decorator which can be used with dynamic enums. The decorator assign enum items to a Python variable what solves the problem above.

Enum items can have custom icons. Custom icons should be stored in the sverchok.ui.icons folder. To use custom icons the ui.sv_icons.custom_icon function should be used. It expects the name of the file in upper case without extension and returns the index of the icon.

Dynamic Properties

There are several nodes which generate dynamic properties - List Levels and Switcher nodes. Dynamic properties are properties which are generated depending on the size of input data. The best way to generate dynamic properties is to use PropertyGroups together with Collection properties. Displaying such properties is possible with for loop inside UI code. The right place to upgrade properties is in the process method.

Warning

Dynamic properties should always store values changed by the user, even if they are not displayed anymore. Otherwise it will lead to degradation of node tree “code”. Otherwise, whenever properties are removed and restored a user would always be forced to repeat choices - this is quite unexpected and time consuming.

In the future the generation of properties (currently done from inside process method) should move to some other method because the process method itself should become an abstract method.

Creating Node Buttons

There are 4 places where a node can show its properties:

  1. Node interface

  2. Node tab of the Property panel of the Node editor

  3. Tool tab of the Property panel of the 3d Viewport editor

  4. Context menu

The Node interface is the appropriate place for adding properties which are used regularly during work with a node tree. They should be defined in sv_draw_buttons method which expects context and layout arguments.

The Property panel of the Node editor is a good place for showing properties which are rarely changed or should be changed only once. To make properties appear on that panel place them inside a sv_draw_buttons_ext method, this method also expects context and layout arguments.

class Node:
    value: IntProperty()
    mode: BoolProperty()

    def sv_draw_buttons(self, context, layout):
        layout.prop(self, "value")

    def sv_draw_buttons_ext(self, context, layout):
        layout.prop(self, "mode")

There are some nodes for which it is useful to see properties from the 3D Viewport editor. Node with such properties should use utils.nodes_mixins.Show3DProperties mix-in. UI code should be placed in draw_buttons_3dpanel method. It expects layout argument and the optional in_menu argument which is False by default. UI should obtain only one string. It’s possible to show UI on several lines but in this case utils.node_mixins.Popup3DMenu operator should be used. The operator calls the same draw_buttons_3dpanel method but with in_menu argument as True.

class Node(Show3DProperties):

    def draw_buttons_3dpanel(self, layout, in_menu=None):
        if not in_menu:
            menu = layout.row(align=True).operator('node.popup_3d_menu', text=f'Show: "{self.label or self.name}"')
            menu.tree_name = self.id_data.name
            menu.node_name = self.name
        else:
            row.prop(self, 'mode1')
            row.prop(self, 'mode2')

Also optionally nodes can show their properties in the context menu. The Node class should override the rclick_menu method which expects context and layout arguments.

Node Sockets

Node sockets are created in sv_init method. new method of input and output collections of sockets should be used. It expects name of a socket type and name socket itself. These names are shown in UI and also usually are used as identifiers. Whole list of available socket types can be found in core.sockets module. The new method returns newly created socket which can be used for setting its extra parameters.

Usually sockets expose their default parameters. By default they are switched off. The proper way to make to show its property is to assign True value to use_prop attribute of the socket. Default value can be changed in default_property attribute.

SvStringsSocket type has two types of default values. Current type stored in default_property_type attribute which can receive either ‘float’ or ‘int’ values. Default values are stored in default_float_property and default_int_property attributes.

class Node:
    def sv_init(self, context):
        socket = self.inputs.new('SvStringsSocket', "Size")
        socket.use_prop = True
        socket.default_float_property = 1.0
        self.outputs.new('SvVerticesSocket', "Verts")

Tip

Alternative way of creating input sockets is using sv_new_input method.

class Node:
    def sv_init(self, context):
        self.sv_new_input('SvStringsSocket', "Size", use_prop=True,
                          default_float_property=1)

Dynamic Sockets

Dynamic sockets are shown only on certain conditions. There are 3 categories of them:

  1. Socket is shown if a node has certain properties.

  2. Socket is shown if other socket is connected.

  3. Socket is shown if node has appropriate input data.

There are many ways to show / hide sockets. First of all it’s possible to use Blender standard API for adding and removing sockets. Most resent nodes use hide_safe attribute of sockets. Disadvantage of this method is that sockets are not really deleted and can be shown with Ctrl+h by user. The proper way now is to use standard Blender enabled attribute.

When type of a socket should be changed it’s possible to use data_structure.changable_sockets function or replace_socket method of a socket. First function changes type of output sockets dependently on type of a socket connected to input one. With the second method you have to define new type of a socket by yourself.

Warning

Change type of a socket is tricky part. Because it’s related with removing, adding, moving sockets and links in a tree. Also it can be quite inefficient because Blender does not expose API which would allow to search connected neighbour sockets efficiently. But usually it’s not a bottle neck in such cases.

To generate sockets upon changes of node properties is possible in update method of properties.

To generate sockets upon changes in node connections is possible in sv_update method of nodes. This method can be called quite intensively so it’s wise to expense resources carefully.

To generate sockets upon changes of input data of a node was quite controversial idea. Now it’s only used in Dictionary output node. The problem is that this can easily lead to losses of user connections what breaks node setups. For example in Geometry Nodes project there was a decision that sockets should be independent to data layer. So to generate such nodes is not recommended now. If there is no way but to have this functionality possible solution could be to add a button to a node which would recreate sockets explicitly.

class Node:
    def mode_update(self, context):
        self.inputs['Value'].enabled = self.mode
        self.process_node()

    mode: BoolProperty(update=mode_update)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Value").use_prop = True
        self.outputs.new('SvStringsSocket', "Value")

    def sv_update(self)
        data_structure.changable_sockets(self, "Value", ["Value"])

Socket Properties

label

Expects a string which is used instead of a socket name in UI.

use_prop

Expects boolean value. If true the socket will display its default property.

https://user-images.githubusercontent.com/28003269/180741280-683987fa-e10c-47e1-91e0-807311697fea.png
show_property_type (SvStringsSocket)

It adds icon to switch default type of the string socket

custom_draw

Expects name of a method of the node of the socket. If defined the method will be used draw UI elements for the socket.

class Node:
    def custom_draw_socket(self, socket, context, layout):
        layout.prop(self, "node_property")
quick_link_to_node

Expects a string of node bl_idname`. This will add an operator which can create quick link to the given node.

link_menu_handler

Expects a string of class name defined inside node of the socket. This only works when displaying quick links is in multiple values mode. In the class its possible to define extra nodes for connections. This is analog of creating nodes during dragging a link from a socket in Blender 3.1.

class Node:
    class MenuHandler:
        @classmethod
        def get_items(cls, socket, context):
            """Return list of extra options for the menu"""
            return [('KEY', "Name", "Description"), ]

        @classmethod
        def on_selected(cls, tree, node, socket, key, context):
            """In this method the node should be created and linked to the socket"""
            if key == 'KEY':
                print("Hello world!")
prop_name

Expects name of a node property to display in UI of the socket.

Warning

This is deprecated way to display default properties for sockets. Use use_prop attribute instead.

object_kinds (SvObjectSocket)

Expects string value of object type to socket to display as possible choice. Its also possible to pass several types which should be separate by only comma: ‘MESH,CURVE,SURFACE,META,FONT,VOLUME,EMPTY,CAMERA,LIGHT’

expanded (SvVerticesSocket, SvQuaternionSocket, SvColorSocket)

Expects boolean value. It’s responsible for the way of the socket to display the socket value.

Socket Vectorization Properties

Vectorization system is on experimental stage

is_mandatory

Expects boolean value. If True the node can’t perform its function without data from the socket.

nesting_level

Expects integer value. Describes the expected shape of input data.

  • 3 for vectors lists (Default for Vertices Socket)

  • 2 for number lists (Default)

  • 1 for single item

default_mode

Expects one of the next strings:

  • ‘NONE’ to leave empty

  • ‘EMPTY_LIST’ for [[]] (Default)

  • ‘MATRIX’ for Matrix()

  • ‘MASK’ for [[True]]

pre_processing

Expects one of the next strings:

  • ‘ONE_ITEM’ for values like the number of subdivision (one value per object). It will match one value per object independently if the list is [[1,2]] or [[1],[2]]. In case of more complex inputs no preprocessing will be made.

  • ‘NONE’ not doing any preprocessing. (Default)

Business logic

The main work of the node is happening inside process method which does not expect any arguments.

The whole process can be split into 3 steps:

  1. Extract data from sockets.

  2. Handle the data.

  3. Record result into output sockets.

Note

In future it is planned to convert the method into abstract one. In this case a node will get parameters via some arguments.

For reading data from sockets their sv_get method can be used. It has tow important parameters. default parameter expects any data which will be returned in case if input socket does not have any external data. deepcopy parameter expects False value if input data is not modified by the node. The node can work quite more efficient if deepcopy is False. But if a node do modify the data the parameter should be with default value, otherwise other nodes which use the same data will get unexpected results.

Note

Many nodes on this stage also do such optimization as checking connection of their output sockets and if they are not connected cancel their father execution. Really it’s not recommended in new nodes. The right place for such optimization is execution system.

After handling input data sv_set method of sockets can be used for saving result. It expects only one parameter - data.

class Node:
    def process(self):
        data = self.inputs['My Socket'].sv_get(default=[], deepcopy=False)

        result = handle_data(data)

        self.outputs['My Socket'].sv_set(result)

Important

Sometimes node does not have enough data to perform its function in this case it should pass available data to output sockets unmodified. It’s important because the whole node tree will stop working otherwise.

Tip

Also sv_get method has third parameter - implicit_conversions. It expects one of the values of core.socket_conversions.ConversionPolicies enum. It’s purpose is to convert format of output data of previous nodes to format of input data of current node. For example via Conversion Policy conversion simple values to vectors is happening. Usually such settings are applied globally to all sockets but sometimes it can be useful to override them via the parameter (not single node do this currently though).

Data vectorization

All nodes should be designed in a way that they can handle not only one object but multiple of them. That is called vectorization in Sverchok. For example if a node works with vertices of an object it should handle list of lists of vertices.

It can happen that some input data has one number of objects and another input data has another number of objects. In this case a node should perform data matching operation. Usually it means that data with shorter number of objects should repeat them to match them to number of objects of the longest data. Repeating objects usually happens in two ways.

  1. Last object fills all missing ones. For example: [1, 2, 3] will be converted into [1, 2, 3, 3 ,3 ,3] if number of required objects is 6.

  2. Objects start to repeat from start of a list (cycling). For example: [1, 2, 3] will be converted into [1, 2, 3, 1, 2, 3] if number of required objects is 6.

Usually number of objects is determined by the longest input data. Sometimes the number can be limited by some particular input in case it does not have sense to repeat it.

There are helping functions / generators to perform data matching in data_structure module. Generators are preferable before functions.

class Node:
    def process(self):
        params = [s.sv_get(deepcopy=False, default=[[]]) for s in self.inputs]
        max_len = max(map(len, params))
        out = []
        for _, v, e, f, fd, m, t, d  in zip(range(max_len), *make_repeaters(params)):
            out.append(handle_data(v, f, t, d, e, fd, m))

        out_verts, out_edges, out_faces, out_face_data, out_mask = zip(*out)
        self.outputs['Verts'].sv_set(out_verts)
        self.outputs['Edges'].sv_set(out_edges)
        self.outputs['Faces'].sv_set(out_faces)
        self.outputs['Face data'].sv_set(out_face_data)
        self.outputs['Mask'].sv_set(out_mask)

Note

There are two experimental approaches to automate data matching. One can be found in utils.nodes_mixins.recursive_nodes and another in utils.vectorize modules. Both of them can handle not only list of objects but and nested to each other lists of objects with arbitrary nestedness and shape. It leads to two disadvantages:

  1. It make the code difficult to understand, to support and to debug. Even for user its more difficult to handle data with complex shape.

  2. Vectorization itself is very expensive thing because it uses pure Python loops. And such complex vectorization system is even more expensive.

Also any vectorization can be performed with loop nodes which can create more clear representation data handling. So this modules should prove first which problems they are going to solve which can’t be tackled in another way and so they can’t be recommended for use for now.

Note

In future vectorization should leave the nodes area and arrive to execution system. In this case nodes only have to add information to sockets to give to execution system to know how to match data.

Data structure

Sverchok can operate on vide variation of data structures. The most important one is mesh data structure. Sverchok uses Face-vertex representation of them. Representation is a simple list of vertices, and a set of edges and polygons that point to the vertices they use.

Note

Usually list of vertices, edges and polygons are ordinary Python lists. Vertices can be represented as numpy arrays. If a node is generator it can have an option in which format to output vertices. If a node has vertices as an input it should output them in the same format in which they came.

For edges and polygons it was decided not to use numpy arrays due little performance benefit and in case of n-gons it’s not trivial how to store and handle them as numpy arrays.

# simple triangle
vertices = [(0, 0, 0), (1, 0, 0), (0, 1, 0)]
edges = [(0, 1), (1, 2), (0, 2)]
polygons = [[0, 1, 2], ]

For vertices there is SvVerticesSocket socket type. For edges and faces there is SvStringsSocket socket type. The last one is also used for lists of numbers (floats, integers).

For storing mesh attributes Sverchok uses simple numbers or more complex data as colors, texts and vectors. Such lists should store values per mesh element. Color data passes via SvColorSocket, number via SvStringsSocket, strings via SvTextSocket.

For orienting meshes in space Blender Matrix and Quaternions are used. Historically they has next format - [matrix, matrix, ...] but this format can move only whole mesh. For this reason some nodes also support such format - [[matrix, matrix, ...], [matrix, ...]]. In this cases matrix can be used for moving separate elements of a mesh. Socket types for them are SvMatrixSocket and SvQuaternionSocket.

Sverchok has family of mathematical objects such as Curves, Surfaces, Feilds, Solids. All of them, except Solids, are defined as Python classes. Solids are used from FreeCAD library. They all have dedicated to them sockets in the core.sockets module.

Also there are some other data structures as Blender objects, File paths, svg, Pulga forces, Dictionaries.

Note

Dictionary has rather experimental stage and should prove in which area they can be used efficiently.

BMesh data structure

For performing operations over geometry it’s possible to create you own algorithms. But also Blender has a library of some basic geometry operations. This library uses special BMesh data structure. It’s similar to Half-edge data structure. To convert data from Sverchok format to BMesh and vice versa there is utils.sv_bmesh_utils module.

Tests

Ideally nodes should go with some tests. But currently there is no framework for automation of tests creation. So it’s optional now. More about tests in the separate section Testing.

Performance

https://user-images.githubusercontent.com/28003269/167471557-e10fb5f4-af31-47a2-86f2-e826a253fd06.png

Dot graph https://github.com/jrfonseca/gprof2dot

https://user-images.githubusercontent.com/28003269/167472803-225b8fd9-4584-4eb5-b7e8-f0ce9695f604.png

Icicle style https://github.com/jiffyclub/snakeviz

Performance of the nodes is very important and quite a big problem in Sverchok currently. Using pure Python is quite weak solution. First step to improve performance is to rewrite code with numpy library if it’s possible.

Sverchok has tool with UI to measure performance of separate nodes or a whole tree. It’s located in the Tree Profiling panel in Sverchok tab of Property panel. It only appears if the Developer mode is enabled in the add-on settings.

In Node Tree Update mode the performance of a whole tree will be measured. To measure performance of separate nodes their process method should be marked with utils.profile.profile decorator.

After measuring the performance the result can be outputted in the console which is standard output of cProfile Python module. Also the result can be saved in separate file which can be visualized with another tools.

Printing / Logging

https://user-images.githubusercontent.com/28003269/180702647-c25d8b58-ed2d-4a7b-98ce-d6aaa594d475.png

Logging level can be set in the add-on settings.

Printing and profiling are very expensive operations. Also console can fastly turn into unreadable mess. So it’s better to avoid using them inside node code. During debugging it’s valid to use print function but it should removed in the end.

Usually logging can be don in some operators in this case you can use loggers from utils.logging module or by using node.debug, node.info and other aliases.

If a node rises an error it will appear in console in next format: data and time [logging level] module name:line number : error name

Traceback is switch off for all logging levels except debug one. If you need it make sure that you have appropriate logging level in the settings.

Node Registration

After a node was created it should be registered to appear in Blender interface. It can be done in function with register name in the same module with node class. This function will be called whenever the add-on is enabled. For the class registration standard Blender function is used.

class Node:
    ...

def register():
    bpy.utils.register_class(Node)

Also node should be placed in some existing category by adding its bl_idname to the index.md file.

Tip

In case new node should obtain new category it’s possible to create it in ui/nodeview_space_menu module. Here is example of adding a category with name Test.

menu_structure = [
    ...,
    ["NODEVIEW_MT_AddTest", 'ICON_NAME'],
    ...,
    ]

classes = [
    ...,
    make_class('Test', "Test"),
    ...,
    ]

Also the category should be added to index.md file similar to other categories.

When the add-on is disabled or reloaded its classes should be unregister. To unregister a node is possible in function with name unregister in the same module with Node class.

Documentation

When new node is added it’s strongly recommended to add its documentation. Without it, in most cases, users will hardly able to use the node and also it can be difficult to distinguish a bug because the desired behaviour was not proclaimed.

To add documentation to a node file with documentation (name_of_the_node.rst) should be added to the docs.nodes.node_category folder.

For generating documentation Sphinx library is used. Also Read the Docs Sphinx theme is used. So both libraries should be available if you want to build documentation locally. There is docs/make.bat file which builds the documentation into docs/_build (excluded from git) folder.

There is action which will automatically build and publish documentation on the next address - http://nortikin.github.io/sverchok/docs/main.html, whenever changes will be introduced in master on GitHub.

Animation

There are nodes which should be updated upon frame change. Usually they read some data from a Blender scene. To make a node to be updated every frame it’s enough to override is_animation_dependent node attribute with True value.

Note

Buttons should be displayed via sv_draw_buttons method otherwise the node won’t display extra property which can be used by user to disable updates for the current node.

Muting

Blender gives opportunity to temporary switch off any node in a tree. In this case its input data paths through the node without any modifications toward next nodes. Bas node class has default sv_internal_links property to determine how the data should path a node. If default behaviour does not fit into a node logic it can override the property. The property should return iterable tuple of input and output sockets of the node.

Note

Before implementing your own sv_internal_links property have a look at the utils/nodes_mixins/sockets_config module. It has implementations of the property for some basic node types.

Warning

Unfortunately the sv_internal_links property does not change how internal links will be displayed in UI. Currently it’s limitation of Blender which API does not give control of displaying internal links properly.

Nodes With Dependencies

Nodes can use some external library which can be installed manually by user from Extra Nodes tab in the add-on settings. When a node uses external library and it is not installed the node should add itself into a list of dummy nodes. Dummy nodes do nothing but display information that a library is not installed.

https://user-images.githubusercontent.com/10011941/85948219-e3957800-b94f-11ea-9040-d1e3009dc016.png

Also when library is not installed the nodes should not register their selves. Also such nodes dose not apper in the Add node menu.

from sverchok.dependencies import FreeCAD

if FreeCAD is None:
    utils.dummy_nodes.add_dummy('SvSolidAreaNode', 'Solid Area', 'FreeCAD')

class SvSolidAreaNode:
    ...

def register():
    if FreeCAD is not None:
        bpy.utils.register_class(SvSolidAreaNode)

The dependencies is a special module from which all dependent library should be imported. If a library is not available instead of NoModuleFound error the None value will be imported. The add_dummy function expects bl_idname of the node, its name and name of the library on which the node is dependent.

Tip

There is alternative and more simple way to handle the nodes with missing dependencies. They should be registered as regular nodes but in their process method they should raise an Error with a message which points that some library should be installed to make the node to work.

The approach can handle the case when node is not dependent on a library except in some of its modes.

JSON Import / Export

Sverchok has special format for sharing node trees and saving them into presets. In most cases nodes developers should not prepare their node to make them work with JSON import / export system. What is important to know:

  • Standard json export saves all properties including collection and nesting collection and pointers.

  • For now only data block names of pointer properties will be saved.

  • During import pointers will be searched in current scene, if there is no data blocks with current name nothing will be assigned to the pointer.

  • It is strongly not recommended to save pointers for viewer nodes. For skipping property to save use: BoolProperty(options={‘SKIP_SAVE’} it will impact only on unsaving property into json file.

  • Custom properties (which uses square bracket interface) are ignored.

  • It is possible to add save_to_json(node_data: dict) and load_from_json(node_data: dict, import_version: float) method to a node for adding extra logic into import export, but it’s better to avoid using it. It’s difficult to add changes into nodes using this methods and support import previous JSON files.

Upgrade Node

It’s possible to improve existing nodes but it should be done carefully, without breaking existing layouts. It can be done in two ways:

Improve existing node

First is when you add extra functionality to some node. It’s possible by adding extra buttons, sockets, modes. When you add something like this you should ensure that default behaviour will be unchanged.

New socket, in most cases, can be placed anywhere among existing ones but it should be checked in the process method that data from sockets is not collecting by their indexes. Also there is no any automation and in old Blender files the socket will be missing. So the socket should be added manually and currently most appropriate place for this is the process method. It leads to some overhead so probably in future there will be a special upgrade method for such things. The socket should be optional and the node should be able to work without the socket to be connected.

Tip

Also quite frequent case is socket renaming. It can be done by adding label attribute with new name. Also for old files this should be repeated in the process method.

Any property can be added to a node but it’s default value should not change initial node behaviour.

It’s possible to add extra values to Enum properties but they always should be placed in the end of the lists because Blender files keep current enum value by its index.

All other changes should be done by creating new version of the node.

Create new node version

If it’s needed to fix some existing behaviour or remove one the new version of a node should be introduced. It’s not necessary step when changes should be applied to changes which were made in not released version of Sverchok. In this case changes can be done with breaking backward compatibility.

Creating new version should be done togather with keeping previous one. In most cases it’s enough to copy module of current node into old_nodes folder. It should be done more carefully if in the module together with the node something else is registered.

New version of the node should be created in the same module of initial node by adding suffix to node class. Convention of the suffix is MKn where n is index of new version. The same should be done to bl_idname attribute of the class. New version of the node can implement anything what can be implemented in new node.

Note

By changing class name and its bl_idname attribute, don’t forget to fix these names in the registration functions and in the index.md file.

When new version is introduced it’s convenient to add replacement operator to the old version of the node which automatically replace old node with new one with keeping all connected links. This can be done by adding replacement_nodes attribute. The operator will appear in the node context menu.

class Node:
    replacement_nodes = [
        (new_node_bl_idname, inputs_mapping_dict, outputs_mapping_dict)
    ]

where new_node_bl_idname is bl_idname of replacement node class, inputs_mapping_dict is a dictionary mapping names of inputs of this node to names of inputs to new node, and outputs_mapping_dict is a dictionary mapping names of outputs of this node to names of outputs of new node. inputs_mapping_dict and outputs_mapping_dict can be None.

Note

This attribute also can be used by regular node for quick replacement with nodes which have similar functionality.

Warning

When a node has multiple previous version the replacement operator should be added (updated) to all of them.

Also the operator will try to copy all node properties by their names. If it’s impossible it’s possible to copy properties manually by adding migrate_from(self, old_node) method to new node. Also if some extra work should be done with sockets it’s possible to implement in migrate_props_pre_relink(self, old_node) method which will be called before links creation.

Alpha/Beta Node State

When new node is created or existing version of a node is improved we usually would like to have some time to catch and fix bugs. It can be done the better the more people will test the node. Thus we have to merge changes into master. But when node is in master users can save them in their layout and farther fixes of the node can break them. To avoid this it’s possible to mark a node to show to users that the node is in development state and that backwards incompatible changes can be introduced. In order to do this Alpa or Beta icon should be assigned to sv_icon attribute of the node class.

class Node:
    sv_icon = 'SV_ALPHA`  # or 'SV_BETA'

It’s recommended to mark new nodes and new version of existing node with in development state if there are doubts in robustness of the nodes. A node should lose the state when new Sverchok’s release is introduced.