Skip to content
Snippets Groups Projects
object_boolean_tools.py 44.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    # <pep8 compliant>
    
    bl_info = {
        "name": "Bool Tool",
        "author": "Vitor Balbio, Mikhail Rachinskiy, TynkaTopi, Meta-Androcto",
    
        "blender": (2, 78, 0),
        "location": "View3D > Toolshelf",
    
        "description": "Bool Tools Hotkey: Ctrl Shift B",
        "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Object/BoolTool",
        "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
    
        "category": "Object",
    
    
    import bpy
    from bpy.app.handlers import persistent
    from bpy.types import (
    
            AddonPreferences,
    
            Menu,
            )
    from bpy.props import (
            BoolProperty,
            StringProperty,
            EnumProperty,
    
    
    
    # -------------------  Bool Tool FUNCTIONS------------------------------
    # Utils:
    
    # Hide boolean objects
    def update_BoolHide(self, context):
        ao = context.scene.objects.active
        objs = [i.object for i in ao.modifiers if i.type == 'BOOLEAN']
        hide_state = context.scene.BoolHide
    
        for o in objs:
            o.hide = hide_state
    
    
    
    # Object is a Canvas
    
    def isCanvas(_obj):
        try:
            if _obj["BoolToolRoot"]:
                return True
        except:
            return False
    
    
    # Object is a Brush Tool Bool
    def isBrush(_obj):
        try:
            if _obj["BoolToolBrush"]:
                return True
        except:
            return False
    
    
    # Object is a Poly Brush Tool Bool collection
    def isPolyBrush(_obj):
        try:
            if _obj["BoolToolPolyBrush"]:
                return True
        except:
            return False
    
    
    def BT_ObjectByName(obj):
        for ob in bpy.context.scene.objects:
            if isCanvas(ob) or isBrush(ob):
                if ob.name == obj:
                    return ob
    
    
    def FindCanvas(obj):
        for ob in bpy.context.scene.objects:
            if isCanvas(ob):
                for mod in ob.modifiers:
                    if ("BTool_" in mod.name):
                        if (obj.name in mod.name):
                            return ob
    
    
    def isFTransf():
        user_preferences = bpy.context.user_preferences
        addons = user_preferences.addons
        addon_prefs = addons[__name__].preferences
        if addon_prefs.fast_transform:
            return True
        else:
            return False
    
    
    """
    # EXPERIMENTAL FEATURES
    def isMakeVertexGroup():
        user_preferences = bpy.context.user_preferences
        addon_prefs = user_preferences.addons[__name__].preferences
        if addon_prefs.make_vertex_groups:
            return True
        else:
            return False
    
    def isMakeBoundary():
        user_preferences = bpy.context.user_preferences
        addon_prefs = user_preferences.addons[__name__].preferences
        if addon_prefs.make_boundary:
            return True
        else:
            return False
    """
    
    
    def ConvertToMesh(obj):
        act = bpy.context.scene.objects.active
        bpy.context.scene.objects.active = obj
        bpy.ops.object.convert(target="MESH")
        bpy.context.scene.objects.active = act
    
    
    # Do the Union, Difference and Intersection Operations with a Brush
    def Operation(context, _operation):
    
        useWire = bpy.context.user_preferences.addons[__name__].preferences.use_wire
    
        for selObj in bpy.context.selected_objects:
            if selObj != context.active_object and (selObj.type == "MESH" or selObj.type == "CURVE"):
                if selObj.type == "CURVE":
                    ConvertToMesh(selObj)
                actObj = context.active_object
                selObj.hide_render = True
                cyclesVis = selObj.cycles_visibility
                # for obj in bpy.context.scene.objects:
                #   if isCanvas(obj):
                #      for mod in obj.modifiers:
                #         if(mod.name == "BTool_" + selObj.name):
                #            obj.modifiers.remove(mod)
    
                if useWire:
                    selObj.draw_type = "WIRE"
                else:
                    selObj.draw_type = "BOUNDS"
    
                cyclesVis.camera = False
                cyclesVis.diffuse = False
                cyclesVis.glossy = False
                cyclesVis.shadow = False
                cyclesVis.transmission = False
                if _operation == "SLICE":
                    # copies dupli_group property(empty), but group property is empty (users_group = None)
                    clone = context.active_object.copy()
                    # clone.select=True
                    context.scene.objects.link(clone)
                    sliceMod = clone.modifiers.new("BTool_" + selObj.name, "BOOLEAN")  # add mod to clone obj
                    sliceMod.object = selObj
                    sliceMod.operation = "DIFFERENCE"
                    clone["BoolToolRoot"] = True
                newMod = actObj.modifiers.new("BTool_" + selObj.name, "BOOLEAN")
                newMod.object = selObj
                if _operation == "SLICE":
                    newMod.operation = "INTERSECT"
                else:
                    newMod.operation = _operation
    
                actObj["BoolToolRoot"] = True
                selObj["BoolToolBrush"] = _operation
                selObj["BoolTool_FTransform"] = "False"
    
    
    # Remove Obejcts form the BoolTool System
    def Remove(context, thisObj_name, Prop):
        # Find the Brush pointed in the Tree View and Restore it, active is the Canvas
        actObj = context.active_object
    
        # Restore the Brush
        def RemoveThis(_thisObj_name):
            for obj in bpy.context.scene.objects:
                # if it's the brush object
                if obj.name == _thisObj_name:
                    cyclesVis = obj.cycles_visibility
                    obj.draw_type = "TEXTURED"
                    del obj["BoolToolBrush"]
                    del obj["BoolTool_FTransform"]
                    cyclesVis.camera = True
                    cyclesVis.diffuse = True
                    cyclesVis.glossy = True
                    cyclesVis.shadow = True
                    cyclesVis.transmission = True
    
                    # Remove it from the Canvas
                    for mod in actObj.modifiers:
                        if ("BTool_" in mod.name):
                            if (_thisObj_name in mod.name):
                                actObj.modifiers.remove(mod)
    
        if Prop == "THIS":
            RemoveThis(thisObj_name)
    
        # If the remove was called from the Properties:
        else:
            # Remove the Brush Property
            if Prop == "BRUSH":
                Canvas = FindCanvas(actObj)
    
                if Canvas:
                    for mod in Canvas.modifiers:
                        if ("BTool_" in mod.name):
                            if (actObj.name in mod.name):
                                Canvas.modifiers.remove(mod)
                cyclesVis = actObj.cycles_visibility
                actObj.draw_type = "TEXTURED"
                del actObj["BoolToolBrush"]
                del actObj["BoolTool_FTransform"]
                cyclesVis.camera = True
                cyclesVis.diffuse = True
                cyclesVis.glossy = True
                cyclesVis.shadow = True
                cyclesVis.transmission = True
    
    
            if Prop == "CANVAS":
                for mod in actObj.modifiers:
                    if ("BTool_" in mod.name):
                        RemoveThis(mod.object.name)
    
    
    # Tooble the Enable the Brush Object Propertie
    def EnableBrush(context, objList, canvas):
        for obj in objList:
            for mod in canvas.modifiers:
                if ("BTool_" in mod.name and mod.object.name == obj):
    
                    if (mod.show_viewport):
                        mod.show_viewport = False
                        mod.show_render = False
                    else:
                        mod.show_viewport = True
                        mod.show_render = True
    
    
    # Find the Canvas and Enable this Brush
    def EnableThisBrush(context, set):
        canvas = None
        for obj in bpy.context.scene.objects:
            if obj != bpy.context.active_object:
                if isCanvas(obj):
                    for mod in obj.modifiers:
                        if ("BTool_" in mod.name):
                            if mod.object == bpy.context.active_object:
                                canvas = obj
    
        for mod in canvas.modifiers:
            if ("BTool_" in mod.name):
                if mod.object == bpy.context.active_object:
                    if set == "None":
                        if (mod.show_viewport):
                            mod.show_viewport = False
                            mod.show_render = False
                        else:
                            mod.show_viewport = True
                            mod.show_render = True
                    else:
                        if (set == "True"):
                            mod.show_viewport = True
                        else:
                            mod.show_viewport = False
                    return
    
    
    # Tooble the Fast Transform Propertie of the Active Brush
    def EnableFTransf(context):
        actObj = bpy.context.active_object
    
        if actObj["BoolTool_FTransform"] == "True":
            actObj["BoolTool_FTransform"] = "False"
        else:
            actObj["BoolTool_FTransform"] = "True"
        return
    
    
    # Apply All Brushes to the Canvas
    def ApplyAll(context, list):
        objDeleteList = []
        for selObj in list:
            if isCanvas(selObj) and selObj == context.active_object:
                for mod in selObj.modifiers:
                    if ("BTool_" in mod.name):
                        objDeleteList.append(mod.object)
                    try:
                        bpy.ops.object.modifier_apply(modifier=mod.name)
                    except:  # if fails the means it is multiuser data
                        context.active_object.data = context.active_object.data.copy()  # so just make data unique
                        bpy.ops.object.modifier_apply(modifier=mod.name)
    
                del selObj['BoolToolRoot']
    
    
        for obj in context.scene.objects:
            if isCanvas(obj):
                for mod in obj.modifiers:
                    if mod.type == 'BOOLEAN':
                        if mod.object in objDeleteList:  # do not delete brush that is used by another canvas
                            objDeleteList.remove(mod.object)  # remove it from deletion
    
        bpy.ops.object.select_all(action='DESELECT')
    
        for obj in objDeleteList:
            obj.select = True
        bpy.ops.object.delete()
    
    
    
    # Apply This Brush to the Canvas
    def ApplyThisBrush(context, brush):
        for obj in context.scene.objects:
            if isCanvas(obj):
                for mod in obj.modifiers:
                    if ("BTool_" + brush.name in mod.name):
                        """
                        # EXPERIMENTAL
                        if isMakeVertexGroup():
                            # Turn all faces of the Brush selected
                            bpy.context.scene.objects.active = brush
                            bpy.ops.object.mode_set(mode='EDIT')
                            bpy.ops.mesh.select_all(action='SELECT')
                            bpy.ops.object.mode_set(mode='OBJECT')
    
                            #Turn off al faces of the Canvas selected
                            bpy.context.scene.objects.active = canvas
                            bpy.ops.object.mode_set(mode='EDIT')
                            bpy.ops.mesh.select_all(action='DESELECT')
                            bpy.ops.object.mode_set(mode='OBJECT')
                        """
    
                        # Apply This Brush
                        context.scene.objects.active = obj
                        try:
                            bpy.ops.object.modifier_apply(modifier=mod.name)
                        except:  # if fails the means it is multiuser data
                            context.active_object.data = context.active_object.data.copy()  # so just make data unique
                            bpy.ops.object.modifier_apply(modifier=mod.name)
                        bpy.ops.object.select_all(action='TOGGLE')
                        bpy.ops.object.select_all(action='DESELECT')
    
                        """
                        # EXPERIMENTAL
                        if isMakeVertexGroup():
                            # Make Vertex Group
                            bpy.ops.object.mode_set(mode='EDIT')
                            bpy.ops.object.vertex_group_assign_new()
                            bpy.ops.mesh.select_all(action='DESELECT')
                            bpy.ops.object.mode_set(mode='OBJECT')
    
                            canvas.vertex_groups.active.name = "BTool_" + brush.name
                        """
    
    
        # Garbage Colletor
    
        brush.select = True
        # bpy.ops.object.delete()
    
    
    def GCollector(_obj):
        if isCanvas(_obj):
            BTRoot = False
            for mod in _obj.modifiers:
                if ("BTool_" in mod.name):
                    BTRoot = True
                    if mod.object is None:
                        _obj.modifiers.remove(mod)
            if not BTRoot:
                del _obj["BoolToolRoot"]
    
    
    # Handle the callbacks when modifing things in the scene
    @persistent
    def HandleScene(scene):
        if bpy.data.objects.is_updated:
            for ob in bpy.data.objects:
                if ob.is_updated:
                    GCollector(ob)
    
    
    # ------------------ Bool Tool OPERATORS-----------------------------------------------------
    
    class BTool_DrawPolyBrush(Operator):
        """Draw Polygonal Mask, can be applyied to Canvas > Brush or Directly. ESC to Exit"""
        bl_idname = "btool.draw_polybrush"
        bl_label = "Draw Poly Brush"
    
        count = 0
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def modal(self, context, event):
            self.count += 1
            actObj = bpy.context.active_object
            if self.count == 1:
                actObj.select = True
                bpy.ops.gpencil.draw('INVOKE_DEFAULT', mode="DRAW_POLY")
    
            if event.type in {'RET', 'NUMPAD_ENTER'}:
    
                bpy.ops.gpencil.convert(type='POLY')
                for obj in context.selected_objects:
                    if obj.type == "CURVE":
                        obj.name = "PolyDraw"
                        bpy.context.scene.objects.active = obj
                        bpy.ops.object.select_all(action='DESELECT')
                        obj.select = True
                        bpy.ops.object.convert(target="MESH")
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.select_all(action='SELECT')
                        bpy.ops.mesh.edge_face_add()
                        bpy.ops.mesh.flip_normals()
                        bpy.ops.object.mode_set(mode='OBJECT')
                        bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
                        bpy.ops.object.modifier_add(type="SOLIDIFY")
                        for mod in obj.modifiers:
                            if mod.name == "Solidify":
                                mod.name = "BTool_PolyBrush"
                                mod.thickness = 1
                                mod.offset = 0
                        obj["BoolToolPolyBrush"] = True
    
                        bpy.ops.object.select_all(action='DESELECT')
                        bpy.context.scene.objects.active = actObj
                        bpy.context.scene.update()
                        actObj.select = True
                        obj.select = True
                        # try:
                        bpy.context.scene.grease_pencil.clear()
                        bpy.ops.gpencil.data_unlink()
    
                return {'FINISHED'}
    
            if event.type in {'ESC'}:
                bpy.ops.ed.undo()  # remove o Grease Pencil
                return {'CANCELLED'}
    
            return {'RUNNING_MODAL'}
    
        def invoke(self, context, event):
            if context.object:
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
            else:
                self.report({'WARNING'}, "No active object, could not finish")
                return {'CANCELLED'}
    
    
    # Fast Transform
    class BTool_FastTransform(Operator):
        """Enable Fast Transform"""
        bl_idname = "btool.fast_transform"
        bl_label = "Fast Transform"
    
    
        operator = StringProperty("")
    
    
        count = 0
    
        def modal(self, context, event):
            self.count += 1
            actObj = bpy.context.active_object
            useWire = bpy.context.user_preferences.addons[__name__].preferences.use_wire
            if self.count == 1:
    
                if isBrush(actObj) and actObj["BoolTool_FTransform"] == "True":
                    EnableThisBrush(bpy.context, "False")
                    if useWire:
                        actObj.draw_type = "WIRE"
                    else:
                        actObj.draw_type = "BOUNDS"
    
                if self.operator == "Translate":
                    bpy.ops.transform.translate('INVOKE_DEFAULT')
                if self.operator == "Rotate":
                    bpy.ops.transform.rotate('INVOKE_DEFAULT')
                if self.operator == "Scale":
                    bpy.ops.transform.resize('INVOKE_DEFAULT')
    
            if event.type == 'LEFTMOUSE':
                if isBrush(actObj):
                    EnableThisBrush(bpy.context, "True")
                    actObj.draw_type = "WIRE"
                return {'FINISHED'}
    
            if event.type in {'RIGHTMOUSE', 'ESC'}:
                if isBrush(actObj):
                    EnableThisBrush(bpy.context, "True")
                    actObj.draw_type = "WIRE"
                return {'CANCELLED'}
    
            return {'RUNNING_MODAL'}
    
        def invoke(self, context, event):
            if context.object:
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
            else:
                self.report({'WARNING'}, "No active object, could not finish")
                return {'CANCELLED'}
    
    
    # -------------------  Bool Tool OPERATOR CLASSES --------------------------------------------------------
    
    # Brush Operators --------------------------------------------
    
    # Boolean Union Operator
    class BTool_Union(Operator):
        """This operator add a union brush to a canvas"""
        bl_idname = "btool.boolean_union"
        bl_label = "Brush Union"
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            Operation(context, "UNION")
            return {'FINISHED'}
    
    
    # Boolean Intersection Operator
    class BTool_Inters(Operator):
        """This operator add a intersect brush to a canvas"""
        bl_idname = "btool.boolean_inters"
        bl_label = "Brush Intersection"
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            Operation(context, "INTERSECT")
            return {'FINISHED'}
    
    
    # Boolean Difference Operator
    class BTool_Diff(Operator):
        """This operator add a difference brush to a canvas"""
        bl_idname = "btool.boolean_diff"
        bl_label = "Brush Difference"
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            Operation(context, "DIFFERENCE")
            return {'FINISHED'}
    
    
    
    # Boolean Slices Operator
    
    class BTool_Slice(Operator):
        """This operator add a intersect brush to a canvas"""
        bl_idname = "btool.boolean_slice"
        bl_label = "Brush Slice"
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            Operation(context, "SLICE")
            return {'FINISHED'}
    
    
    
    # Auto Boolean operators (maintainer Mikhail Rachinskiy) -------------------------------
    
    class AutoBoolean:
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        solver = EnumProperty(
    
                name="Boolean Solver",
                items=(('BMESH', "BMesh", "BMesh solver is faster, but less stable "
                                          "and cannot handle coplanar geometry"),
                       ('CARVE', "Carve", "Carve solver is slower, but more stable "
                                          "and can handle simple cases of coplanar geometry")),
                description="Specify solver for boolean operation",
                options={'SKIP_SAVE'},
    
        def __init__(self):
            self.context = bpy.context
            self.solver = self.context.user_preferences.addons[__name__].preferences.solver
    
        def objects_prepare(self):
    
            for ob in self.context.selected_objects:
                if ob.type != 'MESH':
                    ob.select = False
            bpy.ops.object.convert(target='MESH')
    
        def mesh_selection(self, ob, select_action):
            scene = self.context.scene
            obj = self.context.active_object
    
            scene.objects.active = ob
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.select_all(action=select_action)
            bpy.ops.object.mode_set(mode='OBJECT')
            scene.objects.active = obj
    
        def boolean_operation(self):
    
            obj = self.context.active_object
    
            obs = self.context.selected_objects
    
            self.mesh_selection(obj, 'DESELECT')
    
                self.mesh_selection(ob, 'SELECT')
    
                self.boolean_mod(obj, ob, self.mode)
    
        def boolean_mod(self, obj, ob, mode, ob_delete=True):
            md = obj.modifiers.new("Auto Boolean", 'BOOLEAN')
    
            md.show_viewport = False
            md.operation = mode
    
            md.solver = self.solver
    
            bpy.ops.object.modifier_apply(modifier="Auto Boolean")
            if not ob_delete:
    
            self.context.scene.objects.unlink(ob)
    
            bpy.data.objects.remove(ob)
    
    class Auto_Union(AutoBoolean, Operator):
    
        """Combine selected objects"""
    
        bl_idname = "btool.auto_union"
    
        def execute(self, context):
    
            self.objects_prepare()
    
            self.boolean_operation()
    
    class Auto_Difference(AutoBoolean, Operator):
    
        """Subtract selected objects from active object"""
    
        bl_idname = "btool.auto_difference"
    
        def execute(self, context):
    
            self.objects_prepare()
    
            self.boolean_operation()
    
    class Auto_Intersect(AutoBoolean, Operator):
    
        """Keep only intersecting geometry"""
    
        bl_idname = "btool.auto_intersect"
    
        def execute(self, context):
    
            self.objects_prepare()
    
            self.boolean_operation()
    
    class Auto_Slice(AutoBoolean, Operator):
    
        """Slice active object along the selected object (can handle only two objects at a time)"""
    
        bl_idname = "btool.auto_slice"
    
        bl_label = "Slice"
    
        def execute(self, context):
    
            self.objects_prepare()
    
    
            scene = context.scene
    
            obj = context.active_object
            obj.select = False
            ob = context.selected_objects[0]
    
            self.mesh_selection(obj, 'DESELECT')
            self.mesh_selection(ob, 'SELECT')
    
            obj_copy = obj.copy()
            obj_copy.data = obj.data.copy()
            scene.objects.link(obj_copy)
    
    
            self.boolean_mod(obj, ob, 'DIFFERENCE', ob_delete=False)
    
            scene.objects.active = obj_copy
    
            self.boolean_mod(obj_copy, ob, 'INTERSECT')
    
    class Auto_Subtract(AutoBoolean, Operator):
    
        """Subtract selected object from active object, """ \
    
        """subtracted object not removed (can handle only two objects at a time)"""
    
        bl_idname = "btool.auto_subtract"
    
        bl_label = "Subtract"
    
        def execute(self, context):
    
            self.objects_prepare()
    
    
            obj = context.active_object
            obj.select = False
            ob = context.selected_objects[0]
    
            self.mesh_selection(obj, 'DESELECT')
            self.mesh_selection(ob, 'SELECT')
    
            self.boolean_mod(obj, ob, 'DIFFERENCE', ob_delete=False)
    
    # Utils Class ---------------------------------------------------------------
    
    # Find the Brush Selected in Three View
    class BTool_FindBrush(Operator):
        """Find the this brush"""
        bl_idname = "btool.find_brush"
        bl_label = ""
    
        obj = StringProperty("")
    
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            for ob in bpy.context.scene.objects:
                if (ob.name == self.obj):
                    bpy.ops.object.select_all(action='TOGGLE')
                    bpy.ops.object.select_all(action='DESELECT')
                    bpy.context.scene.objects.active = ob
                    ob.select = True
            return {'FINISHED'}
    
    
    # Mode The Modifier in The Stack Up or Down
    class BTool_MoveStack(Operator):
        """Move this Brush Up/Down in the Stack"""
        bl_idname = "btool.move_stack"
        bl_label = ""
    
        modif = StringProperty("")
        direction = StringProperty("")
    
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            if (self.direction == "UP"):
                bpy.ops.object.modifier_move_up(modifier=self.modif)
            if (self.direction == "DOWN"):
                bpy.ops.object.modifier_move_down(modifier=self.modif)
            return {'FINISHED'}
    
    
    # Enable or Disable a Brush in th Three View
    class BTool_EnableBrush(Operator):
        """Removes all BoolTool config assigned to it"""
        bl_idname = "btool.enable_brush"
        bl_label = ""
    
    
        thisObj = StringProperty("")
    
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            # in this case is just one object but the function accept more than one at once
            EnableBrush(context, [self.thisObj], context.active_object)
            return {'FINISHED'}
    
    
    # Enable or Disabel a Brush Directly
    class BTool_EnableThisBrush(Operator):
        """ Toggles this brush"""
        bl_idname = "btool.enable_this_brush"
        bl_label = ""
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            EnableThisBrush(context, "None")
            return {'FINISHED'}
    
    
    # Enable or Disabel a Brush Directly
    class BTool_EnableFTransform(Operator):
        """Use Fast Transformations to improve speed"""
        bl_idname = "btool.enable_ftransf"
        bl_label = ""
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            EnableFTransf(context)
            return {'FINISHED'}
    
    
    # Other Operations -------------------------------------------------------
    
    # Remove a Brush or a Canvas
    class BTool_Remove(Operator):
        """Removes all BoolTool config assigned to it"""
        bl_idname = "btool.remove"
        bl_label = ""
        bl_options = {'UNDO'}
    
        thisObj = StringProperty("")
        Prop = StringProperty("")
    
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            Remove(context, self.thisObj, self.Prop)
            return {'FINISHED'}
    
    
    # Apply All to Canvas
    class BTool_AllBrushToMesh(Operator):
        """Apply all brushes of this canvas"""
        bl_idname = "btool.to_mesh"
        bl_label = "Apply All Canvas"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def execute(self, context):
            list = bpy.context.selected_objects
            ApplyAll(context, list)
            return {'FINISHED'}
    
    
    # Apply This Brush to the Canvas
    class BTool_BrushToMesh(Operator):
        """Apply this brush to the canvas"""
        bl_idname = "btool.brush_to_mesh"
        bl_label = "Apply this Brush to Canvas"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(cls, context):
    
            if isBrush(context.active_object):
                return True
            else:
                return False
    
        def execute(self, context):
            ApplyThisBrush(context, bpy.context.active_object)
            return {'FINISHED'}
    
    
    # TODO
    # Apply This Brush To Mesh
    
    
    # ------------------- MENU CLASSES ------------------------------
    
    class BoolTool_Menu(Menu):
    
        bl_label = "BoolTool Operators"
        bl_idname = "OBJECT_MT_BoolTool_Menu"
    
        def draw(self, context):
            layout = self.layout
    
            layout.label("Auto Boolean:")
    
            layout.operator(Auto_Difference.bl_idname, icon="ROTACTIVE")
            layout.operator(Auto_Union.bl_idname, icon="ROTATECOLLECTION")
            layout.operator(Auto_Intersect.bl_idname, icon="ROTATECENTER")
            layout.operator(Auto_Slice.bl_idname, icon="ROTATECENTER")
            layout.operator(Auto_Subtract.bl_idname, icon="ROTACTIVE")
    
            layout.separator()
    
            layout.label("Brush Boolean:")
            layout.operator(BTool_Diff.bl_idname, icon="ROTACTIVE")
            layout.operator(BTool_Union.bl_idname, icon="ROTATECOLLECTION")
            layout.operator(BTool_Inters.bl_idname, icon="ROTATECENTER")
            layout.operator(BTool_Slice.bl_idname, icon="ROTATECENTER")
            layout.separator()
    
            if (isCanvas(context.active_object)):
                layout.separator()
                layout.operator(BTool_AllBrushToMesh.bl_idname, icon="MOD_LATTICE", text="Apply All")
                Rem = layout.operator(BTool_Remove.bl_idname, icon="CANCEL", text="Remove All")
                Rem.thisObj = ""
                Rem.Prop = "CANVAS"
    
            if (isBrush(context.active_object)):
                layout.separator()
                layout.operator(BTool_BrushToMesh.bl_idname, icon="MOD_LATTICE", text="Apply Brush")
                Rem = layout.operator(BTool_Remove.bl_idname, icon="CANCEL", text="Remove Brush")
                Rem.thisObj = ""
                Rem.Prop = "BRUSH"
    
    
    def VIEW3D_BoolTool_Menu(self, context):
        self.layout.menu(BoolTool_Menu.bl_idname)
    
    
    
    # ---------------- Toolshelf: Tools ---------------------
    
    
    class BoolTool_Tools(Panel):
    
        bl_category = "Bool Tool"
    
        bl_label = "Tools"
        bl_idname = "BoolTool_Tools"
        bl_space_type = "VIEW_3D"
        bl_region_type = "TOOLS"
        bl_context = "objectmode"
    
        def draw(self, context):
            layout = self.layout
    
    
            col = layout.column(align=True)
    
            col.label("Auto Boolean:", icon="MODIFIER")
            col.separator()
    
            col.operator(Auto_Difference.bl_idname, icon="ROTACTIVE")
            col.operator(Auto_Union.bl_idname, icon="ROTATECOLLECTION")
            col.operator(Auto_Intersect.bl_idname, icon="ROTATECENTER")
    
    
            col = layout.column(align=True)
            col.enabled = len(context.selected_objects) == 2
    
            col.operator(Auto_Slice.bl_idname, icon="ROTATECENTER")
            col.operator(Auto_Subtract.bl_idname, icon="ROTACTIVE")
    
    
            col = layout.column(align=True)
    
            col.label("Brush Boolean:", icon="MODIFIER")
            col.separator()
            col.operator(BTool_Diff.bl_idname, text="Difference", icon="ROTACTIVE")
            col.operator(BTool_Union.bl_idname, text="Union", icon="ROTATECOLLECTION")
            col.operator(BTool_Inters.bl_idname, text="Intersect", icon="ROTATECENTER")
            col.operator(BTool_Slice.bl_idname, text="Slice", icon="ROTATECENTER")
    
            layout.separator()
    
    
            col = layout.column(align=True)
    
            col.label("Draw:", icon="MESH_CUBE")
            col.separator()
            col.operator(BTool_DrawPolyBrush.bl_idname, icon="LINE_DATA")
    
    
    
    # ---------- Toolshelf: Properties --------------------------------------------------------
    
    
    class BoolTool_Config(Panel):
    
        bl_category = "Bool Tool"
    
        bl_label = "Properties"
        bl_idname = "BoolTool_BConfig"
        bl_space_type = "VIEW_3D"
        bl_region_type = "TOOLS"
        bl_context = "objectmode"
    
        @classmethod
        def poll(cls, context):
    
            result = False
            actObj = bpy.context.active_object
            if (isCanvas(actObj) or isBrush(actObj) or isPolyBrush(actObj)):
                result = True
            return result
    
        def draw(self, context):
            actObj = bpy.context.active_object
            icon = ""
    
            layout = self.layout
            row = layout.row(True)
    
            # CANVAS ---------------------------------------------------
            if isCanvas(actObj):
                row.label("CANVAS", icon="MESH_GRID")
                row = layout.row()
                row.prop(context.scene, 'BoolHide', text="Hide Bool objects")