class SvMatrixNormalNode(bpy.types.Node, SverchCustomTreeNode): ''' Construct a Matirx from Normal ''' bl_idname = 'SvMatrixNormalNode' bl_label = 'Matrix normal' bl_icon = 'OUTLINER_OB_EMPTY' F = ['X', 'Y', 'Z', '-X', '-Y', '-Z'] S = ['X', 'Y', 'Z'] track = EnumProperty(name="track", default=F[4], items=e(F), update=updateNode) up = EnumProperty(name="up", default=S[2], items=e(S), update=updateNode) def sv_init(self, context): self.inputs.new('VerticesSocket', "Location").use_prop = True self.inputs.new('VerticesSocket', "Normal").use_prop = True self.outputs.new('MatrixSocket', "Matrix") def draw_buttons(self, context, layout): layout.prop(self, "track", "track") layout.prop(self, "up", "up") def process(self): Ma = self.outputs[0] if not Ma.is_linked: return L, N = self.inputs out = [] loc = L.sv_get()[0] nor = [Vector(i) for i in N.sv_get()[0]] nor = safc(loc, nor) T, U = self.track, self.up for V, N in zip(loc, nor): n = N.to_track_quat(T, U) m = Matrix.Translation(V) * n.to_matrix().to_4x4() out.append([i[:] for i in m]) Ma.sv_set(out)
class SvGetDataObjectNode(bpy.types.Node, SverchCustomTreeNode): ''' Get Object Props ''' bl_idname = 'SvGetDataObjectNode' bl_label = 'Object ID Get' bl_icon = 'OUTLINER_OB_EMPTY' M = ['actions','brushes','filepath','grease_pencil','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','LATTICE','EMPTY','CAMERA','LAMP','SPEAKER'] Modes = EnumProperty(name="getmodes", default="objects", items=e(M), update=updateNode) Types = EnumProperty(name="getmodes", default="MESH", items=e(T), update=updateNode) def draw_buttons(self, context, layout): row = layout.row(align=True) layout.prop(self, "Modes", "mode") if self.Modes == 'objects': layout.prop(self, "Types", "type") def sv_init(self, context): self.outputs.new('StringsSocket', "Objects") def process(self): sob = self.outputs['Objects'] if not sob.is_linked: return L = getattr(bpy.data,self.Modes) if self.Modes != 'objects': sob.sv_set(L[:]) else: sob.sv_set([i for i in L if i.type == self.Types])
class SvMatrixNormalNode(bpy.types.Node, SverchCustomTreeNode): """ Triggers: M. from Loc & Normal Tooltip: Construct a Matirx from Location and Normal vectors """ bl_idname = 'SvMatrixNormalNode' bl_label = 'Matrix normal' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_MATRIX_NORMAL' F = ['X', 'Y', 'Z', '-X', '-Y', '-Z'] S = ['X', 'Y', 'Z'] track: EnumProperty(name="track", default=F[4], items=e(F), update=updateNode) up: EnumProperty(name="up", default=S[2], items=e(S), update=updateNode) flat_output: BoolProperty( name="Flat output", description="Flatten output by list-joining level 1", default=True, update=updateNode) def sv_init(self, context): self.inputs.new('SvVerticesSocket', "Location").use_prop = True self.inputs.new('SvVerticesSocket', "Normal").use_prop = True self.outputs.new('SvMatrixSocket', "Matrix") def draw_buttons(self, context, layout): layout.prop(self, "track", text="track") layout.prop(self, "up", text="up") def draw_buttons_ext(self, context, layout): layout.prop(self, "track", text="track") layout.prop(self, "up", text="up") self.draw_buttons(context, layout) layout.prop(self, "flat_output", text="Flat Output", expand=False) def rclick_menu(self, context, layout): layout.prop_menu_enum(self, "track", text="Track:") layout.prop_menu_enum(self, "up", text="Up:") layout.prop(self, "flat_output", text="Flat Output", expand=False) def process(self): Ma = self.outputs[0] if not Ma.is_linked: return L, N = self.inputs T, U = self.track, self.up loc = L.sv_get() nor = Vector_generate(N.sv_get()) out = [] m_add = out.extend if self.flat_output else out.append params = match_long_repeat([loc, nor]) for par in zip(*params): matrixes = matrix_normal(par, T, U) m_add(matrixes) Ma.sv_set(out)
class SvBMVertsNode(bpy.types.Node, SverchCustomTreeNode): ''' BMesh Verts ''' bl_idname = 'SvBMVertsNode' bl_label = 'BMesh props' bl_icon = 'OUTLINER_OB_EMPTY' Modes = ['verts', 'faces', 'edges'] Mod = EnumProperty(name="getmodes", default="verts", items=e(Modes), update=updateNode) a = ['hide', 'select'] PV = a + [ 'is_manifold', 'is_wire', 'is_boundary', 'calc_shell_factor()', 'calc_edge_angle(-1)' ] PF = a + ['calc_area()', 'calc_perimeter()', 'material_index', 'smooth'] PE = a + [ 'calc_face_angle(0)', 'calc_face_angle_signed(0)', 'calc_length()', 'is_boundary', 'is_contiguous', 'is_convex', 'is_manifold', 'is_wire', 'seam' ] verts = EnumProperty(name="Vprop", default="is_manifold", items=e(PV), update=updateNode) faces = EnumProperty(name="Fprop", default="select", items=e(PF), update=updateNode) edges = EnumProperty(name="Eprop", default="select", items=e(PE), update=updateNode) def sv_init(self, context): self.inputs.new('StringsSocket', 'bmesh_list') self.outputs.new('StringsSocket', 'Value') def draw_buttons(self, context, layout): layout.prop(self, "Mod", "Get") layout.prop(self, self.Mod, "") def process(self): V = [] bm = self.inputs['bmesh_list'].sv_get() elem = getattr(self, self.Mod) exec("for b in bm:\n bv = getattr(b, self.Mod)\n V.append([i." + elem + " for i in bv])") self.outputs['Value'].sv_set(V) def update_socket(self, context): self.update()
class SvBVHtreeNode(bpy.types.Node, SverchCustomTreeNode): ''' BVH Tree ''' bl_idname = 'SvBVHtreeNode' bl_label = 'BVH Tree In' bl_icon = 'OUTLINER_OB_EMPTY' def mode_change(self, context): inputs = self.inputs while len(inputs) > 1: inputs.remove(inputs[-1]) if self.Mod == "FromObject": inputs[0].name = "Objects" inputs[0].hide = 0 elif self.Mod == "FromBMesh": inputs[0].name = "bmesh_list" inputs[0].hide = 0 else: inputs[0].hide = 1 inputs.new('VerticesSocket', 'Verts') inputs.new('StringsSocket', 'Polys') Modes = ['FromObject', 'FromBMesh', 'FromSVdata'] Mod: EnumProperty(name="getmodes", default=Modes[0], items=e(Modes), update=mode_change) def sv_init(self, context): self.inputs.new('StringsSocket', 'Objects') self.outputs.new('StringsSocket', 'BVHtree_list') def draw_buttons(self, context, layout): layout.prop(self, "Mod", "Get") def process(self): bvh = [] if self.Mod == "FromObject": for i in self.inputs[0].sv_get(): bvh.append( BVHTree.FromObject(i, bpy.context.scene, deform=True, render=False, cage=False, epsilon=0.0)) elif self.Mod == "FromBMesh": for i in self.inputs[0].sv_get(): bvh.append(BVHTree.FromBMesh(i)) else: for i, i2 in zip(self.inputs[1].sv_get(), self.inputs[2].sv_get()): bvh.append( BVHTree.FromPolygons(i, i2, all_triangles=False, epsilon=0.0)) self.outputs[0].sv_set(bvh) def update_socket(self, context): self.update()
class SvNumpyArrayNode(bpy.types.Node, SverchCustomTreeNode): ''' Numpy Props ''' bl_idname = 'SvNumpyArrayNode' bl_label = 'Numpy Array' bl_icon = 'OUTLINER_OB_EMPTY' Modes = [ 'x.tolist()', 'x.conj()', 'x.flatten()', 'np.add(x,y)', 'np.subtract(x,y)', 'x.resize()', 'x.transpose()', 'np.trunc(x)', 'x.squeeze()', 'np.ones_like(x)', 'np.minimum(x,y)', 'x.round()', 'np.maximum(x,y)', 'np.sin(x)', 'x.ptp()', 'x.all()', 'x.any()', 'np.remainder(x,y)', 'np.unique(x)', 'x.sum()', 'x.cumsum()', 'x.mean()', 'x.var()', 'x.std()', 'x.prod()', 'x.cumprod()', 'np.array(x)', 'np.array_equal(x,y)', 'np.invert(x)', 'np.rot90(x,1)', 'x[y]', 'x+y', 'x*y', 'Custom' ] Mod: EnumProperty(name="getmodes", default="np.array(x)", items=e(Modes), update=updateNode) Cust: StringProperty(default='x[y.argsort()]', update=updateNode) def sv_init(self, context): self.inputs.new('StringsSocket', 'x') self.inputs.new('StringsSocket', 'y') self.outputs.new('StringsSocket', 'Value') def draw_buttons(self, context, layout): layout.prop(self, "Mod", text="Get") if self.Mod == 'Custom': layout.prop(self, "Cust", text="") def process(self): out = self.outputs[0] if out.is_linked: X, Y = self.inputs Mod = self.Mod string = self.Cust if Mod == 'Custom' else Mod if X.is_linked and Y.is_linked: out.sv_set( eval("[" + string + " for x,y in zip(X.sv_get(),Y.sv_get())]")) elif X.is_linked: out.sv_set(eval("[" + string + " for x in X.sv_get()]")) else: out.sv_set([eval(string)]) def update_socket(self, context): self.update()
class SvGetAssetProperties(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode): ''' Get Asset Props ''' bl_idname = 'SvGetAssetProperties' bl_label = 'Object ID Selector' bl_icon = 'SELECT_SET' sv_icon = 'SV_OBJECT_ID_SELECTOR' def pre_updateNode(self, context): ''' must rebuild for each update''' self.type_collection_name.clear() for o in bpy.data.objects: if o.type == self.Type: self.type_collection_name.add().name = o.name # updateNode(self, context) self.process() def frame_updateNode(self, context): ''' must rebuild for each update''' self.frame_collection_name.clear() gp_layer = bpy.data.grease_pencils[self.gp_name].layers[self.gp_layer] for idx, f in enumerate(gp_layer.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="getmodes", default="objects", items=e(M), update=updateNode) Type: EnumProperty(name="getmodes", default="MESH", items=e(T), update=pre_updateNode) text_name: bpy.props.StringProperty(update=updateNode) object_name: bpy.props.StringProperty(update=updateNode) image_name: bpy.props.StringProperty(update=updateNode) pass_pixels: bpy.props.BoolProperty(update=updateNode) # GP props gp_name: bpy.props.StringProperty(update=updateNode) 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): # -- points [p.co for p in points] # -- color # -- color.color (stroke_color) # -- color.fill_color # -- color.file_alpha # -- line_width # -- draw_cyclic # --- / triangles (only set is useful...) layout.prop_search(self, 'gp_name', bpy.data, 'grease_pencils', text='name') if not self.gp_name: return layout.prop_search(self, 'gp_layer', bpy.data.grease_pencils[self.gp_name], 'layers', text='layer') if not self.gp_layer: return layout.prop(self, 'gp_selected_frame_mode', expand=True) gp_layer = bpy.data.grease_pencils[self.gp_name].layers[self.gp_layer] frame_data = None if self.gp_selected_frame_mode == 'active_frame': frame_data = gp_layer.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 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_name', self, 'type_collection_name', text='name', icon='OBJECT_DATA') elif self.Mode == 'texts': layout.prop_search(self, 'text_name', bpy.data, 'texts', text='name') elif self.Mode == 'images': layout.prop_search(self, 'image_name', bpy.data, 'images', text='name') if self.image_name: layout.prop(self, 'pass_pixels', text='pixels') # size ? new socket outputting [w/h] elif self.Mode == 'grease_pencils': self.draw_gp_options(context, layout) def sv_init(self, context): self.outputs.new('SvStringsSocket', "Objects") self.width = 210 self.Type = 'MESH' # helps init the custom object prop_search def process(self): output_socket = self.outputs['Objects'] if not output_socket.is_linked: return data_list = getattr(bpy.data, self.Mode) if self.Mode == 'objects': if self.object_name: output_socket.sv_set([data_list[self.object_name]]) else: output_socket.sv_set( [i for i in data_list if i.type == self.Type]) elif self.Mode == 'texts': if self.text_name: # print(repr(data_list[self.text_name].as_string())) output_socket.sv_set([[data_list[self.text_name].as_string()]]) else: output_socket.sv_set(data_list[:]) elif self.Mode == 'images': if self.image_name: img = data_list[self.image_name] if self.pass_pixels: output_socket.sv_set([[img.pixels[:]]]) else: output_socket.sv_set([img]) else: output_socket.sv_set(data_list[:]) elif self.Mode == 'grease_pencils': # candidate for refactor if self.gp_name and self.gp_layer: GP_and_layer = data_list[self.gp_name].layers[self.gp_layer] 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, GP_and_layer) strokes = GP_and_layer.frames[key].strokes else: strokes = GP_and_layer.active_frame.strokes if not strokes: return if self.gp_pass_points: output_socket.sv_set([[p.co[:] for p in s.points] for s in strokes]) else: output_socket.sv_set(strokes) else: if self.gp_frame_pick: idx_from_frame_pick = int( self.gp_frame_pick.split(' | ')[0]) frame_data = GP_and_layer.frames[idx_from_frame_pick] if frame_data: if self.gp_pass_points: output_socket.sv_set( [[p.co[:] for p in s.points] for s in frame_data.strokes]) else: output_socket.sv_set(strokes) else: output_socket.sv_set(data_list[:])
class SvMatrixTrackToNode(bpy.types.Node, SverchCustomTreeNode): ''' Construct a Matrix from arbitrary Track and Up vectors ''' bl_idname = 'SvMatrixTrackToNode' bl_label = 'Matrix Track To' bl_icon = 'OUTLINER_OB_EMPTY' TUA = ["X Y", "X Z", "Y X", "Y Z", "Z X", "Z Y"] tu_axes: EnumProperty( name="Track/Up Axes", description= "Select which two of the XYZ axes to be the Track and Up axes", items=e(TUA), default=TUA[0], update=updateNode) normalize: BoolProperty(name="Normalize Vectors", description="Normalize the output X,Y,Z vectors", default=True, update=updateNode) origin: FloatVectorProperty( name='Location', description="The location component of the output matrix", default=(0, 0, 0), update=updateNode) scale: FloatVectorProperty( name='Scale', description="The scale component of the output matrix", default=(1, 1, 1), update=updateNode) vA: FloatVectorProperty(name='A', description="A direction", default=(1, 0, 0), update=updateNode) vB: FloatVectorProperty(name='B', description='B direction', default=(0, 1, 0), update=updateNode) TUM = ["A B", "A -B", "-A B", "-A -B", "B A", "B -A", "-B A", "-B -A"] tu_mapping: EnumProperty( name="Track/Up Mapping", description= "Map the Track and Up vectors to one of the two inputs or their negatives", items=e(TUM), default=TUM[0], update=updateNode) def sv_init(self, context): self.inputs.new('VerticesSocket', "Location").prop_name = "origin" # L self.inputs.new('VerticesSocket', "Scale").prop_name = "scale" # S self.inputs.new('VerticesSocket', "A").prop_name = "vA" # A self.inputs.new('VerticesSocket', "B").prop_name = "vB" # B self.outputs.new('MatrixSocket', "Matrix") self.outputs.new('VerticesSocket', "X") self.outputs.new('VerticesSocket', "Y") self.outputs.new('VerticesSocket', "Z") def split_columns(self, panel, ratios, aligns): """ Splits the given panel into columns based on the given set of ratios. e.g ratios = [1, 2, 1] or [.2, .3, .2] etc Note: The sum of all ratio numbers doesn't need to be normalized """ col2 = panel cols = [] ns = len(ratios) - 1 # number of splits for n in range(ns): n1 = ratios[n] # size of the current column n2 = sum(ratios[n + 1:]) # size of all remaining columns p = n1 / (n1 + n2 ) # percentage split of current vs remaning columns # print("n = ", n, " n1 = ", n1, " n2 = ", n2, " p = ", p) split = col2.split(percentage=p, align=aligns[n]) col1 = split.column(align=True) col2 = split.column(align=True) cols.append(col1) cols.append(col2) return cols def draw_buttons(self, context, layout): row = layout.column().row() cols = self.split_columns(row, [1, 1], [True, True]) cols[0].prop(self, "tu_axes", text="") cols[1].prop(self, "tu_mapping", text="") def draw_buttons_ext(self, context, layout): layout.prop(self, "normalize") def orthogonalizeXY(self, X, Y): # keep X, recalculate Z form X&Y then Y Z = X.cross(Y) Y = Z.cross(X) return X, Y, Z def orthogonalizeXZ(self, X, Z): # keep X, recalculate Y form Z&X then Z Y = Z.cross(X) Z = X.cross(Y) return X, Y, Z def orthogonalizeYX(self, Y, X): # keep Y, recalculate Z form X&Y then X Z = X.cross(Y) X = Y.cross(Z) return X, Y, Z def orthogonalizeYZ(self, Y, Z): # keep Y, recalculate X form Y&Z then Z X = Y.cross(Z) Z = X.cross(Y) return X, Y, Z def orthogonalizeZX(self, Z, X): # keep Z, recalculate Y form Z&X then X Y = Z.cross(X) X = Y.cross(Z) return X, Y, Z def orthogonalizeZY(self, Z, Y): # keep Z, recalculate X form Y&Z then Y X = Y.cross(Z) Y = Z.cross(X) return X, Y, Z def orthogonalizer(self): order = self.tu_axes.replace(" ", "") orthogonalizer = eval("self.orthogonalize" + order) return lambda T, U: orthogonalizer(T, U) def process(self): outputs = self.outputs # return if no outputs are connected if not any(s.is_linked for s in outputs): return # input values lists inputs = self.inputs input_locations = inputs["Location"].sv_get()[0] input_scales = inputs["Scale"].sv_get()[0] input_vAs = inputs["A"].sv_get()[0] input_vBs = inputs["B"].sv_get()[0] locations = [Vector(i) for i in input_locations] scales = [Vector(i) for i in input_scales] vAs = [Vector(i) for i in input_vAs] vBs = [Vector(i) for i in input_vBs] params = match_long_repeat([locations, scales, vAs, vBs]) orthogonalize = self.orthogonalizer() mT, mU = self.tu_mapping.split(" ") xList = [] # ortho-normal X vector list yList = [] # ortho-normal Y vector list zList = [] # ortho-normal Z vector list matrixList = [] for L, S, A, B in zip(*params): T = eval(mT) # map T to one of A, B or its negative U = eval(mU) # map U to one of A, B or its negative X, Y, Z = orthogonalize(T, U) if self.normalize: X.normalize() Y.normalize() Z.normalize() # prepare the Ortho-Normalized outputs if outputs["X"].is_linked: xList.append([X.x, X.y, X.z]) if outputs["Y"].is_linked: yList.append([Y.x, Y.y, Y.z]) if outputs["Z"].is_linked: zList.append([Z.x, Z.y, Z.z]) # composite matrix: M = T * R * S (Tanslation x Rotation x Scale) m = [[X.x * S.x, Y.x * S.y, Z.x * S.z, L.x], [X.y * S.x, Y.y * S.y, Z.y * S.z, L.y], [X.z * S.x, Y.z * S.y, Z.z * S.z, L.z], [0, 0, 0, 1]] matrixList.append(Matrix(m)) outputs["Matrix"].sv_set(matrixList) outputs["X"].sv_set([xList]) outputs["Y"].sv_set([yList]) outputs["Z"].sv_set([zList])
class SvMatrixTrackToNode(bpy.types.Node, SverchCustomTreeNode): """ Triggers: Align to Track & Up vectors Tooltip: Construct a Matrix from arbitrary Track and Up vectors """ bl_idname = 'SvMatrixTrackToNode' bl_label = 'Matrix Track To' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_MATRIX_TRACK_TO' TUA = ["X Y", "X Z", "Y X", "Y Z", "Z X", "Z Y"] tu_axes: EnumProperty( name="Track/Up Axes", description= "Select which two of the XYZ axes to be the Track and Up axes", items=e(TUA), default="X_Y", update=updateNode) normalize: BoolProperty(name="Normalize Vectors", description="Normalize the output X,Y,Z vectors", default=True, update=updateNode) origin: FloatVectorProperty( name='Location', description="The location component of the output matrix", default=(0, 0, 0), update=updateNode) scale: FloatVectorProperty( name='Scale', description="The scale component of the output matrix", default=(1, 1, 1), update=updateNode) vA: FloatVectorProperty(name='A', description="A direction", default=(1, 0, 0), update=updateNode) vB: FloatVectorProperty(name='B', description='B direction', default=(0, 1, 0), update=updateNode) flat_output: BoolProperty( name="Flat output", description="Flatten output by list-joining level 1", default=True, update=updateNode) TUM = ["A B", "A -B", "-A B", "-A -B", "B A", "B -A", "-B A", "-B -A"] tu_mapping: EnumProperty( name="Track/Up Mapping", description= "Map the Track and Up vectors to one of the two inputs or their negatives", items=e(TUM), default="A_B", update=updateNode) def sv_init(self, context): self.inputs.new('SvVerticesSocket', "Location").prop_name = "origin" # L self.inputs.new('SvVerticesSocket', "Scale").prop_name = "scale" # S self.inputs.new('SvVerticesSocket', "A").prop_name = "vA" # A self.inputs.new('SvVerticesSocket', "B").prop_name = "vB" # B self.outputs.new('SvMatrixSocket', "Matrix") self.outputs.new('SvVerticesSocket', "X") self.outputs.new('SvVerticesSocket', "Y") self.outputs.new('SvVerticesSocket', "Z") def split_columns(self, panel, ratios, aligns): """ Splits the given panel into columns based on the given set of ratios. e.g ratios = [1, 2, 1] or [.2, .3, .2] etc Note: The sum of all ratio numbers doesn't need to be normalized """ col2 = panel cols = [] ns = len(ratios) - 1 # number of splits for n in range(ns): n1 = ratios[n] # size of the current column n2 = sum(ratios[n + 1:]) # size of all remaining columns p = n1 / (n1 + n2 ) # percentage split of current vs remaning columns # print("n = ", n, " n1 = ", n1, " n2 = ", n2, " p = ", p) split = col2.split(factor=p, align=aligns[n]) col1 = split.column(align=True) col2 = split.column(align=True) cols.append(col1) cols.append(col2) return cols def draw_buttons(self, context, layout): row = layout.column().row() cols = self.split_columns(row, [1, 1], [True, True]) cols[0].prop(self, "tu_axes", text="") cols[1].prop(self, "tu_mapping", text="") def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, "normalize") layout.prop(self, "flat_output") def rclick_menu(self, context, layout): layout.prop_menu_enum(self, "tu_axes") layout.prop_menu_enum(self, "tu_mapping") layout.prop(self, "normalize") layout.prop(self, "flat_output", text="Flat Output", expand=False) def orthogonalizeXY(self, X, Y): # keep X, recalculate Z form X&Y then Y Z = X.cross(Y) Y = Z.cross(X) return X, Y, Z def orthogonalizeXZ(self, X, Z): # keep X, recalculate Y form Z&X then Z Y = Z.cross(X) Z = X.cross(Y) return X, Y, Z def orthogonalizeYX(self, Y, X): # keep Y, recalculate Z form X&Y then X Z = X.cross(Y) X = Y.cross(Z) return X, Y, Z def orthogonalizeYZ(self, Y, Z): # keep Y, recalculate X form Y&Z then Z X = Y.cross(Z) Z = X.cross(Y) return X, Y, Z def orthogonalizeZX(self, Z, X): # keep Z, recalculate Y form Z&X then X Y = Z.cross(X) X = Y.cross(Z) return X, Y, Z def orthogonalizeZY(self, Z, Y): # keep Z, recalculate X form Y&Z then Y X = Y.cross(Z) Y = Z.cross(X) return X, Y, Z def orthogonalizer(self): order = self.tu_axes.replace("_", "") orthogonalizer = eval("self.orthogonalize" + order) return lambda T, U: orthogonalizer(T, U) def matrix_track_to(self, params, mT, mU, orthogonalize, gates): x_list = [] # ortho-normal X vector list y_list = [] # ortho-normal Y vector list z_list = [] # ortho-normal Z vector list matrix_list = [] print(len(params)) for L, S, A, B in zip(*params): T = eval(mT) # map T to one of A, B or its negative U = eval(mU) # map U to one of A, B or its negative X, Y, Z = orthogonalize(T, U) if gates[4]: X.normalize() Y.normalize() Z.normalize() # prepare the Ortho-Normalized outputs if gates[1]: x_list.append([X.x, X.y, X.z]) if gates[2]: y_list.append([Y.x, Y.y, Y.z]) if gates[3]: z_list.append([Z.x, Z.y, Z.z]) if gates[0]: # composite matrix: M = T * R * S (Tanslation x Rotation x Scale) m = [[X.x * S.x, Y.x * S.y, Z.x * S.z, L.x], [X.y * S.x, Y.y * S.y, Z.y * S.z, L.y], [X.z * S.x, Y.z * S.y, Z.z * S.z, L.z], [0, 0, 0, 1]] matrix_list.append(Matrix(m)) return matrix_list, x_list, y_list, z_list def process(self): outputs = self.outputs # return if no outputs are connected if not any(s.is_linked for s in outputs): return # input values lists inputs = self.inputs params = match_long_repeat( [Vector_generate(s.sv_get()) for s in inputs]) orthogonalize = self.orthogonalizer() mT, mU = self.tu_mapping.split("_") gates = [s.is_linked for s in outputs] gates.append(self.normalize) x_lists = [] # ortho-normal X vector list y_lists = [] # ortho-normal Y vector list z_lists = [] # ortho-normal Z vector list matrix_lists = [] if self.flat_output: m_add, x_add, y_add, z_add = matrix_lists.extend, x_lists.extend, y_lists.extend, z_lists.extend else: m_add, x_add, y_add, z_add = matrix_lists.append, x_lists.append, y_lists.append, z_lists.append for par in zip(*params): matrix_list, x_list, y_list, z_list = self.matrix_track_to( match_long_repeat(par), mT, mU, orthogonalize, gates) m_add(matrix_list) x_add([x_list]) y_add(y_list) z_add(z_list) outputs["Matrix"].sv_set(matrix_lists) outputs["X"].sv_set(x_lists) outputs["Y"].sv_set(y_lists) outputs["Z"].sv_set(z_lists)
class SvBMOpsNodeMK2(bpy.types.Node, SverchCustomTreeNode): ''' BMesh Ops ''' bl_idname = 'SvBMOpsNodeMK2' bl_label = 'BMesh Ops 2' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_BMESH_OPS' PV = [ 'remove_doubles(bm,verts=e,dist=v[0])', 'collapse(bm,edges=e,uvs=v[0])', 'unsubdivide(bm,verts=e,iterations=v[0])', 'holes_fill(bm,edges=e,sides=v[0])', 'dissolve_faces(bm,faces=e,use_verts=v[0])', 'connect_verts_concave(bm,faces=e)', 'recalc_face_normals(bm,faces=e)', 'rotate_edges(bm,edges=e,use_ccw=v[0])', 'connect_verts_nonplanar(bm,angle_limit=v[0],faces=e)', 'triangulate(bm,faces=e,quad_method=v[0],ngon_method=v[1])', 'dissolve_edges(bm,edges=e,use_verts=v[0],use_face_split=v[1])', 'dissolve_verts(bm,verts=e,use_face_split=v[0],use_boundary_tear=v[1])', 'grid_fill(bm,edges=e,mat_nr=v[0],use_smooth=v[1],use_interp_simple=v[2])', 'poke(bm,faces=e,offset=v[0],center_mode=v[1],use_relative_offset=v[2])', 'bridge_loops(bm,edges=e,use_pairs=v[0],use_cyclic=v[1],use_merge=v[2],merge_factor=v[3],twist_offset=v[4])', 'smooth_vert(bm,verts=e,factor=v[0],mirror_clip_x=v[1],mirror_clip_y=v[2],mirror_clip_z=v[3],clip_dist=v[4],use_axis_x=v[5],use_axis_y=v[6],use_axis_z=v[7])', 'join_triangles(bm,faces=e,cmp_seam=v[0],cmp_sharp=v[1],cmp_uvs=v[2],cmp_vcols=v[3],cmp_materials=v[4],angle_face_threshold=v[5],angle_shape_threshold=v[6])', 'subdivide_edgering(bm,edges=e,interp_mode=v[0],smooth=v[1],cuts=v[2],profile_shape=v[3],profile_shape_factor=v[4])', 'inset_individual(bm,faces=e,thickness=v[0],depth=v[1],use_even_offset=v[2],use_interpolate=v[3],use_relative_offset=v[4])', 'inset_region(bm,faces=e,use_boundary=v[0],use_even_offset=v[1],use_interpolate=v[2],use_relative_offset=v[3],use_edge_rail=v[4],thickness=v[5],depth=v[6],use_outset=v[7])', ] oper: EnumProperty(name="BMop", default=PV[0], items=e(PV), update=updateNode) V0: FloatProperty(name='V0', default=0, update=updateNode) V1: FloatProperty(name='V1', default=0, update=updateNode) V2: FloatProperty(name='V2', default=0, update=updateNode) V3: FloatProperty(name='V3', default=0, update=updateNode) V4: FloatProperty(name='V4', default=0, update=updateNode) V5: FloatProperty(name='V5', default=0, update=updateNode) V6: FloatProperty(name='V6', default=0, update=updateNode) V7: FloatProperty(name='V7', default=0, update=updateNode) def sv_init(self, context): si = self.inputs.new si('SvStringsSocket', 'bmesh_list') si('SvStringsSocket', 'BM_element(e)') self.outputs.new('SvStringsSocket', 'bmesh_list') def draw_buttons(self, context, layout): layout.prop(self, "oper", text="Get") for i in range(self.oper.count("=v[")): layout.prop(self, "V" + str(i), text="v" + str(i)) def process(self): if not self.outputs['bmesh_list'].is_linked: return bml, e = self.inputs obj = bml.sv_get() v = [ self.V0, self.V1, self.V2, self.V3, self.V4, self.V5, self.V6, self.V7 ] outp = [] op = "bmesh.ops." + self.oper if e.is_linked: element = e.sv_get() for bm, e in zip(obj, element): exec(op) outp.append(bm.copy()) bm.free() else: if "verts=e" in op: cur = "verts" elif "edges=e" in op: cur = "edges" elif "faces=e" in op: cur = "faces" for bm in obj: e = getattr(bm, cur) exec(op) outp.append(bm.copy()) bm.free() self.outputs['bmesh_list'].sv_set(outp)
class SvBMOpsNode(bpy.types.Node, SverchCustomTreeNode): ''' BMesh Ops ''' bl_idname = 'SvBMOpsNode' bl_label = 'bmesh_ops' bl_icon = 'OUTLINER_OB_EMPTY' PV = [ 'remove_doubles(bm,verts=Vidx,dist=v[0])', 'collapse(bm,edges=Eidx)', 'unsubdivide(bm,verts=Vidx,iterations=v[0])', 'holes_fill(bm,edges=Eidx,sides=v[0])', 'bridge_loops(bm,edges=Eidx,use_pairs=b[0],use_cyclic=b[1],use_merge=b[2],merge_factor=v[0],twist_offset=v[1])', 'smooth_vert(bm,verts=Vidx,factor=v[0],mirror_clip_x=b[0],mirror_clip_y=b[1],mirror_clip_z=b[2],clip_dist=v[1],use_axis_x=b[3],use_axis_y=b[4],use_axis_z=b[5])', 'dissolve_verts(bm,verts=Vidx,use_face_split=b[0],use_boundary_tear=b[1])', 'dissolve_edges(bm,edges=Eidx,use_verts=b[0],use_face_split=b[1])', 'dissolve_faces(bm,faces=Pidx,use_verts=b[0])', 'triangulate(bm,faces=Pidx,quad_method=v[0],ngon_method=v[1])', 'join_triangles(bm,faces=Pidx,cmp_sharp=b[0],cmp_uvs=b[1],cmp_vcols=b[2],cmp_materials=b[3],limit=v[0])', 'connect_verts_concave(bm,faces=Pidx)', 'connect_verts_nonplanar(bm,angle_limit=v[0],faces=Pidx)', 'subdivide_edgering(bm,edges=Eidx,interp_mode=v[0],smooth=v[1],cuts=v[2],profile_shape=v[3],profile_shape_factor=v[4])', 'inset_individual(bm,faces=Pidx,thickness=v[0],depth=v[1],use_even_offset=b[0],use_interpolate=b[1],use_relative_offset=b[2])', 'grid_fill(bm,edges=Eidx,mat_nr=v[0],use_smooth=b[0],use_interp_simple=b[1])', 'edgenet_fill(bm, edges=Eidx, mat_nr=v[0], use_smooth=b[0], sides=v[1])', 'rotate_edges(bm, edges=Eidx, use_ccw=b[0])' ] oper = EnumProperty(name="BMop", default=PV[0], items=e(PV), update=updateNode) def sv_init(self, context): si = self.inputs.new si('StringsSocket', 'Objects') si('StringsSocket', 'Value(v)') si('StringsSocket', 'Bool(b)') si('StringsSocket', 'idx') self.outputs.new('VerticesSocket', 'Verts') self.outputs.new('StringsSocket', 'Edges') self.outputs.new('StringsSocket', 'Polys') def draw_buttons(self, context, layout): layout.prop(self, "oper", "Get") def process(self): if not self.outputs['Verts'].is_linked: return si = self.inputs obj = si['Objects'].sv_get() obl = len(obj) if obl > 1: b = (si['Bool(b)'].sv_get([[0, 0, 0, 0, 0, 0]]) * obl)[:obl] v = (si['Value(v)'].sv_get([[1, 1, 1, 1, 1]]) * obl)[:obl] idx = (si['idx'].sv_get([[0]]) * obl)[:obl] else: b = si['Bool(b)'].sv_get([[0, 0, 0, 0, 0, 0]]) v = si['Value(v)'].sv_get([[1, 1, 1, 1, 1]]) idx = si['idx'].sv_get([[0]]) Sidx = si['idx'].is_linked outv = [] oute = [] outp = [] op = "bmesh.ops." + self.oper if "verts=Vidx" in op: cur = 1 elif "edges=Eidx" in op: cur = 2 elif "faces=Pidx" in op: cur = 3 for ob, b, v, idx in zip(obj, b, v, idx): bm = bmesh.new() bm.from_mesh(ob.data) if Sidx: if cur == 1: bm.verts.ensure_lookup_table() Vidx = [bm.verts[i] for i in idx] elif cur == 2: bm.edges.ensure_lookup_table() Eidx = [bm.edges[i] for i in idx] elif cur == 3: bm.faces.ensure_lookup_table() Pidx = [bm.faces[i] for i in idx] else: Vidx, Eidx, Pidx = bm.verts, bm.edges, bm.faces exec(op) outv.append([i.co[:] for i in bm.verts]) oute.append([[i.index for i in e.verts] for e in bm.edges]) outp.append([[i.index for i in p.verts] for p in bm.faces]) bm.free() self.outputs['Verts'].sv_set(outv) self.outputs['Edges'].sv_set(oute) self.outputs['Polys'].sv_set(outp) def update_socket(self, context): self.update()
class SvBMVertsNode(bpy.types.Node, SverchCustomTreeNode): ''' BMesh Verts ''' bl_idname = 'SvBMVertsNode' bl_label = 'bmesh_props' bl_icon = 'OUTLINER_OB_EMPTY' Modes = ['verts', 'faces', 'edges'] Mod = EnumProperty(name="getmodes", default="verts", items=e(Modes), update=updateNode) a = ['hide', 'select'] PV = a + [ 'is_manifold', 'is_wire', 'is_boundary', 'calc_shell_factor()', 'calc_edge_angle(-1)' ] PF = a + ['calc_area()', 'calc_perimeter()', 'material_index', 'smooth'] PE = a + [ 'calc_face_angle()', 'calc_face_angle_signed()', 'calc_length()', 'is_boundary', 'is_contiguous', 'is_convex', 'is_manifold', 'is_wire', 'seam' ] verts = EnumProperty(name="Vprop", default="is_manifold", items=e(PV), update=updateNode) faces = EnumProperty(name="Fprop", default="select", items=e(PF), update=updateNode) edges = EnumProperty(name="Eprop", default="select", items=e(PE), update=updateNode) def sv_init(self, context): si = self.inputs.new si('StringsSocket', 'Objects') si('VerticesSocket', 'Vert') si('StringsSocket', 'Edge') si('StringsSocket', 'Poly') self.outputs.new('StringsSocket', 'Value') def draw_buttons(self, context, layout): layout.prop(self, "Mod", "Get") layout.prop(self, self.Mod, "") def process(self): Val = [] siob = self.inputs['Objects'] v, e, p = self.inputs['Vert'], self.inputs['Edge'], self.inputs['Poly'] if siob.is_linked: obj = siob.sv_get() for OB in obj: bm = bmesh.new() bm.from_mesh(OB.data) get_value(self, bm, Val) bm.free() if v.is_linked: sive, sied, sipo = match_long_repeat( [v.sv_get(), e.sv_get([[]]), p.sv_get([[]])]) for i in zip(sive, sied, sipo): bm = bmesh_from_pydata(i[0], i[1], i[2]) get_value(self, bm, Val) bm.free() self.outputs['Value'].sv_set(Val) def update_socket(self, context): self.update()
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)
class SvDupliInstancesLite(bpy.types.Node, SverchCustomTreeNode, SvViewerNode): """ Triggers: Fast duplication Tooltip: Create Instances from parent object + child """ bl_idname = 'SvDupliInstancesLite' bl_label = 'Dupli Instancer Lite' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_DUPLI_INSTANCER' def update_sockets(self, context): if self.mode == 'FACES' and self.scale: self.inputs['Scale'].hide_safe = False else: self.inputs['Scale'].hide_safe = True updateNode(self, context) scale: BoolProperty(default=False, description="scale children", update=update_sockets) scale_factor: FloatProperty(default=1, name='Scale', description="Children scale factor", update=updateNode) align: BoolProperty(default=False, description="align with vertex normal", update=updateNode) show_instancer_for_viewport: BoolProperty( default=False, description="Show instancer in viewport", update=updateNode) show_instancer_for_render: BoolProperty( default=False, description="Show instancer in render", update=updateNode) show_base_child: BoolProperty(name='Show base child', default=True, description="Hide base object in viewport", update=updateNode) auto_release: BoolProperty( name='Auto Release', description='Remove childs not called by this node', update=updateNode) modes = [("VERTS", "Verts", "On vertices. Only Translation is used", "", 1), ("FACES", "Polys", "On polygons. Translation, Rotation and Scale supported", "", 2)] T = [ ("POS_X", "X", "", "", 1), ("POS_Y", "Y", "", "", 2), ("POS_Z", "Z", "", "", 3), ("NEG_X", "-X", "", "", 4), ("NEG_Y", "-Y", "", "", 5), ("NEG_Z", "-Z", "", "", 6), ] mode: EnumProperty(name='Mode', items=modes, default='VERTS', update=update_sockets) U = ['X', 'Y', 'Z'] track: EnumProperty(name="track", default=T[0][0], items=T, update=updateNode) up: EnumProperty(name="up", default=U[2], items=e(U), update=updateNode) def sv_init(self, context): self.width = 160 self.inputs.new("SvObjectSocket", "Parent") self.inputs.new("SvObjectSocket", "Child") self.sv_new_input("SvStringsSocket", "Scale", hide_safe=True, prop_name='scale_factor') self.outputs.new("SvObjectSocket", "Parent") self.outputs.new("SvObjectSocket", "Child") def draw_buttons(self, context, layout): layout.row(align=True).prop(self, "mode", expand=True) col = layout.column(align=True) row = col.row(align=True) row.label(text='Instancer') row.prop( self, 'show_instancer_for_viewport', text='', toggle=True, icon= f"RESTRICT_VIEW_{'OFF' if self.show_instancer_for_viewport else 'ON'}" ) row.prop( self, 'show_instancer_for_render', text='', toggle=True, icon= f"RESTRICT_RENDER_{'OFF' if self.show_instancer_for_render else 'ON'}" ) row = col.row(align=True) row.label(text='Child') row.prop(self, 'show_base_child', text='', toggle=True, icon=f"HIDE_{'OFF' if self.show_base_child else 'ON'}") if self.mode == 'FACES': row.prop(self, 'scale', text='', icon_value=custom_icon('SV_SCALE'), toggle=True) else: row.prop(self, 'align', text='', icon='MOD_NORMALEDIT', toggle=True) row.prop(self, 'auto_release', text='', toggle=True, icon='UNLINKED') if self.mode == 'VERTS' and self.align: layout.prop(self, 'track') layout.prop(self, 'up') def set_parent_props(self, parent, scale): parent.instance_type = self.mode parent.use_instance_vertices_rotation = self.align parent.use_instance_faces_scale = self.scale parent.show_instancer_for_viewport = self.show_instancer_for_viewport parent.show_instancer_for_render = self.show_instancer_for_render parent.instance_faces_scale = scale def set_child_props(self, parent, child): child.parent = parent child.track_axis = self.track child.up_axis = self.up child.hide_set(not self.show_base_child) def process(self): print(True) parent_objects = self.inputs['Parent'].sv_get(deepcopy=False) child_objects = self.inputs['Child'].sv_get(deepcopy=False) scale = self.inputs['Scale'].sv_get(deepcopy=False) if not child_objects or not parent_objects: return if len(parent_objects) == 1 or len(child_objects) == 1: parent = parent_objects[0] self.set_parent_props(parent, scale[0][0]) childs_name = [] for child in child_objects: self.set_child_props(parent, child) childs_name.append(child.name) if self.auto_release: auto_release(parent.name, childs_name) else: childs_name = {p.name: [] for p in parent_objects} if len(scale) == 1: scale_f = scale[0] else: scale_f = [sc[0] for sc in scale] for child, parent, sc in zip(child_objects, cycle(parent_objects), cycle(scale_f)): self.set_parent_props(parent, sc) self.set_child_props(parent, child) childs_name[parent.name].append(child.name) if self.auto_release: for p in parent_objects: auto_release(p.name, childs_name[p.name]) self.outputs['Parent'].sv_set(parent_objects) self.outputs['Child'].sv_set(child_objects)