OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / meshtools / applymod.py

228 lines · python ref:
import bpy
# from ... utility import modifier
from ... utility import geometry_nodes, modifier
from bpy.props import EnumProperty
from ... utils.objects import get_current_selected_status
from ... utils.context import ExecutionContext

from ... utils.modifiers import apply_modifiers
from ... utility import addon
from ...ui_framework.operator_ui import Master

def iterate_titled_as_string(iter, separator=','):
    applied_types = ''
    separator = f'{separator} '
    for i in iter:
        applied_types += i.title() + separator
    return applied_types[:-len(separator)]

class HOPS_OT_ApplyModifiers(bpy.types.Operator):
    bl_idname = "hops.apply_modifiers"
    bl_label = "Apply Modifiers"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'WINDOW'
    bl_options = {"REGISTER", "UNDO"}
    bl_description = """Smart Apply

LMB - Smart Apply Modifiers (keeps last BVL and WN)
SHIFT - Smart Apply Duplicate (w/ clear last BVL and WN)
CTRL - Convert to curve (edge / face)
ALT - Step

Last refers to mods within 4 modifiers of the end of stack

"""

    modifier_types: EnumProperty(
        name='Modifier Types',
        description='Settings to display',
        items=[
            ('NONE', 'All', ''),
            ('BOOLEAN', 'Boolean', ''),
            ('MIRROR', 'Mirror', ''),
            ('BEVEL', 'Bevel', ''),
            ('SOLIDIFY', 'Solidify', ''),
            ('ARRAY', 'Array', '')],
        default='BOOLEAN')

    called_ui = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.text_header = "nothing"
        self.extra_text = "nothing"
        HOPS_OT_ApplyModifiers.called_ui = False

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.label(text="Modifiers Applied")
        colrow = col.row(align=True)
        colrow.prop(self, "modifier_types", expand=True)

    def invoke(self, context, event):

        applied = None
        original_remove_setting = addon.preference().property.Hops_smartapply_remove_cutters

        if event.shift and not event.ctrl and not event.alt:
            bpy.ops.object.duplicate()
            extra_text = "Unapplied modifiers removed"
            #Selected Must be here
            selected = context.selected_objects
            for obj in selected:
                if obj.type != 'MESH':
                    continue

                if original_remove_setting:
                    addon.preference().property.Hops_smartapply_remove_cutters = False
                applied = apply_mod(self, obj, clear_last=event.shift)
            addon.preference().property.Hops_smartapply_remove_cutters = original_remove_setting
            self.text_header = "Clone Smart Apply"

        elif event.ctrl and not event.shift and not event.alt:
            applied = 0
            bpy.ops.object.convert(target='CURVE')
            self.report({'INFO'}, F'Converted to Curve')
            self.text_header = "Converted To Curve"
            try:
                bpy.ops.hops.adjust_curve('INVOKE_DEFAULT')
                extra_text = "Mesh Converted to curve"
            except:
                self.text_header = "Nice Try Bud"
                self.extra_text = "This will not convert to curve. Sorry."
                pass

        elif event.alt and not event.shift and not event.ctrl:
            selected = context.selected_objects
            for obj in selected:
                #applied = apply_mod(self, obj, clear_last=False)
                bpy.ops.hops.step()
                self.report({'INFO'}, F'Smart Apply - Step')
                self.text_header = "Object Stepped"
                return {"FINISHED"}

        elif event.ctrl and event.shift and not event.alt:
            self.text_header = "Mod Render Visibility"
            selected = context.selected_objects
            try:
                for obj in selected:
                    for mod in context.object.modifiers:
                        mod.show_render = True
                self.text_header = "Success"
            except:
                self.text_header = "A complication has occured"

        elif event.ctrl and event.alt and not event.shift:
            self.text_header = "nothing"

        elif event.shift and event.alt and not event.ctrl:
            self.text_header = "nothing"

        else:
            selected = context.selected_objects
            for obj in selected:
                if obj.type != 'MESH':
                    continue

                before_mod_count = len(context.active_object.modifiers[:])
                if len(selected) > 1:
                    if original_remove_setting:
                        addon.preference().property.Hops_smartapply_remove_cutters = False
                    self.extra_text = "Remove cutters bypassed for multi-selection"
                applied = apply_mod(self, obj, clear_last=event.shift)
                addon.preference().property.Hops_smartapply_remove_cutters = original_remove_setting
                self.report({'INFO'}, F'Modifiers Applied')
                self.text_header = "Smart Apply"

        # Operator UI
        if not HOPS_OT_ApplyModifiers.called_ui:
            HOPS_OT_ApplyModifiers.called_ui = True

            bpy.ops.hops.draw_wire_mesh_launcher('INVOKE_DEFAULT', target='SELECTED')

            ui = Master()
            ob = [mod.name for mod in context.active_object.modifiers]
            selected = context.selected_objects

            try:
                draw_data = [
                    [self.text_header],
                    [f"Modifiers Applied ", f"{before_mod_count - len(context.active_object.modifiers[:])}"]
                ]
                if len(context.active_object.modifiers[:]) >= 1:
                    draw_data.insert(-1, ["Remaining Modifiers ", len(context.active_object.modifiers[:])]),
                    for mod in reversed(context.active_object.modifiers):
                        draw_data.insert(-2, [" ", mod.name]),

                else:
                    draw_data.insert(-1, ["Remaining Modifiers ", "0"]),
                if len(selected[:]) == 1:
                    if addon.preference().property.Hops_smartapply_remove_cutters:
                        #draw_data.insert(-2, ["Cutters Removed", f'{(len[cutters])}']),
                        draw_data.insert(-2, ["Cutters Removed", (addon.preference().property.Hops_smartapply_remove_cutters)]),
                else:
                    draw_data.insert(-2, ["Cutters Removed", "False / multi-select"]),

            except:
                draw_data = [
                    [self.text_header],
                    [self.extra_text, ""],
                    ["No additional info", ""]
                ]

            ui.receive_draw_data(draw_data=draw_data)
            ui.draw(draw_bg=addon.preference().ui.Hops_operator_draw_bg, draw_border=addon.preference().ui.Hops_operator_draw_border)

        del applied
        return {"FINISHED"}


