def draw_buttons(self, ctx, layout): layout.prop(self, 'current_op', text="", icon_value=custom_icon("SV_FUNCTION")) layout.prop(self, 'clamp_output')
def layout_draw_categories(layout, node_details): for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] # this is a node bl_idname that can be registered but shift+A can drop it from showing. if bl_idname == 'ScalarMathNode': continue node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute',icon_value=custom_icon('SV_REROUTE')) else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def draw_buttons_ext(self, ctx, layout): layout.row().prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION")) layout.row().prop(self, 'input_mode_one', text="input 1") if len(self.inputs) == 2: layout.row().prop(self, 'input_mode_two', text="input 2") if self.current_op not in ['GCD', 'ROUND-N']: layout.row().prop(self, 'list_match', expand=False) layout.prop(self, "output_numpy", expand=False)
def draw_buttons(self, ctx, layout): row = layout.row(align=True) row.prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION")) if self.current_op == 'to_string': layout.prop(self, 'level')
def draw_buttons_ext(self, ctx, layout): layout.row().prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION")) layout.row().prop(self, 'input_mode_one', text="input 1") if len(self.inputs) == 2: layout.row().prop(self, 'input_mode_two', text="input 2")
def draw_buttons_ext(self, ctx, layout): layout.prop(self, 'current_op', text="", icon_value=custom_icon("SV_FUNCTION")) layout.prop(self, 'list_match', expand=False) layout.prop(self, 'clamp_output') layout.prop(self, 'output_numpy', expand=False)
def icon(display_icon): '''returns empty dict if show_icons is False, else the icon passed''' kws = {} if menu_prefs.get('show_icons'): if display_icon.startswith('SV_'): kws = {'icon_value': custom_icon(display_icon)} else: kws = {'icon': display_icon} return kws
def draw_buttons_ext(self, ctx, layout): layout.prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION")) layout.label(text="Implementation:") layout.prop(self, "implementation", expand=True) if self.implementation == "NumPy": layout.prop(self, "output_numpy", toggle=False)
def draw_buttons_ext(self, ctx, layout): layout.row().prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION")) layout.label(text="Wave interpolation options:") if self.current_op == 'Custom': layout.row().prop(self, "wave_interp_mode", expand=True) layout.row().prop(self, "knot_mode", expand=False) layout.prop(self, "list_match", expand=False) layout.prop(self, "output_numpy", expand=False)
def node_icon(node_ref): '''returns empty dict if show_icons is False, else the icon passed''' if not menu_prefs.get('show_icons'): return {} else: if hasattr(node_ref, 'sv_icon'): iconID = custom_icon(node_ref.sv_icon) return {'icon_value': iconID} if iconID else {} elif hasattr(node_ref, 'bl_icon') and node_ref.bl_icon != 'OUTLINER_OB_EMPTY': iconID = node_ref.bl_icon return {'icon': iconID} if iconID else {} else: return {}
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 draw_buttons(self, context, layout): self.draw_viewer_properties(layout) layout.row(align=True).prop(self, "mode", expand=True) col = layout.column(align=True) if self.mode == 'FACES': 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) row.prop(self, 'auto_release', text='', toggle=True, icon='UNLINKED') row.prop(self, 'ignore_base_offset', text='', toggle=True, icon='TRANSFORM_ORIGINS')
def layout_draw_categories(layout, category_name, node_details): global menu_class_by_title for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] if is_submenu_call(bl_idname): submenu_title = get_submenu_call_name(bl_idname) menu_title = compose_submenu_name(category_name, bl_idname) menu_class = menu_class_by_title[menu_title] layout.menu(menu_class.__name__, text=submenu_title) continue # this is a node bl_idname that can be registered but shift+A can drop it from showing. if bl_idname == 'ScalarMathNode': continue node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute', icon_value=custom_icon('SV_REROUTE')) else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def draw_buttons(self, ctx, layout): row = layout.row(align=True) row.prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION"))
# # ##### END GPL LICENSE BLOCK ##### from math import radians import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, list_match_func, list_match_modes, sv_zip from sverchok.ui.sv_icons import custom_icon from sverchok.utils.modules.vertex_utils import center, center_of_many from sverchok.utils.listutils import lists_flat from sverchok.utils.pentagon_geom import generate_penta_grid, generate_penta_tiles, pentagon_dict GRID_TYPE_ITEMS = [ ("PENTAGON1", "Type 1 2-tile", "", custom_icon("SV_PENTAGON_1_1"), 2), ("TYPE_1_4", "Type 1 4-tile", "", custom_icon("SV_PENTAGON_1_2"), 3), ("PENTAGON2", "Type 1 2-tile X", "", custom_icon("SV_PENTAGON_1_3"), 4), ("PENTAGON3", "Type 1 2-tile 2", "", custom_icon("SV_PENTAGON_1_4"), 5), ("TYPE_2_1", "Type 2_1", "", custom_icon("SV_PENTAGON_2"), 7), ("PENTAGON_TYPE_3", "Type 3 3-tile", "", custom_icon("SV_PENTAGON_3"), 8), ("PENTAGON_TYPE_4", "Type 4 4-tile", "", custom_icon("SV_PENTAGON_4"), 9), ("PENTAGON_TYPE_5", "Type 5 6-tile", "", custom_icon("SV_PENTAGON_5"), 10), ("PENTAGON14", "Type 14", "", custom_icon("SV_PENTAGON_14"), 14), ("PENTAGON15", "Type 15", "", custom_icon("SV_PENTAGON_15"), 15) ] ALIGN_ITEMS = [("X", "X", "Align tile primitives to X axis", custom_icon("SV_PENTAGON_X_ROT"), 0), ("Y", "Y", "Align tile primitives to Y axis", custom_icon("SV_PENTAGON_Y_ROT"), 1),
# ##### END GPL LICENSE BLOCK ##### import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from math import sqrt, sin, cos, radians from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, match_long_repeat from sverchok.ui.sv_icons import custom_icon from sverchok.utils.geom import circle from sverchok.utils.sv_mesh_utils import mesh_join from sverchok.nodes.modifier_change.remove_doubles import remove_doubles grid_layout_items = [ ("RECTANGLE", "Rectangle", "", custom_icon("SV_HEXA_GRID_RECTANGLE"), 0), ("TRIANGLE", "Triangle", "", custom_icon("SV_HEXA_GRID_TRIANGLE"), 1), ("DIAMOND", "Diamond", "", custom_icon("SV_HEXA_GRID_DIAMOND"), 2), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXA_GRID_HEXAGON"), 3)] grid_type_items = [ ("TRIANGLE", "Triangle", "", custom_icon("SV_TRIANGLE"), 0), ("SQUARE", "Square", "", custom_icon("SV_SQUARE"), 1), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXAGON"), 2)] size_mode_items = [ ("RADIUS", "Radius", "Define polygon by its radius", custom_icon("SV_RAD"), 0), ("SIDE", "Side", "Define polygon by its side", custom_icon("SV_SIDE"), 1)] def triang_layout(settings, pol_type): '''Define triangular layout''' _, _, level = settings
class SvTriangleNode(bpy.types.Node, SverchCustomTreeNode): """ Triggers: from vertices, sides or angles Tooltip: create triangle from various combinations of vertices, sides length and angles """ bl_idname = "SvTriangleNode" bl_label = "Triangle" bl_icon = "GHOST_ENABLED" sv_icon = "SV_TRIANGLE_NODE" triangle_modes = [ ("A_c_Alpha_Beta", "A_c_Alpha_Beta", "", custom_icon("SV_TRIANGLE_ACALPHABETA"), 0), ("A_Bv_Alpha_Beta", "A_B_Alpha_Beta", "", custom_icon("SV_TRIANGLE_ABALPHABETA"), 1), ("A_b_c_Alpha", "A_b_c_Alpha", "", custom_icon("SV_TRIANGLE_ABCALPHA"), 2), ("A_Bv_b_Alpha", "A_B_b_Alpha", "", custom_icon("SV_TRIANGLE_ABBALPHA"), 3), ("A_as_b_c", "A_a_b_c", "", custom_icon("SV_TRIANGLE_AABC"), 4), ("A_Bv_as_b", "A_B_a_b", "", custom_icon("SV_TRIANGLE_ABAB"), 5), ("A_Bv_C", "A_B_C", "", custom_icon("SV_TRIANGLE_ABC"), 6) ] angle_units = [("Degrees", "Degrees", "", 0), ("Radians", "Radians", "", 1)] def update_sokets(self, context): si = self.inputs inputs = ['A', 'Bv', 'C', 'as', 'b', 'c', 'Alpha', 'Beta'] for idx, i in enumerate(inputs): if i in self.mode: if si[idx].hide_safe: si[idx].hide_safe = False else: si[idx].hide_safe = True updateNode(self, context) mode: EnumProperty(name="Mode", items=triangle_modes, default="A_Bv_C", update=update_sokets) angle_mode: EnumProperty(name="Angle Mode", items=angle_units, default="Degrees", update=update_sokets) v3_input_0: FloatVectorProperty(name='A', description='Vertice A of triangle', size=3, default=(0, 0, 0), update=updateNode) v3_input_1: FloatVectorProperty(name='B', description='Vertice B of triangle', size=3, default=(0.5, 0.5, 0), update=updateNode) v3_input_2: FloatVectorProperty(name='C', description='Vertice C of triangle', size=3, default=(1, 0, 0), update=updateNode) size_a: FloatProperty(name='a', description='Size side a', default=10.0, update=updateNode) size_b: FloatProperty(name='b', description='Size side b', default=10.0, update=updateNode) size_c: FloatProperty(name='c', description='Size side c', default=10.0, update=updateNode) alpha: FloatProperty(name='Alpha', description='Angle at vertice A', default=30.0, update=updateNode) beta: FloatProperty(name='Beta', description='Angle at vertice B', default=30.0, update=updateNode) list_match_global: EnumProperty( name="Match Global", description= "Behavior on different list lengths, multiple objects level", items=list_match_modes, default="REPEAT", update=updateNode) list_match_local: EnumProperty( name="Match Local", description="Behavior on different list lengths, object level", items=list_match_modes, default="REPEAT", update=updateNode) join_level: BoolProperty( name="Join Last level", description="Join (mesh join) last level of triangles", default=False, update=updateNode) flat_output: BoolProperty( name="Flat output", description="Flatten output by list-joining level 1", default=True, update=updateNode) rm_doubles: BoolProperty( name="Remove Doubles", description="Remove doubles of the joined triangles", default=True, update=updateNode) epsilon: FloatProperty(name='Tolerance', description='Removing Doubles Tolerance', default=1e-6, update=updateNode) def draw_buttons(self, context, layout): layout.prop(self, "mode", expand=False) def draw_buttons_ext(self, context, layout): '''draw buttons on the N-panel''' layout.prop(self, "mode", expand=False) layout.prop(self, "angle_mode", expand=False) layout.separator() layout.prop(self, "join_level", text="Join Last Level", expand=False) if self.join_level: layout.prop(self, "rm_doubles", expand=False) if self.rm_doubles: layout.prop(self, "epsilon", text="Merge Distance", expand=False) layout.prop(self, "flat_output", text="Flat Output", expand=False) layout.separator() layout.label(text="List Match:") layout.prop(self, "list_match_global", text="Global Match", expand=False) layout.prop(self, "list_match_local", text="Local Match", expand=False) def rclick_menu(self, context, layout): '''right click sv_menu items''' layout.prop_menu_enum(self, "mode", text="Mode") layout.prop_menu_enum(self, "angle_mode", text="Angle Units") layout.separator() layout.prop(self, "join_level", text="Join Last Level", expand=False) if self.join_level: layout.prop(self, "rm_doubles", expand=False) if self.rm_doubles: layout.prop(self, "epsilon", text="Merge Distance", expand=False) layout.prop(self, "flat_output", text="Flat Output", expand=False) layout.prop_menu_enum(self, "list_match_global", text="List Match Global") layout.prop_menu_enum(self, "list_match_local", text="List Match Local") def sv_init(self, context): sinw = self.inputs.new sinw('SvVerticesSocket', "A").prop_name = "v3_input_0" sinw('SvVerticesSocket', "B").prop_name = "v3_input_1" sinw('SvVerticesSocket', "C").prop_name = "v3_input_2" sinw('SvStringsSocket', "a").prop_name = "size_a" sinw('SvStringsSocket', "b").prop_name = "size_b" sinw('SvStringsSocket', "c").prop_name = "size_c" sinw('SvStringsSocket', "Alpha").prop_name = "alpha" sinw('SvStringsSocket', "Beta").prop_name = "beta" self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Edges") self.outputs.new('SvStringsSocket', "Faces") def main_func(self, params): out_verts, out_edges, out_faces = [], [], [] for A, B, C, a, b, c, alpha, beta in zip(*params): if self.angle_mode == 'Degrees': alpha = radians(alpha) beta = radians(beta) if self.mode == 'A_Bv_C': verts = [A, B, C] if self.mode == 'A_c_Alpha_Beta': verts = triang_A_c_Alpha_Beta(A, c, alpha, beta) elif self.mode == 'A_Bv_Alpha_Beta': verts = triang_A_B_Alpha_Beta(A, B, alpha, beta) elif self.mode == 'A_Bv_as_b': verts = triang_A_B_a_b(A, B, a, b) elif self.mode == 'A_as_b_c': verts = triang_A_a_b_c(A, a, b, c) elif self.mode == 'A_b_c_Alpha': verts = triang_A_b_c_Alpha(A, b, c, alpha) elif self.mode == 'A_Bv_b_Alpha': verts = triang_A_B_b_Alpha(A, B, b, alpha) out_verts.append(verts) out_edges.append([[0, 1], [1, 2], [2, 0]]) out_faces.append([[0, 1, 2]]) if self.join_level: out_verts, out_edges, out_faces = mesh_join( out_verts, out_edges, out_faces) if self.rm_doubles: out_verts, out_edges, out_faces = remove_doubles( out_verts, out_edges, out_faces, self.epsilon) out_verts, out_edges, out_faces = [out_verts], [out_edges ], [out_faces] return out_verts, out_edges, out_faces def process(self): # return if no outputs are connected if not any(s.is_linked for s in self.outputs): return out_verts = [] out_edges = [] out_faces = [] match_func = list_match_func[self.list_match_global] family = match_func([si.sv_get(default=[[]]) for si in self.inputs]) for params in zip(*family): match_func = list_match_func[self.list_match_local] params = match_func(params) verts, edges, faces = self.main_func(params) out_verts.append(verts) out_edges.append(edges) out_faces.append(faces) if self.flat_output: out_verts, out_edges, out_faces = lists_flat( [out_verts, out_edges, out_faces]) self.outputs['Vertices'].sv_set(out_verts) self.outputs['Edges'].sv_set(out_edges) self.outputs['Faces'].sv_set(out_faces)
# ##### END GPL LICENSE BLOCK ##### import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from math import sqrt, sin, cos, radians from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, match_long_repeat from sverchok.ui.sv_icons import custom_icon from sverchok.utils.geom import circle from sverchok.utils.sv_mesh_utils import mesh_join from sverchok.utils.sv_bmesh_utils import remove_doubles grid_layout_items = [ ("RECTANGLE", "Rectangle", "", custom_icon("SV_HEXA_GRID_RECTANGLE"), 0), ("TRIANGLE", "Triangle", "", custom_icon("SV_HEXA_GRID_TRIANGLE"), 1), ("DIAMOND", "Diamond", "", custom_icon("SV_HEXA_GRID_DIAMOND"), 2), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXA_GRID_HEXAGON"), 3)] grid_type_items = [ ("TRIANGLE", "Triangle", "", custom_icon("SV_TRIANGLE"), 0), ("SQUARE", "Square", "", custom_icon("SV_SQUARE"), 1), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXAGON"), 2)] size_mode_items = [ ("RADIUS", "Radius", "Define polygon by its radius", custom_icon("SV_RAD"), 0), ("SIDE", "Side", "Define polygon by its side", custom_icon("SV_SIDE"), 1)] def triang_layout(settings, pol_type): '''Define triangular layout''' _, _, level = settings
def draw_buttons(self, ctx, layout): layout.prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION"))
# # ##### END GPL LICENSE BLOCK ##### from math import pi import bpy from bpy.props import EnumProperty, FloatProperty, BoolProperty from sverchok.ui.sv_icons import custom_icon from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, list_match_func, list_match_modes, numpy_list_match_modes, numpy_list_match_func from sverchok.utils.sv_itertools import (recurse_f_level_control) from sverchok.utils.geom import LinearSpline, CubicSpline import numpy as np mode_Items = [ ("Sine", "Sine", "Sinusoidal wave", custom_icon("SV_OSCILLATOR_SINE"), 0), ("Square", "Square", "Square wave", custom_icon("SV_OSCILLATOR_INT"), 1), ("Saw", "Saw", "Saw wave", custom_icon("SV_OSCILLATOR_SAW"), 2), ("Triangular", "Triangle", "Triangular", custom_icon("SV_OSCILLATOR_TRI"), 3), ("Custom", "Custom", "Custom wave", custom_icon("SV_OSCILLATOR_WAVE"), 4), ] def oscillator(params, constant, matching_f): result = [] mode, spline_func, knots, match_mode, out_numpy = constant params = matching_f(params) numpy_match = numpy_list_match_func[match_mode] for props in zip(*params): wave = props[5]
# ##### END GPL LICENSE BLOCK ##### import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from math import sqrt, sin, cos, radians from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, match_long_repeat from sverchok.ui.sv_icons import custom_icon from sverchok.utils.geom import circle from sverchok.utils.sv_mesh_utils import mesh_join from sverchok.nodes.modifier_change.remove_doubles import remove_doubles grid_layout_items = [ ("RECTANGLE", "Rectangle", "", custom_icon("SV_HEXA_GRID_RECTANGLE"), 0), ("TRIANGLE", "Triangle", "", custom_icon("SV_HEXA_GRID_TRIANGLE"), 1), ("DIAMOND", "Diamond", "", custom_icon("SV_HEXA_GRID_DIAMOND"), 2), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXA_GRID_HEXAGON"), 3) ] grid_type_items = [("TRIANGLE", "Triangle", "", custom_icon("SV_TRIANGLE"), 0), ("SQUARE", "Square", "", custom_icon("SV_SQUARE"), 1), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXAGON"), 2)] size_mode_items = [("RADIUS", "Radius", "Define polygon by its radius", custom_icon("SV_RAD"), 0), ("SIDE", "Side", "Define polygon by its side", custom_icon("SV_SIDE"), 1)] def triang_layout(settings, pol_type): '''Define triangular layout'''
class SvAdaptivePolygonsNodeMk2(bpy.types.Node, SverchCustomTreeNode): """ Triggers: Adaptive Polygons Tessellate Tissue Tooltip: Generate an adapted copy of donor object along each face of recipient object. """ bl_idname = 'SvAdaptivePolygonsNodeMk2' bl_label = 'Adaptive Polygons Mk2' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_ADAPTATIVE_POLS' axes = [ ("X", "X", "Orient donor's X axis along normal", 0), ("Y", "Y", "Orient donor's Y axis along normal", 1), ("Z", "Z", "Orient donor's Z axis along normal", 2) ] normal_axis: EnumProperty( name = "Normal axis", description = "Donor object axis to be oriented along recipient face normal", items = axes, default = 'Z', update = updateNode) width_coef: FloatProperty( name='Width coeff', description = "Donor object width coefficient", default=1.0, max=3.0, min=0.5, update=updateNode) frame_width: FloatProperty( name='Frame width', description = "Frame width coefficient for Frame / Fan mode", default=0.5, max=1.0, min=0.0, update=updateNode) z_coef: FloatProperty( name='Z coeff', default=1.0, max=3.0, min=0.0, update=updateNode) z_offset: FloatProperty( name = "Z offet", default = 0.0, update = updateNode) normal_interp_modes = [ ("LINEAR", "Linear", "Exact / linear normals interpolation", 0), ("SMOOTH", "Unit length", "Use normals of unit length", 1) ] normal_interp_mode : EnumProperty( name = "Interpolate normals", description = "Normals interpolation mode", items = normal_interp_modes, default = "LINEAR", update = updateNode) normal_modes = [ ("MAP", "Map", "Interpolate from donor vertex normals", 0), ("FACE", "Face", "Use donor face normals", 1) ] normal_mode : EnumProperty( name = "Use normals", description = "Normals mapping mode", items = normal_modes, default = "MAP", update = updateNode) use_shell_factor : BoolProperty( name = "Use shell factor", description = "Use shell factor to make shell thickness constant", default = False, update = updateNode) z_scale_modes = [ ("PROP", "Proportional", "Scale along normal proportionally with the donor object", 0), ("CONST", "Constant", "Constant scale along normal", 1), ("AUTO", "Auto", "Try to calculate the correct scale automatically", 2) ] z_scale : EnumProperty( name = "Z Scale", description = "Mode of scaling along the normals", items = z_scale_modes, default = "PROP", update = updateNode) z_rotation: FloatProperty( name = "Z Rotation", description = "Rotate donor object around recipient's face normal", min = 0, max = 2*pi, default = 0, update = updateNode) poly_rotation: IntProperty( name = "Polygons rotation", description = "Rotate indexes in polygons definition", min = 0, default = 0, update = updateNode) xy_modes = [ ("BOUNDS", "Bounds", "Map donor object bounds to recipient face", 0), ("PLAIN", "As Is", "Map donor object's coordinate space to recipient face as-is", 1) ] xy_mode : EnumProperty( name = "Coordinates", description = "Donor object coordinates mapping", items = xy_modes, default = "BOUNDS", update = updateNode) tri_bound_modes = [ ("EQUILATERAL", "Equilateral", "Use unit-sided equilateral triangle as a base area", custom_icon("SV_EQUILATERAL_TRIANGLE"), 0), ("RECTANGULAR", "Rectangular", "Use rectangular triangle with hypotenuse of 2 as a base area", custom_icon("SV_RECTANGULAR_TRIANGLE"), 1) ] tri_bound_mode : EnumProperty( name = "Bounding triangle", description = "Type of triangle to use as a bounding triangle", items = tri_bound_modes, default = "EQUILATERAL", update = updateNode) map_modes = [ ("QUADTRI", "Quads / Tris Auto", "Use Quads or Tris mapping automatically", 0), ("QUADS", "Quads Always", "Use Quads mapping even for the Tris", 1) ] map_mode : EnumProperty( name = "Faces mode", description = "Donor object mapping mode", items = map_modes, default = "QUADTRI", update = updateNode) skip_modes = [ ("SKIP", "Skip", "Do not output anything", 0), ("ASIS", "As Is", "Output these faces as is", 1) ] mask_mode: EnumProperty( name = "Mask mode", description = "What to do with masked out faces", items = skip_modes, default = "SKIP", update = updateNode) ngon_modes = [ ("QUADS", "As Quads", "Try to process as Quads", 0), ("SKIP", "Skip", "Do not output anything", 1), ("ASIS", "As Is", "Output these faces as is", 2) ] ngon_mode: EnumProperty( name = "NGons", description = "What to do with NGons", items = ngon_modes, default = "QUADS", update = updateNode) @throttle_and_update_node def update_sockets(self, context): show_width = self.frame_mode != 'NEVER' if 'FrameWidth' in self.inputs: self.inputs['FrameWidth'].hide_safe = not show_width if 'Threshold' in self.inputs: self.inputs['Threshold'].hide_safe = not self.join or not self.remove_doubles frame_modes = [ ("NEVER", "Do not use", "Do not use Frame / Fan mode", 0), ("NGONS", "NGons only", "Use Frame / Fan mode for NGons (n > 4) only", 1), ("NGONQUAD", "NGons and Quads", "Use Frame / Fan mode for NGons and Quads (n >= 4)", 2), ("ALWAYS", "Always", "Use Frame / Fan mode for all faces", 3) ] frame_mode: EnumProperty( name = "Frame mode", description = "When to use Frame / Fan mode", items = frame_modes, default = 'NEVER', update = update_sockets) matching_modes = [ ("LONG", "Match longest", "Make an iteration for each donor or recipient object - depending on which list is longer", 0), ("PERFACE", "Donor per face", "If there are many donor objects, match each donor object with corresponding recipient object face", 1) ] matching_mode: EnumProperty( name = "Matching", description = "How to match list of recipient objects with list of donor objects", items = matching_modes, default = "LONG", update = updateNode) join : BoolProperty( name = "Join", description = "Output one joined mesh", default = False, update = updateNode) remove_doubles : BoolProperty( name = "Remove doubles", description = "Merge vertices at the same location", default = False, update = update_sockets) threshold : FloatProperty( name = "Threshold", description = "Threshold for vertices to be considered as identical", precision=4, min=0, default = 1e-4, update = updateNode) tri_vert_idxs = [0, 1, 2] quad_vert_idxs = [0, 1, 2, -1] def sv_init(self, context): self.inputs.new('SvVerticesSocket', "VersR") self.inputs.new('SvStringsSocket', "PolsR") self.inputs.new('SvVerticesSocket', "VersD") self.inputs.new('SvStringsSocket', "PolsD") self.inputs.new('SvStringsSocket', "FaceDataD") self.inputs.new('SvStringsSocket', "W_Coef").prop_name = 'width_coef' self.inputs.new('SvStringsSocket', "FrameWidth").prop_name = 'frame_width' self.inputs.new('SvStringsSocket', "Z_Coef").prop_name = 'z_coef' self.inputs.new('SvStringsSocket', "Z_Offset").prop_name = 'z_offset' self.inputs.new('SvStringsSocket', "Z_Rotation").prop_name = 'z_rotation' self.inputs.new('SvStringsSocket', "PolyRotation").prop_name = 'poly_rotation' self.inputs.new('SvStringsSocket', "PolyMask") self.inputs.new('SvStringsSocket', "Threshold").prop_name = 'threshold' self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Polygons") self.outputs.new('SvStringsSocket', "FaceData") self.outputs.new('SvStringsSocket', "VertRecptIdx") self.outputs.new('SvStringsSocket', "FaceRecptIdx") self.update_sockets(context) def draw_buttons(self, context, layout): layout.prop(self, "join") if self.join: layout.prop(self, "remove_doubles") layout.prop(self, "matching_mode") def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.label(text = "Normal axis:") layout.prop(self, "normal_axis", expand=True) layout.prop(self, "z_scale") layout.prop(self, "normal_mode") if self.normal_mode == 'MAP': layout.prop(self, "normal_interp_mode") layout.prop(self, "use_shell_factor") layout.prop(self, "xy_mode") layout.label(text="Bounding triangle:") layout.prop(self, "tri_bound_mode", expand=True) layout.prop(self, "frame_mode") layout.prop(self, "map_mode") layout.prop(self, "mask_mode") layout.prop(self, "ngon_mode") def get_triangle_directions(self): """ Three normal of unit triangle's edges. This is not constant just because the normal can be X or Y or Z. """ if self.tri_bound_mode == 'EQUILATERAL': triangle_direction_1 = Vector((cos_pi_6, sin_pi_6, 0)) triangle_direction_2 = Vector((-cos_pi_6, sin_pi_6, 0)) triangle_direction_3 = Vector((0, -1, 0)) else: triangle_direction_1 = Vector((1, 1, 0)) triangle_direction_2 = Vector((-1, 1, 0)) triangle_direction_3 = Vector((0, -1, 0)) if self.normal_axis == 'X': return triangle_direction_1.zxy, triangle_direction_2.zxy, triangle_direction_3.zxy elif self.normal_axis == 'Y': return triangle_direction_1.xzy, triangle_direction_2.xzy, triangle_direction_3.xzy else: return triangle_direction_1, triangle_direction_2, triangle_direction_3 def to2d(self, v): """ Convert vector to 2D. Remove the coordinate which is responsible for normal axis. """ if self.normal_axis == 'X': return v.yz elif self.normal_axis == 'Y': return v.xz else: return v.xy def from2d(self, x, y): """ Make 3D vector from X and Y. Add zero for the coordinate which is responsible for normal axis. """ if self.normal_axis == 'X': return Vector((0, x, y)) elif self.normal_axis == 'Y': return Vector((x, 0, y)) else: return Vector((x, y, 0)) def bounding_triangle(self, vertices): """ Return three vertices of a triangle with equal sides / rectangular triangle, which contains all provided vertices. """ X, Y = self.get_other_axes() triangle_direction_1, triangle_direction_2, triangle_direction_3 = self.get_triangle_directions() max_1 = self.to2d(max(vertices, key = lambda vertex: triangle_direction_1.dot(vertex))) max_2 = self.to2d(max(vertices, key = lambda vertex: triangle_direction_2.dot(vertex))) max_3 = self.to2d(min(vertices, key = lambda vertex: vertex[Y])) side_1 = LineEquation2D.from_normal_and_point(self.to2d(triangle_direction_1), max_1) side_2 = LineEquation2D.from_normal_and_point(self.to2d(triangle_direction_2), max_2) side_3 = LineEquation2D.from_normal_and_point(self.to2d(triangle_direction_3), max_3) p1 = side_2.intersect_with_line(side_3) p2 = side_1.intersect_with_line(side_3) p3 = side_1.intersect_with_line(side_2) p1 = self.from2d(p1[0], p1[1]) p2 = self.from2d(p2[0], p2[1]) p3 = self.from2d(p3[0], p3[1]) return p1, p2, p3 def interpolate_quad_2d(self, dst_vert_1, dst_vert_2, dst_vert_3, dst_vert_4, v, x_coef, y_coef): """ Map the provided `v` vertex, considering only two of it's coordinates, from the [-1/2; 1/2] x [-1/2; 1/2] square to the face defined by four `dst_vert_n` vertices. """ X, Y = self.get_other_axes() v12 = dst_vert_1 + (dst_vert_2-dst_vert_1)*v[X]*x_coef + ((dst_vert_2-dst_vert_1)/2) v43 = dst_vert_4 + (dst_vert_3-dst_vert_4)*v[X]*x_coef + ((dst_vert_3-dst_vert_4)/2) return v12 + (v43-v12)*v[Y]*y_coef + ((v43-v12)/2) def interpolate_quad_3d(self, dst_vert_1, dst_vert_2, dst_vert_3, dst_vert_4, dst_normal_1, dst_normal_2, dst_normal_3, dst_normal_4, face_normal, v, x_coef, y_coef, z_coef, z_offset): """ Map the provided `v` vertex from the source [-1/2; 1/2] x [-1/2; 1/2] x [-1/2; 1/2] cube to the space defined by `dst_vert_n` vertices. """ loc = self.interpolate_quad_2d(dst_vert_1, dst_vert_2, dst_vert_3, dst_vert_4, v, x_coef, y_coef) if self.normal_mode == 'MAP': if self.normal_interp_mode == 'SMOOTH': normal = self.interpolate_quad_2d(dst_normal_1, dst_normal_2, dst_normal_3, dst_normal_4, v, x_coef, y_coef) normal.normalize() else: normal = self.interpolate_quad_2d(dst_vert_1 + dst_normal_1, dst_vert_2 + dst_normal_2, dst_vert_3 + dst_normal_3, dst_vert_4 + dst_normal_4, v, x_coef, y_coef) normal = normal - loc else: #normal = (dst_vert_1.normal + dst_vert_2.normal + dst_vert_3.normal + dst_vert_4.normal) * 0.25 normal = face_normal Z = self.normal_axis_idx() return loc + normal*(v[Z]*z_coef + z_offset) def interpolate_tri_2d(self, dst_vert_1, dst_vert_2, dst_vert_3, src_vert_1, src_vert_2, src_vert_3, v): """ Map the provided `v` vertex, considering only two of it's coordinates, from the source triangle (defined by `src_vert_n` vertices) to the face defined by three `dst_vert_n` vertices. """ X, Y = self.get_other_axes() v = self.from2d(v[X], v[Y]) return barycentric_transform(v, src_vert_1, src_vert_2, src_vert_3, dst_vert_1, dst_vert_2, dst_vert_3) def interpolate_tri_3d(self, dst_vert_1, dst_vert_2, dst_vert_3, dst_normal_1, dst_normal_2, dst_normal_3, src_vert_1, src_vert_2, src_vert_3, face_normal, v, z_coef, z_offset): """ Map the provided `v` vertex from the source triangle to the space defined by `dst_vert_n` vertices. """ v_at_triangle = self.interpolate_tri_2d(dst_vert_1, dst_vert_2, dst_vert_3, src_vert_1, src_vert_2, src_vert_3, v) if self.normal_mode == 'MAP': if self.normal_interp_mode == 'SMOOTH': normal = self.interpolate_tri_2d(dst_normal_1, dst_normal_2, dst_normal_3, src_vert_1, src_vert_2, src_vert_3, v) normal.normalize() else: normal = self.interpolate_tri_2d(dst_vert_1 + dst_normal_1, dst_vert_2 + dst_normal_2, dst_vert_3 + dst_normal_3, src_vert_1, src_vert_2, src_vert_3, v) normal = normal - v_at_triangle else: #normal = (dst_vert_1.normal + dst_vert_2.normal + dst_vert_3.normal) * 0.333333333 normal = face_normal Z = self.normal_axis_idx() return v_at_triangle + normal * (v[Z] * z_coef + z_offset) def get_other_axes(self): if self.normal_axis == 'X': return 1, 2 elif self.normal_axis == 'Y': return 0, 2 else: return 0, 1 def normal_axis_idx(self): return "XYZ".index(self.normal_axis) def map_bounds(self, min, max, x): c = (min + max) / 2.0 k = 1.0 / (max - min) return (x - c) * k def rotate_z(self, verts, angle): if abs(angle) < 1e-6: return verts projection = [self.to2d(v) for v in verts] x0, y0 = center(projection) c = self.from2d(x0, y0) rot = Matrix.Rotation(angle, 4, self.normal_axis) result = [(rot @ (v - c)) + c for v in verts] return result def calc_z_scale(self, dst_verts, src_verts): src_lens = [] for v1, v2 in zip(src_verts, src_verts[1:]): src_lens.append((v1 - v2).length) src_lens.append((src_verts[-1] - src_verts[0]).length) dst_lens = [] for v1, v2 in zip(dst_verts, dst_verts[1:]): dst_lens.append((v1 - v2).length) dst_lens.append((dst_verts[-1] - dst_verts[0]).length) scales = [dst_len / src_len for src_len,dst_len in zip(src_lens, dst_lens) if abs(src_len) > 1e-6 and abs(dst_len) > 1e-6] n = len(scales) prod = reduce(lambda x,y: x*y, scales, 1.0) return pow(prod, 1.0/n) def _process_face(self, map_mode, output, recpt_face_data, donor, zcoef, zoffset, angle, wcoef, facerot): X, Y = self.get_other_axes() Z = self.normal_axis_idx() #self.info(f"Face: {len(recpt_face_data.vertices_co)}, mode: {map_mode}") if map_mode == 'ASIS': # Leave this recipient's face as it was - as a single face. verts = recpt_face_data.vertices_co[:] n = len(verts) output.verts_out.append(verts) output.faces_out.append([list(range(n))]) output.vert_recpt_idx_out.append([recpt_face_data.index for i in verts]) output.face_recpt_idx_out.append([recpt_face_data.index for i in range(n)]) elif map_mode == 'TRI': # Tris processing mode. # # As interpolate_tri_3d is based on barycentric_transform, # here we do not have to manually map donor vertices to the # unit triangle. i0, i1, i2 = rotate_list(self.tri_vert_idxs, facerot) if self.z_scale == 'AUTO': zcoef = self.calc_z_scale( [recpt_face_data.vertices_co[i0], recpt_face_data.vertices_co[i1], recpt_face_data.vertices_co[i2]], [donor.tri_vert_1/wcoef, donor.tri_vert_2/wcoef, donor.tri_vert_3/wcoef] ) * zcoef new_verts = [] for v in donor.verts_v: new_verts.append(self.interpolate_tri_3d( recpt_face_data.vertices_co[i0], recpt_face_data.vertices_co[i1], recpt_face_data.vertices_co[i2], recpt_face_data.vertices_normal[i0], recpt_face_data.vertices_normal[i1], recpt_face_data.vertices_normal[i2], donor.tri_vert_1/wcoef, donor.tri_vert_2/wcoef, donor.tri_vert_3/wcoef, recpt_face_data.normal, v, zcoef, zoffset)) output.verts_out.append(new_verts) output.faces_out.append(donor.faces_i) output.face_data_out.append(donor.face_data_i) output.vert_recpt_idx_out.append([recpt_face_data.index for i in new_verts]) output.face_recpt_idx_out.append([recpt_face_data.index for i in donor.faces_i]) elif map_mode == 'QUAD': # Quads processing mode. # # It can process Tris, but it will look strange: # triangle will be processed as degenerated Quad, # where third and fourth vertices coincide. # In Tissue addon, this is the only mode possible for Quads. # Someone may like that behaivour, so we allow it with setting... # # This can process NGons in even worse way: # it will take first three vertices and the last one # and consider that as a Quad. i0, i1, i2, i3 = rotate_list(self.quad_vert_idxs, facerot) if self.z_scale == 'AUTO': corner1 = self.from2d(donor.min_x, donor.min_y) corner2 = self.from2d(donor.min_x, donor.max_y) corner3 = self.from2d(donor.max_x, donor.max_y) corner4 = self.from2d(donor.max_x, donor.min_y) zcoef = self.calc_z_scale( [recpt_face_data.vertices_co[i0], recpt_face_data.vertices_co[i1], recpt_face_data.vertices_co[i2], recpt_face_data.vertices_co[i3]], [corner1, corner2, corner3, corner4] ) * zcoef new_verts = [] #self.info("Donor: %s", len(donor.verts_v)) for v in donor.verts_v: if self.xy_mode == 'BOUNDS': # Map the `v` vertex's X, Y coordinates # from it's bounding square to # [-1/2; 1/2] square. # Leave Z coordinate as it was. x = self.map_bounds(donor.min_x, donor.max_x, v[X]) y = self.map_bounds(donor.min_y, donor.max_y, v[Y]) z = v[Z] v = Vector((0, 0, 0)) v[X] = x v[Y] = y v[Z] = z new_verts.append(self.interpolate_quad_3d( recpt_face_data.vertices_co[i0], recpt_face_data.vertices_co[i1], recpt_face_data.vertices_co[i2], recpt_face_data.vertices_co[i3], recpt_face_data.vertices_normal[i0], recpt_face_data.vertices_normal[i1], recpt_face_data.vertices_normal[i2], recpt_face_data.vertices_normal[i3], recpt_face_data.normal, v, wcoef, wcoef, zcoef, zoffset)) output.verts_out.append(new_verts) output.faces_out.append(donor.faces_i) output.face_data_out.append(donor.face_data_i) output.vert_recpt_idx_out.append([recpt_face_data.index for i in new_verts]) output.face_recpt_idx_out.append([recpt_face_data.index for i in donor.faces_i]) elif map_mode == 'FRAME': is_fan = abs(recpt_face_data.frame_width - 1.0) < 1e-6 n = len(recpt_face_data.vertices_co) if self.map_mode == 'QUADS': sub_map_mode = 'QUAD' else: if is_fan: sub_map_mode = 'TRI' else: sub_map_mode = 'QUAD' if is_fan: tri_faces = [(recpt_face_data.vertices_co[i], recpt_face_data.vertices_co[i+1], recpt_face_data.center) for i in range(n-1)] tri_faces.append((recpt_face_data.vertices_co[-1], recpt_face_data.vertices_co[0], recpt_face_data.center)) if self.use_shell_factor: face_normal = sum(recpt_face_data.vertices_normal, Vector()) / n else: face_normal = recpt_face_data.normal tri_normals = [(recpt_face_data.vertices_normal[i], recpt_face_data.vertices_normal[i+1], face_normal) for i in range(n-1)] tri_normals.append((recpt_face_data.vertices_normal[-1], recpt_face_data.vertices_normal[0], face_normal)) for tri_face, tri_normal in zip(tri_faces, tri_normals): sub_recpt = recpt_face_data.copy() sub_recpt.vertices_co = tri_face sub_recpt.vertices_normal = tri_normal sub_recpt.vertices_idxs = [0, 1, 2] self._process_face(sub_map_mode, output, sub_recpt, donor, zcoef, zoffset, angle, wcoef, facerot) else: inner_verts = [vert.lerp(recpt_face_data.center, recpt_face_data.frame_width) for vert in recpt_face_data.vertices_co] if self.use_shell_factor: inner_normals = [normal.lerp(recpt_face_data.normal, recpt_face_data.frame_width) for normal in recpt_face_data.vertices_normal] else: face_normal = sum(recpt_face_data.vertices_normal, Vector()) / n inner_normals = [normal.lerp(face_normal, recpt_face_data.frame_width) for normal in recpt_face_data.vertices_normal] quad_faces = [(recpt_face_data.vertices_co[i], recpt_face_data.vertices_co[i+1], inner_verts[i+1], inner_verts[i]) for i in range(n-1)] quad_faces.append((recpt_face_data.vertices_co[-1], recpt_face_data.vertices_co[0], inner_verts[0], inner_verts[-1])) quad_normals = [(recpt_face_data.vertices_normal[i], recpt_face_data.vertices_normal[i+1], inner_normals[i+1], inner_normals[i]) for i in range(n-1)] quad_normals.append((recpt_face_data.vertices_normal[-1], recpt_face_data.vertices_normal[0], inner_normals[0], inner_normals[-1])) for quad_face, quad_normal in zip(quad_faces, quad_normals): sub_recpt = recpt_face_data.copy() sub_recpt.vertices_co = quad_face sub_recpt.vertices_normal = quad_normal sub_recpt.vertices_idxs = [0, 1, 2, 3] self._process_face(sub_map_mode, output, sub_recpt, donor, zcoef, zoffset, angle, wcoef, facerot) def _process(self, verts_recpt, faces_recpt, verts_donor, faces_donor, face_data_donor, frame_widths, zcoefs, zoffsets, zrotations, wcoefs, facerots, mask): bm = bmesh_from_pydata(verts_recpt, [], faces_recpt, normal_update=True) bm.verts.ensure_lookup_table() single_donor = self.matching_mode == 'LONG' frame_level = get_data_nesting_level(frame_widths) if single_donor: # Original (unrotated) donor vertices donor_verts_o = [Vector(v) for v in verts_donor] verts_donor = [verts_donor] faces_donor = [faces_donor] face_data_donor = [face_data_donor] if frame_level == 0: frame_widths = [frame_widths] n_faces_recpt = len(faces_recpt) fullList(verts_donor, n_faces_recpt) fullList(faces_donor, n_faces_recpt) fullList(face_data_donor, n_faces_recpt) fullList(frame_widths, n_faces_recpt) X, Y = self.get_other_axes() Z = self.normal_axis_idx() donor = DonorData() # Vertices of the unit triangle. # In case xy_mode != BOUNDS, we will never # have to recalculate these. if self.tri_bound_mode == 'EQUILATERAL': donor.tri_vert_1 = self.from2d(-0.5, -sqrt_3_6) donor.tri_vert_2 = self.from2d(0.5, -sqrt_3_6) donor.tri_vert_3 = self.from2d(0, sqrt_3_3) else: donor.tri_vert_1 = self.from2d(-1, 0) donor.tri_vert_2 = self.from2d(1, 0) donor.tri_vert_3 = self.from2d(0, 1) if single_donor: # We will be rotating the donor object around Z axis, # so it's size along Z is not going to change. z_size = diameter(donor_verts_o, Z) output = OutputData() prev_angle = None face_data = zip(faces_recpt, bm.faces, frame_widths, verts_donor, faces_donor, face_data_donor, zcoefs, zoffsets, zrotations, wcoefs, facerots, mask) recpt_face_idx = 0 for recpt_face, recpt_face_bm, frame_width, donor_verts_i, donor_faces_i, donor_face_data_i, zcoef, zoffset, angle, wcoef, facerot, m in face_data: recpt_face_data = RecptFaceData() recpt_face_data.index = recpt_face_idx recpt_face_data.normal = recpt_face_bm.normal recpt_face_data.center = recpt_face_bm.calc_center_median() recpt_face_data.vertices_co = [bm.verts[i].co for i in recpt_face] if self.use_shell_factor: recpt_face_data.vertices_normal = [bm.verts[i].normal * bm.verts[i].calc_shell_factor() for i in recpt_face] else: recpt_face_data.vertices_normal = [bm.verts[i].normal for i in recpt_face] recpt_face_data.vertices_idxs = recpt_face[:] if not isinstance(frame_width, (int, float)): raise Exception(f"Unexpected data type for frame_width: {frame_width}") recpt_face_data.frame_width = frame_width donor.faces_i = donor_faces_i donor.face_data_i = donor_face_data_i if not single_donor: # Original (unrotated) donor vertices donor_verts_o = [Vector(v) for v in donor_verts_i] z_size = diameter(donor_verts_o, Z) # We have to recalculate rotated vertices only if # the rotation angle have changed. if prev_angle is None or angle != prev_angle or not single_donor: donor.verts_v = self.rotate_z(donor_verts_o, angle) if self.xy_mode == 'BOUNDS' or self.z_scale == 'AUTO' : donor.max_x = max(v[X] for v in donor.verts_v) donor.min_x = min(v[X] for v in donor.verts_v) donor.max_y = max(v[Y] for v in donor.verts_v) donor.min_y = min(v[Y] for v in donor.verts_v) if self.xy_mode == 'BOUNDS': donor.tri_vert_1, donor.tri_vert_2, donor.tri_vert_3 = self.bounding_triangle(donor.verts_v) prev_angle = angle if self.z_scale == 'CONST': if abs(z_size) < 1e-6: zcoef = 0 else: zcoef = zcoef / z_size # Define TRI/QUAD mode based on node settings. n = len(recpt_face) if not m: map_mode = self.mask_mode else: if n == 3: if self.frame_mode == 'ALWAYS': map_mode = 'FRAME' else: if self.map_mode == 'QUADTRI': map_mode = 'TRI' else: # self.map_mode == 'QUADS': map_mode = 'QUAD' elif n == 4: if self.frame_mode in ['ALWAYS', 'NGONQUAD']: map_mode = 'FRAME' else: map_mode = 'QUAD' else: if self.frame_mode in ['ALWAYS', 'NGONQUAD', 'NGONS']: map_mode = 'FRAME' else: if self.ngon_mode == 'QUADS': map_mode = 'QUAD' elif self.ngon_mode == 'ASIS': map_mode = 'ASIS' else: map_mode = 'SKIP' if map_mode == 'SKIP': # Skip this recipient's face - do not produce any vertices/faces for it continue self._process_face(map_mode, output, recpt_face_data, donor, zcoef, zoffset, angle, wcoef, facerot) recpt_face_idx += 1 bm.free() return output def process(self): if not any(output.is_linked for output in self.outputs): return verts_recpt_s = self.inputs['VersR'].sv_get(deepcopy=False) faces_recpt_s = self.inputs['PolsR'].sv_get(default=[[]], deepcopy=False) verts_donor_s = self.inputs['VersD'].sv_get() faces_donor_s = self.inputs['PolsD'].sv_get() if 'FaceDataD' in self.inputs: face_data_donor_s = self.inputs['FaceDataD'].sv_get(default=[[]]) else: face_data_donor_s = [[]] zcoefs_s = self.inputs['Z_Coef'].sv_get(deepcopy=False) zoffsets_s = self.inputs['Z_Offset'].sv_get(deepcopy=False) zrotations_s = self.inputs['Z_Rotation'].sv_get(deepcopy=False) wcoefs_s = self.inputs['W_Coef'].sv_get(deepcopy=False) if 'FrameWidth' in self.inputs: frame_widths_s = self.inputs['FrameWidth'].sv_get(deepcopy=True) else: frame_widths_s = [[0.5]] if 'PolyRotation' in self.inputs: facerots_s = self.inputs['PolyRotation'].sv_get(default = [[0]], deepcopy=False) else: facerots_s = [[0]] mask_s = self.inputs['PolyMask'].sv_get(default = [[1]], deepcopy=False) if 'Threshold' in self.inputs: thresholds_s = self.inputs['Threshold'].sv_get() else: thresholds_s = [[self.threshold]] output = OutputData() if self.matching_mode == 'PERFACE': verts_donor_s = [verts_donor_s] faces_donor_s = [faces_donor_s] face_data_donor_s = [face_data_donor_s] #self.info("FW: %s", frame_widths_s) #frame_widths_s = [frame_widths_s] objects = match_long_repeat([verts_recpt_s, faces_recpt_s, verts_donor_s, faces_donor_s, face_data_donor_s, frame_widths_s, zcoefs_s, zoffsets_s, zrotations_s, wcoefs_s, facerots_s, mask_s, thresholds_s]) #self.info("N objects: %s", len(list(zip(*objects)))) for verts_recpt, faces_recpt, verts_donor, faces_donor, face_data_donor, frame_widths, zcoefs, zoffsets, zrotations, wcoefs, facerots, mask, threshold in zip(*objects): n_faces_recpt = len(faces_recpt) fullList(zcoefs, n_faces_recpt) fullList(zoffsets, n_faces_recpt) fullList(zrotations, n_faces_recpt) if get_data_nesting_level(frame_widths) < 1: frame_widths = [frame_widths] fullList(frame_widths, n_faces_recpt) fullList(wcoefs, n_faces_recpt) fullList(facerots, n_faces_recpt) mask = cycle_for_length(mask, n_faces_recpt) if isinstance(threshold, (list, tuple)): threshold = threshold[0] new = self._process(verts_recpt, faces_recpt, verts_donor, faces_donor, face_data_donor, frame_widths, zcoefs, zoffsets, zrotations, wcoefs, facerots, mask) output.verts_out.extend(new.verts_out) output.faces_out.extend(new.faces_out) output.face_data_out.extend(new.face_data_out) output.vert_recpt_idx_out.extend(new.vert_recpt_idx_out) output.face_recpt_idx_out.extend(new.face_recpt_idx_out) output.verts_out = Vector_degenerate(output.verts_out) if self.join: output.verts_out, _, output.faces_out = mesh_join(output.verts_out, [], output.faces_out) output.face_data_out = sum(output.face_data_out, []) output.vert_recpt_idx_out = sum(output.vert_recpt_idx_out, []) output.face_recpt_idx_out = sum(output.face_recpt_idx_out, []) if self.remove_doubles: doubles_res = remove_doubles(output.verts_out, [], output.faces_out, threshold, face_data=output.face_data_out, vert_data=output.vert_recpt_idx_out) if len(doubles_res) == 4: output.verts_out, _, output.faces_out, data_out = doubles_res else: output.verts_out, _, output.faces_out = doubles_res data_out = dict() output.vert_recpt_idx_out = data_out.get('verts', []) if output.face_recpt_idx_out: output.face_recpt_idx_out = [output.face_recpt_idx_out[idx] for idx in data_out['face_init_index']] output.verts_out = [output.verts_out] output.faces_out = [output.faces_out] output.face_data_out = [output.face_data_out] output.vert_recpt_idx_out = [output.vert_recpt_idx_out] output.face_recpt_idx_out = [output.face_recpt_idx_out] self.outputs['Vertices'].sv_set(output.verts_out) self.outputs['Polygons'].sv_set(output.faces_out) if 'FaceData' in self.outputs: self.outputs['FaceData'].sv_set(output.face_data_out) if 'VertRecptIdx' in self.outputs: self.outputs['VertRecptIdx'].sv_set(output.vert_recpt_idx_out) if 'FaceRecptIdx' in self.outputs: self.outputs['FaceRecptIdx'].sv_set(output.face_recpt_idx_out)
# # ##### END GPL LICENSE BLOCK ##### import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from math import sqrt, sin, cos, radians from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, match_long_repeat from sverchok.ui.sv_icons import custom_icon from sverchok.utils.geom import circle from sverchok.utils.sv_mesh_utils import mesh_join gridLayoutItems = [ ("RECTANGLE", "Rectangle", "", custom_icon("SV_HEXA_GRID_RECTANGLE"), 0), ("TRIANGLE", "Triangle", "", custom_icon("SV_HEXA_GRID_TRIANGLE"), 1), ("DIAMOND", "Diamond", "", custom_icon("SV_HEXA_GRID_DIAMOND"), 2), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXA_GRID_HEXAGON"), 3) ] def generate_grid(center, layout, settings): r = settings[0] # radius a = settings[1] # angle dx = r * 3 / 2 # distance between two consecutive points along X dy = r * sqrt(3) # distance between two consecutive points along Y ''' X : number of points along X Y : number of points along Y for each X location
def draw_buttons(self, ctx, layout): layout.row().prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION"))
# # ##### END GPL LICENSE BLOCK ##### import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty from math import sqrt, sin, cos, radians from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, match_long_repeat from sverchok.ui.sv_icons import custom_icon from sverchok.utils.geom import circle from sverchok.utils.sv_mesh_utils import mesh_join gridLayoutItems = [ ("RECTANGLE", "Rectangle", "", custom_icon("SV_HEXA_GRID_RECTANGLE"), 0), ("TRIANGLE", "Triangle", "", custom_icon("SV_HEXA_GRID_TRIANGLE"), 1), ("DIAMOND", "Diamond", "", custom_icon("SV_HEXA_GRID_DIAMOND"), 2), ("HEXAGON", "Hexagon", "", custom_icon("SV_HEXA_GRID_HEXAGON"), 3)] def generate_grid(center, layout, settings): r = settings[0] # radius a = settings[1] # angle dx = r * 3 / 2 # distance between two consecutive points along X dy = r * sqrt(3) # distance between two consecutive points along Y ''' X : number of points along X Y : number of points along Y for each X location