Module sverchok.utils.nodes_mixins.recursive_nodes
Expand source code
# 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
from bpy.props import EnumProperty
from mathutils import Matrix
from bpy.props import BoolProperty, IntVectorProperty
from sverchok.utils.sv_itertools import process_matched
from sverchok.data_structure import (updateNode,
list_match_func, numpy_list_match_modes,
ensure_nesting_level, ensure_min_nesting)
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata
DEFAULT_TYPES = {
'NONE': ...,
'EMPTY_LIST': [[]],
'MATRIX': [Matrix()],
'MASK': [[True]]
}
def one_item_list(data):
if len(data) == 1:
return data[0]
lens = list(map(len, data))
if any([l > 1 for l in lens]):
return data
return [d[0] for d in data]
def create_bms(params):
bmesh_list = []
for p in zip(*params):
if len(params) == 2:
if len(p[1][0]) == 2:
bmesh_list.append(bmesh_from_pydata(verts=p[0], edges=p[1]))
else:
bmesh_list.append(bmesh_from_pydata(verts=p[0], faces=p[1]))
else:
bmesh_list.append(bmesh_from_pydata(*p))
return bmesh_list
class SvRecursiveNode():
'''
This mixin is used to vectorize any node.
In the init function some socket properties have to be defined in order to work properly
They can also be defined in the pre_setup function that will be called before getting the input data
for every input socket(s):
if socket is mandatory:
s.is_mandatory = True (Default value is False)
the desired nesting level
s.nesting_level =
3 for vectors lists (Default for Vertices Socket)
2 for number lists (Default)
1 for single item
the socket default mode
s.default_mode =
'NONE' to leave empty
'EMPTY_LIST' for [[]] (Default)
'MATRIX' for Matrix()
'MASK' for [[True]]
if pre_processing desired:
s.pre_processing =
'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)
from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode
...
class SvAwesomeNode(SverchCustomTreeNode, bpy.types.Node, SvRecursiveNode):
...
def sv_init(self, context):
p1 = self.inputs.new('SvVerticesSocket', "Param1")
p1.is_mandatory = True
p1.nesting_level = 3
p1.default_mode = 'NONE'
p2 = self.inputs.new('SvStringsSocket', "Param2")
p2.nesting_level = 3
self.outputs.new('SvStringsSocket', "Res1")
self.outputs.new('SvStringsSocket', "Res2")
def pre_setup(self):
if self.option == 'SOME_OPTION':
self.inputs[0].nesting_level = 2
def process_data(self, params):
param1, param2 = params
res1, res2 = awesome_func(param1)
return res1, res2
This mixing also adds the list_match property to let the user choose among repeat_last, cycle and match short and so on
to add this property to the layout:
def draw_buttons_ext(self, context, layout):
layout.prop(self, 'list_match')
def rclick_menu(self, context, layout):
layout.prop_menu_enum(self, "list_match", text="List Match")
in case of needing to generate bmesh geometry you can
set self.build_bmesh = True
define base sockets indices (verts, edges and faces) or (verts, edg_pol) in self.bmesh_inputs
then process_data will receive a list with bmesh mesh as first item
def process_data(self, params)
bmesh_list, other_param1, other_param2 = params
creating the bmesh_list before matching improves performance a lot, but if
you are modifying the bm in your function do it over a copy -> bm.copy()
'''
list_match: EnumProperty(
name="List Match",
description="Behavior on different list lengths",
items=numpy_list_match_modes, default="REPEAT",
update=updateNode)
build_bmesh = False
bmesh_inputs = [0, 1, 2]
def update_params_to_bmesh(self, params, input_nesting):
bms = process_matched([p for i, p in enumerate(params) if i in self.bmesh_inputs],
create_bms,
self.list_match,
[3 for n in self.bmesh_inputs],
1)
params = [bms, *[p for i, p in enumerate(params) if i not in self.bmesh_inputs]]
input_nesting = [1, *[n for i, n in enumerate(input_nesting) if i not in self.bmesh_inputs]]
return params, input_nesting
def pre_setup(self):
'''
function to be overridden in the node in case something has to change
before getting input data
'''
# pass
def process(self):
self.pre_setup()
if not all([s.is_linked for s in self.inputs if s.is_mandatory]):
return
if not any([s.is_linked for s in self.outputs]):
return
params = []
input_nesting = []
for s in self.inputs:
input_nesting.append(s.nesting_level)
default = DEFAULT_TYPES[s.default_mode]
if s.pre_processing == 'ONE_ITEM':
p = one_item_list(ensure_min_nesting(s.sv_get(deepcopy=False, default=default), 2))
else:
p = ensure_min_nesting(s.sv_get(deepcopy=False, default=default), s.nesting_level)
params.append(p)
one_output = len(self.outputs) == 1
if self.build_bmesh:
params, input_nesting = self.update_params_to_bmesh(params, input_nesting)
result = process_matched(params, self.process_data, self.list_match, input_nesting, len(self.outputs))
if one_output:
self.outputs[0].sv_set(result)
else:
for s, r in zip(self.outputs, result):
if s.is_linked:
s.sv_set(r)
Functions
def create_bms(params)
-
Expand source code
def create_bms(params): bmesh_list = [] for p in zip(*params): if len(params) == 2: if len(p[1][0]) == 2: bmesh_list.append(bmesh_from_pydata(verts=p[0], edges=p[1])) else: bmesh_list.append(bmesh_from_pydata(verts=p[0], faces=p[1])) else: bmesh_list.append(bmesh_from_pydata(*p)) return bmesh_list
def one_item_list(data)
-
Expand source code
def one_item_list(data): if len(data) == 1: return data[0] lens = list(map(len, data)) if any([l > 1 for l in lens]): return data return [d[0] for d in data]
Classes
class SvRecursiveNode
-
This mixin is used to vectorize any node. In the init function some socket properties have to be defined in order to work properly They can also be defined in the pre_setup function that will be called before getting the input data
for every input socket(s): if socket is mandatory: s.is_mandatory = True (Default value is False) the desired nesting level s.nesting_level = 3 for vectors lists (Default for Vertices Socket) 2 for number lists (Default) 1 for single item
the socket default mode s.default_mode = 'NONE' to leave empty 'EMPTY_LIST' for [[]] (Default) 'MATRIX' for Matrix() 'MASK' for [[True]] if pre_processing desired: s.pre_processing = '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)
from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode … class SvAwesomeNode(SverchCustomTreeNode, bpy.types.Node, SvRecursiveNode): … def sv_init(self, context): p1 = self.inputs.new('SvVerticesSocket', "Param1") p1.is_mandatory = True p1.nesting_level = 3 p1.default_mode = 'NONE' p2 = self.inputs.new('SvStringsSocket', "Param2") p2.nesting_level = 3 self.outputs.new('SvStringsSocket', "Res1") self.outputs.new('SvStringsSocket', "Res2")
def pre_setup(self): if self.option == 'SOME_OPTION': self.inputs[0].nesting_level = 2
def process_data(self, params): param1, param2 = params res1, res2 = awesome_func(param1) return res1, res2
This mixing also adds the list_match property to let the user choose among repeat_last, cycle and match short and so on to add this property to the layout: def draw_buttons_ext(self, context, layout): layout.prop(self, 'list_match')
def rclick_menu(self, context, layout): layout.prop_menu_enum(self, "list_match", text="List Match")
in case of needing to generate bmesh geometry you can
set self.build_bmesh = True define base sockets indices (verts, edges and faces) or (verts, edg_pol) in self.bmesh_inputs then process_data will receive a list with bmesh mesh as first item def process_data(self, params) bmesh_list, other_param1, other_param2 = params creating the bmesh_list before matching improves performance a lot, but if you are modifying the bm in your function do it over a copy -> bm.copy()
Expand source code
class SvRecursiveNode(): ''' This mixin is used to vectorize any node. In the init function some socket properties have to be defined in order to work properly They can also be defined in the pre_setup function that will be called before getting the input data for every input socket(s): if socket is mandatory: s.is_mandatory = True (Default value is False) the desired nesting level s.nesting_level = 3 for vectors lists (Default for Vertices Socket) 2 for number lists (Default) 1 for single item the socket default mode s.default_mode = 'NONE' to leave empty 'EMPTY_LIST' for [[]] (Default) 'MATRIX' for Matrix() 'MASK' for [[True]] if pre_processing desired: s.pre_processing = '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) from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode ... class SvAwesomeNode(SverchCustomTreeNode, bpy.types.Node, SvRecursiveNode): ... def sv_init(self, context): p1 = self.inputs.new('SvVerticesSocket', "Param1") p1.is_mandatory = True p1.nesting_level = 3 p1.default_mode = 'NONE' p2 = self.inputs.new('SvStringsSocket', "Param2") p2.nesting_level = 3 self.outputs.new('SvStringsSocket', "Res1") self.outputs.new('SvStringsSocket', "Res2") def pre_setup(self): if self.option == 'SOME_OPTION': self.inputs[0].nesting_level = 2 def process_data(self, params): param1, param2 = params res1, res2 = awesome_func(param1) return res1, res2 This mixing also adds the list_match property to let the user choose among repeat_last, cycle and match short and so on to add this property to the layout: def draw_buttons_ext(self, context, layout): layout.prop(self, 'list_match') def rclick_menu(self, context, layout): layout.prop_menu_enum(self, "list_match", text="List Match") in case of needing to generate bmesh geometry you can set self.build_bmesh = True define base sockets indices (verts, edges and faces) or (verts, edg_pol) in self.bmesh_inputs then process_data will receive a list with bmesh mesh as first item def process_data(self, params) bmesh_list, other_param1, other_param2 = params creating the bmesh_list before matching improves performance a lot, but if you are modifying the bm in your function do it over a copy -> bm.copy() ''' list_match: EnumProperty( name="List Match", description="Behavior on different list lengths", items=numpy_list_match_modes, default="REPEAT", update=updateNode) build_bmesh = False bmesh_inputs = [0, 1, 2] def update_params_to_bmesh(self, params, input_nesting): bms = process_matched([p for i, p in enumerate(params) if i in self.bmesh_inputs], create_bms, self.list_match, [3 for n in self.bmesh_inputs], 1) params = [bms, *[p for i, p in enumerate(params) if i not in self.bmesh_inputs]] input_nesting = [1, *[n for i, n in enumerate(input_nesting) if i not in self.bmesh_inputs]] return params, input_nesting def pre_setup(self): ''' function to be overridden in the node in case something has to change before getting input data ''' # pass def process(self): self.pre_setup() if not all([s.is_linked for s in self.inputs if s.is_mandatory]): return if not any([s.is_linked for s in self.outputs]): return params = [] input_nesting = [] for s in self.inputs: input_nesting.append(s.nesting_level) default = DEFAULT_TYPES[s.default_mode] if s.pre_processing == 'ONE_ITEM': p = one_item_list(ensure_min_nesting(s.sv_get(deepcopy=False, default=default), 2)) else: p = ensure_min_nesting(s.sv_get(deepcopy=False, default=default), s.nesting_level) params.append(p) one_output = len(self.outputs) == 1 if self.build_bmesh: params, input_nesting = self.update_params_to_bmesh(params, input_nesting) result = process_matched(params, self.process_data, self.list_match, input_nesting, len(self.outputs)) if one_output: self.outputs[0].sv_set(result) else: for s, r in zip(self.outputs, result): if s.is_linked: s.sv_set(r)
Subclasses
- sverchok.nodes.CAD.inset_special_mk2.SvInsetSpecialMk2
- sverchok.nodes.analyzer.bbox_mk3.SvBBoxNodeMk3
- sverchok.nodes.analyzer.component_analyzer.SvComponentAnalyzerNode
- sverchok.nodes.analyzer.mesh_select_mk2.SvMeshSelectNodeMk2
- sverchok.nodes.analyzer.nearest_point_on_mesh.SvNearestPointOnMeshNode
- sverchok.nodes.analyzer.normals_mk2.SvGetNormalsNodeMk2
- sverchok.nodes.generator.icosphere.SvIcosphereNode
- sverchok.nodes.matrix.matrix_out_mk2.SvMatrixOutNodeMK2
- sverchok.nodes.modifier_change.extrude_edges_mk2.SvExtrudeEdgesNodeMk2
- sverchok.nodes.modifier_change.mesh_clean.SvMeshCleanNode
- sverchok.nodes.modifier_change.mesh_join_mk2.SvMeshJoinNodeMk2
- sverchok.nodes.modifier_change.subdivide_to_quads.SvSubdivideToQuadsNode
- sverchok.nodes.modifier_make.bisect.SvBisectNode
- sverchok.nodes.vector.interpolation_mk3.SvInterpolationNodeMK3
- sverchok.nodes.vector.vector_polar_out.VectorPolarOutNode
Class variables
var bmesh_inputs
var build_bmesh
var list_match : <_PropertyDeferred,
, {'name': 'List Match', 'description': 'Behavior on different list lengths', 'items': [('SHORT', 'Short', 'Match shortest List', 1), ('CYCLE', 'Cycle', 'Match longest List by cycling', 2), ('REPEAT', 'Repeat Last', 'Match longest List by repeating last item', 3)], 'default': 'REPEAT', 'update': , 'attr': 'list_match'}>
Methods
def pre_setup(self)
-
function to be overridden in the node in case something has to change before getting input data
Expand source code
def pre_setup(self): ''' function to be overridden in the node in case something has to change before getting input data ''' # pass
def process(self)
-
Expand source code
def process(self): self.pre_setup() if not all([s.is_linked for s in self.inputs if s.is_mandatory]): return if not any([s.is_linked for s in self.outputs]): return params = [] input_nesting = [] for s in self.inputs: input_nesting.append(s.nesting_level) default = DEFAULT_TYPES[s.default_mode] if s.pre_processing == 'ONE_ITEM': p = one_item_list(ensure_min_nesting(s.sv_get(deepcopy=False, default=default), 2)) else: p = ensure_min_nesting(s.sv_get(deepcopy=False, default=default), s.nesting_level) params.append(p) one_output = len(self.outputs) == 1 if self.build_bmesh: params, input_nesting = self.update_params_to_bmesh(params, input_nesting) result = process_matched(params, self.process_data, self.list_match, input_nesting, len(self.outputs)) if one_output: self.outputs[0].sv_set(result) else: for s, r in zip(self.outputs, result): if s.is_linked: s.sv_set(r)
def update_params_to_bmesh(self, params, input_nesting)
-
Expand source code
def update_params_to_bmesh(self, params, input_nesting): bms = process_matched([p for i, p in enumerate(params) if i in self.bmesh_inputs], create_bms, self.list_match, [3 for n in self.bmesh_inputs], 1) params = [bms, *[p for i, p in enumerate(params) if i not in self.bmesh_inputs]] input_nesting = [1, *[n for i, n in enumerate(input_nesting) if i not in self.bmesh_inputs]] return params, input_nesting