def apply_mod(self, obj, clear_last=False):

    mod = None

    mods = [mod for mod in obj.modifiers]
    bevels = [mod for mod in obj.modifiers if mod.type == 'BEVEL']
    mirrors = [mod for mod in obj.modifiers if mod.type == 'MIRROR']
    excluded = [mod for mod in obj.modifiers if mod.type == 'WEIGHTED_NORMAL' or geometry_nodes.SmoothByAngle.is_valid_modifier(mod)]

    if len(bevels) > 1 or (len(bevels) and bevels[0].limit_method in {'WEIGHT', 'ANGLE',}):
        if not mods.index(bevels[-1]) < len(mods) - 4:
            excluded.append(bevels[-1])

    if len(mirrors) > 1:
        excluded.append(mirrors[-1])

    # if len(bevels) > 1:
    #     for mod in bevels:
    #         if mod not in excluded:
    #             if mod.limit_method == 'VGROUP':
    #                 excluded.append(mod)

    if addon.preference().property.Hops_smartapply_remove_cutters:
        cutters = [modifier.bool_object_get(mod) for mod in obj.modifiers if mod.type in {'BOOLEAN', 'NODES'}]
        cutters = {o for o in cutters if o}

    modifier.apply(obj, ignore=excluded)

    if addon.preference().property.Hops_smartapply_remove_cutters:
        for cutter in cutters:
            try:
                bpy.data.objects.remove(cutter)
            except:
                addon.preference().property.Hops_smartapply_remove_cutters = False
                self.report({'ERROR_INVALID_INPUT'}, F'Cannot remove same Cutter from multiple objects')

    if clear_last:
        #bpy.ops.object.duplicate()
        for mod in obj.modifiers:
            if mod.type in {'WEIGHTED_NORMAL', 'BEVEL'}:
                obj.modifiers.remove(mod)

    for mod in excluded:
        mods.remove(mod)

    return mods