예제 #1
0
class SvPulgaFitForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Grow / Shrink Force
    Tooltip: Shrink Radius (reducing mass) if collide with others / Grow if does not
    """
    bl_idname = 'SvPulgaFitForceNode'
    bl_label = 'Pulga Fit Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_FIT_FORCE'

    force: FloatProperty(
        name='Magnitude', description='Shrink if collide with others / Grow if does not ',
        default=0.1, update=updateNode)
    min_rad: FloatProperty(
        name='Min. Radius', description='Do not shrink under this value',
        default=0.1, precision=3, update=updateNode)
    max_rad: FloatProperty(
        name='Max. Radius', description='Do not grow over this value',
        default=1.0, precision=3, update=updateNode)

    mode: EnumProperty(name="Mode", items=enum_item_4(['Absolute', 'Relative', 'Percent']), update=updateNode)

    algorithm: EnumProperty(
        name='Algorithm',
        description='Algorithm used for calculation',
        items=enum_item_4(['Brute Force', 'Kd-tree']),
        default='Kd-tree', update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Magnitude").prop_name = 'force'
        self.inputs.new('SvStringsSocket', "Min Radius").prop_name = 'min_rad'
        self.inputs.new('SvStringsSocket', "Max Radius").prop_name = 'max_rad'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        layout.prop(self, 'mode')
        if scipy is not None and Cython is not None:
            layout.prop(self, 'algorithm')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        forces_in = self.inputs["Magnitude"].sv_get(deepcopy=False)
        min_rad_in = self.inputs["Min Radius"].sv_get(deepcopy=False)
        max_rad_in = self.inputs["Max Radius"].sv_get(deepcopy=False)
        forces_out = []
        use_kdtree = self.algorithm == "Kd-tree" and scipy is not None and Cython is not None
        for force in zip(forces_in, min_rad_in, max_rad_in):
            forces_out.append(SvFitForce(*force, self.mode, use_kdtree=use_kdtree))
        self.outputs[0].sv_set([forces_out])
예제 #2
0
class SvPulgaAlignForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Velocity Alignment
    Tooltip: Take part of the velocity of the near particles
    """
    bl_idname = 'SvPulgaAlignForceNode'
    bl_label = 'Pulga Align Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_ALIGN_FORCE'


    strength: FloatProperty(
        name='Strength', description='Drag Force Constant',
        default=0.1, precision=3, update=updateNode)
    decay: FloatProperty(
        name='Decay', description='0 = no decay, 1 = linear, 2 = quadratic...',
        default=1.0, precision=3, update=updateNode)
    max_distance: FloatProperty(
        name='Max. Distance', description='Maximum distance',
        default=10.0, precision=3, update=updateNode)
    mode: EnumProperty(
        name='Mode',
        description='Algorithm used for calculation',
        items=enum_item_4(['Brute Force', 'Kd-tree']),
        default='Kd-tree', update=updateNode)


    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Strength").prop_name = 'strength'
        self.inputs.new('SvStringsSocket', "Decay").prop_name = 'decay'
        self.inputs.new('SvStringsSocket', "Max. Distance").prop_name = 'max_distance'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        if scipy is not None and Cython is not None:
            layout.prop(self, 'mode')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        strength = self.inputs["Strength"].sv_get(deepcopy=False)
        decay = self.inputs["Decay"].sv_get(deepcopy=False)
        max_distance = self.inputs["Max. Distance"].sv_get(deepcopy=False)
        use_kdtree = self.mode in "Kd-tree" and scipy is not None and Cython is not None

        forces_out = []

        for force in zip_long_repeat(strength, decay, max_distance):

            forces_out.append(SvAlignForce(*force, use_kdtree=use_kdtree))


        self.outputs[0].sv_set([forces_out])
