1
$\begingroup$

So in this script for Csharpen I am getting an error after the first if for the bpy.ops.object.convert(target='MESH'). I am not sure why that is since it was working fine before adding conditionals. Is there a reason why that is happening?

class sharpenOperator(bpy.types.Operator):
    '''Sharpen With Modifiers and Bevelling'''
    bl_idname = "csharpen.objects"
    bl_label = "CSharpen"
    bl_options = {'REGISTER', 'UNDO'} 

    ssharpangle = FloatProperty(name="SSharpening Angle", description="Set SSharp Angle", default= 30.0, min = 0.0, max = 180.0)

    angle = FloatProperty(name="AutoSmooth Angle", description="Set AutoSmooth angle", default= 60.0, min = 0.0, max = 180.0)

    bevelwidth = FloatProperty(name="Bevel Width Amount", description="Set Bevel Width", default= 0.0071, min = 0.002, max = .25)

    applyall = BoolProperty(default = True)

    originalbevel = FloatProperty()

    # ADD A DRAW FUNCTION TO DISPLAY PROPERTIES ON THE F6 MENU
    def draw(self, context):
        layout = self.layout

        box = layout.box()
        # DRAW YOUR PROPERTIES IN A BOX
        box.prop( self, 'ssharpangle', text = "SsharpAngle" )
        box.prop( self, 'angle', text = "SmoothingAngle" )
        box.prop( self, 'bevelwidth', text = "BevelWidth")
        box.prop( self, 'applyall', text = "ApplyAll")

    def execute(self, context):
        #convert angle
        ob = bpy.context.selected_objects
        angle = self.angle
        originalbevel = self.originalbevel
        ssharpangle = self.ssharpangle
        angle = angle * (3.14159265359/180)
        ssharpangle = ssharpangle * (3.14159265359/180)
        bevelwidth = self.bevelwidth

        #Sets the Bevel Width To The Orig Bevel Width
        originalbevel = bpy.context.object.modifiers["Bevel"].width

        if self.applyall:
            #remove modifiers no one would want applied in this instance
            bpy.ops.object.modifier_remove(modifier="Bevel")
            bpy.ops.object.modifier_remove(modifier="Solidify")
            #convert to mesh for sanity
            bpy.ops.object.convert(target='MESH')

            #apply the scale to keep me sane
            bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

            #Start In Edit Mode
            bpy.ops.object.mode_set(mode='EDIT')

            #Unhide all The Geo!
            bpy.ops.mesh.reveal()

            #Clear SSharps Then Redo It
            #bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            #bpy.ops.mesh.select_all(action='DESELECT')
            #bpy.ops.mesh.select_all(action='TOGGLE')

            #AR suggested using -1s instead of Zeroes
            #bpy.ops.transform.edge_bevelweight(value=-1)
            #bpy.ops.mesh.mark_sharp(clear=True)
            #bpy.ops.transform.edge_crease(value=-1)

            #then do the csharp operator stuff.
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            bpy.ops.mesh.select_all(action='DESELECT')

            #Sharpening now using the ssharpangle parameter.
            bpy.ops.mesh.edges_select_sharp(sharpness=ssharpangle)

            bpy.ops.transform.edge_bevelweight(value=1)
            bpy.ops.transform.edge_crease(value=1)
            bpy.ops.mesh.mark_sharp()
            bpy.ops.object.editmode_toggle()     

        else:
            #Start In Edit Mode
            bpy.ops.object.mode_set(mode='EDIT')

            #Unhide all The Geo!
            bpy.ops.mesh.reveal()

            #Clear SSharps Then Redo It
            #bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            #bpy.ops.mesh.select_all(action='DESELECT')
            #bpy.ops.mesh.select_all(action='TOGGLE')

            #AR suggested using -1s instead of Zeroes
            #bpy.ops.transform.edge_bevelweight(value=-1)
            #bpy.ops.mesh.mark_sharp(clear=True)
            #bpy.ops.transform.edge_crease(value=-1)

            #then do the csharp operator stuff.
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            bpy.ops.mesh.select_all(action='DESELECT')

            #Sharpening now using the ssharpangle parameter.
            bpy.ops.mesh.edges_select_sharp(sharpness=ssharpangle)

            bpy.ops.transform.edge_bevelweight(value=1)
            bpy.ops.transform.edge_crease(value=1)
            bpy.ops.mesh.mark_sharp()
            bpy.ops.object.editmode_toggle()     

        #keep the old here for now
        bpy.ops.object.modifier_add(type='BEVEL')
        bpy.context.object.modifiers["Bevel"].use_clamp_overlap = False
        bpy.context.object.modifiers["Bevel"].show_in_editmode = False
        bpy.context.object.modifiers["Bevel"].width = 0.0071
        bpy.context.object.modifiers["Bevel"].segments = 3
        bpy.context.object.modifiers["Bevel"].profile = 0.70
        bpy.context.object.modifiers["Bevel"].limit_method = 'WEIGHT'

        #Sets Bevel To Bevel Width needs IF
        bpy.context.object.modifiers["Bevel"].width = bevelwidth

        bpy.context.object.data.use_auto_smooth = True

        #now sets angle to Var angle.
        bpy.context.object.data.auto_smooth_angle = angle


        bpy.ops.object.shade_smooth()

        return {'FINISHED'}    
$\endgroup$
0

1 Answer 1

5
$\begingroup$

My suggestion is don't use operators when you can use low level API instead. Possibly a little difficult at first, but can save a lot of context hassles associated with calling operators, as well as giving (IMO) more control.

Object.to_mesh can be used to create a mesh with modifiers applied etc, details in link.

