class SvListModifierNode(bpy.types.Node, SverchCustomTreeNode): ''' List Modifier''' bl_idname = 'SvListModifierNode' bl_label = 'List Modifier' bl_icon = 'MODIFIER' sv_icon = 'SV_LIST_MODIFIER' mode_items = [(no_space(name), name, "", idx) for _, idx, name, _ in node_item_list] func_: EnumProperty(name="Modes", description="Mode Choices", default=SET, items=mode_items, update=updateNode) listify: BoolProperty(default=True, description='Output lists or proper sets', update=updateNode) help_url: StringProperty(default='list_main/list_modifier') def draw_buttons(self, context, layout): layout.prop(self, "func_", text='') layout.prop(self, "listify", text='output as list') def sv_init(self, context): self.inputs.new('SvStringsSocket', "Data1") self.inputs.new('SvStringsSocket', "Data2") self.outputs.new('SvStringsSocket', "Result") def draw_label(self): return self.func_ def process(self): inputs = self.inputs outputs = self.outputs if not outputs[0].is_linked: return unary = (num_inputs[no_space(self.func_)] == 1) f = self.get_f(unary) if unary: if inputs['Data1'].is_linked: data1 = inputs['Data1'].sv_get() elif inputs['Data2'].is_linked: data1 = inputs['Data2'].sv_get() else: return out = f(data1) else: data1 = inputs['Data1'].sv_get() data2 = inputs['Data2'].sv_get() out = f(data1, data2) # params = match_long_repeat([data1, data2]) # out = f(*params) outputs[0].sv_set(out) def get_f(self, unary): operation = func_dict[no_space(self.func_)] do_post = (no_space(self.func_) in SET_OPS) and self.listify post_process = list if do_post else lambda x: x # identity function if unary: def f(d): if isinstance(d[0], (int, float)): return post_process(operation(d)) else: return [f(x) for x in d] else: def f(a, b): if isinstance(a[0], (int, float)): return post_process(operation(a, b)) else: return [f(*_) for _ in zip(a, b)] return f
# using a purposely broad indexing value range incase other functions get into this.. node_item_list = [ (1, 1, SET, set), (1, 10, "Ordered Set by input", ordered_set), (1, 20, "Unique Consecutives", unique_consecutives), (1, 30, "Sequential Set", lambda a: sorted(set(a))), (1, 40, "Sequential Set Rev", lambda a: sorted(set(a), reverse=True)), (1, 50, "Normalize", normalize), (1, 60, "Accumulating Sum", lambda a: list(accumulate(a))), (2, 69, "Mask subset (A in B)", mask_subset), (2, 70, INTX, lambda a, b: set(a) & set(b)), (2, 80, UNION, lambda a, b: set(a) | set(b)), (2, 90, DIFF, lambda a, b: set(a) - set(b)), (2, 100, SYMDIFF, lambda a, b: set(a) ^ set(b)) ] func_dict = {no_space(k): v for _, _, k, v in node_item_list} num_inputs = {no_space(k): v for v, _, k, _ in node_item_list} class SvListModifierNode(bpy.types.Node, SverchCustomTreeNode): ''' List Modifier''' bl_idname = 'SvListModifierNode' bl_label = 'List Modifier' bl_icon = 'MODIFIER' sv_icon = 'SV_LIST_MODIFIER' mode_items = [(no_space(name), name, "", idx) for _, idx, name, _ in node_item_list] func_: EnumProperty(name="Modes", description="Mode Choices",
class SvGetAssetPropertiesMK2(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode): ''' Get Asset Props ''' bl_idname = 'SvGetAssetPropertiesMK2' bl_label = 'Object ID Selector+' bl_icon = 'SELECT_SET' sv_icon = 'SV_OBJECT_ID_SELECTOR' def type_filter(self, object): return object.type == self.Type def frame_updateNode(self, context): ''' must rebuild for each update''' self.frame_collection_name.clear() if not (self.gp_pointer and self.gp_layer): return layer_ref = self.gp_pointer.layers.get(self.gp_layer) if not layer_ref: return for idx, f in enumerate(layer_ref.frames): self.frame_collection_name.add().name = str(idx) + ' | ' + str( f.frame_number) # updateNode(self, context) if self.gp_selected_frame_mode == 'active_frame': if len(self.inputs) == 0: self.inputs.new("SvStringsSocket", 'frame#') else: if len(self.inputs) > 0: self.inputs.remove(self.inputs[-1]) self.process() type_collection_name: bpy.props.CollectionProperty( type=bpy.types.PropertyGroup) frame_collection_name: bpy.props.CollectionProperty( type=bpy.types.PropertyGroup) M = [ 'actions', 'brushes', 'filepath', 'grease_pencils', 'groups', 'images', 'libraries', 'linestyles', 'masks', 'materials', 'movieclips', 'node_groups', 'particles', 'scenes', 'screens', 'shape_keys', 'sounds', 'speakers', 'texts', 'textures', 'worlds', 'objects' ] T = [ 'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'ARMATURE', 'GPENCIL', 'LATTICE', 'EMPTY', 'CAMERA', 'LIGHT', 'SPEAKER' ] Mode: EnumProperty(name="get modes", default="objects", items=e(M), update=updateNode) Type: EnumProperty(name="get types", default="MESH", items=e(T), update=updateNode) properties_to_skip_iojson: [ "object_pointer", "image_pointer", "text_pointer" ] text_pointer: PointerProperty(type=bpy.types.Text, poll=lambda s, o: True, update=updateNode) object_pointer: PointerProperty(type=bpy.types.Object, poll=type_filter, update=updateNode) image_pointer: PointerProperty(type=bpy.types.Image, poll=lambda s, o: True, update=updateNode) pass_pixels: bpy.props.BoolProperty(update=updateNode) # GP props gp_pointer: PointerProperty(type=bpy.types.GreasePencil, poll=lambda s, o: True, update=updateNode) # i can't get this to work. so am revering to StringProperty for gp_layer. # gp_layer_pointer: PointerProperty(type=bpy.types.GreasePencilLayers, poll=lambda s, o: True, update=updateNode) # <---- no? gp_layer: bpy.props.StringProperty(update=updateNode) gp_frame_current: bpy.props.BoolProperty(default=True, update=updateNode) gp_frame_override: bpy.props.IntProperty(default=1, update=updateNode) gp_stroke_idx: bpy.props.IntProperty(update=updateNode) gp_frame_mode_options = [ (no_space(k), k, '', i) for i, k in enumerate(["pick frame", "active frame"]) ] gp_selected_frame_mode: bpy.props.EnumProperty( items=gp_frame_mode_options, description="offers choice between current frame or available frames", default="pick_frame", update=frame_updateNode) gp_frame_pick: bpy.props.StringProperty(update=frame_updateNode) gp_pass_points: bpy.props.BoolProperty(default=True, update=updateNode) def draw_gp_options(self, context, layout): layout.prop_search(self, 'gp_pointer', bpy.data, 'grease_pencils', text='name') if not self.gp_pointer: return layout.prop_search(self, 'gp_layer', self.gp_pointer, 'layers', text='layer') layer_ref = self.gp_pointer.layers.get(self.gp_layer) if not layer_ref: return layout.prop(self, 'gp_selected_frame_mode', expand=True) frame_data = None if self.gp_selected_frame_mode == 'active_frame': frame_data = layer_ref.active_frame else: # maybe display uilist with frame_index and frame_nmber. layout.prop_search(self, 'gp_frame_pick', self, 'frame_collection_name') layout.prop(self, 'gp_pass_points', text='pass points') def process_mode_grease_pencils(self): data_list = bpy.data.grease_pencils if not (self.gp_pointer and self.gp_layer): return data_list[:] layer_ref = self.gp_pointer.layers.get(self.gp_layer) if not layer_ref: return data_list[:] if self.gp_selected_frame_mode == 'active_frame': if len(self.inputs) > 0 and self.inputs[0].is_linked: frame_number = self.inputs[0].sv_get()[0][0] key = frame_from_available2(frame_number, layer_ref) strokes = layer_ref.frames[key].strokes else: strokes = layer_ref.active_frame.strokes if not strokes: return [] if self.gp_pass_points: return [[p.co[:] for p in s.points] for s in strokes] else: return strokes else: if self.gp_frame_pick: idx_from_frame_pick = int(self.gp_frame_pick.split(' | ')[0]) frame_data = layer_ref.frames[idx_from_frame_pick] if frame_data: if self.gp_pass_points: return [[p.co[:] for p in s.points] for s in frame_data.strokes] else: return frame_data.strokes def process_mode_objects(self): data_list = bpy.data.objects if self.object_pointer: return [self.object_pointer] else: return [i for i in data_list if i.type == self.Type] def process_mode_texts(self): data_list = bpy.data.texts if self.text_pointer: return [[self.text_pointer.as_string()]] else: return data_list[:] def process_mode_images(self): data_list = bpy.data.images if self.image_pointer: if self.pass_pixels: return [[self.image_pointer.pixels[:]], self.image_pointer.size[:]] else: return [self.image_pointer] else: return data_list[:] def draw_buttons(self, context, layout): # layout.operator('node.' ,text='refresh from scene') self.draw_animatable_buttons(layout, icon_only=True) layout.row().prop(self, "Mode", text="data") if self.Mode == 'objects': layout.prop(self, "Type", text="type") layout.prop_search(self, 'object_pointer', bpy.data, 'objects', text='name', icon='OBJECT_DATA') elif self.Mode == 'texts': layout.prop_search(self, 'text_pointer', bpy.data, 'texts', text='name') elif self.Mode == 'images': layout.prop_search(self, 'image_pointer', bpy.data, 'images', text='name') if self.image_pointer and self.image_pointer.name: layout.prop(self, 'pass_pixels', text='pixels') layout.label(text=f"dimensions: {self.image_pointer.size[:]}") elif self.Mode == 'grease_pencils': self.draw_gp_options(context, layout) else: col = layout.column() col.alert = True col.label(text=f"This node is not yet programmed to do") col.label(text=f"anything sensible with bpy.data.{self.Mode}") def sv_init(self, context): self.outputs.new('SvStringsSocket', "Objects") self.width = 210 def process(self): output_socket = self.outputs['Objects'] if not output_socket.is_linked: return if self.Mode in {'objects', 'texts', 'images', 'grease_pencils'}: data_output = getattr(self, f"process_mode_{self.Mode}")() else: data_list = getattr(bpy.data, self.Mode) data_output = data_list[:] output_socket.sv_set(data_output)
result = [] color_channel, mapping_mode, match_mode = constant params = matching_f(params) local_match = iter_list_match_func[match_mode] mapper_func = mapper_funcs[mapping_mode] extract_func = color_channels[color_channel][1] for props in zip(*params): verts, texture = props if not type(texture) == list: texture = [texture] m_texture = local_match([texture])[0] result.append([texture_evaluate(v_prop, mapper_func, extract_func) for v_prop in zip(verts, m_texture)]) return result color_channels_modes = [(no_space(t), t, t, '', color_channels[t][0]) for t in color_channels if not t == 'RGBA'] mapper_funcs = { 'UV': lambda v_uv: Vector((v_uv[0]*2-1, v_uv[1]*2-1, v_uv[2])), 'Object': lambda v: Vector(v), } class SvTextureEvaluateNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode): """ Triggers: Scence Texture In Tooltip: Evaluate Scene texture at input coordinates """ bl_idname = 'SvTextureEvaluateNode' bl_label = 'Texture Evaluate'
mapper_func = mapper_funcs[mapping_mode] extract_func = color_channels[color_channel][1] for props in zip(*params): verts, texture = props if not type(texture) == list: texture = [texture] m_texture = local_match([texture])[0] result.append([ texture_evaluate(v_prop, mapper_func, extract_func) for v_prop in zip(verts, m_texture) ]) return result color_channels_modes = [(no_space(t), t, t, '', color_channels[t][0]) for t in color_channels if not t == 'RGBA'] mapper_funcs = { 'UV': lambda v_uv: Vector((v_uv[0] * 2 - 1, v_uv[1] * 2 - 1, v_uv[2])), 'Object': lambda v: Vector(v), } class SvTextureEvaluateNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode): """ Triggers: Scence Texture In Tooltip: Evaluate Scene texture at input coordinates """
class SvMergeMesh2DLite(ModifierLiteNode, bpy.types.Node, SverchCustomTreeNode): """ Triggers: Merge 2D mesh into one Tooltip: Takes in account intersections and holes Has hidden output socket, look N panel """ bl_idname = 'SvMergeMesh2DLite' bl_label = 'Merge mesh 2D lite' bl_icon = 'AUTOMERGE_ON' def update_sockets(self, context): links = { sock.name: [link.to_socket for link in sock.links] for sock in self.outputs } [self.outputs.remove(sock) for sock in self.outputs[2:]] new_socks = [] if self.face_index: new_socks.append(self.outputs.new('SvStringsSocket', 'Face index')) if self.overlap_number: new_socks.append( self.outputs.new('SvStringsSocket', 'Overlap number')) [[self.id_data.links.new(sock, link) for link in links[sock.name]] for sock in new_socks if sock.name in links] updateNode(self, context) alg_mode_items = [(no_space(k), k, "", i) for i, k in enumerate(['Sweep line', 'Blender'])] face_index: bpy.props.BoolProperty( name="Show face mask", update=update_sockets, description="Show output socket of index face mask") overlap_number: bpy.props.BoolProperty( name="Show number of overlapping", update=update_sockets, description="Show socket with information about number " "of overlapping of polygon with other polygons") accuracy: bpy.props.IntProperty( name='Accuracy', update=updateNode, default=5, min=3, max=12, description= 'Some errors of the node can be fixed by changing this value') alg_mode: bpy.props.EnumProperty(items=alg_mode_items, name="Name of algorithm", update=updateNode) def draw_buttons(self, context, layout): layout.prop(self, 'alg_mode', expand=True) if self.alg_mode == "Blender" and not bl_merge_mesh: layout.label(text="For 2.81+ only", icon='ERROR') def draw_buttons_ext(self, context, layout): col = layout.column(align=True) col.prop(self, 'face_index', toggle=True) col.prop(self, 'overlap_number', toggle=True) layout.prop(self, 'accuracy') def sv_init(self, context): self.inputs.new('SvVerticesSocket', 'Verts') self.inputs.new('SvStringsSocket', "Faces") self.outputs.new('SvVerticesSocket', 'Verts') self.outputs.new('SvStringsSocket', "Faces") def process(self): if not all([sock.is_linked for sock in self.inputs]): return if self.alg_mode == "Blender" and not bl_merge_mesh: return out = [] for sv_verts, sv_faces in zip(self.inputs['Verts'].sv_get(), self.inputs['Faces'].sv_get()): if self.alg_mode == "Sweep_line": out.append( merge_mesh_light(sv_verts, sv_faces, self.face_index, self.overlap_number, self.accuracy)) else: out.append( get_bl_merge_mesh(sv_verts, sv_faces, 1 / 10**self.accuracy)) out_verts, out_faces, face_index, overlap_number = zip(*out) self.outputs['Verts'].sv_set(out_verts) self.outputs['Faces'].sv_set(out_faces) if self.face_index: self.outputs['Face index'].sv_set(face_index) if self.overlap_number: self.outputs['Overlap number'].sv_set(overlap_number)