class SvMeshBeautify(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: beauty existing fill
    Tooltip: rearrange faces with bmesh operator

    useful for typography converted to geometry.
    """

    bl_idname = 'SvMeshBeautify'
    bl_label = 'Mesh Beautify'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_MESH_BEAUTIFY'

    beautify_mode: bpy.props.EnumProperty(name='Beautify',
                                          items=enum_item_4(['AREA', 'ANGLE']),
                                          default="AREA",
                                          update=updateNode)

    def draw_buttons(self, context, layout):
        layout.prop(self, 'beautify_mode', expand=True)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', 'Verts')
        self.inputs.new('SvStringsSocket', 'Faces')
        self.outputs.new('SvVerticesSocket', 'Verts')
        self.outputs.new('SvStringsSocket', 'Faces')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return

        in_verts = self.inputs['Verts'].sv_get()
        in_faces = self.inputs['Faces'].sv_get()

        out_verts, out_faces = [], []

        if in_verts and in_faces:
            fill = bmesh.ops.beautify_fill
            for verts, faces in zip(in_verts, in_faces):
                bm = bmesh_from_pydata(verts, [], faces)
                bm.verts.ensure_lookup_table()
                fill(bm,
                     faces=bm.faces[:],
                     edges=bm.edges[:],
                     use_restrict_tag=False,
                     method=self.beautify_mode)
                nv, ne, nf = pydata_from_bmesh(bm)
                out_verts.append(nv)
                out_faces.append(nf)

        self.outputs['Verts'].sv_set(out_verts)
        self.outputs['Faces'].sv_set(out_faces)
예제 #4
0
class SvPulgaPinForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Constrain Vertices
    Tooltip: Pin Particles (Vertices) movement along defined axis 
    """
    bl_idname = 'SvPulgaPinForceNode'
    bl_label = 'Pulga Pin Force'
    bl_icon = 'PINNED'

    fixed_len: FloatProperty(name='Length',
                             description='Force',
                             default=0.0,
                             update=updateNode)
    pin_type: EnumProperty(name='Axis',
                           description='Constrained',
                           items=enum_item_4(
                               ['XYZ', 'XY', 'XZ', 'YZ', 'X', 'Y', 'Z']),
                           default='XYZ',
                           update=updateNode)
    force: FloatVectorProperty(name='Force',
                               description='Force',
                               size=3,
                               default=(0.0, 0, 0),
                               update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Pins")
        self.inputs.new('SvStringsSocket', "Pin Type").prop_name = 'pin_type'
        self.inputs.new('SvVerticesSocket', "Pins Goal")

        self.outputs.new('SvPulgaForceSocket', "Force")

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        # indices, pin_type, pins_goal_pos,use_pins_goal
        pins_in = self.inputs["Pins"].sv_get(deepcopy=False)
        pin_type = self.inputs["Pin Type"].sv_get(deepcopy=False)
        pins_goal_pos = self.inputs["Pins Goal"].sv_get(deepcopy=False,
                                                        default=[[]])
        forces_out = []
        use_pin_goal = self.inputs["Pins Goal"].is_linked
        for force_params in zip_long_repeat(pins_in, pin_type, pins_goal_pos):

            forces_out.append(SvPinForce(*force_params, use_pin_goal))
        self.outputs[0].sv_set([forces_out])
예제 #5
0
class SvPulgaCollisionForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Collide verts
    Tooltip: Collision forces between vertices
    """
    bl_idname = 'SvPulgaCollisionForceNode'
    bl_label = 'Pulga Collision Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_COLLISION_FORCE'

    strength: FloatProperty(name='Strength',
                            description='Collision forces between vertices',
                            default=0.01,
                            precision=4,
                            step=1e-2,
                            update=updateNode)
    mode: EnumProperty(name='Mode',
                       description='Algorithm used for calculation',
                       items=enum_item_4(['Brute Force', 'Kd-tree']),
                       default='Kd-tree',
                       update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Strength").prop_name = 'strength'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        if scipy is not None and Cython is not None:
            layout.prop(self, 'mode')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        forces_in = self.inputs["Strength"].sv_get(deepcopy=False)

        forces_out = []
        use_kdtree = self.mode in "Kd-tree" and scipy is not None and Cython is not None
        for force in forces_in:
            forces_out.append(SvCollisionForce(force, use_kdtree=use_kdtree))
        self.outputs[0].sv_set([forces_out])
예제 #6
0
class SvFlatGeometryNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: 3D to 2D
    Tooltip: Projection of 3d vertices into defined plane
    """
    bl_idname = 'SvFlatGeometryNode'
    bl_label = 'Flat Geometry'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_FLAT_GEOMETRY'

    projection_mode: EnumProperty(
        name='Mode',
        description='Projection mode',
        items=enum_item_4(["Orthogrphic", 'Perspective']),
        update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', 'Vertices')
        self.inputs.new('SvMatrixSocket', 'Plane Matrix')


        self.outputs.new('SvVerticesSocket', 'Vertices')
        self.outputs.new('SvStringsSocket', 'Z coord')

    def draw_buttons(self, context, layout):
        layout.prop(self, 'projection_mode')

    def process(self):
        if not any(s.is_linked for s in self.outputs):
            return
        if self.inputs['Vertices'].is_linked:
            verts_in = self.inputs['Vertices'].sv_get(deepcopy=False)
            plane_in = self.inputs['Plane Matrix'].sv_get(deepcopy=False)
            if self.projection_mode == 'Orthogrphic':
                verts_out, z_coord_out = ortho_projection(verts_in, plane_in)
            else:
                verts_out, z_coord_out = perspective_projection(verts_in, plane_in, 2)

            self.outputs['Vertices'].sv_set(verts_out)
            self.outputs['Z coord'].sv_set(z_coord_out)
예제 #7
0
class SvSmoothLines(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: smooth lines fil 
    Tooltip: accepts seq of verts and weights, returns smoothed lines
    
    This node should accept verts and weights and return the smoothed line representation.
    """

    bl_idname = 'SvSmoothLines'
    bl_label = 'Smooth Lines'
    bl_icon = 'NORMALIZE_FCURVES'

    smooth_selected_mode: EnumProperty(
        items=enum_item_4(["absolute", "relative", "arc"]),
        default="absolute",
        description="gives various representations of the smooth corner",
        update=updateNode)

    type_selected_mode: EnumProperty(items=enum_item_4(["cyclic", "open"]),
                                     default="open",
                                     update=updateNode)

    n_verts: IntProperty(default=5, name="n_verts", min=2, update=updateNode)
    weights: FloatProperty(default=0.0,
                           name="weights",
                           min=0.0,
                           update=updateNode)

    def sv_init(self, context):
        self.inputs.new("VerticesSocket", "vectors")
        self.inputs.new("StringsSocket", "weights").prop_name = "weights"
        self.inputs.new("StringsSocket", "attributes")
        self.outputs.new("VerticesSocket", "verts")
        self.outputs.new("StringsSocket", "edges")

    def draw_buttons(self, context, layout):

        # with attrs socket connected all params must be passed via this socket, to override node variables
        attr_socket = self.inputs.get('attributes')
        if attr_socket and attr_socket.is_linked:
            return

        col = layout.column()
        row1 = col.row(align=True)
        row1.prop(self, "smooth_selected_mode", text="mode", expand=True)
        row2 = col.row(align=True)
        row2.prop(self, "type_selected_mode", text="type", expand=True)
        col.prop(self, "n_verts", text='num verts')

    def process(self):
        necessary_sockets = [
            self.inputs["vectors"],
        ]
        if not all(s.is_linked for s in necessary_sockets):
            return

        if self.inputs["attributes"].is_linked:
            # gather own data, rather than socket data
            # NOT IMPLEMENTED YET
            ...

        edges_socket = self.outputs['edges']
        verts_socket = self.outputs['verts']

        V_list = self.inputs['vectors'].sv_get()
        W_list = self.inputs['weights'].sv_get()
        verts_out = []
        edges_out = []

        if W_list and V_list:
            # ensure all vectors from V_list are matched by a weight.
            W_list = extend_if_needed(V_list, W_list, default=0.5)

        params = self.get_params()
        for vlist, wlist in zip(V_list, W_list):

            new_verts = func_xpline_2d(vlist, wlist, params)
            verts_out.append(new_verts)
            if edges_socket.is_linked:
                edges_out.append(
                    edge_sequence_from_verts(len(new_verts), params))

        verts_socket.sv_set(verts_out)
        edges_socket.sv_set(edges_out)

    def get_params(self):
        params = lambda: None
        params.num_points = self.n_verts
        params.loop = False if not self.type_selected_mode == 'cyclic' else True
        params.remove_doubles = False
        params.weight = self.weights
        params.mode = self.smooth_selected_mode
        return params
예제 #8
0
class SverchokPreferences(AddonPreferences):

    bl_idname = __package__

    def update_debug_mode(self, context):
        data_structure.DEBUG_MODE = self.show_debug

    def update_heat_map(self, context):
        data_structure.heat_map_state(self.heat_map)

    def set_frame_change(self, context):
        handlers.set_frame_change(self.frame_change_mode)

    def update_theme(self, context):
        color_def.rebuild_color_cache()
        if self.auto_apply_theme:
            color_def.apply_theme()

    tab_modes = data_structure.enum_item_4(["General", "Node Defaults", "Extra Nodes", "Theme"])

    selected_tab: bpy.props.EnumProperty(
        items=tab_modes,
        description="pick viewing mode",
        default="General"
    )

    #  debugish...
    show_debug: BoolProperty(
        name="Print update timings",
        description="Print update timings in console",
        default=False, subtype='NONE',
        update=update_debug_mode)

    no_data_color: FloatVectorProperty(
        name="No data", description='When a node can not get data',
        size=3, min=0.0, max=1.0,
        default=(1, 0.3, 0), subtype='COLOR',
        update=update_system.update_error_colors)

    exception_color: FloatVectorProperty(
        name="Error", description='When node has an exception',
        size=3, min=0.0, max=1.0,
        default=(0.8, 0.0, 0), subtype='COLOR',
        update=update_system.update_error_colors)

    #  heat map settings
    heat_map: BoolProperty(
        name="Heat map",
        description="Color nodes according to time",
        default=False, subtype='NONE',
        update=update_heat_map)

    heat_map_hot: FloatVectorProperty(
        name="Heat map hot", description='',
        size=3, min=0.0, max=1.0,
        default=(.8, 0, 0), subtype='COLOR')

    heat_map_cold: FloatVectorProperty(
        name="Heat map cold", description='',
        size=3, min=0.0, max=1.0,
        default=(1, 1, 1), subtype='COLOR')

    # Profiling settings
    profiling_sections = [
        ("NONE", "Disable", "Disable profiling", 0),
        ("MANUAL", "Marked methods only", "Profile only methods that are marked with @profile decorator", 1),
        ("UPDATE", "Node tree update", "Profile whole node tree update process", 2)
    ]

    profile_mode: EnumProperty(name = "Profiling mode",
            items = profiling_sections,
            default = "NONE",
            description = "Performance profiling mode")

    developer_mode: BoolProperty(name = "Developer mode",
            description = "Show some additional panels or features useful for Sverchok developers only",
            default = False)

    #  theme settings

    sv_theme: EnumProperty(
        items=color_def.themes,
        name="Theme preset",
        description="Select a theme preset",
        update=color_def.color_callback,
        default="default_theme")

    auto_apply_theme: BoolProperty(
        name="Apply theme", description="Apply theme automaticlly",
        default=False)

    apply_theme_on_open: BoolProperty(
        name="Apply theme", description="Apply theme automaticlly",
        default=False)

    color_viz: FloatVectorProperty(
        name="Visualization", description='',
        size=3, min=0.0, max=1.0,
        default=(1, 0.589, 0.214), subtype='COLOR',
        update=update_theme)

    color_tex: FloatVectorProperty(
        name="Text", description='',
        size=3, min=0.0, max=1.0,
        default=(0.5, 0.5, 1), subtype='COLOR',
        update=update_theme)

    color_sce: FloatVectorProperty(
        name="Scene", description='',
        size=3, min=0.0, max=1.0,
        default=(0, 0.5, 0.2), subtype='COLOR',
        update=update_theme)

    color_lay: FloatVectorProperty(
        name="Layout", description='',
        size=3, min=0.0, max=1.0,
        default=(0.674, 0.242, 0.363), subtype='COLOR',
        update=update_theme)

    color_gen: FloatVectorProperty(
        name="Generator", description='',
        size=3, min=0.0, max=1.0,
        default=(0, 0.5, 0.5), subtype='COLOR',
        update=update_theme)

    #  frame change
    frame_change_modes = [
        ("PRE", "Pre", "Update Sverchok before frame change", 0),
        ("POST", "Post", "Update Sverchok after frame change", 1),
        ("NONE", "None", "Sverchok doesn't update on frame change", 2)
    ]

    frame_change_mode: EnumProperty(
        items=frame_change_modes,
        name="Frame change",
        description="Select frame change handler",
        default="POST",
        update=set_frame_change)

    #  ctrl+space settings

    show_icons: BoolProperty(
        name="Show icons in ctrl+space menu",
        default=False,
        description="Use icons in ctrl+space menu")

    over_sized_buttons: BoolProperty(
        default=False, name="Big buttons", description="Very big buttons")

    node_panel_modes = [
            ("X", "Do not show", "Do not show node buttons", 0),
            ("T", "T panel", "Show node buttons under the T panel", 1),
            ("N", "N panel", "Show node under the N panel", 2)
        ]

    node_panels: EnumProperty(
        items = node_panel_modes,
        name = "Display node buttons",
        description = "Where to show node insertion buttons. Restart Blender to apply changes.",
        default = "X")

    node_panels_icons_only : BoolProperty(
            name = "Display icons only",
            description = "Show node icon only when icon has an icon, otherwise show it's name",
            default = True
        )

    node_panels_columns : IntProperty(
            name = "Columns",
            description = "Number of icon panels per row; Set to 0 for automatic selection",
            default = 4,
            min = 0, max = 12
        )

    enable_live_objin: BoolProperty(
        description="Objects in edit mode will be updated in object-in Node")

    ##  BLF/BGL/GPU  scale and location props

    render_scale: FloatProperty(
        default=1.0, min=0.01, step=0.01, description='default render scale')

    render_location_xy_multiplier: FloatProperty(
        default=1.0, min=0.01, step=0.01, description='default render location scale')


    stethoscope_view_scale: FloatProperty(
        default=1.0, min=0.01, step=0.01, description='default stethoscope scale')

    index_viewer_scale: FloatProperty(
        default=1.0, min=0.01, step=0.01, description='default index viewer scale')

    auto_update_angle_values: BoolProperty(
        default=True,
        description="Auto update angle values when angle units are changed to preserve the angle")

    def set_nodeview_render_params(self, context):
        # i think these are both the same..
        self.render_scale = get_dpi_factor()
        self.render_location_xy_multiplier = get_dpi_factor()
        print(f'set render_scale to: {self.render_scale}')
        print(f'set render_location_xy_multiplier to: {self.render_location_xy_multiplier}')

    ##

    datafiles = os.path.join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True))
    defaults_location: StringProperty(default=datafiles, description='usually ..data_files\\sverchok\\defaults\\nodes.json')
    external_editor: StringProperty(description='which external app to invoke to view sources')
    real_sverchok_path: StringProperty(description='use with symlinked to get correct src->dst')

    github_token : StringProperty(name = "GitHub API Token",
                    description = "GitHub API access token. Should have 'gist' OAuth scope.",
                    subtype="PASSWORD")

    # Logging settings

    def update_log_level(self, context):
        logging.info("Setting log level to %s", self.log_level)
        logging.setLevel(self.log_level)

    log_levels = [
            ("DEBUG", "Debug", "Debug output", 0),
            ("INFO", "Information", "Informational output", 1),
            ("WARNING", "Warnings", "Show only warnings and errors", 2),
            ("ERROR", "Errors", "Show errors only", 3)
        ]

    log_level: EnumProperty(name = "Logging level",
            description = "Minimum events severity level to output. All more severe messages will be logged as well.",
            items = log_levels,
            update = update_log_level,
            default = "INFO")

    log_update_events: BoolProperty(
        name="Log update events",
        description="Print name of methods which are triggered upon changes in BLender",
        default=False)

    log_to_buffer: BoolProperty(name = "Log to text buffer",
            description = "Enable log output to internal Blender's text buffer",
            default = True)
    log_to_buffer_clean: BoolProperty(name = "Clear buffer at startup",
            description = "Clear text buffer at each Blender startup",
            default = False)
    log_to_file: BoolProperty(name = "Log to file",
            description = "Enable log output to external file",
            default = False)
    log_to_console: BoolProperty(name = "Log to console",
            description = "Enable log output to console / terminal / standard output.",
            default = True)

    log_buffer_name: StringProperty(name = "Buffer name", default = "sverchok.log")
    log_file_name: StringProperty(name = "File path", default = os.path.join(datafiles, "sverchok.log"))


    # updating sverchok
    dload_archive_name: StringProperty(name="archive name", default="master") # default = "master"
    dload_archive_path: StringProperty(name="archive path", default="https://github.com/nortikin/sverchok/archive/")

    FreeCAD_folder: StringProperty(name="FreeCAD python 3.7 folder")

    def general_tab(self, layout):
        col = layout.row().column()
        col_split = col.split(factor=0.5)
        col1 = col_split.column()
        col1.label(text="UI:")
        col1.prop(self, "show_icons")

        toolbar_box = col1.box()
        toolbar_box.label(text="Node toolbars")
        toolbar_box.prop(self, "node_panels")
        if self.node_panels != "X":
            toolbar_box.prop(self, "node_panels_icons_only")
            if self.node_panels_icons_only:
                toolbar_box.prop(self, "node_panels_columns")

        col1.prop(self, "over_sized_buttons")
        col1.prop(self, "enable_live_objin", text='Enable Live Object-In')
        col1.prop(self, "external_editor", text="Ext Editor")
        col1.prop(self, "real_sverchok_path", text="Src Directory")

        box = col1.box()
        box.label(text="Export to Gist")
        box.prop(self, "github_token")
        box.label(text="To export node trees to gists, you have to create a GitHub API access token.")
        box.label(text="For more information, visit " + TOKEN_HELP_URL)
        box.operator("node.sv_github_api_token_help", text="Visit documentation page")

        col2 = col_split.split().column()
        col2.label(text="Frame change handler:")
        col2.row().prop(self, "frame_change_mode", expand=True)
        col2.separator()

        col2box = col2.box()
        col2box.label(text="Debug:")
        col2box.prop(self, "profile_mode")
        col2box.prop(self, "show_debug")
        col2box.prop(self, "heat_map")
        col2box.prop(self, "developer_mode")

        log_box = col2.box()
        log_box.label(text="Logging:")
        log_box.prop(self, "log_level")

        if self.log_level == "DEBUG":
            log_box.prop(self, "log_update_events")

        buff_row = log_box.row()
        buff_row.prop(self, "log_to_buffer")
        if self.log_to_buffer:
            buff_row.prop(self, "log_buffer_name")
            log_box.prop(self, "log_to_buffer_clean")

        file_row = log_box.row()
        file_row.prop(self, "log_to_file")
        if self.log_to_file:
            file_row.prop(self, "log_file_name")

        log_box.prop(self, "log_to_console")

    def node_defaults_tab(self, layout):
        row = layout.row()
        col = row.column(align=True)
        row_sub1 = col.row().split(factor=0.5)
        box_sub1 = row_sub1.box()
        box_sub1_col = box_sub1.column(align=True)

        box_sub1_col.label(text='Render Scale & Location')
        # box_sub1_col.prop(self, 'render_location_xy_multiplier', text='xy multiplier')
        # box_sub1_col.prop(self, 'render_scale', text='scale')
        box_sub1_col.label(text=f'xy multiplier: {self.render_location_xy_multiplier}')
        box_sub1_col.label(text=f'render_scale : {self.render_scale}')

        box_sub1_col.label(text='Stethoscope')
        box_sub1_col.prop(self, 'stethoscope_view_scale', text='scale')

        box_sub1_col.label(text='Index Viewer')
        box_sub1_col.prop(self, 'index_viewer_scale', text='scale')

        box_sub2 = box_sub1.box()
        box_sub2_col = box_sub2.column(align=True)
        box_sub2_col.label(text='Angle Units')
        box_sub2_col.prop(self, 'auto_update_angle_values', text="Auto Update Angle Values")

        col3 = row_sub1.split().column()
        col3.label(text='Location of custom defaults')
        col3.prop(self, 'defaults_location', text='')

    def theme_tab(self, layout):
        row = layout.row()
        col = row.column(align=True)
        split = col.row().split(factor=0.66)
        split2 = col.row().split(factor=0.66)
        left_split = split.row()
        right_split = split.row()

        split_viz_colors = left_split.column().split(factor=0.5, align=True)

        if True:
            col1 = split_viz_colors.column()
            for name in ['color_viz', 'color_tex', 'color_sce']:
                r = col1.row()
                r.prop(self, name)

            col2 = split_viz_colors.column()
            for name in ['color_lay', 'color_gen']:
                r = col2.row()
                r.prop(self, name)

        split_extra_colors = split2.column().split()
        col_x1 = split_extra_colors.column()
        col_x1.label(text="Error colors: ( error / no data )")
        row_x1 = col_x1.row()
        row_x1.prop(self, "exception_color", text='')
        row_x1.prop(self, "no_data_color", text='')

        col_x2 = split_extra_colors.split().column()
        col_x2.label(text="Heat map colors: ( hot / cold )")
        row_x2 = col_x2.row()
        row_x2.active = self.heat_map
        row_x2.prop(self, "heat_map_hot", text='')
        row_x2.prop(self, "heat_map_cold", text='')

        col3 = right_split.column()
        col3.label(text='Theme:')
        col3.prop(self, 'sv_theme', text='')
        col3.separator()
        col3.prop(self, 'auto_apply_theme', text="Auto apply theme changes")
        col3.prop(self, 'apply_theme_on_open', text="Apply theme when opening file")
        col3.operator('node.sverchok_apply_theme', text="Apply theme to layouts")

    def extra_nodes_tab(self, layout):

        def draw_freecad_ops():
            dependency = sv_dependencies['freecad']
            col = box.column(align=True)
            col.label(text=dependency.message, icon=get_icon(dependency.module))
            row = col.row(align=True)
            row.operator('wm.url_open', text="Visit package website").url = dependency.url
            if dependency.module is None:
                tx = "Set path"
            else:
                tx = "Reset path"
            row.prop(self, 'FreeCAD_folder')
            row.operator('node.sv_set_freecad_path', text=tx).FreeCAD_folder = self.FreeCAD_folder
            return row

        box = layout.box()
        box.label(text="Dependencies:")
        
        row = draw_message(box, "pip")
        if pip is not None:
            row.operator('node.sv_ex_pip_install', text="Upgrade PIP").package = "pip setuptools wheel"
        else:
            if ensurepip is not None:
                row.operator('node.sv_ex_ensurepip', text="Install PIP")
            else:
                row.operator('wm.url_open', text="Installation instructions").url = "https://pip.pypa.io/en/stable/installing/"

        draw_message(box, "scipy")
        draw_message(box, "geomdl")
        draw_message(box, "skimage")
        draw_message(box, "mcubes")
        draw_message(box, "circlify")
        draw_message(box, "lbt-ladybug")

        draw_freecad_ops()

        if any(package.module is None for package in sv_dependencies.values()):
            box.operator('wm.url_open', text="Read installation instructions for missing dependencies").url = "https://github.com/portnov/sverchok-extra"

    def draw(self, context):

        layout = self.layout
        layout.row().prop(self, 'selected_tab', expand=True)

        if self.selected_tab == "General":
            self.general_tab(layout)


        if self.selected_tab == "Node_Defaults":
            self.node_defaults_tab(layout)

        if self.selected_tab == "Extra_Nodes":
            self.extra_nodes_tab(layout)

        if self.selected_tab == "Theme":
            self.theme_tab(layout)

        # FOOTER

        row = layout.row()
        col = row.column(align=True)
        col.label(text="Links:")
        row1 = col.row(align=True)
        row1.scale_y = 2.0
        row1.operator('wm.url_open', text='Sverchok home page').url = 'http://nikitron.cc.ua/blend_scripts.html'
        row1.operator('wm.url_open', text='Documentation').url = 'http://nikitron.cc.ua/sverch/html/main.html'

        if context.scene.sv_new_version:
            row1.operator('node.sverchok_update_addon', text='Upgrade Sverchok addon')
        else:
            row1.operator('node.sverchok_check_for_upgrades_wsha', text='Check for new version')
예제 #9
0
class SvPulgaAngleForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Angles at edges
    Tooltip: Force the keeps angles between edges
    """
    bl_idname = 'SvPulgaAngleForceNode'
    bl_label = 'Pulga Angle Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_ANGLES_FORCE'

    fixed_angle: FloatProperty(
        name='Rest Angle',
        description=
        'Specify spring rest angle, 0 to calculate it from initial position',
        default=0.0,
        update=updateNode)
    stiffness: FloatProperty(name='Stiffness',
                             description='Springs stiffness constant',
                             default=0.1,
                             precision=4,
                             update=updateNode)

    def update_sockets(self, context):
        self.inputs[0].label = self.mode

    mode: EnumProperty(name='Mode',
                       items=enum_item_4(['Edges', 'Polygons']),
                       default='Edges',
                       update=update_sockets)

    mass_dependent: BoolProperty(name='mass_dependent', update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Edge_Pol")
        self.inputs[0].label = 'Edges'
        self.inputs.new('SvStringsSocket', "Stiffness").prop_name = 'stiffness'
        self.inputs.new('SvStringsSocket', "Angle").prop_name = 'fixed_angle'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        layout.prop(self, 'mode')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        springs_in = self.inputs["Edge_Pol"].sv_get(deepcopy=False)
        stiffness_in = self.inputs["Stiffness"].sv_get(deepcopy=False)
        lengths_in = self.inputs["Angle"].sv_get(deepcopy=False)

        forces_out = []
        use_fix_len = self.inputs["Angle"].is_linked
        for force_params in zip_long_repeat(springs_in, stiffness_in,
                                            lengths_in):
            if self.mode == 'Edges':
                forces_out.append(SvEdgesAngleForce(*force_params,
                                                    use_fix_len))
            else:
                forces_out.append(
                    SvPolygonsAngleForce(*force_params, use_fix_len))
        self.outputs[0].sv_set([forces_out])
예제 #10
0
class SvSvgDimensionNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Text SVG
    Tooltip: Creates SVG Dimensions
    """
    bl_idname = 'SvSvgDimensionNode'
    bl_label = 'Dimension SVG'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_DIMENSION_SVG'

    font_size: FloatProperty(name='Font Size',
                             description='Font Size',
                             default=10,
                             update=updateNode)

    font_family: EnumProperty(name='Font',
                              description='Font Size',
                              items=enum_item_4([
                                  "serif", 'sans-serif', 'monospace',
                                  'cursive', 'fantasy', 'user'
                              ]),
                              default='monospace',
                              update=updateNode)

    user_font: StringProperty(name='Font Name',
                              description='Define font name',
                              default='',
                              update=updateNode)

    dimension_type: EnumProperty(
        name='Type',
        description='Dimension type. Horizontal = 0, Vertical = 1, Aligned = 2',
        items=enum_item_4(["Horizontal", 'Vertical', 'Aligned']),
        default='Aligned',
        update=updateNode)

    dimension_offset: FloatProperty(name='Dim. Offset',
                                    description='Dimension offset',
                                    default=0,
                                    update=updateNode)

    line_extension: FloatProperty(name='Lines Extension',
                                  description='Text Rotation',
                                  default=0,
                                  update=updateNode)
    decimal_precision: IntProperty(name='Decimals',
                                   description='Text Rotation',
                                   default=2,
                                   update=updateNode)

    units: StringProperty(name='Units',
                          description='units',
                          default='',
                          update=updateNode)

    units_real: EnumProperty(name='Units_real',
                             description='Dimentions feets or meters',
                             items=enum_item_4(['Metric', 'Imperialistic']),
                             default='Metric',
                             update=updateNode)

    text: StringProperty(name='Text',
                         description='Text',
                         default='',
                         update=updateNode)

    text_offset: FloatProperty(name='Text Offset',
                               description='Text offset',
                               default=0,
                               update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', "Location A")
        self.inputs.new('SvVerticesSocket', "Location B")
        self.inputs.new('SvStringsSocket',
                        "Dim. Type").prop_name = 'dimension_type'
        self.inputs.new('SvStringsSocket',
                        "Dim. Offset").prop_name = 'dimension_offset'
        self.inputs.new('SvStringsSocket', "Font Size").prop_name = 'font_size'
        self.inputs.new('SvStringsSocket',
                        "Text Offset").prop_name = 'text_offset'

        self.inputs.new('SvSvgSocket', "Text Fill / Stroke")
        self.inputs.new('SvSvgSocket', "Lines Fill / Stroke")

        self.outputs.new('SvSvgSocket', "SVG Objects")

    def draw_buttons(self, context, layout):
        layout.prop(self, "line_extension", expand=False)
        layout.prop(self, "decimal_precision", expand=False)
        layout.prop(self, "units", expand=False)
        layout.prop(self, "units_real", expand=True)
        layout.prop(self, "font_family", expand=False)
        if self.font_family == 'user':
            layout.prop(self, "user_font")

    def process(self):

        if not self.outputs[0].is_linked:
            return
        params_in = [s.sv_get(deepcopy=False) for s in self.inputs[:6]]
        texts_out = []
        params_in.append(self.inputs['Text Fill / Stroke'].sv_get(
            deepcopy=False, default=[[None]]))
        params_in.append(self.inputs['Lines Fill / Stroke'].sv_get(
            deepcopy=False, default=[[None]]))
        font_family = self.user_font if self.font_family == 'user' else self.font_family

        for params in zip(*mlr(params_in)):
            svg_texts = []

            for local_params in zip(*mlr(params)):
                svg_texts.append(SvgDimension(*local_params, font_family,
                                              self))

            texts_out.append(SvgGroup(svg_texts))

        self.outputs[0].sv_set(texts_out)
예제 #11
0
class SvPulgaBoundingBoxForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Spacial Ambit
    Tooltip: Define simulation Limits by Volume or Surface
    """
    bl_idname = 'SvPulgaBoundingBoxForceNode'
    bl_label = 'Pulga Boundaries Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_BOUNDARIES_FORCE'

    def update_sockets_and_node(self, context):
        self.update_sockets()
        updateNode(self, context)

    def update_sockets(self):
        self.inputs['Bounding Box'].hide_safe = self.mode != 'Box'
        self.inputs[
            'Center'].hide_safe = not 'Sphere' in self.mode and not 'Plane' in self.mode
        self.inputs['Radius'].hide_safe = not 'Sphere' in self.mode
        self.inputs['Normal'].hide_safe = not 'Plane' in self.mode
        self.inputs['Vertices'].hide_safe = not 'Mesh' in self.mode
        self.inputs['Polygons'].hide_safe = not 'Mesh' in self.mode
        self.inputs['Solid'].hide_safe = not 'Solid_(' in self.mode
        self.inputs['Solid Face'].hide_safe = self.mode != 'Solid_Face'

    mode_items = [
        'Box', 'Sphere', 'Sphere Surface', 'Plane', 'Mesh (Surface)',
        'Mesh (Volume)'
    ]
    if FreeCAD is not None:
        mode_items.append('Solid (Surface)')
        mode_items.append('Solid (Volume)')
        mode_items.append('Solid Face')

    mode: EnumProperty(name='Mode',
                       description='Boundaries definition mode',
                       items=enum_item_4(mode_items),
                       default='Box',
                       update=update_sockets_and_node)
    center: FloatVectorProperty(name='Center',
                                description='Bounding Sphere center',
                                default=(0, 0, 0),
                                size=3,
                                update=updateNode)
    radius: FloatProperty(name='Radius',
                          description='Bounding Sphere radius',
                          default=0.0,
                          update=updateNode)
    normal: FloatVectorProperty(name='Normal',
                                description='Bounding Sphere center',
                                default=(0, 0, 0),
                                size=3,
                                update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', "Bounding Box")
        self.inputs.new('SvVerticesSocket', "Center").prop_name = 'center'
        self.inputs.new('SvStringsSocket', "Radius").prop_name = 'radius'
        self.inputs.new('SvVerticesSocket', "Normal").prop_name = 'normal'
        self.inputs.new('SvVerticesSocket', "Vertices")
        self.inputs.new('SvStringsSocket', "Polygons")
        self.inputs.new('SvSolidSocket', "Solid")
        self.inputs.new('SvSurfaceSocket', "Solid Face")
        self.update_sockets()

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        layout.prop(self, 'mode')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        if self.mode == 'Box':
            b_box = self.inputs["Bounding Box"].sv_get(deepcopy=False)

            forces_out = []
            for force in b_box:
                forces_out.append(SvBoundingBoxForce(force))
        elif self.mode == 'Sphere':

            center = self.inputs["Center"].sv_get(deepcopy=False)
            radius = self.inputs["Radius"].sv_get(deepcopy=False)

            forces_out = []
            for force in zip_long_repeat(center, radius):
                forces_out.append(SvBoundingSphereForce(*force))
        elif self.mode == 'Sphere_Surface':

            center = self.inputs["Center"].sv_get(deepcopy=False)
            radius = self.inputs["Radius"].sv_get(deepcopy=False)

            forces_out = []
            for force in zip_long_repeat(center, radius):
                forces_out.append(SvBoundingSphereSurfaceForce(*force))
        elif self.mode == 'Plane':

            center = self.inputs["Center"].sv_get(deepcopy=False)
            radius = self.inputs["Normal"].sv_get(deepcopy=False)

            forces_out = []
            for force in zip_long_repeat(center, radius):
                forces_out.append(SvBoundingPlaneSurfaceForce(*force))
        elif 'Mesh' in self.mode:

            verts = self.inputs["Vertices"].sv_get(deepcopy=False)
            polygons = self.inputs["Polygons"].sv_get(deepcopy=False)
            volume = self.mode == "Mesh_(Volume)"
            forces_out = []
            for force in zip_long_repeat(verts, polygons):
                forces_out.append(SvBoundingMeshForce(*force, volume))

        elif 'Solid' in self.mode:
            input_name = 'Solid' if self.mode in [
                'Solid_(Surface)', 'Solid_(Volume)'
            ] else 'Solid Face'
            solid = self.inputs[input_name].sv_get(deepcopy=False)
            volume = self.mode == "Solid_(Volume)"
            forces_out = []
            for force in solid:
                forces_out.append(SvBoundingSolidForce(force, volume=volume))

        self.outputs[0].sv_set([forces_out])
예제 #12
0
class SvSvgTextNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Text SVG
    Tooltip: Creates SVG text.
    """
    bl_idname = 'SvSvgTextNode'
    bl_label = 'Text SVG'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_TEXT_SVG'

    font_size: FloatProperty(name='Text Size',
                             description='Font Size',
                             default=10,
                             update=updateNode)

    font_family: EnumProperty(name='Font',
                              description='Font Size',
                              items=enum_item_4([
                                  'serif', 'sans-serif', 'monospace',
                                  'cursive', 'fantasy', 'user'
                              ]),
                              default='monospace',
                              update=updateNode)

    user_font: StringProperty(name='Name',
                              description='Define font name',
                              default='',
                              update=updateNode)

    font_alignment: EnumProperty(name='Font Name',
                                 description='Define font name',
                                 items=enum_item_4(['start', 'middle', 'end']),
                                 update=updateNode)
    weight: EnumProperty(name='Font Name',
                         description='Define font name',
                         items=enum_item_4(['normal', 'bold']),
                         update=updateNode)

    angle: FloatProperty(name='Angle',
                         description='Text Rotation',
                         default=0,
                         update=updateNode)

    text: StringProperty(name='Text',
                         description='Text',
                         default='',
                         update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', "Location")
        self.inputs.new('SvStringsSocket', "Text").prop_name = 'text'
        self.inputs.new('SvStringsSocket', "Font Size").prop_name = 'font_size'
        self.inputs.new('SvStringsSocket', "Angle").prop_name = 'angle'

        self.inputs.new('SvSvgSocket', "Fill / Stroke")

        self.outputs.new('SvSvgSocket', "SVG Objects")

    def draw_buttons(self, context, layout):
        layout.prop(self, "font_family", expand=False)
        if self.font_family == 'user':
            layout.prop(self, "user_font")
        layout.prop(self, "font_alignment", expand=True)
        layout.prop(self, "weight", expand=True)

    def process(self):

        if not self.outputs[0].is_linked:
            return
        params_in = [s.sv_get(deepcopy=False) for s in self.inputs[:4]]
        texts_out = []
        params_in.append(self.inputs['Fill / Stroke'].sv_get(deepcopy=False,
                                                             default=[[None]]))

        font_family = self.user_font if self.font_family == 'user' else self.font_family
        print("process")
        for params in zip(*mlr(params_in)):
            svg_texts = []
            for loc, text, size, angle, atts in zip(*mlr(params)):
                svg_texts.append(
                    SvgText(loc, text, size, angle, self.weight, atts,
                            font_family, self.font_alignment))

            texts_out.append(SvgGroup(svg_texts))

        self.outputs[0].sv_set(texts_out)
예제 #13
0
class SvKDTreeEdgesNodeMK2(bpy.types.Node, SverchCustomTreeNode):
    '''
    Triggers: Create Edges by distance
    Tooltip: Join verts pairs by defining distance range and number of connections
    '''
    bl_idname = 'SvKDTreeEdgesNodeMK2'
    bl_label = 'KDT Closest Edges MK2'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_KDT_EDGES'

    mindist: FloatProperty(
        name='mindist', description='Minimum dist', min=0.0,
        default=0.1, update=updateNode)

    maxdist: FloatProperty(
        name='maxdist', description='Maximum dist', min=0.0,
        default=2.0, update=updateNode)

    maxNum: IntProperty(
        name='maxNum', description='max edge count',
        default=4, min=1, update=updateNode)

    skip: IntProperty(
        name='skip', description='skip first n',
        default=0, min=0, update=updateNode)

    def update_sockets(self, context):
        self.inputs['maxNum'].hide_safe = self.mode == 'Fast'
        self.inputs['skip'].hide_safe = self.mode in ['Fast', 'No_Skip']

        updateNode(self, context)
    mode: EnumProperty(
        name='Mode', description='Implementation used',
        items=enum_item_4(['Fast', 'Max Queried', 'No Skip', 'Complete']),
        default='Fast', update=update_sockets)
    list_match: EnumProperty(
        name="List Match",
        description="Behavior on different list lengths",
        items=list_match_modes, default="REPEAT",
        update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', 'Verts')
        self.inputs.new('SvStringsSocket', 'mindist').prop_name = 'mindist'
        self.inputs.new('SvStringsSocket', 'maxdist').prop_name = 'maxdist'
        self.inputs.new('SvStringsSocket', 'maxNum').prop_name = 'maxNum'
        self.inputs.new('SvStringsSocket', 'skip').prop_name = 'skip'
        self.inputs['maxNum'].hide_safe = True
        self.inputs['skip'].hide_safe = True
        self.outputs.new('SvStringsSocket', 'Edges')

    def draw_buttons(self, context, layout):
        if fast_mode():
            layout.prop(self, 'mode')
    def draw_buttons_ext(self, context, layout):
        if fast_mode():
            layout.prop(self, 'mode')
        layout.prop(self, 'list_match')

    def process(self):
        inputs = self.inputs
        outputs = self.outputs

        if not inputs['Verts'].is_linked or not outputs['Edges'].is_linked:
            return
        params = [inputs['Verts'].sv_get(deepcopy=False)]
        match = list_match_func[self.list_match]
        if fast_mode() and self.mode != 'Complete':
            if self.mode == 'Fast':
                params.extend([sk.sv_get(deepcopy=False)[0] for sk in self.inputs[1:3]])
                result = [scipy_kdt_closest_edges_fast(vs, min_d, max_d) for vs, min_d, max_d  in zip(*match(params))]
            elif self.mode == 'Max_Queried':
                params.extend([sk.sv_get(deepcopy=False)[0] for sk in self.inputs[1:]])
                result = [scipy_kdt_closest_max_queried(vs, min_d, max_d, max_num, skip) for vs, min_d, max_d, max_num, skip  in zip(*match(params))]
            elif self.mode == 'No_Skip':
                params.extend([sk.sv_get(deepcopy=False)[0] for sk in self.inputs[1:]])
                result = [scipy_kdt_closest_edges_no_skip(vs, min_d, max_d, max_num, skip) for vs, min_d, max_d, max_num, skip  in zip(*match(params))]

        else:
            params.extend([sk.sv_get(deepcopy=False)[0] for sk in self.inputs[1:]])
            result = [kdt_closest_edges(p[0], p[1:]) for p in zip(*match(params))]
        outputs['Edges'].sv_set(result)
예제 #14
0
class SvSvgFillStrokeNodeMk2(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: color, line width
    Tooltip: Define Fill /Stroke Style for svg objects
    """
    bl_idname = 'SvSvgFillStrokeNodeMk2'
    bl_label = 'Fill / Stroke SVG'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_FILL_STROKE_SVG'

    def update_actual_sockets(self):
        self.inputs['Fill Color'].hide_safe = self.fill_mode != 'FLAT'
        self.inputs['Fill Pattern'].hide_safe = self.fill_mode != 'PATTERN'
        self.inputs['Stroke Color'].hide_safe = self.stroke_mode != 'FLAT'
        self.inputs['Stroke Pattern'].hide_safe = self.stroke_mode != 'PATTERN'
        self.inputs['Stroke Width'].hide_safe = self.stroke_mode == 'NONE'
        self.inputs[
            'Dash Pattern'].hide_safe = self.stroke_type == 'Solid' or self.stroke_mode == 'NONE'

    def update_sockets(self, context):
        self.update_actual_sockets()
        updateNode(self, context)

    blend_mode: EnumProperty(name='Blend',
                             items=enum_item_4([
                                 'Normal', 'Multiply', 'Screen', 'Overlay',
                                 'Darken', 'Lighten', 'Color-dodge',
                                 'Color-burn', 'Hard-light', 'Soft-light',
                                 'Difference', 'Exclusion', 'Hue',
                                 'Saturation', 'Color', 'Luminosity'
                             ]),
                             default="Normal",
                             update=update_sockets)
    fill_modes = [('NONE', 'None', '', 0), ('FLAT', 'Flat', '', 1),
                  ('PATTERN', 'Pattern', '', 2)]
    fill_mode: EnumProperty(name='Fill',
                            items=fill_modes,
                            default="FLAT",
                            update=update_sockets)
    stroke_mode: EnumProperty(name='Stroke',
                              items=fill_modes,
                              update=update_sockets)
    stroke_width: FloatProperty(name='Stroke width',
                                description='Stroke width',
                                default=1.0,
                                update=updateNode)
    stroke_color: FloatVectorProperty(name="Stroke Color",
                                      description="Color",
                                      size=4,
                                      min=0.0,
                                      max=1.0,
                                      default=(0, 0, 0, 1),
                                      subtype='COLOR',
                                      update=update_sockets)
    stroke_linecap: EnumProperty(name='Cap',
                                 description='Line Cap',
                                 items=enum_item_4(['Butt', 'Round',
                                                    'Square']),
                                 update=updateNode)
    stroke_linejoin: EnumProperty(name='Join',
                                  description='Line Join',
                                  items=enum_item_4(
                                      ['Bevel', 'Miter', 'Round']),
                                  update=updateNode)
    paint_order: EnumProperty(name='Order',
                              description="Paint Order",
                              items=enum_item_4(['Fill Stroke',
                                                 'Stroke Fill']),
                              update=updateNode)
    stroke_type: EnumProperty(name='Type',
                              items=enum_item_4(['Solid', 'Dashed']),
                              update=update_sockets)

    fill_color: FloatVectorProperty(name="Fill Color",
                                    description="Color",
                                    size=4,
                                    min=0.0,
                                    max=1.0,
                                    default=(0, 0, 0, 1),
                                    subtype='COLOR',
                                    update=updateNode)

    def sv_init(self, context):

        self.inputs.new('SvColorSocket', "Fill Color").prop_name = 'fill_color'
        self.inputs.new('SvSvgSocket', "Fill Pattern")
        self.inputs.new('SvColorSocket',
                        "Stroke Color").prop_name = 'stroke_color'
        self.inputs.new('SvSvgSocket', "Stroke Pattern")
        self.inputs.new('SvStringsSocket',
                        "Stroke Width").prop_name = 'stroke_width'
        self.inputs.new('SvStringsSocket', "Dash Pattern")
        self.update_actual_sockets()
        self.outputs.new('SvSvgSocket', "Fill / Stroke")

    def draw_buttons(self, context, layout):
        layout.prop(self, "blend_mode", expand=False)
        layout.prop(self, "fill_mode", expand=False)
        col = layout.column(align=True)
        col.prop(self, "stroke_mode", expand=False)
        if self.stroke_mode != 'NONE':

            col.prop(self, "stroke_linecap", expand=False)
            col.prop(self, "stroke_linejoin", expand=False)
            col.prop(self, "stroke_type", expand=False)
            if self.fill_mode != 'NONE':
                layout.prop(self, "paint_order", expand=False)

    def get_data(self):
        if self.fill_mode == 'FLAT':
            fill = self.inputs['Fill Color'].sv_get(deepcopy=False)
        elif self.fill_mode == 'PATTERN':
            fill = self.inputs['Fill Pattern'].sv_get(deepcopy=False,
                                                      default=[[None]])
        else:
            fill = [[None]]
        if self.stroke_mode == 'FLAT':
            stroke = self.inputs['Stroke Color'].sv_get(deepcopy=False)
        elif self.stroke_mode == 'PATTERN':
            stroke = self.inputs['Stroke Pattern'].sv_get(deepcopy=False,
                                                          default=[[None]])
        else:
            stroke = [[None]]

        stroke_width = self.inputs['Stroke Width'].sv_get(deepcopy=False)
        dash_pattern = self.inputs['Dash Pattern'].sv_get(deepcopy=False,
                                                          default=[[None]])

        return mlr([fill, stroke, stroke_width, dash_pattern])

    def process(self):

        if not self.outputs[0].is_linked:
            return
        params_in = [
            s.sv_get(deepcopy=False, default=[[None]]) for s in self.inputs
        ]
        params_in = self.get_data()

        attributes_out = []
        for params in zip(*params_in):
            attributes = []
            dash_pattern = params[-1]
            for fill, stroke, stroke_width in zip(*mlr(params[:-1])):
                attributes.append(
                    SvgAttributes(fill, stroke, stroke_width, dash_pattern,
                                  self))
            attributes_out.append(attributes)
        self.outputs[0].sv_set(attributes_out)
예제 #15
0
class SvLoopOutNode(SverchCustomTreeNode, bpy.types.Node):
    """
    Triggers: For Loop End,
    Tooltip: End node to define a nodes for-loop.
    """
    bl_idname = 'SvLoopOutNode'
    bl_label = 'Loop Out'
    bl_icon = 'CON_FOLLOWPATH'

    mode: EnumProperty(
        name='Mode',
        description='Maximum allowed iterations',
        items=enum_item_4(['Range', 'For Each']),
        default='Range',
    )

    def sv_init(self, context):
        self.inputs.new('SvLoopControlSocket', 'Loop In')
        self.inputs.new('SvStringsSocket', 'Break')
        self.inputs.new('SvStringsSocket', 'Data 0')

        self.outputs.new('SvStringsSocket', 'Data 0')

    def draw_buttons_ext(self, context, layout):
        if self.mode == 'For_Each':
            socket_labels_box = layout.box()
            socket_labels_box.label(text="Socket Labels")
            for socket in self.inputs[2:]:
                socket_labels_box.prop(socket, "label", text=socket.name)
            socket_labels_box.operator("node.update_loop_out_socket_labels",
                                       icon='CON_FOLLOWPATH',
                                       text="Update Socket Labels")

    def update_sockets_range_mode(self, loop_in_node):
        while len(loop_in_node.inputs) > len(self.inputs):
            name = 'Data ' + str(len(self.inputs) - 2)
            self.inputs.new('SvStringsSocket', name)
            self.outputs.new('SvStringsSocket', name)
        while len(loop_in_node.inputs) < len(self.inputs):
            self.inputs.remove(self.inputs[-1])
            self.outputs.remove(self.outputs[-1])

        # in case the loop_in has not been updated yet
        if loop_in_node.inputs[-1].links:
            name = 'Data ' + str(len(self.inputs) - 2)
            self.inputs.new('SvStringsSocket', name)
            self.outputs.new('SvStringsSocket', name)

        # dynamic sockets
        for idx, socket in enumerate(loop_in_node.inputs):
            if idx == 0:
                continue

            if socket.links:
                if type(socket.links[0].from_socket) != type(
                        self.outputs[socket.name]):
                    self.inputs.remove(self.inputs[socket.name])
                    self.inputs.new(socket.links[0].from_socket.bl_idname,
                                    socket.name)
                    self.inputs.move(len(self.inputs) - 1, idx + 1)
                    self.outputs.remove(self.outputs[socket.name])
                    self.outputs.new(socket.links[0].from_socket.bl_idname,
                                     socket.name)
                    self.outputs.move(len(self.outputs) - 1, idx - 1)
        for inp, self_inp, self_outp in zip(loop_in_node.inputs[1:],
                                            self.inputs[2:], self.outputs):
            self_outp.label = inp.label
            self_inp.label = inp.label

    def update_sockets_for_each_mode(self):
        # socket handling
        if len(self.outputs) + 3 > len(self.inputs):
            self.outputs.remove(self.outputs[-1])
        if self.inputs[-1].links:
            name_input = 'Data ' + str(len(self.inputs) - 2)
            name_output = 'Data ' + str(len(self.inputs) - 3)
            other_socket = self.inputs[-1].other
            new_label = other_socket.label if other_socket.label else other_socket.name
            self.inputs[-1].label = new_label
            self.inputs.new('SvStringsSocket', name_input)
            self.outputs.new('SvStringsSocket', name_output)
        else:
            while len(self.inputs) > 3 and not self.inputs[-2].links:
                self.inputs.remove(self.inputs[-1])
                self.outputs.remove(self.outputs[-1])

        # match input socket n with output socket n
        for idx, socket in enumerate(self.inputs[2:]):

            if socket.links:

                if type(socket.links[0].from_socket) != type(
                        self.outputs[socket.name]):
                    self.outputs.remove(self.outputs[socket.name])
                    self.outputs.new(socket.links[0].from_socket.bl_idname,
                                     socket.name)
                    self.outputs.move(len(self.outputs) - 1, idx)
        for inp, outp in zip(self.inputs[2:-1], self.outputs):
            outp.label = inp.label

    def change_mode(self, loop_in_node):
        if loop_in_node.mode == 'For_Each':
            name = 'Data ' + str(len(self.inputs) - 2)
            self.inputs.new('SvStringsSocket', name)
        self.mode = loop_in_node.mode

    def sv_update(self):

        if not self.inputs[0].is_linked:
            return

        loop_in_node = self.inputs[0].links[0].from_socket.node
        if not loop_in_node.bl_idname == 'SvLoopInNode':
            return

        if loop_in_node.mode != self.mode:
            self.change_mode(loop_in_node)

        if loop_in_node.mode == 'Range':
            self.update_sockets_range_mode(loop_in_node)
        else:
            self.update_sockets_for_each_mode()

    def bad_inner_loops(self, intersection):
        inner_loops_out, inner_loops_in = [], []
        ng = self.id_data
        for node in intersection:
            if ng.nodes[node].bl_idname == 'SvLoopOutNode':
                inner_loops_out.append(node)
            if ng.nodes[node].bl_idname == 'SvLoopInNode':
                inner_loops_in.append(node)
        for node in inner_loops_out:
            if not ng.nodes[node].ready():
                return True

            inner_loop_in_node = ng.nodes[node].inputs[0].links[
                0].from_socket.node
            if not inner_loop_in_node.name in inner_loops_in:
                print("Inner Loop not well connected")
                return True
            inner_loops_in.remove(inner_loop_in_node.name)

        if inner_loops_in:
            return True

        return False

    def get_affected_nodes(self, loop_in_node):
        tree = self.id_data
        nodes_to_loop_out = make_tree_from_nodes([self.name], tree, down=False)
        nodes_from_loop_in = make_tree_from_nodes([loop_in_node.name],
                                                  tree,
                                                  down=True)
        nodes_from_loop_out = make_tree_from_nodes([self.name],
                                                   tree,
                                                   down=True)

        set_nodes_from_loop_in = frozenset(nodes_from_loop_in)
        set_nodes_from_loop_out = frozenset(nodes_from_loop_out)

        intersection = [
            x for x in nodes_to_loop_out if x in set_nodes_from_loop_in
            and tree.nodes[x].bl_idname != 'NodeReroute'
        ]
        related_nodes = [
            x for x in nodes_from_loop_in
            if x not in set_nodes_from_loop_out and x not in intersection
        ]

        return intersection, related_nodes

    def ready(self):
        if not self.inputs[0].is_linked:
            print("Inner Loop not connected")
            return False
        if not any([socket.is_linked for socket in self.outputs]):
            return False
        loop_in_node = self.inputs[0].links[0].from_socket.node
        if not loop_in_node.bl_idname == 'SvLoopInNode':
            print("Inner Loop not well connected")
            return False
        return True

    def process(self):
        if not self.ready():
            return

        loop_in_node = self.inputs[0].links[0].from_socket.node

        self.inputs[1].label = socket_labels[loop_in_node.mode]
        if loop_in_node.mode == 'Range':
            self.range_mode(loop_in_node)
        else:
            self.for_each_mode(loop_in_node)

    def break_loop(self):
        stop_ = self.inputs['Break'].sv_get(deepcopy=False, default=[[False]])
        return stop_[0][0]

    def append_data(self, out_data):
        if not self.break_loop():
            for inp, out in zip(self.inputs[2:len(self.outputs) + 2],
                                out_data):
                out.append(inp.sv_get(deepcopy=False, default=[[]])[0])

    def for_each_mode(self, loop_in_node):

        list_match = list_match_func[loop_in_node.list_match]
        params = list_match([
            inp.sv_get(deepcopy=False, default=[])
            for inp in loop_in_node.inputs[1:-1]
        ])

        if len(params[0]) == 1:
            if not self.break_loop():
                for inp, outp in zip(self.inputs[2:], self.outputs):
                    outp.sv_set(inp.sv_get(deepcopy=False, default=[]))
            else:
                for outp in self.outputs:
                    outp.sv_set([])
        else:
            intersection, related_nodes = self.get_affected_nodes(loop_in_node)
            if self.bad_inner_loops(intersection):
                raise Exception("Loops inside not well connected")

            tree_nodes = self.id_data.nodes
            do_print = loop_in_node.print_to_console
            idx = 0
            out_data = [[] for inp in self.inputs[2:]]
            do_update(intersection[:-1], tree_nodes)
            self.append_data(out_data)
            for item_params in zip(*params):
                if idx == 0:
                    idx += 1
                    continue
                for j, data in enumerate(item_params):
                    loop_in_node.outputs[j + 3].sv_set([data])
                loop_in_node.outputs['Loop Number'].sv_set([[idx]])
                idx += 1
                if do_print:
                    print(f"Looping Object Number {idx}")
                process_looped_nodes(intersection[1:-1], tree_nodes, 'Element',
                                     idx)
                self.append_data(out_data)

            for inp, outp in zip(out_data, self.outputs):
                outp.sv_set(inp)

            do_update(related_nodes, self.id_data.nodes)

    def range_mode(self, loop_in_node):
        iterations = min(int(loop_in_node.inputs['Iterations'].sv_get()[0][0]),
                         loop_in_node.max_iterations)
        if iterations == 0:
            for inp, outp in zip(loop_in_node.inputs[1:-1], self.outputs):
                outp.sv_set(inp.sv_get(deepcopy=False, default=[]))

        elif iterations == 1:

            for inp, outp in zip(self.inputs[2:], self.outputs):
                outp.sv_set(inp.sv_get(deepcopy=False, default=[]))
        else:

            intersection, related_nodes = self.get_affected_nodes(loop_in_node)
            if self.bad_inner_loops(intersection):
                raise Exception("Loops inside not well connected")

            do_print = loop_in_node.print_to_console

            tree_nodes = self.id_data.nodes
            do_update(intersection[:-1], tree_nodes)

            for i in range(iterations - 1):
                if self.break_loop():
                    break
                for j, socket in enumerate(self.inputs[2:]):
                    data = socket.sv_get(deepcopy=False, default=[])
                    loop_in_node.outputs[j + 3].sv_set(data)
                loop_in_node.outputs['Loop Number'].sv_set([[i + 1]])
                if do_print:
                    print(f"Looping iteration Number {i+1}")
                process_looped_nodes(intersection[1:-1], tree_nodes,
                                     'Iteration', i + 1)

            for inp, outp in zip(self.inputs[2:], self.outputs):
                outp.sv_set(inp.sv_get(deepcopy=False, default=[]))

            do_update(related_nodes, self.id_data.nodes)
예제 #16
0
class SvSolidifyNodeMk2(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Extrude/Thicken Mesh
    Tooltip: Extrude along normal the mesh surface.

    """
    bl_idname = 'SvSolidifyNodeMk2'
    bl_label = 'Solidify'
    bl_icon = 'MOD_SOLIDIFY'

    thickness: FloatProperty(
        name='Thickness', description='Shell thickness',
        default=0.1, update=updateNode)
    offset: FloatProperty(
        name='Offset', description='Offset Thickness from center',
        default=1, soft_min=-1, soft_max=1, update=updateNode)
    even: BoolProperty(
        name='Even Thickness', description='Mantain Thinkness by adjusting sharp corners',
        default=True, update=updateNode)

    def update_sockets(self, context):
        self.inputs['Offset'].hide_safe = self.implementation == 'Blender'
        updateNode(self, context)

    implementation: EnumProperty(
        name='Implementation', items=enum_item_4(['Sverchok', 'Blender']),
        description='Mantain Thinkness by adjusting sharp corners',
        default='Sverchok', update=update_sockets)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', 'Vertices')
        self.inputs.new('SvStringsSocket', 'Edges')
        self.inputs.new('SvStringsSocket', 'Polygons')
        self.inputs.new('SvStringsSocket', 'Thickness').prop_name = 'thickness'
        self.inputs.new('SvStringsSocket', 'Offset').prop_name = 'offset'

        self.outputs.new('SvVerticesSocket', 'Vertices')
        self.outputs.new('SvStringsSocket', 'Edges')
        self.outputs.new('SvStringsSocket', 'Polygons')
        self.outputs.new('SvStringsSocket', 'New Pols')
        self.outputs.new('SvStringsSocket', 'Rim Pols')
        self.outputs.new('SvStringsSocket', 'Pols Group')
        self.outputs.new('SvStringsSocket', 'New Verts Mask')

    def draw_buttons(self, context, layout):
        if self.implementation == 'Sverchok':
            layout.prop(self, 'even')

    def draw_buttons_ext(self, context, layout):
        layout.prop(self, 'implementation')
        self.draw_buttons(context, layout)

    def process(self):
        if not any((s.is_linked for s in self.outputs)):
            return

        if not (self.inputs['Vertices'].is_linked and self.inputs['Polygons'].is_linked):
            return

        verts = self.inputs['Vertices'].sv_get(deepcopy=False)
        edges = self.inputs['Edges'].sv_get(deepcopy=False, default=[[]])
        polys = self.inputs['Polygons'].sv_get(deepcopy=False)
        thickness = self.inputs['Thickness'].sv_get(deepcopy=False)
        offset = self.inputs['Offset'].sv_get(deepcopy=False)

        if self.implementation == 'Blender':
            func = solidify_blender
        else:
            func = solidify

        res = []

        for v, e, p, t, o in zip_long_repeat(verts, edges, polys, thickness, offset):
            res.append(func(v, e, p, t, o, self.even))

        verts_out, edges_out, polys_out, new_pols, rim_pols, pols_groups, new_verts_mask = zip(*res)


        self.outputs['Vertices'].sv_set(verts_out)
        self.outputs['Edges'].sv_set(edges_out)
        self.outputs['Polygons'].sv_set(polys_out)
        self.outputs['New Pols'].sv_set(new_pols)
        self.outputs['Rim Pols'].sv_set(rim_pols)
        self.outputs['Pols Group'].sv_set(pols_groups)
        self.outputs['New Verts Mask'].sv_set(new_verts_mask)
예제 #17
0
class SvPulgaAttractionForceNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Attraction among vertices
    Tooltip: Attraction between vertices
    """
    bl_idname = 'SvPulgaAttractionForceNode'
    bl_label = 'Pulga Attraction Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_ATTRACTION_FORCE'

    strength: FloatProperty(name='Strength',
                            description='Attraction between vertices',
                            default=0.01,
                            precision=4,
                            step=1e-2,
                            update=updateNode)
    decay: FloatProperty(
        name='Decay',
        description='0 = no decay, 1 = linear, 2 = quadratic...',
        default=1.0,
        precision=3,
        update=updateNode)
    max_distance: FloatProperty(name='Max Distance',
                                description='Maximum distance',
                                default=10.0,
                                precision=3,
                                update=updateNode)
    stop_on_collide: BoolProperty(
        name='Stop when colliding',
        description='Stop attraction if they are colliding',
        update=updateNode)
    mode: EnumProperty(name='Mode',
                       description='Algorithm used for calculation',
                       items=enum_item_4(['Brute Force', 'Kd-tree']),
                       default='Kd-tree',
                       update=updateNode)

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Strength").prop_name = 'strength'
        self.inputs.new('SvStringsSocket', "Decay").prop_name = 'decay'
        self.inputs.new('SvStringsSocket',
                        "Max Distance").prop_name = 'max_distance'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        if scipy is not None and Cython is not None:
            layout.prop(self, 'mode')
        layout.prop(self, 'stop_on_collide')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        strength = self.inputs["Strength"].sv_get(deepcopy=False)
        decay = self.inputs["Decay"].sv_get(deepcopy=False)
        max_distance = self.inputs["Max Distance"].sv_get(deepcopy=False)
        use_kdtree = self.mode in "Kd-tree" and scipy is not None and Cython is not None
        forces_out = []
        for force_params in zip_long_repeat(strength, decay, max_distance):
            forces_out.append(
                SvAttractionForce(*force_params,
                                  stop_on_collide=self.stop_on_collide,
                                  use_kdtree=use_kdtree))
        self.outputs[0].sv_set([forces_out])
예제 #18
0
class SvFormulaNodeMk5(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Formula
    Tooltip: Calculate by custom formula.
    """
    bl_idname = 'SvFormulaNodeMk5'
    bl_label = 'Formula'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_FORMULA'

    def on_update(self, context):
        self.adjust_sockets()
        updateNode(self, context)

    def on_update_dims(self, context):
        if self.output_dimensions < 4:
            self.formula4 = ""
        if self.output_dimensions < 3:
            self.formula3 = ""
        if self.output_dimensions < 2:
            self.formula2 = ""

        self.adjust_sockets()
        updateNode(self, context)

    output_dimensions: IntProperty(name="Dimensions",
                                   default=1,
                                   min=1,
                                   max=4,
                                   update=on_update_dims)

    formula1: StringProperty(default="x+y", update=on_update)
    formula2: StringProperty(update=on_update)
    formula3: StringProperty(update=on_update)
    formula4: StringProperty(update=on_update)

    separate: BoolProperty(name="Separate", default=False, update=updateNode)
    wrapping: bpy.props.EnumProperty(
        items=[(k, k, '', i) for i, k in enumerate(["-1", "0", "+1"])],
        description=
        "+1: adds a set of square brackets around the output\n 0: Keeps result unchanged\n-1: Removes a set of outer square brackets",
        default="0",
        update=updateNode)

    use_ast: BoolProperty(name="AST",
                          description="uses the ast.literal_eval module",
                          update=updateNode)
    as_list: BoolProperty(name="List output",
                          description="Forces a regular list output",
                          update=updateNode)
    ui_message: StringProperty(name="ui message")
    list_match: EnumProperty(name="List Match",
                             description="Behavior on different list lengths",
                             items=numpy_list_match_modes,
                             default="REPEAT",
                             update=updateNode)

    def update_output_socket(self, context):
        if self.outputs[0].bl_idname != socket_dict[self.output_type]:
            self.outputs.remove(self.outputs[0])
            self.outputs.new(socket_dict[self.output_type], 'Result')
            updateNode(self, context)

    output_type: EnumProperty(name="Output",
                              description="Behavior on different list lengths",
                              items=enum_item_4(socket_dict.keys()),
                              default="Number_/_Generic",
                              update=update_output_socket)

    def formulas(self):
        return [self.formula1, self.formula2, self.formula3, self.formula4]

    def formula(self, k):
        return self.formulas()[k]

    def draw_buttons(self, context, layout):
        if self.ui_message:
            r = layout.row()
            r.alert = True
            r.label(text=self.ui_message, icon='INFO')
        layout.prop(self, "formula1", text="")
        if self.output_dimensions > 1:
            layout.prop(self, "formula2", text="")
        if self.output_dimensions > 2:
            layout.prop(self, "formula3", text="")
        if self.output_dimensions > 3:
            layout.prop(self, "formula4", text="")
        row = layout.row()
        if self.inputs:
            row.prop(self, "separate", text="Split", toggle=True)
        else:
            row.prop(self, "use_ast", text="", icon="SCRIPTPLUGINS")
        row.prop(self, "wrapping", expand=True)

    def draw_buttons_ext(self, context, layout):
        layout.prop(self, "output_dimensions")
        layout.prop(self, "list_match")
        layout.prop(self, "as_list")

        layout.prop(self, "output_type")

        self.draw_buttons(context, layout)

    def sv_init(self, context):
        self.width = 230
        self.inputs.new('SvFormulaSocket', "x")

        self.outputs.new('SvStringsSocket', "Result")

    def get_variables(self):
        variables = set()

        for formula in self.formulas():
            vs = get_variables(formula)
            variables.update(vs)

        return list(sorted(variables))

    def adjust_sockets(self):
        variables = self.get_variables()

        # if current node sockets match the variables sequence, do nothing skip
        # this is the logic path that will be encountered most often.
        if len(self.inputs) == len(variables):
            if variables == [socket.name for socket in self.inputs]:
                # self.info("no UI change: socket inputs same")
                return

        # else to avoid making things complicated we rebuild the UI inputs, even when it is technically sub optimal
        self.hot_reload_sockets()

    def clear_and_repopulate_sockets_from_variables(self):
        self.inputs.clear()
        variables = self.get_variables()
        for v in variables:
            self.inputs.new('SvFormulaSocket', v)

    def hot_reload_sockets(self):
        """
        function hoisted from functorb, with deletions and edits

         - store current input socket links by name/origin
         - wipe all inputs
         - recreate new sockets from variables
         - relink former links by name on this socket, but by index from their origin.

        """

        self.debug('handling input wipe and relink')
        nodes = self.id_data.nodes
        node_tree = self.id_data

        # if any current connections... gather them
        reconnections = []
        for i in (i for i in self.inputs if i.is_linked):
            for L in i.links:
                link = lambda: None
                link.from_node = L.from_socket.node.name
                link.from_socket = L.from_socket.index  # index used here because these can come from reroute
                link.to_socket = L.to_socket.name  # this node will always have unique socket names
                reconnections.append(link)
        depths = {}
        transform = {}
        for node_input in self.inputs:
            depths[node_input.name] = node_input.depth
            transform[node_input.name] = node_input.transform

        self.clear_and_repopulate_sockets_from_variables()

        for node_input in self.inputs:
            if node_input.name in depths:
                node_input.depth = depths[node_input.name]
                node_input.transform = transform[node_input.name]
        # restore connections where applicable (by socket name), if no links.. this is a no op.
        for link in reconnections:
            try:
                from_part = nodes[link.from_node].outputs[link.from_socket]
                to_part = self.inputs[link.to_socket]
                node_tree.links.new(from_part, to_part)
            except Exception as err:
                str_from = f'nodes[{link.from_node}].outputs[{link.from_socket}]'
                str_to = f'nodes[{self}].inputs[{link.to_socket}]'
                self.exception(f'failed: {str_from} -> {str_to}')
                self.exception(err)

    def sv_update(self):
        '''
        update analyzes the state of the node and returns if the criteria to start processing
        are not met.
        '''
        if not any(len(formula) for formula in self.formulas()):
            return

        self.adjust_sockets()

    def get_input(self):
        variables = self.get_variables()
        inputs = {}

        for var in variables:
            if var in self.inputs and self.inputs[var].is_linked:
                inputs[var] = self.inputs[var].sv_get()

        return inputs

    def all_inputs_connected(self):
        if self.inputs:
            return all([socket.is_linked for socket in self.inputs])
        return True

    def migrate_from(self, old_node):
        self.output_dimensions = old_node.dimensions

    def process(self):

        if not self.outputs[0].is_linked:
            return

        # if the user specifies a variable, they must also link a value into that socket, this will prevent Exception
        self.ui_message = ""
        if not self.all_inputs_connected():
            self.ui_message = "node not fully connected"
            return

        var_names = self.get_variables()
        inputs = self.get_input()
        results = []

        if var_names:
            input_values = [inputs.get(name) for name in var_names]
            matching_f = list_match_func[self.list_match]
            parameters = matching_f(input_values)
            desired_levels = [s.depth for s in self.inputs]
            ops = [
                self.formulas(), self.separate, var_names,
                [s.transform for s in self.inputs], self.as_list
            ]

            results = recurse_f_level_control(parameters, ops, formula_func,
                                              matching_f, desired_levels)

        else:

            def joined_formulas(f1, f2, f3, f4):
                built_string = ""
                if f1: built_string += f1
                if f2: built_string += f",{f2}"
                if f3: built_string += f",{f3}"
                if f4: built_string += f",{f4}"
                return list(ast.literal_eval(built_string))

            if self.use_ast:
                results = joined_formulas(*self.formulas())
            else:
                vector = []
                for formula in self.formulas():
                    if formula:
                        value = safe_eval(formula, dict())
                        vector.append(value)
                results.extend(vector)

        if self.wrapping == "+1":
            results = [results]
        elif self.wrapping == "-1":
            results = results[0] if len(results) else results

        self.outputs['Result'].sv_set(results)
예제 #19
0
class SvLoopInNode(SverchCustomTreeNode, bpy.types.Node):
    """
    Triggers: For Loop Start,
    Tooltip: Start node to define a nodes for-loop.
    """
    bl_idname = 'SvLoopInNode'
    bl_label = 'Loop In'
    bl_icon = 'FILE_REFRESH'

    def update_iterations(self, context):
        if self.iterations > self.max_iterations:
            self.iterations = self.max_iterations
        else:
            updateNode(self, context)
    iterations: IntProperty(
        name='Iterations', description='Times to repeat the loop (define maximum value on N-Panel properties)',
        default=1, min=0, update=update_iterations)

    def update_max_iterations(self, context):
        if self.iterations > self.max_iterations:
            self.iterations = self.max_iterations

    max_iterations: IntProperty(
        name='Max Iterations', description='Maximum allowed iterations',
        default=5, min=2, update=update_max_iterations)

    linked_to_loop_out: BoolProperty(
        name='linked_to_loop_out', description='Maximum allowed iterations',
        default=False)

    print_to_console: BoolProperty(
        name='Print progress in console', description='Maximum allowed iterations',
        default=False)

    def update_mode(self, context):
        self.inputs['Iterations'].hide_safe = self.mode == "For_Each"
        if self.mode == "For_Each":
            self.outputs[1].label = 'Item Number'
            self.outputs[2].label = 'Total Items'
        else:
            self.outputs[1].label = 'Loop Number'
            self.outputs[2].label = 'Total Loops'
        updateNode(self, context)

    mode: EnumProperty(
        name='Mode', description='Maximum allowed iterations',
        items=enum_item_4(['Range', 'For Each']), default='Range',
        update=update_mode)

    list_match: EnumProperty(
        name="List Match",
        description="Behavior on different list lengths",
        items=numpy_list_match_modes, default="REPEAT",
        update=updateNode)


    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', 'Iterations').prop_name = "iterations"
        self.inputs.new('SvStringsSocket', 'Data 0')

        self.outputs.new('SvLoopControlSocket', 'Loop Out')
        self.outputs["Loop Out"].link_limit = 1
        self.outputs.new('SvStringsSocket', 'Loop Number')
        self.outputs.new('SvStringsSocket', 'Total Loops')

    def draw_buttons(self, ctx, layout):
        if not self.linked_to_loop_out:
            layout.operator("node.create_loop_out", icon='CON_FOLLOWPATH', text="Create Loop Out")
        layout.prop(self, 'mode', expand=True)

    def draw_buttons_ext(self, ctx, layout):
        layout.prop(self, 'mode', expand=True)
        if self.mode == "Range":
            layout.prop(self, "max_iterations")
        else:
            layout.prop(self, "list_match")
        layout.prop(self, 'print_to_console')
        socket_labels = layout.box()
        socket_labels.label(text="Socket Labels")
        for socket in self.inputs[1:]:
            socket_labels.prop(socket, "label", text=socket.name)
        socket_labels.operator("node.update_loop_in_socket_labels", icon='CON_FOLLOWPATH', text="Update Socket Labels")

    def rclick_menu(self, context, layout):
        layout.prop_menu_enum(self, 'mode')
        if self.mode == "Range":
            layout.prop(self, "max_iterations")
        else:
            layout.prop_menu_enum(self, 'list_match')


    def sv_update(self):

        # socket handling
        if self.inputs[-1].links:
            name_input = 'Data '+str(len(self.inputs)-1)
            name_output = 'Data '+str(len(self.inputs)-2)
            other_socket = self.inputs[-1].other
            new_label = other_socket.label if other_socket.label else other_socket.name
            self.inputs[-1].label = new_label
            self.inputs.new('SvStringsSocket', name_input)
            self.outputs.new('SvStringsSocket', name_output)
        else:
            while len(self.inputs) > 2 and not self.inputs[-2].links:
                self.inputs.remove(self.inputs[-1])
                self.outputs.remove(self.outputs[-1])
        # match input socket n with output socket n
        for idx, socket in enumerate(self.inputs[1:]):

            if socket.links:

                if type(socket.links[0].from_socket) != type(self.outputs[socket.name]):
                    self.outputs.remove(self.outputs[socket.name])
                    self.outputs.new(socket.links[0].from_socket.bl_idname, socket.name)
                    self.outputs.move(len(self.outputs)-1, idx+3)
        for inp, outp in zip(self.inputs[1:], self.outputs[3:]):
            outp.label = inp.label
        if self.outputs:
            if self.outputs[0].is_linked and self.outputs[0].links[0].to_socket.node.bl_idname == 'SvLoopOutNode':
                self.linked_to_loop_out = True
            else:
                self.linked_to_loop_out = False

    def process(self):


        self.outputs[0].sv_set([["Link to Loop Out node"]])
        self.outputs[1].sv_set([[0]])
        if self.mode == 'Range':
            iterations = int(self.inputs['Iterations'].sv_get()[0][0])
            self.outputs['Total Loops'].sv_set([[min(iterations, self.max_iterations)]])
            for inp, outp in zip(self.inputs[1:-1], self.outputs[3:]):
                outp.sv_set(inp.sv_get(deepcopy=False, default=[]))
        else:
            lens = []
            for inp, outp in zip(self.inputs[1:-1], self.outputs[3:]):
                data = inp.sv_get(deepcopy=False, default=[])
                lens.append(len(data))
                outp.sv_set([data[0]])
            self.outputs[2].sv_set([[max(lens)]])
예제 #20
0
class SvEasingNode(bpy.types.Node, SverchCustomTreeNode):
    '''Curved interpolation'''
    bl_idname = 'SvEasingNode'
    bl_label = 'Easing 0..1'
    sv_icon = 'SV_EASING'

    n_id: StringProperty(default='')

    activate: BoolProperty(name='Show',
                           description='Activate drawing',
                           default=True,
                           update=updateNode)

    selected_mode: EnumProperty(items=easing_list,
                                description="Set easing Function to:",
                                default="0",
                                update=updateNode)

    in_float: FloatProperty(min=0.0,
                            max=1.0,
                            default=0.0,
                            name='Float Input',
                            description='input to the easy function',
                            update=updateNode)

    selected_theme_mode: EnumProperty(items=enum_item_4(
        ["default", "scope", "sniper"]),
                                      default="default",
                                      update=updateNode)

    def custom_draw_socket(self, socket, context, l):
        info = socket.get_socket_info()

        r = l.row(align=True)
        split = r.split(factor=0.85)
        r1 = split.row(align=True)
        r1.prop(self, "selected_mode", text="")
        r1.prop(self, 'activate', icon='NORMALIZE_FCURVES', text="")
        if info:
            r2 = split.row()
            r2.label(text=info)

    def draw_buttons_ext(self, context, l):
        l.prop(self, "selected_theme_mode")

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Float").prop_name = 'in_float'
        self.outputs.new('SvStringsSocket',
                         "Float").custom_draw = 'custom_draw_socket'
        self.get_and_set_gl_scale_info()

    def get_drawing_attributes(self):
        """
        adjust render location based on preference multiplier setting
        """
        x, y = [
            int(j) for j in (Vector(self.absolute_location) +
                             Vector((self.width + 20, 0)))[:]
        ]

        try:
            with sv_preferences() as prefs:
                multiplier = prefs.render_location_xy_multiplier
                scale = prefs.render_scale
        except:
            # print('did not find preferences - you need to save user preferences')
            multiplier = 1.0
            scale = 1.0
        x, y = [x * multiplier, y * multiplier]

        return x, y, scale, multiplier

    def generate_shader(self, geom):
        shader = gpu.shader.from_builtin('2D_SMOOTH_COLOR')
        batch = batch_for_shader(shader,
                                 'LINES', {
                                     "pos": geom.vertices,
                                     "color": geom.vertex_colors
                                 },
                                 indices=geom.indices)
        return batch, shader

    def generate_graph_geom(self, config):

        geom = lambda: None
        x, y = config.loc
        size = 140 * config.scale
        back_color, grid_color, line_color = config.palette
        easing_func = config.easing_func

        # background geom
        w = size
        h = size
        geom.background_coords = [(x, y), (x + w, y), (w + x, y - h),
                                  (x, y - h)]
        geom.background_indices = [(0, 1, 2), (0, 2, 3)]

        # grid geom and associated vertex colors
        num_divs = 8
        offset = size / num_divs

        vertices = []
        vertex_colors = []
        indices = []
        for i in range(num_divs + 1):
            xpos1 = x + (i * offset)
            ypos1 = y
            ypos2 = y - size
            vertices.extend([[xpos1, ypos1], [xpos1, ypos2]])

            ypos = y - (i * offset)
            vertices.extend([[x, ypos], [x + size, ypos]])
            vertex_colors.extend([
                grid_color,
            ] * 4)

        for i in range(0, (num_divs + 1) * 4, 2):
            indices.append([i, i + 1])

        # graph-line geom and associated vertex colors
        idx_offset = len(vertices)
        graphline = []
        num_points = 100
        seg_diff = 1 / num_points
        for i in range(num_points + 1):
            _px = x + ((i * seg_diff) * size)
            _py = y - (1 - easing_func(i * seg_diff) * size) - size
            graphline.append([_px, _py])
            vertex_colors.append(line_color)

        vertices.extend(graphline)

        for i in range(num_points):
            indices.append([idx_offset + i, idx_offset + i + 1])

        geom.vertices = vertices
        geom.vertex_colors = vertex_colors
        geom.indices = indices
        return geom

    def process(self):
        p = self.inputs['Float'].sv_get()
        n_id = node_id(self)

        # end early
        nvBGL.callback_disable(n_id)

        float_out = self.outputs['Float']
        easing_func = easing_dict.get(int(self.selected_mode))
        if float_out.is_linked:
            out = []
            for obj in p:
                r = []
                for i in obj:
                    r.append(easing_func(i))
                out.append(r)
            float_out.sv_set(out)
        else:
            float_out.sv_set([[None]])

        if self.activate:

            config = lambda: None
            x, y, scale, multiplier = self.get_drawing_attributes()

            config.loc = (x, y)
            config.palette = palette_dict.get(self.selected_theme_mode)[:]
            config.scale = scale
            config.easing_func = easing_func

            geom = self.generate_graph_geom(config)
            config.batch, config.shader = self.generate_shader(geom)

            draw_data = {
                'mode': 'custom_function',
                'tree_name': self.id_data.name[:],
                'loc': (x, y),
                'custom_function': simple28_grid_xy,
                'args': (geom, config)
            }
            nvBGL.callback_enable(n_id, draw_data)

    def sv_free(self):
        nvBGL.callback_disable(node_id(self))

    def sv_copy(self, node):
        # reset n_id on copy
        self.n_id = ''

    def sv_update(self):
        # handle disconnecting sockets, also disconnect drawing to view?
        if not ("Float" in self.inputs):
            return
        try:
            if not self.inputs[0].other:
                nvBGL.callback_disable(node_id(self))
        except:
            print('Easing node update holdout (not a problem)')
예제 #21
0
class SvEasingNode(bpy.types.Node, SverchCustomTreeNode):
    '''Curved interpolation'''
    bl_idname = 'SvEasingNode'
    bl_label = 'Easing 0..1'
    sv_icon = 'SV_EASING'

    n_id: StringProperty(default='')

    activate: BoolProperty(name='Show',
                           description='Activate drawing',
                           default=True,
                           update=updateNode)

    selected_mode: EnumProperty(items=easing_list,
                                description="Set easing Function to:",
                                default="0",
                                update=updateNode)

    in_float: FloatProperty(min=0.0,
                            max=1.0,
                            default=0.0,
                            name='Float Input',
                            description='input to the easy function',
                            update=updateNode)

    selected_theme_mode: EnumProperty(items=enum_item_4(
        ["default", "scope", "sniper"]),
                                      default="sniper",
                                      update=updateNode)

    location_theta: FloatProperty(name="location theta")

    def custom_draw_socket(self, socket, context, l):
        r = l.row(align=True)
        split = r.split(factor=0.85)
        r1 = split.row(align=True)
        r1.prop(self, "selected_mode", text="")
        r1.prop(self, 'activate', icon='NORMALIZE_FCURVES', text="")
        r2 = split.row()
        r2.label(text=f"{socket.objects_number or ''}")

    def draw_buttons_ext(self, context, l):
        l.prop(self, "selected_theme_mode")

    def sv_init(self, context):
        self.inputs.new('SvStringsSocket', "Float").prop_name = 'in_float'
        self.outputs.new('SvStringsSocket',
                         "Float").custom_draw = 'custom_draw_socket'
        self.id_data.update_gl_scale_info()

    def get_offset(self):
        return [
            int(j) for j in (Vector(self.absolute_location) +
                             Vector((self.width + 20, 0)))[:]
        ]

    def get_drawing_attributes(self):
        """
        adjust render location based on preference multiplier setting
        """
        from sverchok.settings import get_param
        self.location_theta = get_param('render_location_xy_multiplier', 1.0)

    def generate_graph_geom(self, config):

        geom = lambda: None
        x, y = config.loc
        size = 140
        back_color, grid_color, line_color = config.palette
        easing_func = config.easing_func

        # background geom
        w = size
        h = size
        geom.background_coords = [(x, y), (x + w, y), (w + x, y - h),
                                  (x, y - h)]
        geom.background_indices = [(0, 1, 2), (0, 2, 3)]

        # grid geom and associated vertex colors
        num_divs = 8
        offset = size / num_divs

        vertices = []
        vertex_colors = []
        indices = []
        for i in range(num_divs + 1):
            xpos1 = x + (i * offset)
            ypos1 = y
            ypos2 = y - size
            vertices.extend([[xpos1, ypos1], [xpos1, ypos2]])

            ypos = y - (i * offset)
            vertices.extend([[x, ypos], [x + size, ypos]])
            vertex_colors.extend([
                grid_color,
            ] * 4)

        for i in range(0, (num_divs + 1) * 4, 2):
            indices.append([i, i + 1])

        # graph-line geom and associated vertex colors
        idx_offset = len(vertices)
        graphline = []
        num_points = 100
        seg_diff = 1 / num_points
        for i in range(num_points + 1):
            _px = x + ((i * seg_diff) * size)
            _py = y - (1 - easing_func(i * seg_diff) * size) - size
            graphline.append([_px, _py])
            vertex_colors.append(line_color)

        vertices.extend(graphline)

        for i in range(num_points):
            indices.append([idx_offset + i, idx_offset + i + 1])

        geom.vertices = vertices
        geom.vertex_colors = vertex_colors
        geom.indices = indices
        return geom

    def process(self):
        p = self.inputs['Float'].sv_get()
        n_id = node_id(self)

        # end early
        nvBGL.callback_disable(n_id)

        float_out = self.outputs['Float']
        easing_func = easing_dict.get(int(self.selected_mode))
        if float_out.is_linked:
            out = []
            for obj in p:
                r = []
                for i in obj:
                    r.append(easing_func(i))
                out.append(r)
            float_out.sv_set(out)
        else:
            float_out.sv_set([[None]])

        if self.activate and self.inputs[0].is_linked:

            self.get_drawing_attributes()
            config = lambda: None
            config.loc = (0, 0)
            config.palette = palette_dict.get(self.selected_theme_mode)[:]
            config.easing_func = easing_func

            geom = self.generate_graph_geom(config)
            # config.batch, config.shader = self.generate_shader(geom)

            draw_data = {
                'mode': 'custom_function',
                'tree_name': self.id_data.name[:],
                'node_name': self.name[:],
                'loc': get_drawing_location,
                'custom_function': simple28_grid_xy,
                'args': (geom, config)
            }
            nvBGL.callback_enable(n_id, draw_data)

    def sv_free(self):
        nvBGL.callback_disable(node_id(self))

    def sv_copy(self, node):
        self.n_id = ''
예제 #22
0
class SvNewSocketOpExp(Operator, MonadOpCommon):
    """Generate new socket"""
    bl_idname = "node.sverchok_new_socket_exp"
    bl_label = "New Socket"

    # private
    kind: StringProperty(name="io kind")

    # client
    socket_type: EnumProperty(items=socket_types, default="SvStringsSocket")
    new_prop_name: StringProperty(name="prop name")
    new_prop_type: EnumProperty(name="prop type",
                                items=enum_item_4(["Int", "Float"]),
                                default='Int')
    new_prop_description: StringProperty(name="description", default="lazy?")

    # no subtype. it is not worth it.
    enable_min: BoolProperty(default=False, name="enable min")
    enable_max: BoolProperty(default=False, name="enable max")
    enable_soft_min: BoolProperty(default=False, name="enable soft min")
    enable_soft_max: BoolProperty(default=False, name="enable soft max")

    # int specific
    new_prop_int_default: IntProperty(default=0, name="default")
    new_prop_int_min: IntProperty(default=-2**31, name="min")
    new_prop_int_max: IntProperty(default=2**31 - 1, name="max")
    new_prop_int_soft_min: IntProperty(default=-2**31, name="soft min")
    new_prop_int_soft_max: IntProperty(default=2**31 - 1, name="soft max")
    new_prop_int_step: IntProperty(default=1, name="step")

    # float specific
    new_prop_float_default: FloatProperty(default=0, name="default")
    new_prop_float_min: FloatProperty(default=-2**31, name="min")
    new_prop_float_max: FloatProperty(default=2**31 - 1, name="max")
    new_prop_float_soft_min: FloatProperty(default=-2**31, name="soft min")
    new_prop_float_soft_max: FloatProperty(default=2**31 - 1, name="soft max")
    new_prop_float_step: FloatProperty(default=1.0, name="step")

    @classmethod
    def poll(cls, context):
        try:
            if context.space_data.edit_tree.bl_idname == 'SverchGroupTreeType':
                return not context.space_data.edit_tree.library
        except:
            return False

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def get_display_props(self):
        prop_prefix = f"new_prop_{self.new_prop_type.lower()}_"
        props = "default", "min", "max", "soft_min", "soft_max", "step"
        return [(prop_prefix + prop) for prop in props]

    def draw(self, context):
        layout = self.layout
        col1 = layout.column()
        socket_row = col1.row()
        socket_row.prop(self, 'socket_type', text='Socket Type', expand=True)
        col1.prop(self, 'new_prop_name', text="Name")

        if self.kind == "outputs":
            # there are no other properties to configure for the <output node>
            return

        col1.prop(self, 'new_prop_type')

        if self.socket_type == "SvStringsSocket":
            default, min, max, soft_min, soft_max, step = self.get_display_props(
            )
            col1.prop(self, default, text="default")

            # enable min / max
            row1 = col1.row(align=True)
            row1_col1_r = row1.column().row(align=True)
            row1_col1_r.active = self.enable_min
            row1_col1_r.prop(self, "enable_min", text="", icon="CHECKMARK")
            row1_col1_r.prop(self, min)
            row1_col2_r = row1.column().row(align=True)
            row1_col2_r.active = self.enable_max
            row1_col2_r.prop(self, "enable_max", text="", icon="CHECKMARK")
            row1_col2_r.prop(self, max)

            # enable soft min / max
            row2 = col1.row(align=True)
            row2_col1_r = row2.column().row(align=True)
            row2_col1_r.active = self.enable_soft_min
            row2_col1_r.prop(self,
                             "enable_soft_min",
                             text="",
                             icon="CHECKMARK")
            row2_col1_r.prop(self, soft_min)
            row2_col2_r = row2.column().row(align=True)
            row2_col2_r.active = self.enable_soft_max
            row2_col2_r.prop(self,
                             "enable_soft_max",
                             text="",
                             icon="CHECKMARK")
            row2_col2_r.prop(self, soft_max)

        col1.prop(self, "new_prop_description")

    def get_prop_dict(self):
        prop_dict = {}
        prop_dict['name'] = self.new_prop_name

        # we do not set the slider on the <output node> sockets
        if self.kind == 'outputs':
            return {}

        sig = self.new_prop_type.lower()
        if sig in {"int", "float"}:
            # prop_dict['update'] = updateNode
            prop_dict['default'] = getattr(self, f"new_prop_{sig}_default")
            properties = 'min max soft_min soft_max'.split()
            for prop in properties:
                if getattr(self, f"enable_{prop}"):
                    prop_dict[prop] = getattr(self, f"new_prop_{sig}_{prop}")

        return prop_dict

    def execute(self, context):
        monad = context.space_data.edit_tree
        monad_node = monad.instances[0]
        with monad_node.sv_throttle_tree_update():
            self.add_node(monad)

        return {'FINISHED'}

    def add_node(self, tree):
        class SvSingleSocketNode():
            bl_idname = 'SvSingleSocketNode'
            bl_label = 'DO NOT USE'
            bl_icon = 'MOD_CURVE'

        # [x] define new node
        prop_dict = self.get_prop_dict()
        bases = (SvSingleSocketNode, Node, SverchCustomTreeNode)
        prop_func = IntProperty if self.new_prop_type == "int" else FloatProperty

        # [x] -- add prop if needed (only when inputs)
        cls_dict = {}
        cls_dict['__annotations__'] = {}
        cls_dict['__annotations__'][self.new_prop_name] = prop_func(
            **prop_dict)
        cls_name = SvSingleSocketNode.bl_idname
        cls_ref = type(cls_name, bases, cls_dict)

        # [x] register new node (but unregister first if needed..)
        old_cls_ref = get_node_class_reference(cls_name)
        if old_cls_ref:
            unregister_node_class(old_cls_ref)

        register_node_class(cls_ref)

        # [x] add node
        property_node = tree.nodes.new(cls_name)

        # [x] -- add socket to node (add both in the template)
        io_sockets = getattr(property_node, self.kind)
        if self.kind == 'outputs':
            io_sockets.new(self.socket_type, self.new_prop_name)
        else:
            io_sockets.new(self.socket_type,
                           prop_dict['name']).prop_name = prop_dict['name']

        # [x] link node
        io_node = tree.input_node if self.kind == 'inputs' else tree.output_node
        out2in = self.kind == 'inputs'
        AB_LINK = (io_node.outputs[-1],
                   io_sockets[-1]) if out2in else (io_sockets[-1],
                                                   io_node.inputs[-1])
        tree.links.new(*AB_LINK)

        # [x] unlink, remove node
        # tree.links.remove(*AB_LINK)
        tree.nodes.remove(property_node)

        # [x] unregister node
        unregister_node_class(cls_ref)
예제 #23
0
class SvSvgFillStrokeNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: color, line width
    Tooltip: Define Fill /Stroke Style for svg objects
    """
    bl_idname = 'SvSvgFillStrokeNode'
    bl_label = 'Fill / Stroke SVG'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_FILL_STROKE_SVG'

    replacement_nodes = [('SvSvgFillStrokeNodeMk2', None, None)]

    def update_actual_sockets(self):
        self.inputs['Fill Color'].hide_safe = self.fill_mode == 'NONE'
        self.inputs['Stroke Color'].hide_safe = self.stroke_mode == 'NONE'
        self.inputs['Stroke Width'].hide_safe = self.stroke_mode == 'NONE'
        self.inputs[
            'Dash Pattern'].hide_safe = self.stroke_type == 'Solid' or self.stroke_mode == 'NONE'

    def update_sockets(self, context):
        self.update_actual_sockets()
        updateNode(self, context)

    fill_modes = [('NONE', 'None', '', 0), ('FLAT', 'Flat', '', 1)]
    fill_mode: EnumProperty(name='Fill',
                            items=fill_modes,
                            default="FLAT",
                            update=update_sockets)
    stroke_mode: EnumProperty(name='Stroke',
                              items=fill_modes,
                              update=update_sockets)
    stroke_width: FloatProperty(name='Stroke width',
                                description='Stroke width',
                                default=1.0,
                                update=updateNode)
    stroke_color: FloatVectorProperty(name="Stroke Color",
                                      description="Color",
                                      size=4,
                                      min=0.0,
                                      max=1.0,
                                      default=(0, 0, 0, 1),
                                      subtype='COLOR',
                                      update=update_sockets)
    stroke_linecap: EnumProperty(name='Cap',
                                 description='Line Cap',
                                 items=enum_item_4(['Butt', 'Round',
                                                    'Square']),
                                 update=updateNode)
    stroke_linejoin: EnumProperty(name='Join',
                                  description='Line Join',
                                  items=enum_item_4(
                                      ['Bevel', 'Miter', 'Round']),
                                  update=updateNode)
    paint_order: EnumProperty(name='Order',
                              description="Paint Order",
                              items=enum_item_4(['Fill Stroke',
                                                 'Stroke Fill']),
                              update=updateNode)
    stroke_type: EnumProperty(name='Type',
                              items=enum_item_4(['Solid', 'Dashed']),
                              update=update_sockets)

    fill_color: FloatVectorProperty(name="Fill Color",
                                    description="Color",
                                    size=4,
                                    min=0.0,
                                    max=1.0,
                                    default=(0, 0, 0, 1),
                                    subtype='COLOR',
                                    update=updateNode)

    # fill-rule stroke-dasharray stroke-linecap, stroke-linejoin

    def sv_init(self, context):

        self.inputs.new('SvColorSocket', "Fill Color").prop_name = 'fill_color'
        self.inputs.new('SvColorSocket',
                        "Stroke Color").prop_name = 'stroke_color'
        self.inputs.new('SvStringsSocket',
                        "Stroke Width").prop_name = 'stroke_width'
        self.inputs.new('SvStringsSocket', "Dash Pattern")
        self.update_actual_sockets()
        self.outputs.new('SvSvgSocket', "Fill / Stroke")

    def draw_buttons(self, context, layout):
        layout.prop(self, "fill_mode", expand=False)
        col = layout.column(align=True)
        col.prop(self, "stroke_mode", expand=False)
        if self.stroke_mode != 'NONE':

            col.prop(self, "stroke_linecap", expand=False)
            col.prop(self, "stroke_linejoin", expand=False)
            col.prop(self, "stroke_type", expand=False)
            if self.fill_mode != 'NONE':
                layout.prop(self, "paint_order", expand=False)

    def process(self):

        if not self.outputs[0].is_linked:
            return
        params_in = [
            s.sv_get(deepcopy=False, default=[[None]]) for s in self.inputs
        ]
        attributes_out = []
        for params in zip(*mlr(params_in)):
            attributes = []
            dash_pattern = params[-1]
            for fill_color, stroke_color, stroke_width in zip(
                    *mlr(params[:-1])):
                attributes.append(
                    SvgAttributes(fill_color, stroke_width, stroke_color,
                                  dash_pattern, self))
            attributes_out.append(attributes)
        self.outputs[0].sv_set(attributes_out)
예제 #24
0
class SvSvgMeshNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Polygons/Edges SVG
    Tooltip: Generate SVG Mesh Data
    """
    bl_idname = 'SvSvgMeshNode'
    bl_label = 'Mesh SVG'
    bl_icon = 'MESH_CIRCLE'
    sv_icon = 'SV_MESH_SVG'

    elements_sort: BoolProperty(
        name='Elements Sort',
        description='Sort Polygons / Edges according to distance',
        update=updateNode)
    sort_mode: EnumProperty(name="Sort mode",
                            description="How polygons/ edges are sorted",
                            items=z_sort_mode_items,
                            update=updateNode)

    invert_sort: BoolProperty(name='Invert Order', update=updateNode)

    def update_sockets(self, context):
        self.inputs[
            'Projection Plane'].hide_safe = self.projection_mode == "Orthogrphic" and self.projection_plane != 'User'
        updateNode(self, context)

    projection_mode: EnumProperty(name='Mode',
                                  description='Projection mode',
                                  items=enum_item_4(
                                      ["Orthogrphic", 'Perspective']),
                                  update=update_sockets)

    projection_plane: EnumProperty(name='Plane',
                                   description='Projection plane',
                                   items=enum_item_4(
                                       ['XY', 'XZ', 'YZ', 'User']),
                                   update=update_sockets)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', "Vertices")
        self.inputs.new('SvStringsSocket', "Polygons / Edges")
        self.inputs.new('SvMatrixSocket', "Projection Plane")
        self.inputs["Projection Plane"].hide_safe = True
        self.inputs.new('SvMatrixSocket', "Offset")
        self.inputs.new('SvSvgSocket', "Fill / Stroke")

        self.outputs.new('SvSvgSocket', "SVG Objects")
        self.outputs.new('SvVerticesSocket', "Verts to project")

    def draw_buttons(self, context, layout):
        layout.prop(self, 'projection_mode')
        if self.projection_mode == 'Orthogrphic':
            layout.prop(self, 'projection_plane')
        layout.prop(self, 'elements_sort')
        if self.elements_sort:
            layout.prop(self, 'sort_mode')
            layout.prop(self, 'invert_sort')

    def process(self):

        if not self.outputs[0].is_linked:
            return

        verts_in = self.inputs['Vertices'].sv_get(deepcopy=True)
        pols_in = self.inputs['Polygons / Edges'].sv_get(deepcopy=True)
        planes_in = self.inputs['Projection Plane'].sv_get(deepcopy=True,
                                                           default=[Matrix()])
        offset_in = self.inputs['Offset'].sv_get(deepcopy=True,
                                                 default=[Matrix()])
        atts_in = self.inputs['Fill / Stroke'].sv_get(deepcopy=False,
                                                      default=None)

        shapes = []
        verts_to_project = []
        if self.projection_mode == 'Orthogrphic':
            projection_func = ortho_projection_func_dict[self.projection_plane]
        else:
            projection_func = perspective_proyection

        for verts, pols, p_plane, offset, atts in zip(
                *mlr([verts_in, pols_in, planes_in, offset_in, atts_in])):

            verts_p = projection_func(verts, p_plane, offset)
            verts_to_project.append(verts_p)
            shapes.append(SvgMesh(verts_p, pols, atts, self))

        self.outputs[0].sv_set(shapes)
        self.outputs[1].sv_set(verts_to_project)
예제 #25
0
class SvPulgaAttractorsForceNodeMk2(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Attraction Points
    Tooltip: Attractor/Repeller points with distance limit
    """
    bl_idname = 'SvPulgaAttractorsForceNodeMk2'
    bl_label = 'Pulga Attractors Force'
    bl_icon = 'MOD_PHYSICS'
    sv_icon = 'SV_PULGA_ATTRACTORS_FORCE'

    strength: FloatProperty(name='Strength',
                            description='Attractors Force magnitude',
                            default=1.0,
                            precision=3,
                            update=updateNode)
    max_distance: FloatProperty(
        name='Max Distance',
        description='Attractors maximum influence distance',
        default=10.0,
        precision=3,
        update=updateNode)
    decay_power: FloatProperty(
        name='Decay',
        description=
        'Decay with distance 0 = no decay, 1 = linear, 2 = cubic...',
        default=0.0,
        precision=3,
        update=updateNode)

    def update_sockets(self, context):
        self.inputs['Direction'].hide_safe = self.mode not in ['Line', 'Plane']
        if self.mode == 'Line':
            self.inputs['Direction'].label = 'Direction'
        else:
            self.inputs['Direction'].label = 'Normal'
        updateNode(self, context)

    mode: EnumProperty(name='Mode',
                       items=enum_item_4(['Point', 'Line', 'Plane']),
                       default='Point',
                       update=update_sockets)

    def sv_init(self, context):
        self.inputs.new('SvVerticesSocket', "Location")
        self.inputs.new('SvVerticesSocket', "Direction")
        self.inputs["Direction"].hide_safe = True
        self.inputs.new('SvStringsSocket', "Strength").prop_name = 'strength'
        self.inputs.new('SvStringsSocket',
                        "Max. Distance").prop_name = 'max_distance'
        self.inputs.new('SvStringsSocket', "Decay").prop_name = 'decay_power'

        self.outputs.new('SvPulgaForceSocket', "Force")

    def draw_buttons(self, context, layout):
        layout.prop(self, 'mode')

    def process(self):

        if not any(s.is_linked for s in self.outputs):
            return
        loc = self.inputs["Location"].sv_get(deepcopy=False)
        strength = self.inputs["Strength"].sv_get(deepcopy=False)
        max_distance = self.inputs["Max. Distance"].sv_get(deepcopy=False)
        decay_power = self.inputs["Decay"].sv_get(deepcopy=False)

        forces_out = []

        forces_out = []
        if self.mode == 'Point':
            for force in zip_long_repeat(loc, strength, max_distance,
                                         decay_power):

                forces_out.append(SvAttractorsForce(*force))
        else:
            direction = self.inputs["Direction"].sv_get(deepcopy=False)
            force_class = SvAttractorsLineForce if self.mode == 'Line' else SvAttractorsPlaneForce
            for force in zip_long_repeat(loc, direction, strength,
                                         max_distance, decay_power):

                forces_out.append(force_class(*force))

        self.outputs[0].sv_set([forces_out])