Modifiers can be added / removed from the objects modifier collection, using obj.modifiers.new(...), modifiers.remove(mod) respectively. I've used a dictionary to store the original settings of the Bevel modifier(s), removed them, created a new mesh with other modifiers applied, then re-added bevel mod to the modifiers collection.

To add a bevel modifier and set properties.

mod = ob.modifiers.new("Bevel", 'BEVEL')
mod.width = self.bevelwidth

Another thing, blender natively uses radians as an angle unit, set the subtype to 'ANGLE' of a FloatProperty and the "conversions" are done for you via the UI. The math.radians(degrees) method can be used to set the props.

import bpy
from bpy.props import *
from math import pi, radians

class sharpenOperator(bpy.types.Operator):
    '''Sharpen With Modifiers and Bevelling'''
    bl_idname = "csharpen.objects"
    bl_label = "CSharpen"
    bl_options = {'REGISTER', 'UNDO'} 
    items = [(x.identifier, x.name, x.description, x.icon) 
             for x in bpy.types.Modifier.bl_rna.properties['type'].enum_items]

    modtypes = EnumProperty(name="Modifier Types",
                           items=[(id, name, desc, icon, 2**i) for i, (id, name, desc, icon) in enumerate(items)
                                   if id in ['BEVEL', 'BOOLEAN', 'SOLIDIFY', 'EDGE_SPLIT']],
                           description="Don't apply these",
                           default={'BEVEL'},
                           options={'ENUM_FLAG'})

    angle = FloatProperty(name="AutoSmooth Angle",
                          description="Set AutoSmooth angle",
                          default= radians(60.0),
                          min = 0.0,
                          max = radians(180.0),
                          subtype='ANGLE')

    bevelwidth = FloatProperty(name="Bevel Width Amount",
                               description="Set Bevel Width",
                               default=0.0071,
                               min =
                               0.002,
                               max =0.25)

    apply_all = BoolProperty(default = True)

    original_bevel = FloatProperty()

    @classmethod
    def poll(cls, context):
        ob = context.object
        if ob is None:
            return False
        return (ob.type == 'MESH')

        # ADD A DRAW FUNCTION TO DISPLAY PROPERTIES ON THE F6 MENU
    def draw(self, context):
        layout = self.layout

        box = layout.box()
        # DRAW YOUR PROPERTIES IN A BOX
        #box.prop( self, 'ssharpangle', text = "SsharpAngle")
        col = box.column()
        col.prop(self, "modtypes", expand=True)
        box.prop( self, 'angle', text = "SmoothingAngle" )
        box.prop( self, 'bevelwidth', text = "BevelWidth")
        box.prop( self, 'apply_all', text = "ApplyAll")

    def execute(self, context):
        scene = context.scene
        ob = context.object  # soapbox call don't use bpy.context as context is passed
        obs = context.selected_objects
        angle = self.angle
        original_bevel = self.original_bevel
        bevelwidth = self.bevelwidth

        #Sets the Bevel Width To The Orig Bevel Width

        mod_dic = {}
        if self.apply_all:
            #remove modifiers no one would want applied in this instance

            #bpy.ops.object.modifier_remove(modifier="Bevel")
            #bpy.ops.object.modifier_remove(modifier="Solidify")
            # replace with       
            mods = [m for m in ob.modifiers if m.type in self.modtypes]
            for mod in mods:

                mod_dic[mod.name] = {k:getattr(mod, k) for k in mod.bl_rna.properties.keys()
                                     if k not in ["rna_type"]}
                print(mod_dic)
                ob.modifiers.remove(mod)

            #convert to mesh for sanity
            #bpy.ops.object.convert(target='MESH')            
            #Object.to_mesh(scene, apply_modifiers, settings, calc_tessface=True, calc_undeformed=False)

            mesh_name = ob.data.name
            ob.data.name = 'XXXX'
            # remove the old mesh
            if not ob.data.users:
                bpy.data.meshes.remove(ob.data)               
            mesh = ob.to_mesh(scene, True, 'PREVIEW') # or 'RENDER'
            ob.modifiers.clear()
            mesh.name = mesh_name
            ob.data = mesh

            for name, settings in mod_dic.items():
                m = ob.modifiers.new(name, settings["type"])
                for s, v in settings.items():
                    if s == "type":
                        continue
                    setattr(m, s, v)

        return {'FINISHED'}

def register():
    bpy.utils.register_module(__name__)

register()
bpy.ops.csharpen.objects()

EDIT Added an EnumProperty as an example of how to choose which mods to "keep" (remove then re-add)

$\endgroup$
7
  • $\begingroup$ your syntax is so different than what i was used to. I figured i was writing code pretty badly. So in its current state it removes the bevel. What if I wanted to apply basically all boolean modifiers but keep the values and settings of the bevel? $\endgroup$ Commented Oct 24, 2015 at 8:59
  • $\begingroup$ Basically Csharpen's goal is to add a bevel modifier with specific attributes but also alternatively collapse modifiers(mainly booleans) while keeping the bevels in action. $\endgroup$ Commented Oct 24, 2015 at 9:06
  • $\begingroup$ Edited post to i) re-add all modifier types removed, added a tip on how select what mods to remove and how to add a new modifier. Added a multi enum prop to decide which mods to keep. $\endgroup$
    – batFINGER
    Commented Oct 24, 2015 at 10:17
  • $\begingroup$ now what about having it do bevelling normally and then on reapplication goes through the other process? On the normal run I would expect it to ask for bevel / sharpening information and then on subsequent runs go through the other process. Would i be able to add a property to the mesh connected to the bevel parameter and then use it as the bevel parameter default on the operator. $\endgroup$ Commented Oct 24, 2015 at 10:36
  • $\begingroup$ As in if there is no bevel modifier add one? $\endgroup$
    – batFINGER
    Commented Oct 24, 2015 at 11:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .